Class: Nfe::Resources::ProductInvoicesRtc

Inherits:
AbstractResource show all
Defined in:
lib/nfe/resources/product_invoices_rtc.rb,
sig/nfe/resources/product_invoices_rtc.rbs

Overview

RTC (Reforma Tributária do Consumo) product invoices (NF-e mod 55 / NFC-e mod 65) resource for the :cte host family (+https://api.nfse.io/v2/...+). Full lifecycle: issue, list, retrieve, cancel, correction letters (CC-e), disablement (inutilização) and file downloads, hydrating the RTC generated DTOs in Generated::ProductInvoiceRtcV1.

This resource shares the SAME endpoints as the classic ProductInvoices. There is NO discriminator header or query param: the RTC tax layout is selected by the API from the SHAPE of the payload — specifically the presence of the item-level IBSCBS group (+items.tax.IBSCBS+). NF-e (mod 55) vs NFC-e (mod 65) is likewise inferred from the payload shape. Use this resource (vs the classic one) when you want the RTC response DTOs hydrated.

Emission is asynchronous (HTTP 202, queued; completion via webhook): #create/#create_with_state_tax return either a ProductInvoiceRtcPending or a ProductInvoiceRtcIssued. There is no +create_and_wait+/+create_batch+ — poll manually with #retrieve + FlowStatus.terminal?.

NOTE: as with the classic resource, the download methods return a NfeFileResource (a URI to the file), NOT raw bytes — the API responds with a JSON { uri } envelope.

Instance Attribute Summary

Attributes inherited from AbstractResource

#client

Instance Method Summary collapse

Methods inherited from AbstractResource

#build_list_page, #build_multipart_body, #cursor_list_page, #delete, #dig_key, #download, #full_path, #get, #handle_async_response, #hydrate, #hydrate_list, #initialize, #multipart_part, #page_list_page, #parse_json, #post, #put, #unwrap, #upload_multipart

Constructor Details

This class inherits a constructor from Nfe::Resources::AbstractResource

Instance Method Details

#api_familySymbol

Returns:

  • (Symbol)


39
40
41
# File 'lib/nfe/resources/product_invoices_rtc.rb', line 39

def api_family
  :cte
end

#api_versionString

The :cte host serves the v2 API; paths embed /v2 explicitly, so no version segment is auto-prefixed.

Returns:

  • (String)


45
46
47
# File 'lib/nfe/resources/product_invoices_rtc.rb', line 45

def api_version
  ""
end

#base_path(company_id) ⇒ String

Parameters:

  • company_id (String)

Returns:

  • (String)


261
262
263
# File 'lib/nfe/resources/product_invoices_rtc.rb', line 261

def base_path(company_id)
  "/v2/companies/#{company_id}/productinvoices"
end

#build_pending(response) ⇒ Nfe::Resources::ProductInvoiceRtcPending

Build a Nfe::Resources::ProductInvoiceRtcPending from a 202 response, or raise when the Location header is missing/unparsable.



312
313
314
315
316
317
318
319
320
321
322
# File 'lib/nfe/resources/product_invoices_rtc.rb', line 312

def build_pending(response)
  location = response.location
  invoice_id = extract_invoice_id(location)
  if location.nil? || location.empty? || invoice_id.nil?
    raise Nfe::InvoiceProcessingError.new(
      "Resposta 202 sem Location utilizável: não é possível identificar a NF-e em processamento.",
      status_code: response.status, response_headers: response.headers
    )
  end
  ProductInvoiceRtcPending.new(invoice_id: invoice_id, location: location)
end

#cancel(company_id:, invoice_id:, reason: nil) ⇒ Nfe::Generated::ProductInvoiceRtcV1::RequestCancellationResource?

Cancel an RTC product invoice (async); reason forwarded as a query param. Hydrates Generated::ProductInvoiceRtcV1::RequestCancellationResource.

Parameters:

  • company_id (String)
  • invoice_id (String)
  • reason (String, nil) (defaults to: nil)
  • company_id: (String)
  • invoice_id: (String)
  • reason: (String, nil) (defaults to: nil)

Returns:



139
140
141
142
143
144
# File 'lib/nfe/resources/product_invoices_rtc.rb', line 139

def cancel(company_id:, invoice_id:, reason: nil)
  cid = Nfe::IdValidator.company_id(company_id)
  iid = Nfe::IdValidator.invoice_id(invoice_id)
  response = delete("#{base_path(cid)}/#{iid}", query: { reason: reason }.compact)
  hydrate(Nfe::Generated::ProductInvoiceRtcV1::RequestCancellationResource, parse_json(response.body))
end

#create(company_id:, data:, idempotency_key: nil, request_options: nil) ⇒ ProductInvoiceRtcPending, ProductInvoiceRtcIssued

Issue an RTC product invoice (NF-e/NFC-e). Returns a discriminated result: a Nfe::Resources::ProductInvoiceRtcPending on HTTP 202 or a Nfe::Resources::ProductInvoiceRtcIssued (hydrating Generated::ProductInvoiceRtcV1::InvoiceResource) on 201.

data is a Hash with camelCase keys. The generated request DTO Generated::ProductInvoiceRtcV1::ProductInvoiceRequest documents the expected payload SHAPE; it is NOT accepted as input (the generated DTOs deserialize only and have no camelCase serialization path). The RTC layout is selected by the presence of the item-level IBSCBS group (+items.tax.IBSCBS+); NF-e (mod 55) vs NFC-e (mod 65) follows the payload shape. Same endpoint as the classic resource.

Parameters:

  • company_id (String)
  • data (Hash)

    invoice payload (camelCase keys per the API).

  • idempotency_key (String, nil) (defaults to: nil)

    sent as the Idempotency-Key header.

  • request_options (Nfe::RequestOptions, nil) (defaults to: nil)

    per-call overrides.

  • company_id: (String)
  • data: (Hash[untyped, untyped])
  • idempotency_key: (String, nil) (defaults to: nil)
  • request_options: (Nfe::RequestOptions, nil) (defaults to: nil)

Returns:



69
70
71
72
73
74
75
# File 'lib/nfe/resources/product_invoices_rtc.rb', line 69

def create(company_id:, data:, idempotency_key: nil, request_options: nil)
  cid = Nfe::IdValidator.company_id(company_id)
  response = post(base_path(cid), body: json_body(data), headers: json_headers,
                                  idempotency_key: idempotency_key,
                                  request_options: request_options)
  discriminate(response)
end

#create_with_state_tax(company_id:, state_tax_id:, data:, idempotency_key: nil, request_options: nil) ⇒ ProductInvoiceRtcPending, ProductInvoiceRtcIssued

Issue an RTC product invoice scoped to a state tax registration.

data is a Hash with camelCase keys (see #create for the payload shape and RTC-layout selection notes).

Parameters:

  • company_id (String)
  • state_tax_id (String)
  • data (Hash)
  • idempotency_key (String, nil) (defaults to: nil)
  • request_options (Nfe::RequestOptions, nil) (defaults to: nil)
  • company_id: (String)
  • state_tax_id: (String)
  • data: (Hash[untyped, untyped])
  • idempotency_key: (String, nil) (defaults to: nil)
  • request_options: (Nfe::RequestOptions, nil) (defaults to: nil)

Returns:



88
89
90
91
92
93
94
95
# File 'lib/nfe/resources/product_invoices_rtc.rb', line 88

def create_with_state_tax(company_id:, state_tax_id:, data:, idempotency_key: nil, request_options: nil)
  cid = Nfe::IdValidator.company_id(company_id)
  sid = Nfe::IdValidator.state_tax_id(state_tax_id)
  response = post("/v2/companies/#{cid}/statetaxes/#{sid}/productinvoices",
                  body: json_body(data), headers: json_headers,
                  idempotency_key: idempotency_key, request_options: request_options)
  discriminate(response)
end

#disable(company_id:, invoice_id:, reason: nil) ⇒ Nfe::Generated::ProductInvoiceRtcV1::DisablementResource?

Disable (inutilizar) a single invoice (async). reason optional; forwarded as a query param. Hydrates Generated::ProductInvoiceRtcV1::DisablementResource.

Parameters:

  • company_id (String)
  • invoice_id (String)
  • reason (String, nil) (defaults to: nil)
  • company_id: (String)
  • invoice_id: (String)
  • reason: (String, nil) (defaults to: nil)

Returns:



240
241
242
243
244
245
# File 'lib/nfe/resources/product_invoices_rtc.rb', line 240

def disable(company_id:, invoice_id:, reason: nil)
  cid = Nfe::IdValidator.company_id(company_id)
  iid = Nfe::IdValidator.invoice_id(invoice_id)
  response = post("#{base_path(cid)}/#{iid}/disablement", query: { reason: reason }.compact)
  hydrate(Nfe::Generated::ProductInvoiceRtcV1::DisablementResource, parse_json(response.body))
end

#disable_range(company_id:, data:) ⇒ Nfe::Generated::ProductInvoiceRtcV1::DisablementResource?

Disable a range of invoice numbers (single number = same begin/last). data is a Hash with camelCase keys.

Parameters:

  • company_id (String)
  • data (Hash)

    { environment, serie, state, beginNumber, lastNumber, reason? }.

  • company_id: (String)
  • data: (Hash[untyped, untyped])

Returns:



253
254
255
256
257
# File 'lib/nfe/resources/product_invoices_rtc.rb', line 253

def disable_range(company_id:, data:)
  cid = Nfe::IdValidator.company_id(company_id)
  response = post("#{base_path(cid)}/disablement", body: json_body(data), headers: json_headers)
  hydrate(Nfe::Generated::ProductInvoiceRtcV1::DisablementResource, parse_json(response.body))
end

#discriminate(response) ⇒ Nfe::Resources::ProductInvoiceRtcPending, Nfe::Resources::ProductInvoiceRtcIssued

Interpret the emission response into the discriminated value object.



302
303
304
305
306
307
308
# File 'lib/nfe/resources/product_invoices_rtc.rb', line 302

def discriminate(response)
  return build_pending(response) if response.status == 202

  ProductInvoiceRtcIssued.new(
    resource: hydrate(Nfe::Generated::ProductInvoiceRtcV1::InvoiceResource, parse_json(response.body))
  )
end

#download_correction_letter_pdf(company_id:, invoice_id:) ⇒ Nfe::NfeFileResource

CC-e DANFE PDF file resource (URI, not bytes).

Parameters:

  • company_id: (String)
  • invoice_id: (String)

Returns:



221
222
223
# File 'lib/nfe/resources/product_invoices_rtc.rb', line 221

def download_correction_letter_pdf(company_id:, invoice_id:)
  file_resource(company_id: company_id, invoice_id: invoice_id, segment: "correctionletter/pdf")
end

#download_correction_letter_xml(company_id:, invoice_id:) ⇒ Nfe::NfeFileResource

CC-e XML file resource (URI, not bytes).

Parameters:

  • company_id: (String)
  • invoice_id: (String)

Returns:



228
229
230
# File 'lib/nfe/resources/product_invoices_rtc.rb', line 228

def download_correction_letter_xml(company_id:, invoice_id:)
  file_resource(company_id: company_id, invoice_id: invoice_id, segment: "correctionletter/xml")
end

#download_epec_xml(company_id:, invoice_id:) ⇒ Nfe::NfeFileResource

Contingency authorization (EPEC) XML file resource (URI, not bytes).

Parameters:

  • company_id: (String)
  • invoice_id: (String)

Returns:



197
198
199
# File 'lib/nfe/resources/product_invoices_rtc.rb', line 197

def download_epec_xml(company_id:, invoice_id:)
  file_resource(company_id: company_id, invoice_id: invoice_id, segment: "xml-epec")
end

#download_pdf(company_id:, invoice_id:, force: false) ⇒ Nfe::NfeFileResource

DANFE PDF file resource (URI, not bytes). force regenerates the PDF.

Parameters:

  • company_id: (String)
  • invoice_id: (String)
  • force: (Boolean) (defaults to: false)

Returns:



175
176
177
178
# File 'lib/nfe/resources/product_invoices_rtc.rb', line 175

def download_pdf(company_id:, invoice_id:, force: false)
  file_resource(company_id: company_id, invoice_id: invoice_id,
                segment: "pdf", query: { force: force }.compact)
end

#download_rejection_xml(company_id:, invoice_id:) ⇒ Nfe::NfeFileResource

Rejection XML file resource (URI, not bytes).

Parameters:

  • company_id: (String)
  • invoice_id: (String)

Returns:



190
191
192
# File 'lib/nfe/resources/product_invoices_rtc.rb', line 190

def download_rejection_xml(company_id:, invoice_id:)
  file_resource(company_id: company_id, invoice_id: invoice_id, segment: "xml-rejection")
end

#download_xml(company_id:, invoice_id:) ⇒ Nfe::NfeFileResource

Authorized NF-e XML file resource (URI, not bytes).

Parameters:

  • company_id: (String)
  • invoice_id: (String)

Returns:



183
184
185
# File 'lib/nfe/resources/product_invoices_rtc.rb', line 183

def download_xml(company_id:, invoice_id:)
  file_resource(company_id: company_id, invoice_id: invoice_id, segment: "xml")
end

#extract_invoice_id(location) ⇒ String?

Parameters:

  • location (String, nil)

Returns:

  • (String, nil)


324
325
326
327
328
329
# File 'lib/nfe/resources/product_invoices_rtc.rb', line 324

def extract_invoice_id(location)
  return nil if location.nil?

  match = location.match(%r{productinvoices/([a-z0-9-]+)}i)
  match ? match[1] : nil
end

#file_resource(company_id:, invoice_id:, segment:, query: {}) ⇒ Nfe::NfeFileResource?

Parameters:

  • company_id: (String)
  • invoice_id: (String)
  • segment: (String)
  • query: (Hash[untyped, untyped]) (defaults to: {})

Returns:



294
295
296
297
298
299
# File 'lib/nfe/resources/product_invoices_rtc.rb', line 294

def file_resource(company_id:, invoice_id:, segment:, query: {})
  cid = Nfe::IdValidator.company_id(company_id)
  iid = Nfe::IdValidator.invoice_id(invoice_id)
  response = get("#{base_path(cid)}/#{iid}/#{segment}", query: query)
  hydrate(Nfe::NfeFileResource, parse_json(response.body))
end

#json_body(data) ⇒ String

Parameters:

  • data (Object)

Returns:

  • (String)


269
270
271
# File 'lib/nfe/resources/product_invoices_rtc.rb', line 269

def json_body(data)
  JSON.generate(data)
end

#json_headersHash[String, String]

Returns:

  • (Hash[String, String])


265
266
267
# File 'lib/nfe/resources/product_invoices_rtc.rb', line 265

def json_headers
  { "Content-Type" => "application/json" }
end

#list(company_id:, environment:, starting_after: nil, ending_before: nil, limit: nil, q: nil) ⇒ Nfe::ListResponse

List RTC product invoices (cursor-style). environment is REQUIRED.

Parameters:

  • company_id (String)
  • environment (String)

    "Production" or "Test" (required).

  • starting_after (String, nil) (defaults to: nil)
  • ending_before (String, nil) (defaults to: nil)
  • limit (Integer, nil) (defaults to: nil)
  • q (String, nil) (defaults to: nil)
  • company_id: (String)
  • environment: (String)
  • starting_after: (String, nil) (defaults to: nil)
  • ending_before: (String, nil) (defaults to: nil)
  • limit: (Integer, nil) (defaults to: nil)
  • q: (String, nil) (defaults to: nil)

Returns:

Raises:



108
109
110
111
112
113
114
115
116
# File 'lib/nfe/resources/product_invoices_rtc.rb', line 108

def list(company_id:, environment:, starting_after: nil, ending_before: nil, limit: nil, q: nil)
  cid = Nfe::IdValidator.company_id(company_id)
  require_environment(environment)
  query = list_query(starting_after: starting_after, ending_before: ending_before,
                     limit: limit, q: q).merge(environment: environment)
  response = get(base_path(cid), query: query)
  hydrate_list(Nfe::Generated::ProductInvoiceRtcV1::InvoiceResource,
               parse_json(response.body), wrapper_key: "productInvoices")
end

#list_events(company_id:, invoice_id:) ⇒ Nfe::Generated::ProductInvoiceRtcV1::InvoiceEventsResource?

List fiscal events of an invoice. Hydrates Generated::ProductInvoiceRtcV1::InvoiceEventsResource.

Parameters:

  • company_id (String)
  • invoice_id (String)
  • company_id: (String)
  • invoice_id: (String)

Returns:



165
166
167
168
169
170
# File 'lib/nfe/resources/product_invoices_rtc.rb', line 165

def list_events(company_id:, invoice_id:)
  cid = Nfe::IdValidator.company_id(company_id)
  iid = Nfe::IdValidator.invoice_id(invoice_id)
  response = get("#{base_path(cid)}/#{iid}/events")
  hydrate(Nfe::Generated::ProductInvoiceRtcV1::InvoiceEventsResource, parse_json(response.body))
end

#list_items(company_id:, invoice_id:) ⇒ Nfe::Generated::ProductInvoiceRtcV1::InvoiceItemsResource?

List items of an invoice. Hydrates Generated::ProductInvoiceRtcV1::InvoiceItemsResource.

Parameters:

  • company_id (String)
  • invoice_id (String)
  • company_id: (String)
  • invoice_id: (String)

Returns:



152
153
154
155
156
157
# File 'lib/nfe/resources/product_invoices_rtc.rb', line 152

def list_items(company_id:, invoice_id:)
  cid = Nfe::IdValidator.company_id(company_id)
  iid = Nfe::IdValidator.invoice_id(invoice_id)
  response = get("#{base_path(cid)}/#{iid}/items")
  hydrate(Nfe::Generated::ProductInvoiceRtcV1::InvoiceItemsResource, parse_json(response.body))
end

#list_query(starting_after:, ending_before:, limit:, q:) ⇒ Hash[Symbol, untyped]

Parameters:

  • starting_after: (Object)
  • ending_before: (Object)
  • limit: (Object)
  • q: (Object)

Returns:

  • (Hash[Symbol, untyped])


279
280
281
282
283
284
# File 'lib/nfe/resources/product_invoices_rtc.rb', line 279

def list_query(starting_after:, ending_before:, limit:, q:)
  {
    startingAfter: starting_after, endingBefore: ending_before,
    limit: limit, q: q
  }.compact
end

#require_environment(environment) ⇒ void

This method returns an undefined value.

Parameters:

  • environment (Object)

Raises:



273
274
275
276
277
# File 'lib/nfe/resources/product_invoices_rtc.rb', line 273

def require_environment(environment)
  return unless environment.nil? || environment.to_s.strip.empty?

  raise Nfe::InvalidRequestError, "environment é obrigatório (Production ou Test)"
end

#retrieve(company_id:, invoice_id:) ⇒ Nfe::Generated::ProductInvoiceRtcV1::InvoiceResource?

Retrieve an RTC product invoice by id.

Parameters:

  • company_id (String)
  • invoice_id (String)
  • company_id: (String)
  • invoice_id: (String)

Returns:

Raises:



124
125
126
127
128
129
# File 'lib/nfe/resources/product_invoices_rtc.rb', line 124

def retrieve(company_id:, invoice_id:)
  cid = Nfe::IdValidator.company_id(company_id)
  iid = Nfe::IdValidator.invoice_id(invoice_id)
  response = get("#{base_path(cid)}/#{iid}")
  hydrate(Nfe::Generated::ProductInvoiceRtcV1::InvoiceResource, parse_json(response.body))
end

#send_correction_letter(company_id:, invoice_id:, reason:) ⇒ Nfe::Generated::ProductInvoiceRtcV1::RequestCancellationResource?

Send a correction letter (CC-e). reason must be 15..1000 chars; the length is validated client-side before any HTTP request.

Parameters:

  • company_id (String)
  • invoice_id (String)
  • reason (String)
  • company_id: (String)
  • invoice_id: (String)
  • reason: (String)

Returns:

Raises:



209
210
211
212
213
214
215
216
# File 'lib/nfe/resources/product_invoices_rtc.rb', line 209

def send_correction_letter(company_id:, invoice_id:, reason:)
  cid = Nfe::IdValidator.company_id(company_id)
  iid = Nfe::IdValidator.invoice_id(invoice_id)
  validate_correction_reason(reason)
  response = put("#{base_path(cid)}/#{iid}/correctionletter",
                 body: json_body({ reason: reason }), headers: json_headers)
  hydrate(Nfe::Generated::ProductInvoiceRtcV1::RequestCancellationResource, parse_json(response.body))
end

#validate_correction_reason(reason) ⇒ void

This method returns an undefined value.

Parameters:

  • reason (Object)

Raises:



286
287
288
289
290
291
292
# File 'lib/nfe/resources/product_invoices_rtc.rb', line 286

def validate_correction_reason(reason)
  length = reason.to_s.length
  return if length.between?(15, 1000)

  raise Nfe::InvalidRequestError,
        "motivo da carta de correção deve conter entre 15 e 1000 caracteres"
end