Class: Nfe::Resources::ServiceInvoices

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

Overview

Service invoices (NFS-e) resource for the :main host family (+https://api.nfe.io/v1/...+). This is the canonical emission resource of the platform.

Emission is typically asynchronous: #create returns either a ServiceInvoicePending (HTTP 202, queued) or a ServiceInvoiceIssued (HTTP 201, materialized). There is no +create_and_wait+/+create_batch+ in v1.0 — poll manually:

result = client.service_invoices.create(company_id: id, data: payload)
if result.pending?
loop do
  status = client.service_invoices.get_status(company_id: id, invoice_id: result.invoice_id)
  break if status.complete?
  sleep 2
end
end

Examples:

client.service_invoices.create(company_id: "co_1", data: { borrower: {...}, ... })

Defined Under Namespace

Classes: StatusResult

Constant Summary collapse

FAILED_STATUSES =

Returns:

  • (Array[String])
%w[IssueFailed CancelFailed].freeze

Instance Attribute Summary

Attributes inherited from AbstractResource

#client

Instance Method Summary collapse

Methods inherited from AbstractResource

#api_version, #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)


50
51
52
# File 'lib/nfe/resources/service_invoices.rb', line 50

def api_family
  :main
end

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

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



205
206
207
208
209
210
211
212
213
214
215
# File 'lib/nfe/resources/service_invoices.rb', line 205

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 NFS-e em processamento.",
      status_code: response.status, response_headers: response.headers
    )
  end
  ServiceInvoicePending.new(invoice_id: invoice_id, location: location)
end

#cancel(company_id:, invoice_id:) ⇒ Nfe::ServiceInvoice

Cancel a service invoice (synchronous); returns the updated model.

Parameters:

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

Returns:



108
109
110
111
112
113
# File 'lib/nfe/resources/service_invoices.rb', line 108

def cancel(company_id:, invoice_id:)
  cid = Nfe::IdValidator.company_id(company_id)
  iid = Nfe::IdValidator.invoice_id(invoice_id)
  response = delete("/companies/#{cid}/serviceinvoices/#{iid}")
  hydrate(Nfe::ServiceInvoice, parse_json(response.body))
end

#create(company_id:, data:, idempotency_key: nil, request_options: nil) ⇒ ServiceInvoicePending, ServiceInvoiceIssued

Create (emit) a service invoice. Returns a discriminated result: a Nfe::Resources::ServiceInvoicePending on HTTP 202 (queued, invoice_id parsed from the Location header) or a Nfe::Resources::ServiceInvoiceIssued on HTTP 201 (materialized).

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:

Raises:



66
67
68
69
70
71
72
73
# File 'lib/nfe/resources/service_invoices.rb', line 66

def create(company_id:, data:, idempotency_key: nil, request_options: nil)
  cid = Nfe::IdValidator.company_id(company_id)
  response = post("/companies/#{cid}/serviceinvoices", body: json_body(data),
                                                       headers: json_headers,
                                                       idempotency_key: idempotency_key,
                                                       request_options: request_options)
  discriminate(response)
end

#discriminate(response) ⇒ Nfe::Resources::ServiceInvoicePending, Nfe::Resources::ServiceInvoiceIssued

Interpret the emission response into the discriminated value object.



197
198
199
200
201
# File 'lib/nfe/resources/service_invoices.rb', line 197

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

  ServiceInvoiceIssued.new(resource: hydrate(Nfe::ServiceInvoice, parse_json(response.body)))
end

#download_document(company_id:, invoice_id:, ext:, accept:) ⇒ String

GET the single-invoice or bulk document path and return binary bytes.

Parameters:

  • company_id: (String)
  • invoice_id: (String, nil)
  • ext: (String)
  • accept: (String)

Returns:

  • (String)


185
186
187
188
189
190
191
192
193
194
# File 'lib/nfe/resources/service_invoices.rb', line 185

def download_document(company_id:, invoice_id:, ext:, accept:)
  cid = Nfe::IdValidator.company_id(company_id)
  path = if invoice_id.nil?
           "/companies/#{cid}/serviceinvoices/#{ext}"
         else
           iid = Nfe::IdValidator.invoice_id(invoice_id)
           "/companies/#{cid}/serviceinvoices/#{iid}/#{ext}"
         end
  download(path, headers: { "Accept" => accept })
end

#download_pdf(company_id:, invoice_id: nil) ⇒ String

Download the invoice PDF (or the company ZIP when invoice_id is nil).

Parameters:

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

Returns:

  • (String)

    PDF/ZIP bytes (ASCII-8BIT).



133
134
135
136
# File 'lib/nfe/resources/service_invoices.rb', line 133

def download_pdf(company_id:, invoice_id: nil)
  download_document(company_id: company_id, invoice_id: invoice_id,
                    ext: "pdf", accept: "application/pdf")
end

#download_xml(company_id:, invoice_id: nil) ⇒ String

Download the invoice XML (or the company ZIP when invoice_id is nil).

Parameters:

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

Returns:

  • (String)

    XML/ZIP bytes (ASCII-8BIT).



143
144
145
146
# File 'lib/nfe/resources/service_invoices.rb', line 143

def download_xml(company_id:, invoice_id: nil)
  download_document(company_id: company_id, invoice_id: invoice_id,
                    ext: "xml", accept: "application/xml")
end

#extract_invoice_id(location) ⇒ String?

Extract the trailing id from a Location path.

Parameters:

  • location (String, nil)

Returns:

  • (String, nil)


218
219
220
221
222
223
# File 'lib/nfe/resources/service_invoices.rb', line 218

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

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

#get_status(company_id:, invoice_id:) ⇒ StatusResult

Status snapshot derived from #retrieve (Node parity — no extra HTTP).

Parameters:

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

Returns:



153
154
155
156
157
158
159
160
161
# File 'lib/nfe/resources/service_invoices.rb', line 153

def get_status(company_id:, invoice_id:)
  invoice = retrieve(company_id: company_id, invoice_id: invoice_id)
  status = invoice&.flow_status || "WaitingSend"
  StatusResult.new(
    status: status, invoice: invoice,
    complete: Nfe::FlowStatus.terminal?(status),
    failed: FAILED_STATUSES.include?(status)
  )
end

#json_body(data) ⇒ String

Parameters:

  • data (Object)

Returns:

  • (String)


169
170
171
# File 'lib/nfe/resources/service_invoices.rb', line 169

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

#json_headersHash[String, String]

Returns:

  • (Hash[String, String])


165
166
167
# File 'lib/nfe/resources/service_invoices.rb', line 165

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

#list(company_id:, **options) ⇒ Nfe::ListResponse

List service invoices (page-style pagination plus date filters).

Parameters:

  • company_id (String)
  • options (Hash)

    page_index, page_count, issued_begin, issued_end, created_begin, created_end, has_totals.

  • company_id: (String)

Returns:



81
82
83
84
85
# File 'lib/nfe/resources/service_invoices.rb', line 81

def list(company_id:, **options)
  cid = Nfe::IdValidator.company_id(company_id)
  response = get("/companies/#{cid}/serviceinvoices", query: list_query(options))
  hydrate_list(Nfe::ServiceInvoice, parse_json(response.body), wrapper_key: "serviceInvoices")
end

#list_query(options) ⇒ Hash[Symbol, untyped]

Map snake_case list options onto the API's camelCase query parameters, dropping nil values.

Parameters:

  • options (Hash[Symbol, untyped])

Returns:

  • (Hash[Symbol, untyped])


175
176
177
178
179
180
181
182
# File 'lib/nfe/resources/service_invoices.rb', line 175

def list_query(options)
  {
    pageIndex: options[:page_index], pageCount: options[:page_count],
    issuedBegin: options[:issued_begin], issuedEnd: options[:issued_end],
    createdBegin: options[:created_begin], createdEnd: options[:created_end],
    hasTotals: options[:has_totals]
  }.compact
end

#retrieve(company_id:, invoice_id:) ⇒ Nfe::ServiceInvoice

Retrieve a service invoice by id.

Parameters:

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

Returns:

Raises:



93
94
95
96
97
98
99
100
101
# File 'lib/nfe/resources/service_invoices.rb', line 93

def retrieve(company_id:, invoice_id:)
  cid = Nfe::IdValidator.company_id(company_id)
  iid = Nfe::IdValidator.invoice_id(invoice_id)
  response = get("/companies/#{cid}/serviceinvoices/#{iid}")
  payload = parse_json(response.body)
  raise Nfe::NotFoundError, "nota de serviço #{iid} não encontrada" if payload.nil?

  hydrate(Nfe::ServiceInvoice, payload)
end

#send_email(company_id:, invoice_id:) ⇒ Hash

E-mail the invoice to the borrower. No e-mail list argument (Node parity).

Parameters:

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

Returns:

  • (Hash)

    { sent:, message: }.



120
121
122
123
124
125
126
# File 'lib/nfe/resources/service_invoices.rb', line 120

def send_email(company_id:, invoice_id:)
  cid = Nfe::IdValidator.company_id(company_id)
  iid = Nfe::IdValidator.invoice_id(invoice_id)
  response = put("/companies/#{cid}/serviceinvoices/#{iid}/sendemail")
  payload = parse_json(response.body) || {}
  { sent: payload.fetch("sent", response.success?), message: payload["message"] }
end