diff --git a/.gitignore b/.gitignore index 1eb987e..27a23af 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ bower_components/ jspm_packages/ openspec/ nfeio-docs +client-ruby # ---------------------------------------------------------------------------- # Build Outputs # ---------------------------------------------------------------------------- diff --git a/CHANGELOG.md b/CHANGELOG.md index 6295721..11a4118 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,87 @@ Todas as mudanças notáveis neste projeto serão documentadas neste arquivo. O formato é baseado em [Keep a Changelog](https://keepachangelog.com/pt-BR/1.0.0/), e este projeto adere ao [Versionamento Semântico](https://semver.org/lang/pt-BR/). +## [5.0.0] - 2026-06-30 + +> Primeira release de **funcionalidades** desde a v3 — a v4 foi apenas o bump de runtime +> (Node 22), sem mudanças de API. A v5 reúne os novos recursos (RTC, NFC-e, inscrições +> municipais, certificados, notificações, webhooks de conta) e correções de contrato +> descobertas testando contra a API ao vivo. Guia: [MIGRATION.md](MIGRATION.md#v4--v5). + +### ⚠️ BREAKING CHANGES + +Todas as quebras são em superfícies que **já estavam quebradas** (métodos que só lançavam +404, retornos que vinham `undefined`); nenhum código correto deixa de funcionar, mas +usuários TypeScript devem revisar estes pontos no upgrade: + +- **`addresses.lookupByPostalCode()`** agora retorna um `Address` (antes retornava o + envelope cru tipado como `{ addresses: Address[] }`). Acesse os campos direto: + `address.street` em vez de `result.addresses[0].street`. +- **`addresses.search()` e `addresses.lookupByTerm()` removidos** — os endpoints não + existem no host real (404). Use `lookupByPostalCode()`. O tipo `AddressSearchOptions` + foi removido e `AddressLookupResponse` agora descreve o envelope `{ address: Address }`. +- **`serviceInvoices.cancel()`** agora retorna `CancelInvoiceResponse` (união discriminada + `{ status: 'async', response } | { status: 'immediate', invoice }`), pois o cancelamento + é assíncrono. Antes retornava um stub tipado como `ServiceInvoiceData`. Use + `cancelAndWait()` para bloquear até concluir. +- **`CertificateValidator.validate()`** não retorna mais `metadata` fabricada (subject/ + issuer/validade falsos). O pré-flight local é só de formato; os dados do certificado são + verificados no servidor durante o upload. + +### ✨ Adicionado — Companies v2, notificações e avulsos + +- **`companies.exists(companyId)`**: checagem de existência via `HEAD /v2/companies/{id}` (api.nfse.io; 404 → `false`). HTTP client ganhou o verbo **`head`**. +- **`nfe.notifications`** (`NotificationsResource`, api.nfe.io): `list`/`retrieve`/`delete`/`sendEmail` de notificações da empresa. +- **`serviceInvoices.retrieveByExternalId(companyId, externalId)`**: busca por id externo (idempotência/reconciliação). +- **`stateTaxes.switchAuthorizer(companyId, stateTaxId, data?)`**: troca de autorizador NF-e (`POST .../switch-authorizer`). + +### ✨ Adicionado — Gestão de certificados por thumbprint + +- **`nfe.certificates`** (`CertificatesResource`, api.nfse.io): `list`, `getByThumbprint`/`deleteByThumbprint` (v2) e variantes v1 (`getByThumbprintV1`/`deleteByThumbprintV1`). Cobre o gap da consulta/remoção de certificado por thumbprint. Complementa o `companies.uploadCertificate` legado (host api.nfe.io) — recurso dedicado por estar em host diferente (contribuintes-v2 @ api.nfse.io). + +### ✨ Adicionado — NFC-e (consumer invoices) + +- **`nfe.consumerInvoices`** (`ConsumerInvoicesResource`, api.nfse.io): ciclo de vida da NFC-e company-scoped — `create` (**webhook-driven**, sem polling), `list`, `retrieve`, `cancel`, `getItems`, `getEvents`, `downloadPdf`/`downloadXml`/`downloadRejectionXml`, e `disable` (inutilização). Distinto do `consumerInvoiceQuery` (consulta de cupom, somente leitura). Tipos: `ConsumerInvoiceData`, `ConsumerInvoice`, `ConsumerInvoiceDisablementData`. + +### ✨ Adicionado — Inscrições Municipais (municipal taxes) + +- **`nfe.municipalTaxes`** (`MunicipalTaxesResource`, api.nfse.io): CRUD de inscrições municipais — pré-requisito para emissão de NFS-e —, espelhando `stateTaxes`. Inclui `updatePrefecture` (HTTP **PATCH** `.../updateprefecture`) e `getSeries` (`.../series/{serie}`). Tipos: `MunicipalTax`, `CreateMunicipalTaxData`, `UpdateMunicipalTaxData`. +- HTTP client ganhou o verbo **`patch`** (mesma cadeia de retry/timeout/erro dos demais). + +### ✨ Adicionado — Webhooks de conta + +- `WebhooksResource` ganhou operações **de conta** (`/v2/webhooks`, sem `companyId`): `listAccountWebhooks`, `createAccountWebhook`, `retrieveAccountWebhook`, `updateAccountWebhook`, `deleteAccountWebhook`, `pingAccountWebhook`, e `deleteAllAccountWebhooks` (método distinto e marcado como destrutivo). Os métodos company-scoped existentes seguem inalterados. +- **`fetchEventTypes()`**: lista de tipos de evento **ao vivo** (`GET /v2/webhooks/eventTypes`), com retorno em união aberta. `getAvailableEvents()` (lista hardcoded) foi marcado `@deprecated`. + +### ✨ Adicionado — Empresas (contribuintes-v2) e tipagem de domínio + +- Spec **`contribuintes-v2`** (Empresas, OpenAPI 3.x) adotada na geração — destrava tipos reais de empresa/certificado/endereço. +- **`Company`** enriquecido de forma **aditiva** (minor, sem quebra): mantém os campos atuais e o índice `[key: string]: unknown`, e adiciona os campos documentados do spec (`address`, `taxRegime`, `tradeName`, `stateTaxes`, …) como **opcionais** — melhora o autocomplete sem apertar o tipo nem o input do `create()`. +- Novos tipos exportados (opt-in, estritos): `CompanyResourceItem`, `CompanyResourceV1`, `CreateCompanyResourceItem`, `UpdateCompanyResourceItem`, `CertificateMetadataResource`, `CompanyAddress`. +- Guard de regressão de tipo executável: `npm run test:types` (vitest typecheck) garante que leituras de campo arbitrário continuam compilando e que `federalTaxNumber` segue `number`. + +### ✨ Adicionado — Reforma Tributária do Consumo (RTC) + +- **`nfe.serviceInvoicesRtc`**: emissão de NFS-e no leiaute RTC (grupo `ibsCbs`), com `create`/`createAndWait` (polling) e `downloadCancellationXml` (XML do evento de cancelamento — Ambiente Nacional). Mesmo endpoint da emissão atual; RTC é selecionado pelo payload. Tipo de request: `NFSeRtcRequest` (schema `NFSeRequest`). +- **`nfe.productInvoicesRtc`**: emissão de NF-e/NFC-e no leiaute RTC (IBS estadual/municipal, CBS, IS), `create` **webhook-driven** (não faz polling), espelhando `productInvoices`. Tipo de request: `ProductInvoiceRtcRequest` (schema `ProductInvoiceRequest`). +- Recursos de emissão existentes (`serviceInvoices`/`productInvoices`) permanecem inalterados; RTC é opt-in. Proveniência das specs registrada em `openapi/spec/SOURCES.json` (NT_2025.002_v1.30). +- Pipeline de geração agora descobre specs `.json` e falha em skip inesperado (allowlist dos 5 Swagger 2.0 legados). + +### 🐛 Correções + +- **CertificateValidator**: `validate()` deixou de **fabricar metadata** (subject/issuer/validade falsos) e de reportar validade de 1 ano para qualquer arquivo bem-formado. Agora é um pré-flight **só de formato** (buffer, senha presente, magic bytes PKCS#12); subject/issuer/validade e a senha são verificados no servidor durante o upload. `metadata` passa a ser ausente no pré-flight local. +- **`updateConfig()`**: agora invalida **todos** os resources e HTTP clients em cache (via `resetCaches()`). Antes, resetava apenas ~10 — `productInvoices`, `stateTaxes`, `taxCalculation`, `taxCodes`, lookups e os clients de query/legalEntity/naturalPerson permaneciam com a configuração antiga após `updateConfig`. +- **README**: exemplo de webhook corrigido para o header `x-hub-signature` (HMAC-SHA1), alinhado ao fix de verificação de assinatura; antes referenciava `x-nfe-signature`. +- **`addresses.lookupByPostalCode()`**: passou a **desempacotar** o envelope `{ address }` da API e retornar um único `Address`. Antes retornava o envelope cru tipado como `{ addresses: Address[] }` — quem seguia o JSDoc (`result.addresses[0].street`) recebia `undefined`. Verificado contra a API ao vivo (`address.api.nfe.io/v2`). +- **`addresses.search()` e `addresses.lookupByTerm()` removidos**: os endpoints que chamavam (`/v2/addresses` e `/v2/addresses/{term}`) respondem **404** no host real — os métodos só lançavam `NotFoundError`. Como nunca funcionaram, a remoção não quebra nenhum consumidor real. O tipo `AddressSearchOptions` foi removido e `AddressLookupResponse` agora descreve o envelope real (`{ address: Address }`). Se o backend confirmar um endpoint de busca, ele volta como change separada com contrato verificado. +- **`serviceInvoices.cancel()`**: o cancelamento de NFS-e é **assíncrono** (HTTP `202` + `Location`). Antes, `cancel()` retornava o stub de polling `{ code, status, location }` **tipado como `ServiceInvoiceData`** (então `cancelled.id`/`flowStatus` vinham `undefined`). Agora retorna uma **união discriminada** `CancelInvoiceResponse` (`{ status: 'async', response }` ou `{ status: 'immediate', invoice }`), espelhando `create()`. Novo método **`cancelAndWait()`** faz polling até o cancelamento concluir (espelha `createAndWait`). Verificado contra a API ao vivo. Tipos exportados: `CreateInvoiceResponse`, `CancelInvoiceResponse`. + +#### Correções de recursos novos (descobertas em smoke test ao vivo de toda a SDK) + +- **`taxCodes.*`**: passou a usar a **chave principal** (`apiKey`) contra `api.nfse.io`. Antes estava ligado ao cliente CT-e (chave de dados), o que retornava **403** em setups com `dataApiKey` separada. Os 4 endpoints (`/tax-codes/*`) agora respondem 200. +- **`consumerInvoices.list()`**: agora exige `environment` (`Production`/`Test`), obrigatório pela API — antes a chamada saía sem o parâmetro e retornava **400**. Também passou a usar a **chave principal** (antes 403 com chave de dados separada). Os métodos de leitura (`retrieve`/`getItems`/`getEvents`/downloads) aceitam `environment` opcional. +- **`webhooks` de conta**: os métodos de conta (`listAccountWebhooks`, `createAccountWebhook`, `fetchEventTypes`, etc.) montavam o caminho `/v1/v2/webhooks` (duplo prefixo de versão) → **404**. Agora usam um cliente dedicado em `api.nfe.io/v2`. Além disso, `listAccountWebhooks` desempacota o envelope `{ webHooks }` para `{ data }` e `fetchEventTypes` extrai os ids de `{ eventTypes }`. + ## [4.0.0] - 2026-06-12 ### ⚠️ BREAKING CHANGE — Node.js >= 22 diff --git a/MIGRATION.md b/MIGRATION.md index 3bb2f6c..b8936e7 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -1,5 +1,87 @@ # Guia de Migração +## v4 → v5 + +A v5 é a primeira release de **funcionalidades** desde a v3 (a v4 foi apenas o bump para +Node 22). Ela traz muitos recursos novos — todos **aditivos** — e um conjunto pequeno de +correções de contrato que são tecnicamente _breaking_. + +```bash +npm install nfe-io@^5 +``` + +> **Na prática, a maioria dos projetos não precisa mudar nada.** As quebras abaixo são em +> superfícies que já estavam quebradas (métodos que só lançavam 404, retornos que vinham +> `undefined`). Se você usa essas APIs, ajuste conforme a seguir. + +### 1. `addresses.lookupByPostalCode()` retorna um `Address` + +Antes devolvia um envelope tipado como `{ addresses: Address[] }` (mas a API real retorna +um único endereço, então `result.addresses` vinha `undefined`). Agora retorna o `Address` +direto. + +```ts +// Antes (v4) — não funcionava como documentado +const result = await nfe.addresses.lookupByPostalCode('01310-100'); +const street = result.addresses[0].street; // undefined + +// Agora (v5) +const address = await nfe.addresses.lookupByPostalCode('01310-100'); +const street = address.street; // 'Paulista' +``` + +### 2. `addresses.search()` e `addresses.lookupByTerm()` foram removidos + +Os endpoints (`/v2/addresses` e `/v2/addresses/{term}`) respondem **404** no host real — +os métodos só lançavam `NotFoundError`. Use `lookupByPostalCode()`. O tipo +`AddressSearchOptions` foi removido e `AddressLookupResponse` agora descreve o envelope +real (`{ address: Address }`). + +> Busca de endereço por texto livre só volta se/quando o backend expuser um endpoint real +> — aí como adição não-breaking. + +### 3. `serviceInvoices.cancel()` retorna uma união discriminada + +O cancelamento de NFS-e é **assíncrono** (HTTP 202 + `Location`). Antes, `cancel()` +retornava um stub de polling tipado como `ServiceInvoiceData` (então `cancelled.id` / +`cancelled.flowStatus` vinham `undefined`). Agora retorna `CancelInvoiceResponse`. + +```ts +// Antes (v4) +const cancelled = await nfe.serviceInvoices.cancel(companyId, invoiceId); +console.log(cancelled.flowStatus); // undefined + +// Agora (v5) — união discriminada (espelha create()) +const result = await nfe.serviceInvoices.cancel(companyId, invoiceId); +if (result.status === 'async') { + console.log(result.response.invoiceId); +} + +// ou bloqueie até concluir: +const invoice = await nfe.serviceInvoices.cancelAndWait(companyId, invoiceId); +console.log(invoice.flowStatus); // 'Cancelled' +``` + +### 4. `CertificateValidator.validate()` não fabrica mais `metadata` + +O pré-flight local valida apenas o **formato** (buffer, senha presente, magic bytes +PKCS#12). Não retorna mais `subject`/`issuer`/`validade` inventados — esses dados são +verificados no servidor durante o upload do certificado. Se você lia `result.metadata`, +ele agora é ausente no pré-flight. + +### Novidades (aditivas, sem ação necessária) + +- Emissão RTC (Reforma Tributária): `nfe.serviceInvoicesRtc`, `nfe.productInvoicesRtc` +- NFC-e: `nfe.consumerInvoices` +- Inscrições municipais: `nfe.municipalTaxes` +- Certificados por thumbprint: `nfe.certificates` +- Notificações: `nfe.notifications` +- Webhooks de conta + `fetchEventTypes()` +- `companies.exists()`, `serviceInvoices.retrieveByExternalId()`, `serviceInvoices.cancelAndWait()`, `stateTaxes.switchAuthorizer()` +- `Company` enriquecido com campos do spec (opcionais) + +--- + ## v3 → v4 A v4.0.0 **não contém mudanças de API** — todo código escrito para a v3 funciona sem alterações. diff --git a/README.md b/README.md index 10bf5e5..05ac7d1 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# NFE.io SDK para Node.js (v4) +# NFE.io SDK para Node.js (v5) [![npm version](https://img.shields.io/npm/v/nfe-io.svg)](https://www.npmjs.com/package/nfe-io) [![Node.js Version](https://img.shields.io/node/v/nfe-io.svg)](https://nodejs.org) @@ -8,7 +8,7 @@ **SDK Oficial NFE.io para Node.js 22+** - SDK TypeScript moderno para emissão de notas fiscais de serviço eletrônicas (NFS-e). -> ✨ **Versão 4** - TypeScript nativo, zero dependências em runtime e API moderna async/await. +> ✨ **Versão 5** - TypeScript nativo, zero dependências em runtime e API moderna async/await. Inclui emissão RTC (Reforma Tributária), NFC-e, inscrições municipais, certificados, notificações e webhooks de conta. Veja a [migração v4 → v5](MIGRATION.md#v4--v5). ## 📋 Índice @@ -971,9 +971,9 @@ const webhook = await nfe.webhooks.create(empresaId, { active: true }); -// No seu endpoint de webhook +// No seu endpoint de webhook (capture o corpo cru: app.use(express.raw({ type: '*/*' }))) app.post('/api/webhooks/nfe', (req, res) => { - const assinatura = req.headers['x-nfe-signature']; + const assinatura = req.headers['x-hub-signature']; // header correto (HMAC-SHA1), não 'x-nfe-signature' const ehValido = nfe.webhooks.validateSignature( req.body, assinatura, diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..070fb87 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,75 @@ +--- +title: Biblioteca NFE.io em Node.js para Emissão de Notas Fiscais (NFS-e, NF-e, NFC-e, CT-e) +description: SDK Node.js oficial da NFE.io — TypeScript nativo, Node 22+, zero dependências de runtime, ESM + CommonJS e respostas discriminadas. +sidebar_label: Biblioteca Node.js +slug: /desenvolvedores/bibliotecas/node +provider: NFE.io +badge: SDK +layout_type: IntegrationLayout +heroImage: /docs/img/bibliotecas/nodejs.svg +ctaLabel: GitHub NFE.io Node.js +ctaUrl: https://github.com/nfe/client-nodejs +--- + +# Biblioteca Node.js NFE.io + +SDK oficial da [NFE.io](https://nfe.io) para Node.js: emissão e gestão de +documentos fiscais eletrônicos brasileiros (NFS-e, NF-e, NFC-e, CT-e) com +ergonomia moderna e **zero dependências de runtime**. + +- **Cliente único** `NfeClient` com acessores `camelCase` por recurso, inicializados sob demanda (lazy). +- **TypeScript nativo** — tipos gerados das specs OpenAPI oficiais; autocompletar e checagem no editor. +- **Respostas discriminadas** para emissão assíncrona (`{ status: 'immediate' | 'async' }`) e utilitários de polling. +- **ESM + CommonJS** no mesmo pacote; alvo **Node.js 22+**. + +## Requisitos + +- Node.js **22** ou superior. +- **Zero dependências de runtime** — apenas APIs nativas do Node (fetch, AbortController, Buffer). + +## Instalação + +```sh +npm install nfe-io +# ou: pnpm add nfe-io / yarn add nfe-io +``` + +## Primeiros passos + +```typescript +import { NfeClient } from 'nfe-io'; + +const nfe = new NfeClient({ apiKey: process.env.NFE_API_KEY }); + +const result = await nfe.serviceInvoices.create('55df4dc6b6cd9007e4f13ee8', { + cityServiceCode: '2690', + description: 'Manutenção e suporte técnico', + servicesAmount: 100.0, + borrower: { federalTaxNumber: 191, name: 'Banco do Brasil SA' }, +}); +``` + +O fluxo completo (retorno discriminado + polling) está em +[Primeiros passos](./getting-started.md). + +## Guias + +- [Primeiros passos](./getting-started.md) — instalação, cliente e primeira NFS-e. +- [Configuração](./configuration.md) — chaves de API, ambiente, timeout e retry. +- [Emissão assíncrona e polling](./async-and-polling.md) — retorno discriminado e `createAndWait`. +- [Roteamento multi-host / multi-chave](./multi-host-routing.md) — quais recursos usam qual host e chave. +- [Paginação](./pagination.md) — listas page-style e cursor. +- [Downloads (PDF/XML)](./downloads.md) — bytes binários e ZIPs. +- [Webhooks](./webhooks.md) — assinatura, eventos e webhooks de conta. +- [Erros](./errors.md) — hierarquia de erros e type guards. +- [Emissão RTC (Reforma Tributária)](./rtc-emission.md) — leiautes IBS/CBS/IS. + +## Recursos (cookbook) + +Um guia prático por recurso em [Recursos](./recursos/). Destaques: + +- [Notas de serviço (NFS-e)](./recursos/service-invoices.md) +- [Notas de produto (NF-e)](./recursos/product-invoices.md) +- [NFC-e (consumidor)](./recursos/consumer-invoices.md) +- [Empresas](./recursos/companies.md) · [Inscrições municipais](./recursos/municipal-taxes.md) · [Inscrições estaduais](./recursos/state-taxes.md) +- [Consulta CNPJ](./recursos/legal-entity-lookup.md) · [Consulta CPF](./recursos/natural-person-lookup.md) · [Consulta CEP](./recursos/addresses.md) diff --git a/docs/_category_.json b/docs/_category_.json new file mode 100644 index 0000000..c63fe5e --- /dev/null +++ b/docs/_category_.json @@ -0,0 +1,5 @@ +{ + "label": "Biblioteca Node.js", + "position": 3, + "collapsed": true +} diff --git a/docs/async-and-polling.md b/docs/async-and-polling.md new file mode 100644 index 0000000..b7a7e71 --- /dev/null +++ b/docs/async-and-polling.md @@ -0,0 +1,72 @@ +--- +title: Emissão assíncrona e polling no SDK Node.js da NFE.io +sidebar_label: Assíncrono e polling +sidebar_position: 3 +slug: assincrono-e-polling +description: Retorno discriminado (202 vs 201), estados de FlowStatus, createAndWait/cancelAndWait e emissão webhook-driven. +--- + +# Emissão assíncrona e polling + +A emissão fiscal costuma ser **assíncrona**. O SDK expõe isso de duas formas, +dependendo do produto: **polling** (NFS-e) e **webhook-driven** (NF-e/NFC-e). + +## Retorno discriminado (NFS-e) + +`serviceInvoices.create` (e `serviceInvoicesRtc.create`) devolvem uma união +discriminada pela tag `status`: + +```typescript +const r = await nfe.serviceInvoices.create(companyId, data); +if (r.status === 'async') { + // HTTP 202 — enfileirada + r.response.invoiceId; // id para reconsultar + r.response.location; // header Location +} else { + // HTTP 201 — já materializada + r.invoice; // ServiceInvoiceData +} +``` + +## createAndWait (polling embutido) + +Combina `create` + polling com backoff exponencial e devolve a nota em estado +terminal. Lança `InvoiceProcessingError` em `IssueFailed`/`CancelFailed`. + +```typescript +const invoice = await nfe.serviceInvoices.createAndWait(companyId, data, { + timeout: 120000, // total, ms (padrão 2 min) + initialDelay: 1000, // ms antes do 1º poll + maxDelay: 10000, // teto entre polls + backoffFactor: 1.5, + onPoll: (attempt, flowStatus) => console.log(attempt, flowStatus), +}); +``` + +`cancelAndWait` faz o mesmo para cancelamento (também assíncrono). + +## Estados de `FlowStatus` + +| Terminal | Em processamento | +|---|---| +| `Issued`, `IssueFailed`, `Cancelled`, `CancelFailed` | `WaitingCalculateTaxes`, `WaitingDefineRpsNumber`, `WaitingSend`, `WaitingSendCancel`, `WaitingReturn`, `WaitingDownload`, `PullFromCityHall` | + +O polling encerra ao atingir um estado **terminal**. + +## Webhook-driven (NF-e / NFC-e) + +`productInvoices.create`, `productInvoicesRtc.create` e `consumerInvoices.create` +**não fazem polling**: um `202` indica que a nota foi enfileirada e a conclusão +é notificada por **webhook**. Configure webhooks e reaja aos eventos em vez de +consultar em loop. + +:::tip Escolha o padrão certo +- NFS-e: `createAndWait` (polling) para um fluxo síncrono simples. +- NF-e/NFC-e: emita e trate a conclusão via [Webhooks](./webhooks.md). +::: + +## Próximos passos + +- [Webhooks](./webhooks.md) +- [Notas de serviço (NFS-e)](./recursos/service-invoices.md) +- [Emissão RTC](./rtc-emission.md) diff --git a/docs/configuration.md b/docs/configuration.md new file mode 100644 index 0000000..ac7dac0 --- /dev/null +++ b/docs/configuration.md @@ -0,0 +1,64 @@ +--- +title: Configuração do SDK Node.js da NFE.io +sidebar_label: Configuração +sidebar_position: 2 +slug: configuracao +description: Chaves de API (apiKey e dataApiKey), ambiente, timeout, retry e reconfiguração em runtime do NfeClient. +--- + +# Configuração + +O `NfeClient` recebe um objeto `NfeConfig`. Todos os campos são opcionais, mas +ao menos uma chave de API é necessária para a maioria dos recursos. + +```typescript +import { NfeClient } from 'nfe-io'; + +const nfe = new NfeClient({ + apiKey: process.env.NFE_API_KEY, // chave principal (emissão, empresas, ...) + dataApiKey: process.env.NFE_DATA_API_KEY, // chave de dados/consulta (opcional) + environment: 'production', // 'production' | 'development' + timeout: 30000, // ms + retryConfig: { maxRetries: 3, baseDelay: 1000, maxDelay: 5000, backoffMultiplier: 2 }, +}); +``` + +## Campos + +| Campo | Tipo | Descrição | +|---|---|---| +| `apiKey` | `string` | Chave principal: emissão (NFS-e/NF-e/NFC-e), empresas, webhooks, tax-codes. | +| `dataApiKey` | `string` | Chave de dados/consulta: CEP, CNPJ, CPF, CT-e. Faz fallback para `apiKey`. | +| `environment` | `'production' \| 'development'` | Seleciona homologação vs produção. | +| `baseUrl` | `string` | Sobrescreve o host padrão (uso avançado/testes). | +| `timeout` | `number` | Timeout por requisição, em ms. | +| `retryConfig` | `RetryConfig` | `maxRetries`, `baseDelay`, `maxDelay?`, `backoffMultiplier?`. | + +:::info Duas chaves, dois escopos +Recursos de **dados/consulta** (endereço, CNPJ, CPF) usam `dataApiKey` (com +fallback para `apiKey`). Os demais usam `apiKey`. Veja +[Roteamento multi-host / multi-chave](./multi-host-routing.md). +::: + +## A partir do ambiente + +```typescript +import { createClientFromEnv } from 'nfe-io'; +// lê NFE_API_KEY (e NFE_DATA_API_KEY, se houver) +const nfe = createClientFromEnv('production'); +``` + +## Reconfigurar em runtime + +`updateConfig` reaplica a configuração e **invalida todos os caches** de +recursos e clientes HTTP — nenhuma instância retém host/chave/timeout antigos. + +```typescript +nfe.updateConfig({ timeout: 60000 }); +``` + +## Próximos passos + +- [Roteamento multi-host / multi-chave](./multi-host-routing.md) +- [Erros](./errors.md) e política de retry +- [Primeiros passos](./getting-started.md) diff --git a/docs/downloads.md b/docs/downloads.md new file mode 100644 index 0000000..7f87c5e --- /dev/null +++ b/docs/downloads.md @@ -0,0 +1,56 @@ +--- +title: Downloads de PDF e XML no SDK Node.js da NFE.io +sidebar_label: Downloads (PDF/XML) +sidebar_position: 6 +slug: downloads-pdf-xml +description: Baixe DANFE/PDF e XML como Buffer, individualmente ou em ZIP por empresa, com o Accept correto. +--- + +# Downloads (PDF/XML) + +Os métodos de download retornam um **`Buffer`** com os bytes do arquivo. Passar +o `invoiceId` baixa a nota individual; **omitir** o `invoiceId` baixa um **ZIP** +com todas as notas da empresa (quando o recurso suporta). + +```typescript +import { writeFileSync } from 'node:fs'; + +// PDF individual +const pdf = await nfe.serviceInvoices.downloadPdf(companyId, invoiceId); +writeFileSync('nota.pdf', pdf); + +// XML individual +const xml = await nfe.serviceInvoices.downloadXml(companyId, invoiceId); +writeFileSync('nota.xml', xml); + +// ZIP de todas as notas da empresa (sem invoiceId) +const zip = await nfe.serviceInvoices.downloadPdf(companyId); +writeFileSync('notas.zip', zip); +``` + +## Disponibilidade por estado + +O PDF/XML só existe após a nota atingir um **estado terminal** +(`Issued`/`Cancelled`). Antes disso, o download pode retornar `404` — use +[polling ou webhooks](./async-and-polling.md) para aguardar a emissão. + +## Outros downloads + +| Recurso | Métodos | +|---|---| +| `serviceInvoices` | `downloadPdf`, `downloadXml` | +| `serviceInvoicesRtc` | `downloadCancellationXml` (XML do evento de cancelamento) | +| `consumerInvoices` (NFC-e) | `downloadPdf`, `downloadXml`, `downloadRejectionXml` (aceitam `environment`) | +| `productInvoices` | `downloadPdf`, `downloadXml` | +| `productInvoiceQuery` / `consumerInvoiceQuery` | `downloadPdf`/`downloadXml` por chave de acesso | + +:::tip Streaming para arquivos grandes +Os métodos carregam o arquivo inteiro em memória (`Buffer`). Para lotes grandes +(ZIP da empresa), considere baixar fora do horário de pico e persistir em disco +logo após o retorno. +::: + +## Próximos passos + +- [Notas de serviço (NFS-e)](./recursos/service-invoices.md) +- [NFC-e](./recursos/consumer-invoices.md) diff --git a/docs/errors.md b/docs/errors.md new file mode 100644 index 0000000..510b7a4 --- /dev/null +++ b/docs/errors.md @@ -0,0 +1,69 @@ +--- +title: Tratamento de erros no SDK Node.js da NFE.io +sidebar_label: Erros +sidebar_position: 8 +slug: erros +description: Hierarquia de erros (NfeError), type guards, mapeamento de status HTTP e política de retry. +--- + +# Erros + +Todos os erros do SDK herdam de **`NfeError`**. Cada classe mapeia um tipo de +falha; use `instanceof` ou os **type guards** para ramificar. + +## Hierarquia + +| Classe | Quando ocorre | +|---|---| +| `NfeError` | Base de todos os erros do SDK. | +| `ValidationError` | 400 / validação local (inputs inválidos) — lançado **antes** do HTTP quando possível. | +| `AuthenticationError` | 401 (chave inválida/ausente). | +| `NotFoundError` | 404. | +| `ConflictError` | 409. | +| `RateLimitError` | 429 (com informação de retry). | +| `TimeoutError` | Timeout da requisição. | +| `ConnectionError` | Falha de rede/conexão. | +| `ServerError` / `InternalServerError` | 5xx. | +| `InvoiceProcessingError` | Emissão/cancelamento falhou (`IssueFailed`/`CancelFailed`) ou 202 sem `Location`. | +| `PollingTimeoutError` | Polling excedeu o `timeout` sem atingir estado terminal. | +| `ConfigurationError` | Configuração inválida (ex.: recurso sem chave). | + +## Type guards + +```typescript +import { + isNfeError, isValidationError, isAuthenticationError, + isNotFoundError, isTimeoutError, isConnectionError, isPollingTimeoutError, +} from 'nfe-io'; + +try { + await nfe.serviceInvoices.createAndWait(companyId, data); +} catch (err) { + if (isValidationError(err)) { + // erro de dados — corrija o payload + } else if (isPollingTimeoutError(err)) { + // ainda processando — reconsulte depois com retrieve() + } else if (isNfeError(err)) { + console.error(err.message); + } else { + throw err; + } +} +``` + +## Retry + +O cliente HTTP repete automaticamente falhas transitórias (rede, 429, 5xx) +conforme o `retryConfig` (`maxRetries`, `baseDelay`, `maxDelay?`, +`backoffMultiplier?`). Erros de validação e 4xx **não** são repetidos. + +:::warning POST não é repetido às cegas +Requisições que criam documentos fiscais **não** são repetidas automaticamente. +Em timeouts, reconsulte por `retrieveByExternalId`/`retrieve` (ou reenvie com a +mesma chave de idempotência) para evitar emissão duplicada. +::: + +## Próximos passos + +- [Configuração](./configuration.md) (retry/timeout) +- [Assíncrono e polling](./async-and-polling.md) diff --git a/docs/getting-started.md b/docs/getting-started.md new file mode 100644 index 0000000..53a9799 --- /dev/null +++ b/docs/getting-started.md @@ -0,0 +1,97 @@ +--- +title: Primeiros passos com o SDK Node.js da NFE.io +sidebar_label: Primeiros passos +sidebar_position: 1 +slug: primeiros-passos +description: Instale o pacote nfe-io, crie um NfeClient, emita sua primeira NFS-e e acompanhe o processamento com createAndWait/polling. +--- + +# Primeiros passos + +Este guia cobre a instalação, a criação do cliente e a emissão da sua primeira +nota fiscal de serviço (NFS-e), incluindo o acompanhamento do processamento. + +## 1. Instale o pacote + +```sh +npm install nfe-io +# ou: pnpm add nfe-io / yarn add nfe-io +``` + +Requer **Node.js 22+**. O pacote publica ESM e CommonJS com tipos `.d.ts`. + +## 2. Crie um cliente + +```typescript +import { NfeClient } from 'nfe-io'; + +const nfe = new NfeClient({ + apiKey: process.env.NFE_API_KEY, + environment: 'development', // 'development' (homologação) ou 'production' +}); +``` + +Ou a partir do ambiente (lê `NFE_API_KEY`): + +```typescript +import { createClientFromEnv } from 'nfe-io'; +const nfe = createClientFromEnv('production'); +``` + +## 3. Emita uma NFS-e + +```typescript +const result = await nfe.serviceInvoices.create('55df4dc6b6cd9007e4f13ee8', { + cityServiceCode: '2690', + description: 'Manutenção e suporte técnico', + servicesAmount: 100.0, + borrower: { federalTaxNumber: 191, name: 'Banco do Brasil SA' }, +}); +``` + +## 4. Trate o retorno discriminado + +`create` devolve uma união discriminada: `{ status: 'async' }` (HTTP 202, +enfileirada) ou `{ status: 'immediate' }` (HTTP 201, já materializada). + +```typescript +if (result.status === 'async') { + console.log('em processamento:', result.response.invoiceId); + // reconsulte com retrieve(...) ou use createAndWait (passo 5) +} else { + console.log('emitida:', result.invoice.id, result.invoice.number); +} +``` + +## 5. Acompanhe até um estado terminal (polling) + +Para uma experiência síncrona, `createAndWait` combina `create` + polling com +backoff exponencial e devolve a nota já em estado terminal. + +```typescript +const invoice = await nfe.serviceInvoices.createAndWait( + '55df4dc6b6cd9007e4f13ee8', + { + cityServiceCode: '2690', + description: 'Manutenção e suporte técnico', + servicesAmount: 100.0, + borrower: { federalTaxNumber: 191, name: 'Banco do Brasil SA' }, + }, + { timeout: 120000, onPoll: (attempt, status) => console.log(attempt, status) } +); + +console.log(invoice.flowStatus); // 'Issued' (ou lança InvoiceProcessingError em 'IssueFailed') +``` + +:::tip Idempotência em retentativas +O SDK nunca refaz o POST sozinho. Em um timeout de rede, reinvoque `create` +com o mesmo `externalId`/idempotency para o servidor deduplicar e não emitir +documento fiscal duplicado. +::: + +## Próximos passos + +- [Emissão assíncrona e polling](./async-and-polling.md) +- [Configuração](./configuration.md) (timeout, retry, multi-chave) +- [Erros](./errors.md) e [Webhooks](./webhooks.md) +- Cookbook por recurso em [Recursos](./recursos/) diff --git a/docs/multi-host-routing.md b/docs/multi-host-routing.md new file mode 100644 index 0000000..e43fbd8 --- /dev/null +++ b/docs/multi-host-routing.md @@ -0,0 +1,51 @@ +--- +title: Roteamento multi-host e multi-chave no SDK Node.js da NFE.io +sidebar_label: Multi-host / multi-chave +sidebar_position: 4 +slug: multi-host-e-multi-chave +description: A NFE.io é composta por várias APIs em hosts distintos e duas chaves (principal e de dados). Entenda qual recurso usa qual host e chave. +--- + +# Roteamento multi-host / multi-chave + +A plataforma NFE.io não é uma única API: são **vários serviços em hosts +distintos**, e há **duas chaves** — a principal (`apiKey`) e a de dados +(`dataApiKey`). O SDK roteia cada recurso para o host/chave corretos; você só +precisa fornecer as chaves na configuração. + +## Hosts e chaves por recurso + +| Host | Chave | Recursos | +|---|---|---| +| `api.nfe.io/v1` | principal | `serviceInvoices`, `serviceInvoicesRtc`, `companies`, `legalPeople`, `naturalPeople`, `notifications` | +| `api.nfe.io/v2` | principal | `webhooks` (nível de **conta**) | +| `api.nfse.io` | principal | `consumerInvoices` (NFC-e), `taxCodes` | +| `api.nfse.io` | dados | `productInvoices`, `productInvoicesRtc`, `stateTaxes`, `municipalTaxes`, `certificates`, `transportationInvoices`, `inboundProductInvoices` | +| `address.api.nfe.io/v2` | dados | `addresses` | +| `legalentity.api.nfe.io` | dados | `legalEntityLookup` (CNPJ) | +| `naturalperson.api.nfe.io` | dados | `naturalPersonLookup` (CPF) | +| `nfe.api.nfe.io` | dados | `productInvoiceQuery`, `consumerInvoiceQuery` | + +:::info Fallback de chave +`dataApiKey` faz fallback para `apiKey` quando não informada. Se você usa **uma +só chave** com todos os escopos, basta configurar `apiKey`. +::: + +:::warning Contas com chaves separadas +Se sua conta usa chaves **distintas** para dados e emissão, garanta que a chave +principal tenha acesso aos produtos de emissão/consulta que você vai usar +(NFС-e, tax-codes e emissão usam a chave principal). Uma chave sem escopo +retorna `403`. +::: + +## Por que isso importa + +O SDK já embute o mapeamento acima. Isso evita erros clássicos como montar o +host/caminho errado ou usar a chave sem permissão para um endpoint. Ao abrir um +chamado ou depurar um `403`/`404`, confira se a **chave** informada cobre o +escopo do recurso na tabela. + +## Próximos passos + +- [Configuração](./configuration.md) +- [Erros](./errors.md) diff --git a/docs/pagination.md b/docs/pagination.md new file mode 100644 index 0000000..646192a --- /dev/null +++ b/docs/pagination.md @@ -0,0 +1,62 @@ +--- +title: Paginação no SDK Node.js da NFE.io +sidebar_label: Paginação +sidebar_position: 5 +slug: paginacao +description: Listas page-style com filtros de data, envelopes de resposta por recurso e iteração automática de empresas. +--- + +# Paginação + +Os recursos de listagem retornam **envelopes** — o array vem sob uma chave +específica, não na raiz. O formato varia por recurso. + +## Envelopes por recurso + +| Recurso | Formato | +|---|---| +| `serviceInvoices.list` | `{ serviceInvoices, page }` | +| `companies.list` | `{ data, page }` (via `ListResponse`) | +| `municipalTaxes.list` / `stateTaxes.list` | `{ municipalTaxes \| stateTaxes, hasMore }` | +| `consumerInvoices.list` | `{ consumerInvoices, hasMore }` | +| `certificates.list` | `{ certificates }` | +| `notifications.list` | `{ notifications }` | + +```typescript +const { serviceInvoices = [], page } = await nfe.serviceInvoices.list(companyId, { + pageIndex: 1, // 1-based + pageCount: 20, // até 50 + issuedBegin: '2026-01-01', + issuedEnd: '2026-01-31', +}); +``` + +:::warning Índice 1-based e limite de página +As listas page-style da plataforma são **1-based** (`pageIndex` começa em 1) e +`pageCount` tem teto (tipicamente 50). Valores fora disso podem ser rejeitados +pela API com `400`. +::: + +## Iterar todas as empresas + +`companies` oferece iteração automática, cuidando das páginas para você: + +```typescript +// Coleta tudo em memória: +const all = await nfe.companies.listAll(); + +// Ou itere lazily (async iterator): +for await (const company of nfe.companies.listIterator()) { + console.log(company.id, company.name); +} +``` + +## Filtros por data (NFS-e) + +`serviceInvoices.list` aceita `issuedBegin`/`issuedEnd` e +`createdBegin`/`createdEnd` (formato `yyyy-MM-dd`), além de `hasTotals`. + +## Próximos passos + +- [Notas de serviço (NFS-e)](./recursos/service-invoices.md) +- [Empresas](./recursos/companies.md) diff --git a/docs/recursos/_category_.json b/docs/recursos/_category_.json new file mode 100644 index 0000000..6a0316c --- /dev/null +++ b/docs/recursos/_category_.json @@ -0,0 +1,6 @@ +{ + "label": "Recursos (cookbook)", + "position": 10, + "collapsed": true, + "link": { "type": "generated-index", "slug": "recursos", "title": "Recursos (cookbook)" } +} diff --git a/docs/recursos/addresses.md b/docs/recursos/addresses.md new file mode 100644 index 0000000..e377ea0 --- /dev/null +++ b/docs/recursos/addresses.md @@ -0,0 +1,39 @@ +--- +title: Consulta de endereço (CEP) +sidebar_label: Endereços (CEP) +sidebar_position: 11 +slug: consulta-cep +description: Consulte endereços por CEP com nfe.addresses (host address.api.nfe.io). Suporta apenas lookup por CEP; retorna um Address único. +--- + +# Consulta de endereço (CEP) + +`nfe.addresses` (host `address.api.nfe.io/v2`, chave de dados) consulta +endereços brasileiros por CEP. A API real suporta **somente lookup por CEP**. + +## Métodos + +| Método | Descrição | Retorno | +|---|---|---| +| `lookupByPostalCode(postalCode)` | Consulta o endereço do CEP. | `Address` | + +## Exemplo + +```typescript +const address = await nfe.addresses.lookupByPostalCode('01310-100'); +console.log(address.street); // 'Paulista' +console.log(address.city.name); // 'São Paulo' +console.log(address.postalCode); // '01310-100' (com hífen) +``` + +O CEP é validado (8 dígitos, com ou sem hífen) **antes** da chamada. + +:::warning Removido na v5 +`addresses.search()` e `addresses.lookupByTerm()` foram removidos — os endpoints +retornavam `404` no host real. Use `lookupByPostalCode`. +::: + +## Próximos passos + +- [Consulta CNPJ](./legal-entity-lookup.md) · [Consulta CPF](./natural-person-lookup.md) +- [Roteamento multi-host / multi-chave](../multi-host-routing.md) diff --git a/docs/recursos/certificates.md b/docs/recursos/certificates.md new file mode 100644 index 0000000..9d7958a --- /dev/null +++ b/docs/recursos/certificates.md @@ -0,0 +1,44 @@ +--- +title: Certificados digitais (por thumbprint) +sidebar_label: Certificados +sidebar_position: 7 +slug: certificados +description: Consulta e remoção de certificados digitais por thumbprint com nfe.certificates (host api.nfse.io), com variantes v1. +--- + +# Certificados (por thumbprint) + +`nfe.certificates` (host `api.nfse.io`) lista, consulta e remove certificados +digitais por **thumbprint**. Complementa o upload legado +(`companies.uploadCertificate`, host `api.nfe.io`). + +## Métodos + +| Método | Descrição | Retorno | +|---|---|---| +| `list(companyId)` | Lista os certificados da empresa. | `{ certificates }` | +| `getByThumbprint(companyId, thumbprint)` | Consulta por thumbprint. | metadados do certificado | +| `deleteByThumbprint(companyId, thumbprint)` | Remove por thumbprint. | `void` | +| `getByThumbprintV1(companyId, thumbprint)` | Variante v1. | metadados | +| `deleteByThumbprintV1(companyId, thumbprint)` | Variante v1. | `void` | + +## Exemplo + +```typescript +const { certificates = [] } = await nfe.certificates.list(companyId); + +const thumb = certificates[0]?.thumbprint; +if (thumb) { + const cert = await nfe.certificates.getByThumbprint(companyId, thumb); + // await nfe.certificates.deleteByThumbprint(companyId, thumb); +} +``` + +:::info Upload de certificado +O envio do `.pfx` é feito por `companies.uploadCertificate` (host `api.nfe.io`). +Aqui você consulta/remove o que já foi enviado. +::: + +## Próximos passos + +- [Empresas](./companies.md) diff --git a/docs/recursos/companies.md b/docs/recursos/companies.md new file mode 100644 index 0000000..5041a97 --- /dev/null +++ b/docs/recursos/companies.md @@ -0,0 +1,61 @@ +--- +title: Empresas (emitentes) +sidebar_label: Empresas +sidebar_position: 4 +slug: empresas +description: CRUD de empresas emitentes, iteração paginada, verificação de existência e gestão de certificados digitais com nfe.companies. +--- + +# Empresas (emitentes) + +`nfe.companies` gerencia as empresas emitentes (host `api.nfe.io`). A empresa é +o pré-requisito de toda emissão — e precisa de inscrição +([municipal](./municipal-taxes.md)/[estadual](./state-taxes.md)) e certificado. + +## Métodos + +| Método | Descrição | Retorno | +|---|---|---| +| `create(data)` | Cria uma empresa. | `Company` | +| `retrieve(id)` / `update(id, data)` / `remove(id)` | CRUD. | `Company` / `void` | +| `exists(id)` | Existência via `HEAD` (404 → `false`). | `boolean` | +| `list(options?)` | Lista paginada. | `ListResponse` (`{ data, page }`) | +| `listAll()` | Coleta todas as páginas. | `Company[]` | +| `listIterator()` | Async iterator (lazy). | `AsyncIterableIterator` | +| `findByTaxNumber(cnpj)` / `findByName(name)` | Busca. | `Company` / lista | +| `uploadCertificate` / `replaceCertificate` / `validateCertificate` | Certificado (.pfx). | metadados | +| `getCertificateStatus` / `checkCertificateExpiration` | Status/validade do certificado. | info | + +## Criar e verificar + +```typescript +const company = await nfe.companies.create({ + name: 'Minha Empresa LTDA', + federalTaxNumber: 11222333000181, + email: 'fiscal@empresa.com', +}); + +if (await nfe.companies.exists(company.id)) { + // pronta para configurar inscrições e certificado +} +``` + +## Iterar todas as empresas + +```typescript +for await (const c of nfe.companies.listIterator()) { + console.log(c.id, c.name); +} +``` + +:::info Certificado +O upload do certificado (`uploadCertificate`) vive no host `api.nfe.io`. A +consulta/remoção por thumbprint fica em [Certificados](./certificates.md) +(host `api.nfse.io`). +::: + +## Próximos passos + +- [Inscrições municipais](./municipal-taxes.md) e [estaduais](./state-taxes.md) +- [Certificados](./certificates.md) +- [Paginação](../pagination.md) diff --git a/docs/recursos/consumer-invoice-query.md b/docs/recursos/consumer-invoice-query.md new file mode 100644 index 0000000..c7e115a --- /dev/null +++ b/docs/recursos/consumer-invoice-query.md @@ -0,0 +1,32 @@ +--- +title: Consulta de cupom (CFe-SAT) +sidebar_label: Consulta CFe-SAT +sidebar_position: 21 +slug: consulta-cfe-sat +description: Consulte cupons CFe-SAT por chave de acesso e baixe o XML com nfe.consumerInvoiceQuery (host nfe.api.nfe.io). +--- + +# Consulta de cupom (CFe-SAT) + +`nfe.consumerInvoiceQuery` (host `nfe.api.nfe.io`, chave de dados) consulta +cupons fiscais CFe-SAT por **chave de acesso**. É somente leitura — distinto de +[`consumerInvoices`](./consumer-invoices.md) (emissão de NFC-e). + +## Métodos + +| Método | Descrição | Retorno | +|---|---|---| +| `retrieve(accessKey)` | Consulta o cupom por chave. | dados do cupom | +| `downloadXml(accessKey)` | XML do cupom. | `Buffer` | + +## Exemplo + +```typescript +const coupon = await nfe.consumerInvoiceQuery.retrieve(accessKey); +const xml = await nfe.consumerInvoiceQuery.downloadXml(accessKey); +``` + +## Próximos passos + +- [Consulta de NF-e](./product-invoice-query.md) +- [NFC-e (emissão)](./consumer-invoices.md) diff --git a/docs/recursos/consumer-invoices.md b/docs/recursos/consumer-invoices.md new file mode 100644 index 0000000..ab1c33d --- /dev/null +++ b/docs/recursos/consumer-invoices.md @@ -0,0 +1,55 @@ +--- +title: NFC-e — notas de consumidor +sidebar_label: NFC-e (consumidor) +sidebar_position: 3 +slug: nfc-e +description: Emita e gerencie NFC-e com nfe.consumerInvoices no host api.nfse.io — emissão webhook-driven e environment obrigatório na listagem/leituras. +--- + +# NFC-e (notas de consumidor) + +`nfe.consumerInvoices` cobre o ciclo de vida da NFC-e (host `api.nfse.io`). A +emissão é **webhook-driven** (202 = enfileirada; conclusão via webhook). É +distinto de `nfe.consumerInvoiceQuery` (consulta de cupom CFe-SAT, somente +leitura). + +## Métodos + +| Método | Descrição | Retorno | +|---|---|---| +| `create(companyId, data)` | Emite a NFC-e (webhook-driven). | `ConsumerInvoice` | +| `list(companyId, options)` | Lista NFC-e. **`options.environment` é obrigatório.** | `{ consumerInvoices, hasMore }` | +| `retrieve(companyId, invoiceId, environment?)` | Consulta por id. | `ConsumerInvoice` | +| `cancel(companyId, invoiceId)` | Cancela a NFC-e. | `ConsumerInvoice` | +| `getItems(companyId, invoiceId, environment?)` | Itens da nota. | resposta de itens | +| `getEvents(companyId, invoiceId, environment?)` | Eventos da nota. | resposta de eventos | +| `downloadPdf` / `downloadXml` / `downloadRejectionXml` (`, environment?`) | Downloads (Buffer). | `Buffer` | +| `disable(companyId, data)` | Inutilização de numeração. | resultado | + +`ConsumerInvoiceListOptions = { environment: 'Production' \| 'Test'; startingAfter?; endingBefore?; limit?; q? }`. + +:::warning `environment` obrigatório +A API exige `environment` (`Production`/`Test`) na listagem; as leituras aceitam +`environment` opcional. Sem ele, a listagem retorna `400`. +::: + +## Listar e emitir + +```typescript +const { consumerInvoices = [] } = await nfe.consumerInvoices.list(companyId, { + environment: 'Test', + limit: 5, +}); + +const invoice = await nfe.consumerInvoices.create(companyId, { + buyer: { federalTaxNumber: 52998224725, name: 'Consumidor Final' }, + items: [{ code: '001', description: 'Produto', quantity: 1, unitAmount: 10.0 }], + payment: { method: 'Cash', amount: 10.0 }, +}); +// 202: aguarde o webhook +``` + +## Próximos passos + +- [Webhooks](../webhooks.md) e [Downloads](../downloads.md) +- [NFC-e RTC](./rtc.md) diff --git a/docs/recursos/inbound-product-invoices.md b/docs/recursos/inbound-product-invoices.md new file mode 100644 index 0000000..21a2022 --- /dev/null +++ b/docs/recursos/inbound-product-invoices.md @@ -0,0 +1,35 @@ +--- +title: NF-e de entrada (inbound / distribuição) +sidebar_label: NF-e de entrada +sidebar_position: 19 +slug: nfe-de-entrada +description: Captura automática de NF-e de entrada (distribuição DF-e) com nfe.inboundProductInvoices (host api.nfse.io). +--- + +# NF-e de entrada (inbound) + +`nfe.inboundProductInvoices` (host `api.nfse.io`, chave de dados) gerencia a +captura automática de NF-e de entrada (distribuição DF-e) e a consulta dos +documentos capturados. + +## Métodos + +| Método | Descrição | Retorno | +|---|---|---| +| `enableAutoFetch(companyId, data)` / `disableAutoFetch(companyId)` | Liga/desliga a captura automática. | settings | +| `getSettings(companyId)` | Configuração atual. | settings | +| `getDetails(companyId, ...)` / `getProductInvoiceDetails(...)` | Detalhes dos documentos. | dados | +| `getEventDetails(...)` / `getProductInvoiceEventDetails(...)` | Detalhes de eventos. | dados | +| `getXml(companyId, accessKey)` | XML do documento capturado. | XML | + +## Exemplo + +```typescript +await nfe.inboundProductInvoices.enableAutoFetch(companyId, { /* config */ }); +const settings = await nfe.inboundProductInvoices.getSettings(companyId); +``` + +## Próximos passos + +- [CT-e](./transportation-invoices.md) +- [Consulta de NF-e por chave](./product-invoice-query.md) diff --git a/docs/recursos/legal-entity-lookup.md b/docs/recursos/legal-entity-lookup.md new file mode 100644 index 0000000..258c200 --- /dev/null +++ b/docs/recursos/legal-entity-lookup.md @@ -0,0 +1,33 @@ +--- +title: Consulta de CNPJ (pessoa jurídica) +sidebar_label: Consulta CNPJ +sidebar_position: 12 +slug: consulta-cnpj +description: Consulte dados cadastrais e de inscrição estadual por CNPJ com nfe.legalEntityLookup (host legalentity.api.nfe.io). +--- + +# Consulta de CNPJ + +`nfe.legalEntityLookup` (host `legalentity.api.nfe.io`, chave de dados) consulta +dados cadastrais de pessoa jurídica na Receita Federal e informações de +inscrição estadual. + +## Métodos + +| Método | Descrição | Retorno | +|---|---|---| +| `getBasicInfo(cnpj)` | Dados cadastrais (razão social, endereço, CNAE, etc.). | `{ legalEntity }` | +| `getStateTaxInfo(cnpj)` | Inscrição estadual. | dados de IE | +| `getStateTaxForInvoice(cnpj, ...)` | IE adequada para emissão. | dados de IE | +| `getSuggestedStateTaxForInvoice(cnpj, ...)` | IE sugerida para emissão. | dados de IE | + +## Exemplo + +```typescript +const { legalEntity } = await nfe.legalEntityLookup.getBasicInfo('06990590000123'); +console.log(legalEntity.name, legalEntity.address?.city?.name); +``` + +## Próximos passos + +- [Consulta CPF](./natural-person-lookup.md) · [Consulta CEP](./addresses.md) diff --git a/docs/recursos/legal-people.md b/docs/recursos/legal-people.md new file mode 100644 index 0000000..1fcc86c --- /dev/null +++ b/docs/recursos/legal-people.md @@ -0,0 +1,37 @@ +--- +title: Pessoas jurídicas (tomadores PJ) +sidebar_label: Pessoas jurídicas +sidebar_position: 14 +slug: pessoas-juridicas +description: CRUD de pessoas jurídicas (tomadores) por empresa com nfe.legalPeople, incluindo criação em lote e busca por CNPJ. +--- + +# Pessoas jurídicas (tomadores PJ) + +`nfe.legalPeople` (host `api.nfe.io`) gerencia pessoas jurídicas vinculadas a +uma empresa (tomadores/clientes PJ). + +## Métodos + +| Método | Descrição | Retorno | +|---|---|---| +| `list(companyId)` | Lista as PJ. | `ListResponse` | +| `create(companyId, data)` | Cria uma PJ. | `LegalPerson` | +| `retrieve(companyId, personId)` / `update(...)` / `delete(...)` | CRUD. | `LegalPerson` / `void` | +| `createBatch(companyId, data)` | Criação em lote. | resultado do lote | +| `findByTaxNumber(companyId, cnpj)` | Busca por CNPJ. | `LegalPerson` | + +## Exemplo + +```typescript +const person = await nfe.legalPeople.create(companyId, { + federalTaxNumber: 11444555000149, + name: 'Cliente PJ LTDA', + email: 'cliente-pj@exemplo.com', +}); +``` + +## Próximos passos + +- [Pessoas físicas](./natural-people.md) +- [Empresas](./companies.md) diff --git a/docs/recursos/municipal-taxes.md b/docs/recursos/municipal-taxes.md new file mode 100644 index 0000000..3c2130e --- /dev/null +++ b/docs/recursos/municipal-taxes.md @@ -0,0 +1,45 @@ +--- +title: Inscrições municipais +sidebar_label: Inscrições municipais +sidebar_position: 5 +slug: inscricoes-municipais +description: CRUD de inscrições municipais (pré-requisito da NFS-e) com nfe.municipalTaxes, incluindo updatePrefecture (PATCH) e getSeries. +--- + +# Inscrições municipais + +`nfe.municipalTaxes` (host `api.nfse.io`) gerencia as inscrições municipais da +empresa — **pré-requisito para emissão de NFS-e**. Espelha +[`stateTaxes`](./state-taxes.md). + +## Métodos + +| Método | Descrição | Retorno | +|---|---|---| +| `list(companyId)` | Lista as inscrições. | `{ municipalTaxes, hasMore }` | +| `create(companyId, data)` | Cria uma inscrição. | `MunicipalTax` | +| `retrieve(companyId, municipalTaxId)` | Consulta por id. | `MunicipalTax` | +| `update(companyId, municipalTaxId, data)` | Atualiza. | `MunicipalTax` | +| `delete(companyId, municipalTaxId)` | Remove. | `void` | +| `updatePrefecture(companyId, municipalTaxId, data)` | Atualiza credenciais da prefeitura (HTTP **PATCH**). | `MunicipalTax` | +| `getSeries(companyId, municipalTaxId, serie)` | Consulta uma série de RPS. | dados da série | + +## Exemplo + +```typescript +const { municipalTaxes = [] } = await nfe.municipalTaxes.list(companyId); + +const created = await nfe.municipalTaxes.create(companyId, { + code: '123456', + // ...credenciais/config da prefeitura +}); + +await nfe.municipalTaxes.updatePrefecture(companyId, created.id, { + // login/senha da prefeitura +}); +``` + +## Próximos passos + +- [Notas de serviço (NFS-e)](./service-invoices.md) +- [Empresas](./companies.md) diff --git a/docs/recursos/natural-people.md b/docs/recursos/natural-people.md new file mode 100644 index 0000000..5942c29 --- /dev/null +++ b/docs/recursos/natural-people.md @@ -0,0 +1,37 @@ +--- +title: Pessoas físicas (tomadores PF) +sidebar_label: Pessoas físicas +sidebar_position: 15 +slug: pessoas-fisicas +description: CRUD de pessoas físicas (tomadores) por empresa com nfe.naturalPeople, incluindo criação em lote e busca por CPF. +--- + +# Pessoas físicas (tomadores PF) + +`nfe.naturalPeople` (host `api.nfe.io`) gerencia pessoas físicas vinculadas a +uma empresa (tomadores/clientes PF). + +## Métodos + +| Método | Descrição | Retorno | +|---|---|---| +| `list(companyId)` | Lista as PF. | `ListResponse` | +| `create(companyId, data)` | Cria uma PF. | `NaturalPerson` | +| `retrieve(companyId, personId)` / `update(...)` / `delete(...)` | CRUD. | `NaturalPerson` / `void` | +| `createBatch(companyId, data)` | Criação em lote. | resultado do lote | +| `findByTaxNumber(companyId, cpf)` | Busca por CPF. | `NaturalPerson` | + +## Exemplo + +```typescript +const person = await nfe.naturalPeople.create(companyId, { + federalTaxNumber: 52998224725, + name: 'Cliente PF', + email: 'cliente-pf@exemplo.com', +}); +``` + +## Próximos passos + +- [Pessoas jurídicas](./legal-people.md) +- [Empresas](./companies.md) diff --git a/docs/recursos/natural-person-lookup.md b/docs/recursos/natural-person-lookup.md new file mode 100644 index 0000000..943ea19 --- /dev/null +++ b/docs/recursos/natural-person-lookup.md @@ -0,0 +1,33 @@ +--- +title: Consulta de CPF (pessoa física) +sidebar_label: Consulta CPF +sidebar_position: 13 +slug: consulta-cpf +description: Consulte a situação cadastral de um CPF com nfe.naturalPersonLookup (host naturalperson.api.nfe.io). Requer data de nascimento. +--- + +# Consulta de CPF + +`nfe.naturalPersonLookup` (host `naturalperson.api.nfe.io`, chave de dados) +consulta a situação cadastral de uma pessoa física. + +## Métodos + +| Método | Descrição | Retorno | +|---|---|---| +| `getStatus(cpf, birthDate)` | Situação cadastral do CPF. | dados de situação | + +:::warning Data de nascimento é obrigatória +A API exige a **data de nascimento** junto do CPF. Chamar sem ela retorna +erro de validação. +::: + +## Exemplo + +```typescript +const status = await nfe.naturalPersonLookup.getStatus('52998224725', '1990-01-31'); +``` + +## Próximos passos + +- [Consulta CNPJ](./legal-entity-lookup.md) · [Consulta CEP](./addresses.md) diff --git a/docs/recursos/notifications.md b/docs/recursos/notifications.md new file mode 100644 index 0000000..7f59419 --- /dev/null +++ b/docs/recursos/notifications.md @@ -0,0 +1,33 @@ +--- +title: Notificações +sidebar_label: Notificações +sidebar_position: 8 +slug: notificacoes +description: Liste, consulte, remova e envie por e-mail as notificações da empresa com nfe.notifications (host api.nfe.io). +--- + +# Notificações + +`nfe.notifications` (host `api.nfe.io`) gerencia as notificações da empresa. + +## Métodos + +| Método | Descrição | Retorno | +|---|---|---| +| `list(companyId)` | Lista as notificações. | `{ notifications }` | +| `retrieve(companyId, notificationId)` | Consulta por id. | `Notification` | +| `delete(companyId, notificationId)` | Remove. | `void` | +| `sendEmail(companyId, data?)` | Envia notificação por e-mail. | `void` | + +## Exemplo + +```typescript +const { notifications = [] } = await nfe.notifications.list(companyId); + +await nfe.notifications.sendEmail(companyId, { to: 'destino@exemplo.com' }); +``` + +## Próximos passos + +- [Webhooks](../webhooks.md) +- [Empresas](./companies.md) diff --git a/docs/recursos/product-invoice-query.md b/docs/recursos/product-invoice-query.md new file mode 100644 index 0000000..c9a5478 --- /dev/null +++ b/docs/recursos/product-invoice-query.md @@ -0,0 +1,33 @@ +--- +title: Consulta de NF-e por chave (SEFAZ) +sidebar_label: Consulta NF-e +sidebar_position: 20 +slug: consulta-nfe +description: Consulte NF-e por chave de acesso e baixe PDF/XML com nfe.productInvoiceQuery (host nfe.api.nfe.io). +--- + +# Consulta de NF-e por chave (SEFAZ) + +`nfe.productInvoiceQuery` (host `nfe.api.nfe.io`, chave de dados) consulta NF-e +por **chave de acesso** e baixa os arquivos. É somente leitura (não emite). + +## Métodos + +| Método | Descrição | Retorno | +|---|---|---| +| `retrieve(accessKey)` | Consulta a NF-e por chave. | dados da NF-e | +| `downloadPdf(accessKey)` | DANFE em PDF. | `Buffer` | +| `downloadXml(accessKey)` | XML da NF-e. | `Buffer` | +| `listEvents(accessKey)` | Eventos da NF-e. | lista de eventos | + +## Exemplo + +```typescript +const nfe_ = await nfe.productInvoiceQuery.retrieve(accessKey); +const pdf = await nfe.productInvoiceQuery.downloadPdf(accessKey); +``` + +## Próximos passos + +- [Consulta de cupom (CFe-SAT)](./consumer-invoice-query.md) +- [NF-e de entrada](./inbound-product-invoices.md) diff --git a/docs/recursos/product-invoices.md b/docs/recursos/product-invoices.md new file mode 100644 index 0000000..56552ed --- /dev/null +++ b/docs/recursos/product-invoices.md @@ -0,0 +1,57 @@ +--- +title: Notas fiscais de produto (NF-e) +sidebar_label: Notas de produto +sidebar_position: 2 +slug: notas-fiscais-de-produto +description: Emita e gerencie NF-e com nfe.productInvoices no host api.nfse.io — emissão webhook-driven, carta de correção, downloads e inutilização. +--- + +# Notas fiscais de produto (NF-e) + +`nfe.productInvoices` fala com o host `api.nfse.io` e cobre o ciclo de vida da +NF-e. A emissão é **webhook-driven**: um `202` indica que a nota foi enfileirada +e a conclusão é notificada por [webhook](../webhooks.md) (sem polling). + +## Métodos + +| Método | Descrição | Retorno | +|---|---|---| +| `create(companyId, data)` | Emite a NF-e (webhook-driven). | `NfeProductInvoiceIssueData` | +| `createWithStateTax(companyId, data)` | Emite garantindo a inscrição estadual. | `NfeProductInvoiceIssueData` | +| `list(companyId, options)` | Lista NF-e. **Requer `environment`** (`Production`/`Test`). | envelope `{ productInvoices, hasMore }` | +| `retrieve(companyId, invoiceId)` | Consulta por id. | dados da NF-e | +| `cancel(companyId, invoiceId)` | Cancela a NF-e. | dados da NF-e | +| `listItems` / `listEvents` | Itens e eventos da nota. | respostas específicas | +| `downloadPdf` / `downloadXml` / `downloadRejectionXml` / `downloadEpecXml` | Downloads (Buffer). | `Buffer` | +| `sendCorrectionLetter(companyId, id, data)` | Emite carta de correção (CC-e). | evento | +| `downloadCorrectionLetterPdf` / `downloadCorrectionLetterXml` | Downloads da CC-e. | `Buffer` | +| `disable` / `disableRange` | Inutilização de numeração. | resultado | + +:::warning `environment` obrigatório na listagem +`list` exige `environment` (`'Production'` ou `'Test'`). Chamar sem o parâmetro +retorna `400`. +::: + +## Emitir e acompanhar + +```typescript +const invoice = await nfe.productInvoices.create(companyId, { + buyer: { federalTaxNumber: 52998224725, name: 'Cliente Teste' }, + items: [{ code: '001', description: 'Produto', quantity: 1, unitAmount: 100.0 }], +}); +// 202: aguarde o webhook de conclusão (não faz polling) +``` + +## Carta de correção (CC-e) + +```typescript +await nfe.productInvoices.sendCorrectionLetter(companyId, invoiceId, { + correction: 'Correção da descrição do item 1', +}); +``` + +## Próximos passos + +- [Webhooks](../webhooks.md) e [Assíncrono](../async-and-polling.md) +- [Inscrições estaduais](./state-taxes.md) (pré-requisito da NF-e) +- [NF-e RTC](./rtc.md) diff --git a/docs/recursos/rtc.md b/docs/recursos/rtc.md new file mode 100644 index 0000000..b8ee05f --- /dev/null +++ b/docs/recursos/rtc.md @@ -0,0 +1,48 @@ +--- +title: Emissão RTC (recursos) +sidebar_label: RTC +sidebar_position: 10 +slug: recursos-rtc +description: Recursos de emissão no leiaute RTC — serviceInvoicesRtc (NFS-e) e productInvoicesRtc (NF-e/NFC-e). +--- + +# RTC (recursos) + +O leiaute RTC (IBS/CBS/IS) é selecionado pelo **payload**, nos mesmos endpoints +da emissão atual. Para o guia conceitual, veja [Emissão RTC](../rtc-emission.md). + +## `nfe.serviceInvoicesRtc` (NFS-e RTC · api.nfe.io) + +| Método | Descrição | Retorno | +|---|---|---| +| `create(companyId, data)` | Emite NFS-e RTC. | `CreateInvoiceResponse` (união discriminada) | +| `createAndWait(companyId, data, options?)` | Emite + polling até terminal. | `ServiceInvoiceData` | +| `retrieve(companyId, invoiceId)` | Consulta por id. | `ServiceInvoiceData` | +| `downloadCancellationXml(companyId, invoiceId)` | XML do evento de cancelamento. | `Buffer` | + +Tipo de request: `NFSeRtcRequest` (grupo `ibsCbs`). + +## `nfe.productInvoicesRtc` (NF-e/NFC-e RTC · api.nfse.io) + +| Método | Descrição | Retorno | +|---|---|---| +| `create(companyId, data)` | Emite NF-e/NFC-e RTC (webhook-driven, sem polling). | `NfeProductInvoiceIssueData` | + +Tipo de request: `ProductInvoiceRtcRequest` (IBS estadual/municipal, CBS, IS). + +## Exemplo + +```typescript +const invoice = await nfe.serviceInvoicesRtc.createAndWait(companyId, { + borrower: { federalTaxNumber: 52998224725, name: 'Cliente Teste' }, + cityServiceCode: '10677', + description: 'Serviço (RTC)', + servicesAmount: 100.0, + ibsCbs: {}, +}); +``` + +## Próximos passos + +- [Emissão RTC (guia)](../rtc-emission.md) +- [Notas de serviço](./service-invoices.md) · [Notas de produto](./product-invoices.md) diff --git a/docs/recursos/service-invoices.md b/docs/recursos/service-invoices.md new file mode 100644 index 0000000..8cfa1fb --- /dev/null +++ b/docs/recursos/service-invoices.md @@ -0,0 +1,112 @@ +--- +title: Notas fiscais de serviço (NFS-e) +sidebar_label: Notas de serviço +sidebar_position: 1 +slug: notas-fiscais-de-servico +description: Emita, liste, consulte, cancele e baixe NFS-e com nfe.serviceInvoices no host api.nfe.io /v1, tratando o retorno discriminado 202. +--- + +# Notas fiscais de serviço (NFS-e) + +`nfe.serviceInvoices` é o recurso canônico de emissão da plataforma. Ele fala +com o host `api.nfe.io` no caminho `/v1` e cobre todo o ciclo de vida da nota: +emissão (síncrona ou assíncrona), consulta, listagem, cancelamento, envio por +e-mail e download de PDF/XML. + +## Métodos + +| Método | Descrição | Retorno | +|---|---|---| +| `create(companyId, data)` | Emite a NFS-e. | `CreateInvoiceResponse` (união discriminada) | +| `createAndWait(companyId, data, options?)` | Emite e faz polling até um estado terminal. | `ServiceInvoiceData` | +| `list(companyId, options?)` | Lista paginada (page-style) com filtros de data. | `ServiceInvoiceListResponse` (`{ serviceInvoices, page }`) | +| `retrieve(companyId, invoiceId)` | Consulta uma NFS-e por id. | `ServiceInvoiceData` | +| `retrieveByExternalId(companyId, externalId)` | Consulta por id externo (idempotência/reconciliação). | `ServiceInvoiceData` | +| `cancel(companyId, invoiceId)` | Cancela (assíncrono). | `CancelInvoiceResponse` (união discriminada) | +| `cancelAndWait(companyId, invoiceId, options?)` | Cancela e faz polling até concluir. | `ServiceInvoiceData` | +| `sendEmail(companyId, invoiceId)` | Reenvia a nota por e-mail ao tomador. | `{ sent, message }` | +| `downloadPdf(companyId, invoiceId?)` | PDF da nota; sem `invoiceId`, baixa o ZIP da empresa. | `Buffer` | +| `downloadXml(companyId, invoiceId?)` | XML da nota; sem `invoiceId`, baixa o ZIP da empresa. | `Buffer` | + +As opções de `list` são `pageIndex`, `pageCount`, `issuedBegin`, `issuedEnd`, +`createdBegin`, `createdEnd` e `hasTotals`. + +:::warning Validação fail-fast +Os métodos validam `companyId`/`invoiceId` **antes** de qualquer chamada HTTP. +Ids vazios ou em branco lançam `ValidationError` sem tráfego de rede. +::: + +## Emitir uma NFS-e e tratar o retorno discriminado + +`create` devolve `{ status: 'async' }` (HTTP 202, enfileirada) ou +`{ status: 'immediate' }` (HTTP 201, já materializada). Distinga pela tag +`status` — o TypeScript estreita o tipo em cada ramo. + +```typescript +const result = await nfe.serviceInvoices.create('55df4dc6b6cd9007e4f13ee8', { + cityServiceCode: '2690', + description: 'Manutenção e suporte técnico', + servicesAmount: 100.0, + borrower: { federalTaxNumber: 191, name: 'Banco do Brasil SA' }, +}); + +if (result.status === 'async') { + result.response.invoiceId; // id para reconsultar enquanto processa + result.response.location; // caminho do header Location +} else { + result.invoice; // ServiceInvoiceData já emitida +} +``` + +## Acompanhar até um estado terminal (polling) + +`createAndWait` combina `create` + polling (backoff exponencial) e devolve a +nota já em estado terminal, lançando `InvoiceProcessingError` em `IssueFailed`. + +```typescript +const invoice = await nfe.serviceInvoices.createAndWait(companyId, data, { + timeout: 120000, // padrão: 2 min + initialDelay: 1000, + onPoll: (attempt, flowStatus) => console.log(attempt, flowStatus), +}); + +invoice.flowStatus; // 'Issued' +``` + +Veja [Emissão assíncrona e polling](../async-and-polling.md) para os estados de +`FlowStatus` e as opções de polling. + +## Baixar PDF e XML (bytes binários) + +```typescript +import { writeFileSync } from 'node:fs'; + +const pdf = await nfe.serviceInvoices.downloadPdf(companyId, invoiceId); +writeFileSync('nota.pdf', pdf); + +// ZIP de todas as notas da empresa (sem invoiceId): +const zip = await nfe.serviceInvoices.downloadXml(companyId); +writeFileSync('notas-xml.zip', zip); +``` + +## Cancelar e reenviar por e-mail + +O cancelamento é **assíncrono** (HTTP 202 + `Location`). Use `cancelAndWait` +para bloquear até o cancelamento concluir. + +```typescript +const settled = await nfe.serviceInvoices.cancelAndWait(companyId, invoiceId); +settled.flowStatus; // 'Cancelled' + +// Sem bloquear — trate a união discriminada: +const c = await nfe.serviceInvoices.cancel(companyId, invoiceId); +if (c.status === 'async') console.log('cancelando:', c.response.invoiceId); + +await nfe.serviceInvoices.sendEmail(companyId, invoiceId); +``` + +## Próximos passos + +- [Emissão assíncrona e polling](../async-and-polling.md) +- [Downloads (PDF/XML)](../downloads.md) e [Paginação](../pagination.md) +- [Empresas](./companies.md) e [Inscrições municipais](./municipal-taxes.md) (pré-requisitos da emissão de NFS-e) diff --git a/docs/recursos/state-taxes.md b/docs/recursos/state-taxes.md new file mode 100644 index 0000000..17f44aa --- /dev/null +++ b/docs/recursos/state-taxes.md @@ -0,0 +1,38 @@ +--- +title: Inscrições estaduais +sidebar_label: Inscrições estaduais +sidebar_position: 6 +slug: inscricoes-estaduais +description: CRUD de inscrições estaduais (pré-requisito da NF-e) com nfe.stateTaxes, incluindo troca de autorizador (switchAuthorizer). +--- + +# Inscrições estaduais + +`nfe.stateTaxes` (host `api.nfse.io`) gerencia as inscrições estaduais (IE) da +empresa — **pré-requisito para emissão de NF-e**. + +## Métodos + +| Método | Descrição | Retorno | +|---|---|---| +| `list(companyId)` | Lista as inscrições. | `{ stateTaxes, hasMore }` | +| `create(companyId, data)` | Cria uma inscrição. | `StateTax` | +| `retrieve(companyId, stateTaxId)` | Consulta por id. | `StateTax` | +| `update(companyId, stateTaxId, data)` | Atualiza. | `StateTax` | +| `delete(companyId, stateTaxId)` | Remove. | `void` | +| `switchAuthorizer(companyId, stateTaxId, data?)` | Troca o autorizador NF-e (ex.: SVRS). | `StateTax` | + +## Exemplo + +```typescript +const { stateTaxes = [] } = await nfe.stateTaxes.list(companyId); + +await nfe.stateTaxes.switchAuthorizer(companyId, stateTaxes[0].id, { + authorizer: 'SVRS', +}); +``` + +## Próximos passos + +- [Notas de produto (NF-e)](./product-invoices.md) +- [Empresas](./companies.md) diff --git a/docs/recursos/tax-calculation.md b/docs/recursos/tax-calculation.md new file mode 100644 index 0000000..6dd4580 --- /dev/null +++ b/docs/recursos/tax-calculation.md @@ -0,0 +1,36 @@ +--- +title: Cálculo de impostos +sidebar_label: Cálculo de impostos +sidebar_position: 17 +slug: calculo-de-impostos +description: Calcule tributos (ICMS, PIS, COFINS, IPI, II) por item com nfe.taxCalculation (host api.nfse.io). +--- + +# Cálculo de impostos + +`nfe.taxCalculation` (host `api.nfse.io`) expõe o motor de cálculo de tributos +para operações com produtos. + +## Métodos + +| Método | Descrição | Retorno | +|---|---|---| +| `calculate(tenantId, request)` | Calcula os tributos da operação. | detalhamento por item | + +Os códigos usados no request (operação, finalidade, perfis fiscais) vêm de +[Códigos de tributos](./tax-codes.md). + +## Exemplo + +```typescript +const result = await nfe.taxCalculation.calculate(tenantId, { + // operação + itens conforme os códigos auxiliares + items: [{ /* ... */ }], +}); +// result contém o detalhamento (ICMS, PIS, COFINS, IPI, II) por item +``` + +## Próximos passos + +- [Códigos de tributos](./tax-codes.md) +- [Notas de produto (NF-e)](./product-invoices.md) diff --git a/docs/recursos/tax-codes.md b/docs/recursos/tax-codes.md new file mode 100644 index 0000000..52faa56 --- /dev/null +++ b/docs/recursos/tax-codes.md @@ -0,0 +1,38 @@ +--- +title: Códigos auxiliares de tributos (tax codes) +sidebar_label: Códigos de tributos +sidebar_position: 16 +slug: codigos-de-tributos +description: Liste códigos de operação, finalidade de aquisição e perfis fiscais com nfe.taxCodes (host api.nfse.io, chave principal). +--- + +# Códigos auxiliares (tax codes) + +`nfe.taxCodes` (host `api.nfse.io`, **chave principal**) lista os códigos +auxiliares usados no motor de [cálculo de impostos](./tax-calculation.md). + +## Métodos + +| Método | Descrição | Retorno | +|---|---|---| +| `listOperationCodes(options?)` | Códigos de operação. | paginado (`{ items, currentPage, totalPages, totalCount }`) | +| `listAcquisitionPurposes(options?)` | Finalidades de aquisição. | paginado | +| `listIssuerTaxProfiles(options?)` | Perfis fiscais do emissor. | paginado | +| `listRecipientTaxProfiles(options?)` | Perfis fiscais do destinatário. | paginado | + +## Exemplo + +```typescript +const ops = await nfe.taxCodes.listOperationCodes({ pageIndex: 1, pageCount: 20 }); +for (const code of ops.items ?? []) console.log(code.code, code.description); +``` + +:::info Chave principal +Ao contrário de outros recursos em `api.nfse.io`, `taxCodes` usa a **chave +principal** (`apiKey`). Em contas com `dataApiKey` separada, o SDK já roteia +para a chave correta. +::: + +## Próximos passos + +- [Cálculo de impostos](./tax-calculation.md) diff --git a/docs/recursos/transportation-invoices.md b/docs/recursos/transportation-invoices.md new file mode 100644 index 0000000..3ca1c3a --- /dev/null +++ b/docs/recursos/transportation-invoices.md @@ -0,0 +1,34 @@ +--- +title: CT-e (conhecimento de transporte) +sidebar_label: CT-e (transporte) +sidebar_position: 18 +slug: cte-transporte +description: Habilite e consulte CT-e por chave de acesso com nfe.transportationInvoices (host api.nfse.io). +--- + +# CT-e (conhecimento de transporte) + +`nfe.transportationInvoices` (host `api.nfse.io`, chave de dados) habilita a +captura de CT-e e consulta documentos por chave de acesso. + +## Métodos + +| Método | Descrição | Retorno | +|---|---|---| +| `enable(companyId, data)` / `disable(companyId)` | Habilita/desabilita a captura de CT-e. | settings | +| `getSettings(companyId)` | Configuração atual. | settings | +| `retrieve(companyId, accessKey)` | Consulta um CT-e por chave. | dados do CT-e | +| `downloadXml(companyId, accessKey)` | XML do CT-e. | XML | +| `getEvent(companyId, ...)` / `downloadEventXml(companyId, ...)` | Eventos do CT-e. | evento / XML | + +## Exemplo + +```typescript +const settings = await nfe.transportationInvoices.getSettings(companyId); +const cte = await nfe.transportationInvoices.retrieve(companyId, accessKey); +``` + +## Próximos passos + +- [NF-e de entrada (inbound)](./inbound-product-invoices.md) +- [Roteamento multi-host / multi-chave](../multi-host-routing.md) diff --git a/docs/recursos/webhooks.md b/docs/recursos/webhooks.md new file mode 100644 index 0000000..ca63964 --- /dev/null +++ b/docs/recursos/webhooks.md @@ -0,0 +1,51 @@ +--- +title: Webhooks (recurso) +sidebar_label: Webhooks +sidebar_position: 9 +slug: recurso-webhooks +description: Métodos de webhooks por empresa e por conta, verificação de assinatura e lista de eventos ao vivo com nfe.webhooks. +--- + +# Webhooks (recurso) + +`nfe.webhooks` gerencia assinaturas de webhook e verifica assinaturas de +entrega. Para o guia conceitual (assinatura HMAC, `express.raw`), veja +[Webhooks](../webhooks.md). + +## Métodos — por empresa (`/companies/{id}/webhooks`) + +| Método | Descrição | +|---|---| +| `list(companyId)` | Lista os webhooks da empresa. | +| `create(companyId, data)` | Cria um webhook. | +| `retrieve(companyId, webhookId)` / `update(...)` / `delete(...)` | CRUD. | +| `test(companyId, webhookId)` | Dispara um teste. | +| `validateSignature(payload, signature, secret)` | Valida a assinatura HMAC-SHA1 (`x-hub-signature`). | + +## Métodos — por conta (`/v2/webhooks`, sem `companyId`) + +| Método | Descrição | +|---|---| +| `listAccountWebhooks()` | Lista webhooks da conta (`{ data }`). | +| `createAccountWebhook(data)` | Cria webhook de conta. | +| `retrieveAccountWebhook(id)` / `updateAccountWebhook(id, data)` / `deleteAccountWebhook(id)` | CRUD. | +| `pingAccountWebhook(id)` | Dispara um ping de teste. | +| `deleteAllAccountWebhooks()` | ⚠️ Remove **todos** os webhooks da conta. | +| `fetchEventTypes()` | Lista de tipos de evento **ao vivo** (`string[]`). | + +## Exemplo + +```typescript +const eventTypes = await nfe.webhooks.fetchEventTypes(); + +const created = await nfe.webhooks.createAccountWebhook({ + uri: 'https://seu-site.com/webhook', + events: eventTypes.slice(0, 2), +}); +await nfe.webhooks.pingAccountWebhook(created.id); +``` + +## Próximos passos + +- [Webhooks (guia)](../webhooks.md) +- [Assíncrono e webhook-driven](../async-and-polling.md) diff --git a/docs/rtc-emission.md b/docs/rtc-emission.md new file mode 100644 index 0000000..a902402 --- /dev/null +++ b/docs/rtc-emission.md @@ -0,0 +1,61 @@ +--- +title: Emissão RTC (Reforma Tributária) no SDK Node.js da NFE.io +sidebar_label: Emissão RTC +sidebar_position: 9 +slug: emissao-rtc +description: Emita NFS-e e NF-e/NFC-e no leiaute RTC (IBS/CBS/IS). O RTC é selecionado pelo payload, no mesmo endpoint da emissão atual. +--- + +# Emissão RTC (Reforma Tributária do Consumo) + +O leiaute RTC (grupos **IBS**, **CBS** e **IS**) é **selecionado pelo payload** — +os endpoints são os mesmos da emissão atual. O SDK expõe recursos dedicados para +deixar o opt-in explícito e tipado. + +- `nfe.serviceInvoicesRtc` — NFS-e no leiaute RTC (grupo `ibsCbs`). +- `nfe.productInvoicesRtc` — NF-e/NFC-e no leiaute RTC (IBS estadual/municipal, CBS, IS). + +Os recursos de emissão existentes (`serviceInvoices`/`productInvoices`) +permanecem inalterados; RTC é opt-in. + +## NFS-e RTC (com polling) + +`serviceInvoicesRtc.create` retorna a mesma união discriminada de +`serviceInvoices`; `createAndWait` faz polling até o estado terminal. + +```typescript +const invoice = await nfe.serviceInvoicesRtc.createAndWait(companyId, { + borrower: { federalTaxNumber: 52998224725, name: 'Cliente Teste' }, + cityServiceCode: '10677', + description: 'Serviço (RTC)', + servicesAmount: 100.0, + ibsCbs: { /* grupo RTC conforme a NT_2025.002 */ }, +}); +``` + +Também há `retrieve` e `downloadCancellationXml` (XML do evento de cancelamento, +Ambiente Nacional). + +## NF-e/NFC-e RTC (webhook-driven) + +`productInvoicesRtc.create` **não faz polling** (202 = enfileirada; conclusão +via webhook), espelhando `productInvoices`. + +```typescript +const invoice = await nfe.productInvoicesRtc.create(companyId, { + buyer: { federalTaxNumber: 52998224725, name: 'Cliente Teste' }, + items: [{ code: '001', description: 'Produto', quantity: 1, unitAmount: 100.0 }], + ibsCbsIs: { /* grupos RTC conforme a NT_2025.002 */ }, +}); +``` + +:::info Tipos de request +`NFSeRtcRequest` (NFS-e) e `ProductInvoiceRtcRequest` (NF-e/NFC-e) são derivados +das specs oficiais. A proveniência das specs está registrada em +`openapi/spec/SOURCES.json` (NT_2025.002_v1.30). +::: + +## Próximos passos + +- [Assíncrono e polling](./async-and-polling.md) e [Webhooks](./webhooks.md) +- [Notas de serviço (NFS-e)](./recursos/service-invoices.md) · [Notas de produto (NF-e)](./recursos/product-invoices.md) diff --git a/docs/webhooks.md b/docs/webhooks.md new file mode 100644 index 0000000..3e6e68d --- /dev/null +++ b/docs/webhooks.md @@ -0,0 +1,67 @@ +--- +title: Webhooks no SDK Node.js da NFE.io +sidebar_label: Webhooks +sidebar_position: 7 +slug: webhooks +description: Verificação de assinatura HMAC-SHA1 (x-hub-signature), webhooks por empresa e por conta, e lista de eventos ao vivo. +--- + +# Webhooks + +A NFE.io notifica eventos (emissão, cancelamento, falha, etc.) por webhook. O +SDK cobre a configuração dos webhooks e a **verificação de assinatura**. + +## Verificar a assinatura (obrigatório) + +As entregas são assinadas com **HMAC-SHA1** no header `x-hub-signature` +(hex maiúsculo, prefixo `sha1=`). Verifique usando o **corpo bruto** (bytes), +não o JSON já parseado. + +```typescript +import express from 'express'; + +app.post('/webhook/nfe', express.raw({ type: '*/*' }), (req, res) => { + const ok = nfe.webhooks.validateSignature( + req.body, // Buffer bruto — NÃO use express.json() + req.headers['x-hub-signature'], // string | string[] | undefined + process.env.NFE_WEBHOOK_SECRET // seu segredo + ); + if (!ok) return res.sendStatus(401); + // ... processe o evento + res.sendStatus(200); +}); +``` + +:::warning Use o corpo bruto +`validateSignature` compara os bytes exatos que a NFE.io assinou. Se você +parsear o corpo antes (`express.json()`), a reserialização muda os bytes e a +verificação falha. Use `express.raw()` e passe o `Buffer`. +::: + +A comparação é feita com `timingSafeEqual` (resistente a timing attacks) e +aceita a assinatura como `string` ou `string[]`. + +## Webhooks por empresa vs por conta + +| Escopo | Acesso | Caminho | +|---|---|---| +| Empresa | `nfe.webhooks.list/create/retrieve/update/delete(companyId, ...)` | `/companies/{id}/webhooks` | +| Conta | `nfe.webhooks.listAccountWebhooks/createAccountWebhook/...` | `/v2/webhooks` (host-root) | + +Métodos de conta (sem `companyId`): `listAccountWebhooks`, `createAccountWebhook`, +`retrieveAccountWebhook`, `updateAccountWebhook`, `deleteAccountWebhook`, +`pingAccountWebhook` e `deleteAllAccountWebhooks` (⚠️ remove **todos**). + +## Tipos de evento ao vivo + +Prefira `fetchEventTypes()` (fonte da verdade é o servidor) ao invés da lista +fixa `getAvailableEvents()` (marcada como `@deprecated`). + +```typescript +const eventTypes = await nfe.webhooks.fetchEventTypes(); // string[] +``` + +## Próximos passos + +- [Emissão assíncrona e webhook-driven](./async-and-polling.md) +- [Erros](./errors.md) diff --git a/examples/README.md b/examples/README.md index 0313a6a..caf316c 100644 --- a/examples/README.md +++ b/examples/README.md @@ -1,4 +1,4 @@ -# 📚 Exemplos Reais do SDK NFE.io v3 +# 📚 Exemplos Reais do SDK NFE.io v5 Este diretório contém exemplos práticos que você pode executar usando suas credenciais do arquivo `.env.test`. @@ -179,6 +179,42 @@ node examples/tax-calculation.js --- +## ✨ Novos na v5 + +Recursos introduzidos na v5 (validados contra a API ao vivo). Todos usam `NFE_API_KEY` e, +os company-scoped, `NFE_COMPANY_ID` de uma empresa apta a emitir. + +### **rtc-invoices.js** - Reforma Tributária (RTC) +Emissão no leiaute RTC (IBS/CBS/IS) — `serviceInvoicesRtc` (polling) e `productInvoicesRtc` (webhook-driven). O RTC é selecionado pelo payload. + +### **consumer-invoices.js** - NFC-e +Ciclo de vida da NFC-e (`consumerInvoices`): emissão webhook-driven, `list`/leituras **exigem `environment`** (`Production`/`Test`), downloads e inutilização. + +### **municipal-taxes.js** - Inscrições Municipais +CRUD de inscrições municipais (`municipalTaxes`) — pré-requisito para NFS-e —, com `updatePrefecture` (PATCH) e `getSeries`. + +### **certificates.js** - Certificados por thumbprint +Consulta/remoção de certificados por thumbprint (`certificates`), com variantes v1. + +### **notifications.js** - Notificações +Notificações da empresa (`notifications`): listar, obter, remover, enviar e-mail. + +### **account-webhooks.js** - Webhooks de Conta +Webhooks no nível da **conta** (`webhooks.*Account*`) + `fetchEventTypes()` (lista de eventos ao vivo). + +```bash +node examples/rtc-invoices.js +node examples/consumer-invoices.js +node examples/municipal-taxes.js +node examples/certificates.js +node examples/notifications.js +node examples/account-webhooks.js +``` + +> Emissão contra a API real cria documentos de homologação (env `development`/`Test`). Ajuste os payloads ao seu caso antes de rodar. + +--- + ## 📖 Ordem Recomendada de Execução Se você é iniciante, recomendamos executar nesta ordem: diff --git a/examples/account-webhooks.js b/examples/account-webhooks.js new file mode 100644 index 0000000..48d3ef4 --- /dev/null +++ b/examples/account-webhooks.js @@ -0,0 +1,44 @@ +/** + * NFE.io SDK v5 - Webhooks de conta (Account-scoped Webhooks) + * + * Diferente dos webhooks por empresa (`/companies/{id}/webhooks`), estes são da + * CONTA inteira (host-root `/v2/webhooks`, sem companyId). Inclui `fetchEventTypes` + * (lista de tipos de evento AO VIVO — fonte da verdade é o servidor). + * + * node examples/account-webhooks.js + */ + +import { NfeClient } from '../dist/index.js'; + +const nfe = new NfeClient({ apiKey: process.env.NFE_API_KEY, environment: 'development' }); + +async function main() { + if (!process.env.NFE_API_KEY) { + console.error('Defina NFE_API_KEY.'); + process.exit(1); + } + + try { + console.log('\n📡 Tipos de evento disponíveis (ao vivo)'); + const eventTypes = await nfe.webhooks.fetchEventTypes(); + console.log(` ${eventTypes.length} tipos, ex.: ${eventTypes.slice(0, 5).join(', ')}`); + + console.log('\n🌐 Webhooks de conta'); + const { data: hooks } = await nfe.webhooks.listAccountWebhooks(); + console.log(` ${hooks.length} webhook(s) de conta`); + + // Criar um webhook de conta: + // const created = await nfe.webhooks.createAccountWebhook({ + // uri: 'https://seu-site.com/webhook', + // events: eventTypes.slice(0, 2), + // }); + // await nfe.webhooks.pingAccountWebhook(created.id); // dispara um ping de teste + // await nfe.webhooks.deleteAccountWebhook(created.id); + // + // ⚠️ deleteAllAccountWebhooks() remove TODOS os webhooks da conta — use com cuidado. + } catch (err) { + console.error('Erro:', err.message); + } +} + +main(); diff --git a/examples/address-lookup.js b/examples/address-lookup.js index 0e11743..aa4b7da 100644 --- a/examples/address-lookup.js +++ b/examples/address-lookup.js @@ -53,60 +53,16 @@ async function lookupByPostalCode() { } /** - * Example 2: Search addresses by term + * Note: the live address.api.nfe.io/v2 host supports postal-code lookup only. + * There is no working free-text/search endpoint, so this SDK exposes + * `lookupByPostalCode` only. */ -async function lookupByTerm() { - console.log('\n🔍 Example 2: Search by Term'); - console.log('='.repeat(50)); - - try { - const result = await client.addresses.lookupByTerm('Avenida Paulista'); - - console.log('Search results:'); - if (result.addresses && result.addresses.length > 0) { - for (const address of result.addresses.slice(0, 3)) { - console.log(` - ${address.postalCode}: ${address.street}, ${address.city?.name}/${address.state}`); - } - if (result.addresses.length > 3) { - console.log(` ... and ${result.addresses.length - 3} more`); - } - } else { - console.log(' No addresses found'); - } - } catch (error) { - console.error('Error:', error.message); - } -} /** - * Example 3: Search with OData filter - */ -async function searchWithFilter() { - console.log('\n🎯 Example 3: Search with Filter'); - console.log('='.repeat(50)); - - try { - // Search addresses in São Paulo - const result = await client.addresses.search({ - filter: "city.name eq 'São Paulo'", - }); - - console.log('Filtered search results:'); - if (result.addresses && result.addresses.length > 0) { - console.log(` Found ${result.addresses.length} addresses in São Paulo`); - } else { - console.log(' No addresses found'); - } - } catch (error) { - console.error('Error:', error.message); - } -} - -/** - * Example 4: Using only dataApiKey (isolated usage) + * Example 2: Using only dataApiKey (isolated usage) */ async function isolatedAddressUsage() { - console.log('\n🔐 Example 4: Isolated Data API Usage'); + console.log('\n🔐 Example 2: Isolated Data API Usage'); console.log('='.repeat(50)); // Create a client with ONLY dataApiKey @@ -130,10 +86,10 @@ async function isolatedAddressUsage() { } /** - * Example 5: Error handling + * Example 3: Error handling */ async function errorHandling() { - console.log('\n⚠️ Example 5: Error Handling'); + console.log('\n⚠️ Example 3: Error Handling'); console.log('='.repeat(50)); try { @@ -143,13 +99,6 @@ async function errorHandling() { console.log('ValidationError for invalid CEP:', error.message); } - try { - // Empty search term - await client.addresses.lookupByTerm(''); - } catch (error) { - console.log('ValidationError for empty term:', error.message); - } - try { // Non-existent CEP (will get NotFoundError from API) await client.addresses.lookupByPostalCode('00000000'); @@ -175,8 +124,6 @@ async function main() { } await lookupByPostalCode(); - await lookupByTerm(); - await searchWithFilter(); await isolatedAddressUsage(); await errorHandling(); diff --git a/examples/certificates.js b/examples/certificates.js new file mode 100644 index 0000000..96495f4 --- /dev/null +++ b/examples/certificates.js @@ -0,0 +1,47 @@ +/** + * NFE.io SDK v5 - Certificados por thumbprint (Certificates) + * + * Consulta/remoção de certificados digitais por thumbprint (host api.nfse.io). + * Complementa o `companies.uploadCertificate` (upload legado, host api.nfe.io). + * Há variantes v1 (`getByThumbprintV1`/`deleteByThumbprintV1`). + * + * node examples/certificates.js + */ + +import { NfeClient } from '../dist/index.js'; + +const nfe = new NfeClient({ apiKey: process.env.NFE_API_KEY, environment: 'development' }); +const companyId = process.env.NFE_COMPANY_ID; + +async function main() { + if (!process.env.NFE_API_KEY || !companyId) { + console.error('Defina NFE_API_KEY e NFE_COMPANY_ID.'); + process.exit(1); + } + + try { + console.log('\n🔐 Listar certificados da empresa'); + const list = await nfe.certificates.list(companyId); + const certs = list.certificates ?? []; + console.log(` ${certs.length} certificado(s)`); + + const thumb = certs[0]?.thumbprint; + if (thumb) { + const cert = await nfe.certificates.getByThumbprint(companyId, thumb); + console.log(' subject:', cert.subjectName ?? cert.subject ?? '(n/a)'); + console.log(' validade:', cert.expiresAt ?? cert.validTo ?? '(n/a)'); + + // Remoção (descomente para remover de fato): + // await nfe.certificates.deleteByThumbprint(companyId, thumb); + + // Variantes v1: + // await nfe.certificates.getByThumbprintV1(companyId, thumb); + } else { + console.log(' (nenhum certificado — use companies.uploadCertificate para enviar um .pfx)'); + } + } catch (err) { + console.error('Erro:', err.message); + } +} + +main(); diff --git a/examples/consumer-invoices.js b/examples/consumer-invoices.js new file mode 100644 index 0000000..fdbc8e1 --- /dev/null +++ b/examples/consumer-invoices.js @@ -0,0 +1,72 @@ +/** + * NFE.io SDK v5 - NFC-e (Consumer Invoices) + * + * Ciclo de vida da NFC-e (nota fiscal de consumidor). Emissão é webhook-driven + * (202 = enfileirada; conclusão notificada via webhook — NÃO faz polling). + * ATENÇÃO: `list` e as leituras exigem `environment` ('Production' | 'Test'). + * + * Prereqs: NFE_API_KEY + empresa habilitada para NFC-e. + * node examples/consumer-invoices.js + */ + +import { NfeClient } from '../dist/index.js'; + +const nfe = new NfeClient({ apiKey: process.env.NFE_API_KEY, environment: 'development' }); +const companyId = process.env.NFE_COMPANY_ID; +const ENV = 'Test'; // homologação; use 'Production' em produção + +async function listAndInspect() { + console.log('\n🧾 Listar NFC-e (environment é obrigatório)'); + const { consumerInvoices = [], hasMore } = await nfe.consumerInvoices.list(companyId, { + environment: ENV, + limit: 5, + }); + console.log(` ${consumerInvoices.length} nota(s), hasMore=${hasMore}`); + + const first = consumerInvoices[0]; + if (!first) return; + + // Leituras aceitam environment opcional (passe se a API exigir) + const full = await nfe.consumerInvoices.retrieve(companyId, first.id, ENV); + console.log(' status:', full.flowStatus ?? full.status); + + const items = await nfe.consumerInvoices.getItems(companyId, first.id, ENV); + const events = await nfe.consumerInvoices.getEvents(companyId, first.id, ENV); + console.log(` itens=${items.items?.length ?? '?'} eventos=${events.events?.length ?? '?'}`); +} + +async function emit() { + console.log('\n📤 Emitir NFC-e (webhook-driven)'); + const invoice = await nfe.consumerInvoices.create(companyId, { + buyer: { federalTaxNumber: 52998224725, name: 'Consumidor Final' }, + items: [{ code: '001', description: 'Produto', quantity: 1, unitAmount: 10.0 }], + payment: { method: 'Cash', amount: 10.0 }, + }); + console.log(' enfileirada (aguarde o webhook):', invoice.id ?? '(202)'); + return invoice.id; +} + +async function downloads(invoiceId) { + if (!invoiceId) return; + console.log('\n⬇️ Downloads (DANFE-NFC-e / XML)'); + const pdf = await nfe.consumerInvoices.downloadPdf(companyId, invoiceId, ENV); + const xml = await nfe.consumerInvoices.downloadXml(companyId, invoiceId, ENV); + console.log(` pdf=${pdf.length}B xml=${xml.length}B`); +} + +async function main() { + if (!process.env.NFE_API_KEY || !companyId) { + console.error('Defina NFE_API_KEY e NFE_COMPANY_ID.'); + process.exit(1); + } + try { + await listAndInspect(); + // await emit(); // descomente para emitir de fato (homologação) + // Cancelamento: await nfe.consumerInvoices.cancel(companyId, invoiceId); + // Inutilização: await nfe.consumerInvoices.disable(companyId, { serie, numberStart, numberEnd }); + } catch (err) { + console.error('Erro:', err.message); + } +} + +main(); diff --git a/examples/municipal-taxes.js b/examples/municipal-taxes.js new file mode 100644 index 0000000..c2ffd40 --- /dev/null +++ b/examples/municipal-taxes.js @@ -0,0 +1,48 @@ +/** + * NFE.io SDK v5 - Inscrições Municipais (Municipal Taxes) + * + * CRUD de inscrições municipais — PRÉ-REQUISITO para emissão de NFS-e. Espelha + * `stateTaxes`. Inclui `updatePrefecture` (HTTP PATCH) e `getSeries`. + * + * node examples/municipal-taxes.js + */ + +import { NfeClient } from '../dist/index.js'; + +const nfe = new NfeClient({ apiKey: process.env.NFE_API_KEY, environment: 'development' }); +const companyId = process.env.NFE_COMPANY_ID; + +async function main() { + if (!process.env.NFE_API_KEY || !companyId) { + console.error('Defina NFE_API_KEY e NFE_COMPANY_ID.'); + process.exit(1); + } + + try { + console.log('\n🏛️ Listar inscrições municipais'); + const { municipalTaxes = [] } = await nfe.municipalTaxes.list(companyId); + console.log(` ${municipalTaxes.length} inscrição(ões)`); + + const first = municipalTaxes[0]; + if (first) { + const detail = await nfe.municipalTaxes.retrieve(companyId, first.id); + console.log(' code:', detail.code, '| status:', detail.status); + + // Série de RPS para essa inscrição + const series = await nfe.municipalTaxes.getSeries(companyId, first.id, '1'); + console.log(' série 1:', JSON.stringify(series).slice(0, 80)); + } + + // Criar (exemplo — descomente e ajuste ao seu município): + // const created = await nfe.municipalTaxes.create(companyId, { + // code: '123456', specialTaxRegime: 'Nenhum', /* ...credenciais da prefeitura */ + // }); + + // Atualizar credenciais da prefeitura (PATCH): + // await nfe.municipalTaxes.updatePrefecture(companyId, created.id, { /* login/senha */ }); + } catch (err) { + console.error('Erro:', err.message); + } +} + +main(); diff --git a/examples/notifications.js b/examples/notifications.js new file mode 100644 index 0000000..0aba6d2 --- /dev/null +++ b/examples/notifications.js @@ -0,0 +1,39 @@ +/** + * NFE.io SDK v5 - Notificações (Notifications) + * + * Notificações da empresa (host api.nfe.io): listar, obter, remover e enviar e-mail. + * + * node examples/notifications.js + */ + +import { NfeClient } from '../dist/index.js'; + +const nfe = new NfeClient({ apiKey: process.env.NFE_API_KEY, environment: 'development' }); +const companyId = process.env.NFE_COMPANY_ID; + +async function main() { + if (!process.env.NFE_API_KEY || !companyId) { + console.error('Defina NFE_API_KEY e NFE_COMPANY_ID.'); + process.exit(1); + } + + try { + console.log('\n🔔 Listar notificações'); + const { notifications = [] } = await nfe.notifications.list(companyId); + console.log(` ${notifications.length} notificação(ões)`); + + const first = notifications[0]; + if (first) { + const detail = await nfe.notifications.retrieve(companyId, first.id); + console.log(' primeira:', detail.id, '-', detail.type ?? detail.subject ?? ''); + // Remover: await nfe.notifications.delete(companyId, first.id); + } + + // Enviar e-mail de notificação (payload conforme sua configuração): + // await nfe.notifications.sendEmail(companyId, { to: 'destino@exemplo.com' }); + } catch (err) { + console.error('Erro:', err.message); + } +} + +main(); diff --git a/examples/rtc-invoices.js b/examples/rtc-invoices.js new file mode 100644 index 0000000..d2c6af5 --- /dev/null +++ b/examples/rtc-invoices.js @@ -0,0 +1,76 @@ +/** + * NFE.io SDK v5 - Reforma Tributária do Consumo (RTC) + * + * Emissão no leiaute RTC (grupos IBS/CBS/IS). O RTC é selecionado pelo PAYLOAD — + * o endpoint é o mesmo da emissão atual. NFS-e usa polling; NF-e/NFC-e é + * webhook-driven (não faz polling). + * + * Prereqs: NFE_API_KEY + empresa com inscrição municipal/estadual apta a emitir. + * node examples/rtc-invoices.js + */ + +import { NfeClient } from '../dist/index.js'; + +const nfe = new NfeClient({ apiKey: process.env.NFE_API_KEY, environment: 'development' }); +const companyId = process.env.NFE_COMPANY_ID; + +/** NFS-e RTC — polling via createAndWait */ +async function serviceInvoiceRtc() { + console.log('\n🧾 NFS-e RTC (serviceInvoicesRtc)'); + + // create() retorna união discriminada { status: 'immediate'|'async' } + const result = await nfe.serviceInvoicesRtc.create(companyId, { + borrower: { federalTaxNumber: 52998224725, name: 'Cliente Teste' }, + cityServiceCode: '10677', + description: 'Serviço de teste RTC', + servicesAmount: 100.0, + ibsCbs: { + /* grupo RTC — preencha conforme a NT_2025.002 (IBS/CBS) */ + }, + }); + + if (result.status === 'async') { + console.log(' enfileirada:', result.response.invoiceId); + // ...ou bloqueie até emitir: + const invoice = await nfe.serviceInvoicesRtc.createAndWait(companyId, { + borrower: { federalTaxNumber: 52998224725, name: 'Cliente Teste' }, + cityServiceCode: '10677', + description: 'Serviço de teste RTC', + servicesAmount: 100.0, + ibsCbs: {}, + }); + console.log(' emitida:', invoice.id, invoice.flowStatus); + } else { + console.log(' emitida (sync):', result.invoice.id); + } +} + +/** NF-e/NFC-e RTC — webhook-driven (202 = enfileirada; conclusão via webhook) */ +async function productInvoiceRtc() { + console.log('\n📦 NF-e RTC (productInvoicesRtc)'); + + const invoice = await nfe.productInvoicesRtc.create(companyId, { + // payload NF-e/NFC-e com grupos RTC (IBS estadual/municipal, CBS, IS) + buyer: { federalTaxNumber: 52998224725, name: 'Cliente Teste' }, + items: [{ code: '001', description: 'Produto', quantity: 1, unitAmount: 100.0 }], + ibsCbsIs: { + /* grupos RTC conforme a NT_2025.002 */ + }, + }); + console.log(' enfileirada (aguarde o webhook):', invoice.id ?? '(202)'); +} + +async function main() { + if (!process.env.NFE_API_KEY || !companyId) { + console.error('Defina NFE_API_KEY e NFE_COMPANY_ID (empresa apta a emitir).'); + process.exit(1); + } + try { + await serviceInvoiceRtc(); + await productInvoiceRtc(); + } catch (err) { + console.error('Erro:', err.message); + } +} + +main(); diff --git a/examples/service-invoice-complete.js b/examples/service-invoice-complete.js index c7fd6b8..e563aec 100644 --- a/examples/service-invoice-complete.js +++ b/examples/service-invoice-complete.js @@ -367,11 +367,13 @@ async function example10_cancel(invoiceId) { try { console.log(`Cancelling invoice ${invoiceId}...`); - const cancelled = await nfe.serviceInvoices.cancel(companyId, invoiceId); + // Cancellation is asynchronous (202 + Location). cancel() returns a + // discriminated union; cancelAndWait() polls until it settles. + const cancelled = await nfe.serviceInvoices.cancelAndWait(companyId, invoiceId); console.log('✅ Invoice cancelled successfully'); console.log(` ID: ${cancelled.id}`); - console.log(` Status: ${cancelled.status} (should be "cancelled")`); + console.log(` Status: ${cancelled.flowStatus} (should be "Cancelled")`); } catch (error) { console.error('❌ Error cancelling invoice:', error.message); } diff --git a/openapi/spec/SOURCES.json b/openapi/spec/SOURCES.json new file mode 100644 index 0000000..72b986b --- /dev/null +++ b/openapi/spec/SOURCES.json @@ -0,0 +1,22 @@ +{ + "_comment": "Provenance of specs synced from nfeio-docs/static/api. Update via the docs->spec sync step. Verify-mode should fail if a spec no longer matches its recorded sha256.", + "source": "nfeio-docs/static/api", + "sourceCommit": "b487823", + "specs": { + "service-invoice-rtc-v1.yaml": { + "sha256": "5c3b751b63e6a0adabb185b974e141f1b06127934fa0bf05fc505676024db32b", + "layout": "RTC", + "nt": "NT_2025.002_v1.30_RTC (NFS-e)" + }, + "product-invoice-rtc-v1.yaml": { + "sha256": "4327ad141eeace6219dc4267678c44a134ae1fdde93f7dd69cf4c9ae9418415a", + "layout": "RTC", + "nt": "NT_2025.002_v1.30_RTC_NF-e_IBS_CBS_IS" + }, + "contribuintes-v2.json": { + "sha256": "e2d215a19f5dc85c08067d51644e807aae32b6c4754390872670f2e18a938102", + "title": "Empresas (contribuintes) v2", + "note": "OpenAPI 3.0.4; verbose .NET schema keys (key-normalization pending, Task 1.2)" + } + } +} diff --git a/openapi/spec/contribuintes-v2.json b/openapi/spec/contribuintes-v2.json new file mode 100644 index 0000000..4e387a9 --- /dev/null +++ b/openapi/spec/contribuintes-v2.json @@ -0,0 +1,5136 @@ +{ + "openapi": "3.0.4", + "servers": [{ "url": "https://api.nfse.io", "description": "Produção" }], + "info": { + "title": "Empresas", + "description": "# Introdução\r\n\r\nSeja bem-vindo a documentação da API de Empresas!\r\nNossa API foi criada utilizando o padrão REST que possibilita a integração de seu sistema ao nosso, sendo assim você também pode extender ou recriar as funcionalidades existentes na nossa plataforma, tudo isso consumindo a API que está documentada abaixo.\r\n\r\n# Como usar a API?\r\nLogo a seguir você encontrará todos os recursos e métodos suportados pela API, sendo que essa página possibilita que você teste os recursos e métodos diretamente através dela.\r\n\r\n# Autenticação\r\nVocê precisa de uma chave de API (API Key) para identificar a conta que está realizando solicitações para a API.\r\nPara isso você deve colocar sua chave de API no campo que se encontra no topo desta página para que os métodos funcionem corretamente.\r\nNo seu código de integração temos suporte para autenticação de diversas formas sendo eles:\r\nHTTP Header (Authorization ou X-NFEIO-APIKEY) ou HTTP Query String (api_key) nos dois modos passando o valor da sua chave de api (API Key).", + "version": "v2" + }, + "paths": { + "/v2/companies": { + "post": { + "tags": [ + "Companies" + ], + "summary": "Criar uma Empresa", + "description": "### Informações adicionais\r\nUtilize esta requisição para criar novas empresas plataforma para processar Documentos Fiscais.\r\n**Empresa** representa uma pessoa jurídica que precisa processar Documentos Fiscais.", + "requestBody": { + "description": "Dados da Empresa a ser criada", + "content": { + "application/json;odata.metadata=minimal;odata.streaming=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateCompanyResource" + } + }, + "application/json;odata.metadata=minimal;odata.streaming=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateCompanyResource" + } + }, + "application/json;odata.metadata=minimal": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateCompanyResource" + } + }, + "application/json;odata.metadata=full;odata.streaming=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateCompanyResource" + } + }, + "application/json;odata.metadata=full;odata.streaming=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateCompanyResource" + } + }, + "application/json;odata.metadata=full": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateCompanyResource" + } + }, + "application/json;odata.metadata=none;odata.streaming=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateCompanyResource" + } + }, + "application/json;odata.metadata=none;odata.streaming=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateCompanyResource" + } + }, + "application/json;odata.metadata=none": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateCompanyResource" + } + }, + "application/json;odata.streaming=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateCompanyResource" + } + }, + "application/json;odata.streaming=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateCompanyResource" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateCompanyResource" + } + }, + "application/json;odata.metadata=minimal;odata.streaming=true;IEEE754Compatible=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateCompanyResource" + } + }, + "application/json;odata.metadata=minimal;odata.streaming=true;IEEE754Compatible=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateCompanyResource" + } + }, + "application/json;odata.metadata=minimal;odata.streaming=false;IEEE754Compatible=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateCompanyResource" + } + }, + "application/json;odata.metadata=minimal;odata.streaming=false;IEEE754Compatible=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateCompanyResource" + } + }, + "application/json;odata.metadata=minimal;IEEE754Compatible=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateCompanyResource" + } + }, + "application/json;odata.metadata=minimal;IEEE754Compatible=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateCompanyResource" + } + }, + "application/json;odata.metadata=full;odata.streaming=true;IEEE754Compatible=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateCompanyResource" + } + }, + "application/json;odata.metadata=full;odata.streaming=true;IEEE754Compatible=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateCompanyResource" + } + }, + "application/json;odata.metadata=full;odata.streaming=false;IEEE754Compatible=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateCompanyResource" + } + }, + "application/json;odata.metadata=full;odata.streaming=false;IEEE754Compatible=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateCompanyResource" + } + }, + "application/json;odata.metadata=full;IEEE754Compatible=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateCompanyResource" + } + }, + "application/json;odata.metadata=full;IEEE754Compatible=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateCompanyResource" + } + }, + "application/json;odata.metadata=none;odata.streaming=true;IEEE754Compatible=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateCompanyResource" + } + }, + "application/json;odata.metadata=none;odata.streaming=true;IEEE754Compatible=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateCompanyResource" + } + }, + "application/json;odata.metadata=none;odata.streaming=false;IEEE754Compatible=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateCompanyResource" + } + }, + "application/json;odata.metadata=none;odata.streaming=false;IEEE754Compatible=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateCompanyResource" + } + }, + "application/json;odata.metadata=none;IEEE754Compatible=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateCompanyResource" + } + }, + "application/json;odata.metadata=none;IEEE754Compatible=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateCompanyResource" + } + }, + "application/json;odata.streaming=true;IEEE754Compatible=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateCompanyResource" + } + }, + "application/json;odata.streaming=true;IEEE754Compatible=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateCompanyResource" + } + }, + "application/json;odata.streaming=false;IEEE754Compatible=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateCompanyResource" + } + }, + "application/json;odata.streaming=false;IEEE754Compatible=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateCompanyResource" + } + }, + "application/json;IEEE754Compatible=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateCompanyResource" + } + }, + "application/json;IEEE754Compatible=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateCompanyResource" + } + }, + "application/xml": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateCompanyResource" + } + }, + "text/plain": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateCompanyResource" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateCompanyResource" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateCompanyResource" + } + } + } + }, + "responses": { + "200": { + "description": "Sucesso na criação da Empresa", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CompanyResource" + } + } + } + }, + "400": { + "description": "Algum parametro informado não é válido, verificar resposta", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.ErrorsResource" + } + } + } + } + } + }, + "get": { + "tags": [ + "Companies" + ], + "summary": "Consultar todas as Empresas da Conta", + "description": "### Informações adicionais\r\nUtilize esta requisição para consultar os dados das empresas vinculadas a conta.", + "parameters": [ + { + "name": "startingAfter", + "in": "query", + "description": "Id de início do contador (Default: Empty)", + "schema": { + "type": "string" + } + }, + { + "name": "endingBefore", + "in": "query", + "description": "Id final do contador (Default: Empty)", + "schema": { + "type": "string" + } + }, + { + "name": "limit", + "in": "query", + "description": "Limite de resultados na página (Default: 10)", + "schema": { + "type": "integer", + "format": "int32", + "default": 10 + } + } + ], + "responses": { + "200": { + "description": "Sucesso na consulta da Empresa", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CompaniesResource" + } + } + } + }, + "400": { + "description": "Algum parametro informado não é válido, verificar resposta", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.ErrorsResource" + } + } + } + }, + "404": { + "description": "Empresa não encontrada", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.ErrorsResource" + } + } + } + } + } + } + }, + "/v2/companies/{company_id}": { + "get": { + "tags": [ + "Companies" + ], + "summary": "Consultar uma Empresa pelo ID", + "description": "### Informações adicionais\r\nUtilize esta requisição para consultar os dados de uma empresas pelo ID.", + "parameters": [ + { + "name": "company_id", + "in": "path", + "description": "ID da Empresa que deverá ser retornado", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Sucesso na consulta da Empresa", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CompanyResource" + } + } + } + }, + "400": { + "description": "Algum parametro informado não é válido, verificar resposta", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.ErrorsResource" + } + } + } + }, + "404": { + "description": "Empresa não encontrada", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.ErrorsResource" + } + } + } + } + } + }, + "head": { + "tags": [ + "Companies" + ], + "summary": "Consultar se Empresa Existe pelo ID", + "description": "### Informações adicionais\r\nUtilize esta requisição para consultar os dados de uma empresas pelo ID.", + "parameters": [ + { + "name": "company_id", + "in": "path", + "description": "ID da Empresa que deverá ser retornado", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Sucesso na consulta da Empresa", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CompanyResource" + } + } + } + }, + "400": { + "description": "Algum parametro informado não é válido, verificar resposta", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.ErrorsResource" + } + } + } + }, + "404": { + "description": "Empresa não encontrada", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.ErrorsResource" + } + } + } + } + } + }, + "put": { + "tags": [ + "Companies" + ], + "summary": "Alterar uma Empresa pelo ID", + "description": "### Informações adicionais\r\nUtilize esta requisição para alterar os dados de uma empresas pelo ID.", + "parameters": [ + { + "name": "company_id", + "in": "path", + "description": "ID da Empresa que deverá ser retornado", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "Dados da Empresa a ser alterada", + "content": { + "application/json;odata.metadata=minimal;odata.streaming=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateCompanyResource" + } + }, + "application/json;odata.metadata=minimal;odata.streaming=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateCompanyResource" + } + }, + "application/json;odata.metadata=minimal": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateCompanyResource" + } + }, + "application/json;odata.metadata=full;odata.streaming=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateCompanyResource" + } + }, + "application/json;odata.metadata=full;odata.streaming=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateCompanyResource" + } + }, + "application/json;odata.metadata=full": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateCompanyResource" + } + }, + "application/json;odata.metadata=none;odata.streaming=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateCompanyResource" + } + }, + "application/json;odata.metadata=none;odata.streaming=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateCompanyResource" + } + }, + "application/json;odata.metadata=none": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateCompanyResource" + } + }, + "application/json;odata.streaming=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateCompanyResource" + } + }, + "application/json;odata.streaming=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateCompanyResource" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateCompanyResource" + } + }, + "application/json;odata.metadata=minimal;odata.streaming=true;IEEE754Compatible=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateCompanyResource" + } + }, + "application/json;odata.metadata=minimal;odata.streaming=true;IEEE754Compatible=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateCompanyResource" + } + }, + "application/json;odata.metadata=minimal;odata.streaming=false;IEEE754Compatible=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateCompanyResource" + } + }, + "application/json;odata.metadata=minimal;odata.streaming=false;IEEE754Compatible=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateCompanyResource" + } + }, + "application/json;odata.metadata=minimal;IEEE754Compatible=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateCompanyResource" + } + }, + "application/json;odata.metadata=minimal;IEEE754Compatible=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateCompanyResource" + } + }, + "application/json;odata.metadata=full;odata.streaming=true;IEEE754Compatible=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateCompanyResource" + } + }, + "application/json;odata.metadata=full;odata.streaming=true;IEEE754Compatible=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateCompanyResource" + } + }, + "application/json;odata.metadata=full;odata.streaming=false;IEEE754Compatible=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateCompanyResource" + } + }, + "application/json;odata.metadata=full;odata.streaming=false;IEEE754Compatible=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateCompanyResource" + } + }, + "application/json;odata.metadata=full;IEEE754Compatible=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateCompanyResource" + } + }, + "application/json;odata.metadata=full;IEEE754Compatible=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateCompanyResource" + } + }, + "application/json;odata.metadata=none;odata.streaming=true;IEEE754Compatible=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateCompanyResource" + } + }, + "application/json;odata.metadata=none;odata.streaming=true;IEEE754Compatible=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateCompanyResource" + } + }, + "application/json;odata.metadata=none;odata.streaming=false;IEEE754Compatible=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateCompanyResource" + } + }, + "application/json;odata.metadata=none;odata.streaming=false;IEEE754Compatible=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateCompanyResource" + } + }, + "application/json;odata.metadata=none;IEEE754Compatible=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateCompanyResource" + } + }, + "application/json;odata.metadata=none;IEEE754Compatible=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateCompanyResource" + } + }, + "application/json;odata.streaming=true;IEEE754Compatible=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateCompanyResource" + } + }, + "application/json;odata.streaming=true;IEEE754Compatible=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateCompanyResource" + } + }, + "application/json;odata.streaming=false;IEEE754Compatible=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateCompanyResource" + } + }, + "application/json;odata.streaming=false;IEEE754Compatible=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateCompanyResource" + } + }, + "application/json;IEEE754Compatible=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateCompanyResource" + } + }, + "application/json;IEEE754Compatible=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateCompanyResource" + } + }, + "application/xml": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateCompanyResource" + } + }, + "text/plain": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateCompanyResource" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateCompanyResource" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateCompanyResource" + } + } + } + }, + "responses": { + "200": { + "description": "Sucesso na alteração da Empresa", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CompanyResource" + } + } + } + }, + "400": { + "description": "Algum parametro informado não é válido, verificar resposta", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.ErrorsResource" + } + } + } + }, + "404": { + "description": "Empresa não encontrada", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.ErrorsResource" + } + } + } + } + } + }, + "delete": { + "tags": [ + "Companies" + ], + "summary": "Excluir uma Empresa por ID", + "description": "### Informações adicionais\r\nUtilize esta requisição para excluir uma empresas pelo ID, cuidado pois esse processo é irreversível.", + "parameters": [ + { + "name": "company_id", + "in": "path", + "description": "ID da Empresa que deverá ser retornado", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "Sucesso na exclusão da Empresa" + }, + "400": { + "description": "Algum parametro informado não é válido, verificar resposta", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.ErrorsResource" + } + } + } + }, + "404": { + "description": "Empresa não encontrada", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.ErrorsResource" + } + } + } + } + } + } + }, + "/v2/companies/{company_id}/certificates": { + "post": { + "tags": [ + "Companies Certificates" + ], + "summary": "Upload de um Certificado", + "description": "### Informações adicionais\r\nUtilize esta requisição para fazer upload de um **Certificado da ICP-Brasil** do tipo __e-CNPJ A1__ ou __NFE A1__ em uma **Empresa** e vincula-lo para processamentos.\r\nO **Certificado da ICP-Brasil** funciona como uma identidade virtual, para empresas e pessoas, que permite a identificação segura e inequívoca do autor de uma mensagem ou transação feita em meios eletrônicos, como a web.", + "parameters": [ + { + "name": "company_id", + "in": "path", + "description": "ID da empresa", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "multipart/form-data": { + "schema": { + "required": [ + "File", + "Password" + ], + "type": "object", + "properties": { + "File": { + "type": "string", + "description": "Arquivo com certificado ICP-Brasil com extensão .pfx ou .p12", + "format": "binary" + }, + "Password": { + "type": "string", + "description": "Senha do certificado ICP-Brasil" + } + } + }, + "encoding": { + "File": { + "style": "form" + }, + "Password": { + "style": "form" + } + } + }, + "application/form-data": { + "schema": { + "required": [ + "File", + "Password" + ], + "type": "object", + "properties": { + "File": { + "type": "string", + "description": "Arquivo com certificado ICP-Brasil com extensão .pfx ou .p12", + "format": "binary" + }, + "Password": { + "type": "string", + "description": "Senha do certificado ICP-Brasil" + } + } + }, + "encoding": { + "File": { + "style": "form" + }, + "Password": { + "style": "form" + } + } + } + } + }, + "responses": { + "200": { + "description": "Sucesso no upload e vinculo com a Empresa", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CertificateMetadataResource" + } + } + } + }, + "400": { + "description": "Algum parametro informado não é válido, verificar resposta", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.ErrorsResource" + } + } + } + } + } + }, + "get": { + "tags": [ + "Companies Certificates" + ], + "summary": "Consultar Certificado habilitado para empresa", + "description": "### Informações adicionais\r\nUtilize esta requisição para consultar os dados do **Certificado da ICP-Brasil** habilitado para a empresa.", + "parameters": [ + { + "name": "company_id", + "in": "path", + "description": "ID da Empresa relacionada ao certificado", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Sucesso na consulta", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CertificatesMetadataResource" + } + } + } + }, + "400": { + "description": "Algum parametro informado não é válido, verificar resposta", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.ErrorsResource" + } + } + } + } + } + } + }, + "/v1/companies/{company_id}/certificate": { + "post": { + "tags": [ + "Companies Certificates" + ], + "summary": "Upload de um Certificado", + "description": "### Informações adicionais\r\nUtilize esta requisição para fazer upload de um **Certificado da ICP-Brasil** do tipo __e-CNPJ A1__ ou __NFE A1__ em uma **Empresa** e vincula-lo para processamentos.\r\nO **Certificado da ICP-Brasil** funciona como uma identidade virtual, para empresas e pessoas, que permite a identificação segura e inequívoca do autor de uma mensagem ou transação feita em meios eletrônicos, como a web.", + "parameters": [ + { + "name": "company_id", + "in": "path", + "description": "ID da empresa", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "multipart/form-data": { + "schema": { + "required": [ + "File", + "Password" + ], + "type": "object", + "properties": { + "File": { + "type": "string", + "description": "Arquivo com certificado ICP-Brasil com extensão .pfx ou .p12", + "format": "binary" + }, + "Password": { + "type": "string", + "description": "Senha do certificado ICP-Brasil" + } + } + }, + "encoding": { + "File": { + "style": "form" + }, + "Password": { + "style": "form" + } + } + }, + "application/form-data": { + "schema": { + "required": [ + "File", + "Password" + ], + "type": "object", + "properties": { + "File": { + "type": "string", + "description": "Arquivo com certificado ICP-Brasil com extensão .pfx ou .p12", + "format": "binary" + }, + "Password": { + "type": "string", + "description": "Senha do certificado ICP-Brasil" + } + } + }, + "encoding": { + "File": { + "style": "form" + }, + "Password": { + "style": "form" + } + } + } + } + }, + "responses": { + "200": { + "description": "Sucesso no upload e vinculo com a Empresa", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CertificateMetadataResource" + } + } + } + }, + "400": { + "description": "Algum parametro informado não é válido, verificar resposta", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.ErrorsResource" + } + } + } + } + } + }, + "get": { + "tags": [ + "Companies Certificates" + ], + "summary": "Consultar Certificado habilitado para empresa", + "description": "### Informações adicionais\r\nUtilize esta requisição para consultar os dados do **Certificado da ICP-Brasil** habilitado para a empresa.", + "parameters": [ + { + "name": "company_id", + "in": "path", + "description": "ID da Empresa relacionada ao certificado", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Sucesso na consulta", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CertificatesMetadataResource" + } + } + } + }, + "400": { + "description": "Algum parametro informado não é válido, verificar resposta", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.ErrorsResource" + } + } + } + } + } + } + }, + "/v2/companies/{company_id}/certificates/{certificate_thumbprint}": { + "get": { + "tags": [ + "Companies Certificates" + ], + "summary": "Consultar um Certificado por sua impressão digital", + "description": "### Informações adicionais\r\nUtilize esta requisição para consultar os dados de um **Certificado da ICP-Brasil** através da **impressão digital do certificado** (__thumbprint__).", + "parameters": [ + { + "name": "company_id", + "in": "path", + "description": "ID da Empresa relacionada ao certificado", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "certificate_thumbprint", + "in": "path", + "description": "Impressão digital do certificado", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Sucesso na consulta", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CertificateMetadataResource" + } + } + } + }, + "400": { + "description": "Algum parametro informado não é válido, verificar resposta", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.ErrorsResource" + } + } + } + }, + "404": { + "description": "Certificado não encontrado", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.ErrorsResource" + } + } + } + } + } + }, + "delete": { + "tags": [ + "Companies Certificates" + ], + "summary": "Excluir um Certificado por sua impressão digital", + "description": "### Informações adicionais\r\nUtilize esta requisição para excluir o **Certificado da ICP-Brasil** através da **impressão digital do certificado** (__thumbprint__) e desvincula-lo da **Empresa**.\r\n**ATENÇÃO pois esta requisição é irreversível**", + "parameters": [ + { + "name": "company_id", + "in": "path", + "description": "ID da Empresa relacionada ao certificado", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "certificate_thumbprint", + "in": "path", + "description": "Impressão digital do certificado", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "Sucesso na exclusão e desvinculo com a Empresa" + }, + "400": { + "description": "Algum parametro informado não é válido, verificar resposta", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.ErrorsResource" + } + } + } + }, + "404": { + "description": "Certificado não encontrado", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.ErrorsResource" + } + } + } + } + } + } + }, + "/v1/companies/{company_id}/certificate/{certificate_thumbprint}": { + "get": { + "tags": [ + "Companies Certificates" + ], + "summary": "Consultar um Certificado por sua impressão digital", + "description": "### Informações adicionais\r\nUtilize esta requisição para consultar os dados de um **Certificado da ICP-Brasil** através da **impressão digital do certificado** (__thumbprint__).", + "parameters": [ + { + "name": "company_id", + "in": "path", + "description": "ID da Empresa relacionada ao certificado", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "certificate_thumbprint", + "in": "path", + "description": "Impressão digital do certificado", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Sucesso na consulta", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CertificateMetadataResource" + } + } + } + }, + "400": { + "description": "Algum parametro informado não é válido, verificar resposta", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.ErrorsResource" + } + } + } + }, + "404": { + "description": "Certificado não encontrado", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.ErrorsResource" + } + } + } + } + } + }, + "delete": { + "tags": [ + "Companies Certificates" + ], + "summary": "Excluir um Certificado por sua impressão digital", + "description": "### Informações adicionais\r\nUtilize esta requisição para excluir o **Certificado da ICP-Brasil** através da **impressão digital do certificado** (__thumbprint__) e desvincula-lo da **Empresa**.\r\n**ATENÇÃO pois esta requisição é irreversível**", + "parameters": [ + { + "name": "company_id", + "in": "path", + "description": "ID da Empresa relacionada ao certificado", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "certificate_thumbprint", + "in": "path", + "description": "Impressão digital do certificado", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "Sucesso na exclusão e desvinculo com a Empresa" + }, + "400": { + "description": "Algum parametro informado não é válido, verificar resposta", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.ErrorsResource" + } + } + } + }, + "404": { + "description": "Certificado não encontrado", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.ErrorsResource" + } + } + } + } + } + } + }, + "/v2/companies/{company_id}/municipaltaxes": { + "get": { + "tags": [ + "Companies Municipal Taxes" + ], + "summary": "Listar as Inscrições Municipais", + "description": "### Informações adicionais\r\nUtilize esta requisição para listar as inscrições municipais na empresa para processar __Documentos Fiscais__.\r\n**Empresa** representa uma pessoa jurídica que precisa processar Documentos Fiscais.\r\n**Inscrição Municipal** representa os dados necessários sobre o cadastro municipal da empresa junto à prefeitura, exigidos para a emissão de notas fiscais de serviços (NFS-e) e para o cumprimento das obrigações tributárias no âmbito municipal.", + "parameters": [ + { + "name": "company_id", + "in": "path", + "description": "ID da Empresa", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "startingAfter", + "in": "query", + "description": "Id de início do contador (Default: Empty)", + "schema": { + "type": "string" + } + }, + { + "name": "endingBefore", + "in": "query", + "description": "Id final do contador (Default: Empty)", + "schema": { + "type": "string" + } + }, + { + "name": "limit", + "in": "query", + "description": "Limite de resultados na página (Default: 10)", + "schema": { + "type": "integer", + "format": "int32", + "default": 10 + } + } + ], + "responses": { + "200": { + "description": "Sucesso na criação da Inscrição Municipal", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.MunicipalTaxResource" + } + } + } + }, + "400": { + "description": "Algum parametro informado não é válido, verificar resposta", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.ErrorsResource" + } + } + } + } + } + }, + "post": { + "tags": [ + "Companies Municipal Taxes" + ], + "summary": "Criar uma Inscrição Municipal", + "description": "### Informações adicionais\r\nUtilize esta requisição para criar novas inscrição municipais na empresa para processar __Documentos Fiscais__.\r\n**Empresa** representa uma pessoa jurídica que precisa processar Documentos Fiscais.\r\n**Inscrição Municipal** representa os dados necessários sobre o cadastro municipal da empresa junto à prefeitura, exigidos para a emissão de notas fiscais de serviços (NFS-e) e para o cumprimento das obrigações tributárias no âmbito municipal.", + "parameters": [ + { + "name": "company_id", + "in": "path", + "description": "ID da Empresa", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "Dados da Inscrição Municipal a ser criada", + "content": { + "application/json;odata.metadata=minimal;odata.streaming=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateMunicipalTaxResource" + } + }, + "application/json;odata.metadata=minimal;odata.streaming=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateMunicipalTaxResource" + } + }, + "application/json;odata.metadata=minimal": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateMunicipalTaxResource" + } + }, + "application/json;odata.metadata=full;odata.streaming=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateMunicipalTaxResource" + } + }, + "application/json;odata.metadata=full;odata.streaming=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateMunicipalTaxResource" + } + }, + "application/json;odata.metadata=full": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateMunicipalTaxResource" + } + }, + "application/json;odata.metadata=none;odata.streaming=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateMunicipalTaxResource" + } + }, + "application/json;odata.metadata=none;odata.streaming=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateMunicipalTaxResource" + } + }, + "application/json;odata.metadata=none": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateMunicipalTaxResource" + } + }, + "application/json;odata.streaming=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateMunicipalTaxResource" + } + }, + "application/json;odata.streaming=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateMunicipalTaxResource" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateMunicipalTaxResource" + } + }, + "application/json;odata.metadata=minimal;odata.streaming=true;IEEE754Compatible=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateMunicipalTaxResource" + } + }, + "application/json;odata.metadata=minimal;odata.streaming=true;IEEE754Compatible=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateMunicipalTaxResource" + } + }, + "application/json;odata.metadata=minimal;odata.streaming=false;IEEE754Compatible=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateMunicipalTaxResource" + } + }, + "application/json;odata.metadata=minimal;odata.streaming=false;IEEE754Compatible=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateMunicipalTaxResource" + } + }, + "application/json;odata.metadata=minimal;IEEE754Compatible=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateMunicipalTaxResource" + } + }, + "application/json;odata.metadata=minimal;IEEE754Compatible=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateMunicipalTaxResource" + } + }, + "application/json;odata.metadata=full;odata.streaming=true;IEEE754Compatible=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateMunicipalTaxResource" + } + }, + "application/json;odata.metadata=full;odata.streaming=true;IEEE754Compatible=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateMunicipalTaxResource" + } + }, + "application/json;odata.metadata=full;odata.streaming=false;IEEE754Compatible=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateMunicipalTaxResource" + } + }, + "application/json;odata.metadata=full;odata.streaming=false;IEEE754Compatible=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateMunicipalTaxResource" + } + }, + "application/json;odata.metadata=full;IEEE754Compatible=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateMunicipalTaxResource" + } + }, + "application/json;odata.metadata=full;IEEE754Compatible=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateMunicipalTaxResource" + } + }, + "application/json;odata.metadata=none;odata.streaming=true;IEEE754Compatible=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateMunicipalTaxResource" + } + }, + "application/json;odata.metadata=none;odata.streaming=true;IEEE754Compatible=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateMunicipalTaxResource" + } + }, + "application/json;odata.metadata=none;odata.streaming=false;IEEE754Compatible=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateMunicipalTaxResource" + } + }, + "application/json;odata.metadata=none;odata.streaming=false;IEEE754Compatible=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateMunicipalTaxResource" + } + }, + "application/json;odata.metadata=none;IEEE754Compatible=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateMunicipalTaxResource" + } + }, + "application/json;odata.metadata=none;IEEE754Compatible=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateMunicipalTaxResource" + } + }, + "application/json;odata.streaming=true;IEEE754Compatible=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateMunicipalTaxResource" + } + }, + "application/json;odata.streaming=true;IEEE754Compatible=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateMunicipalTaxResource" + } + }, + "application/json;odata.streaming=false;IEEE754Compatible=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateMunicipalTaxResource" + } + }, + "application/json;odata.streaming=false;IEEE754Compatible=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateMunicipalTaxResource" + } + }, + "application/json;IEEE754Compatible=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateMunicipalTaxResource" + } + }, + "application/json;IEEE754Compatible=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateMunicipalTaxResource" + } + }, + "application/xml": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateMunicipalTaxResource" + } + }, + "text/plain": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateMunicipalTaxResource" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateMunicipalTaxResource" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateMunicipalTaxResource" + } + } + } + }, + "responses": { + "200": { + "description": "Sucesso na criação da Inscrição Municipal", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.MunicipalTaxResource" + } + } + } + }, + "400": { + "description": "Algum parametro informado não é válido, verificar resposta", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.ErrorsResource" + } + } + } + } + } + } + }, + "/v2/companies/{company_id}/municipaltaxes/{municipal_tax_id}": { + "get": { + "tags": [ + "Companies Municipal Taxes" + ], + "summary": "Consultar uma Inscrição Municipal pelo ID", + "description": "### Informações adicionais\r\nUtilize esta requisição para consultar os dados de uma empresas pelo ID.\r\n**Empresa** representa uma pessoa jurídica que precisa processar Documentos Fiscais.\r\n**Inscrição Municipal** representa os dados necessários sobre o cadastro municipal da empresa junto à prefeitura, exigidos para a emissão de notas fiscais de serviços (NFS-e) e para o cumprimento das obrigações tributárias no âmbito municipal.", + "parameters": [ + { + "name": "company_id", + "in": "path", + "description": "ID da Empresa", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "municipal_tax_id", + "in": "path", + "description": "ID da Inscrição Mnicipal que deverá ser retornado", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Sucesso na consulta da Inscrição Municipal", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.MunicipalTaxResource" + } + } + } + }, + "400": { + "description": "Algum parametro informado não é válido, verificar resposta", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.ErrorsResource" + } + } + } + }, + "404": { + "description": "Inscrição Municipal não encontrada", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.ErrorsResource" + } + } + } + } + } + }, + "put": { + "tags": [ + "Companies Municipal Taxes" + ], + "summary": "Alterar uma Inscrição Municipal pelo ID", + "description": "### Informações adicionais\r\nUtilize esta requisição para alterar os dados de uma Inscrição Municipal pelo ID.\r\n**Empresa** representa uma pessoa jurídica que precisa processar Documentos Fiscais.\r\n**Inscrição Municipal** representa os dados necessários sobre o cadastro municipal da empresa junto à prefeitura, exigidos para a emissão de notas fiscais de serviços (NFS-e) e para o cumprimento das obrigações tributárias no âmbito municipal.", + "parameters": [ + { + "name": "company_id", + "in": "path", + "description": "ID da Empresa", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "municipal_tax_id", + "in": "path", + "description": "ID da Inscrição Municipal que deverá ser retornado", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "Dados da Inscrição Municipal a ser alterada", + "content": { + "application/json;odata.metadata=minimal;odata.streaming=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateMunicipalTaxResource" + } + }, + "application/json;odata.metadata=minimal;odata.streaming=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateMunicipalTaxResource" + } + }, + "application/json;odata.metadata=minimal": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateMunicipalTaxResource" + } + }, + "application/json;odata.metadata=full;odata.streaming=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateMunicipalTaxResource" + } + }, + "application/json;odata.metadata=full;odata.streaming=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateMunicipalTaxResource" + } + }, + "application/json;odata.metadata=full": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateMunicipalTaxResource" + } + }, + "application/json;odata.metadata=none;odata.streaming=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateMunicipalTaxResource" + } + }, + "application/json;odata.metadata=none;odata.streaming=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateMunicipalTaxResource" + } + }, + "application/json;odata.metadata=none": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateMunicipalTaxResource" + } + }, + "application/json;odata.streaming=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateMunicipalTaxResource" + } + }, + "application/json;odata.streaming=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateMunicipalTaxResource" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateMunicipalTaxResource" + } + }, + "application/json;odata.metadata=minimal;odata.streaming=true;IEEE754Compatible=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateMunicipalTaxResource" + } + }, + "application/json;odata.metadata=minimal;odata.streaming=true;IEEE754Compatible=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateMunicipalTaxResource" + } + }, + "application/json;odata.metadata=minimal;odata.streaming=false;IEEE754Compatible=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateMunicipalTaxResource" + } + }, + "application/json;odata.metadata=minimal;odata.streaming=false;IEEE754Compatible=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateMunicipalTaxResource" + } + }, + "application/json;odata.metadata=minimal;IEEE754Compatible=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateMunicipalTaxResource" + } + }, + "application/json;odata.metadata=minimal;IEEE754Compatible=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateMunicipalTaxResource" + } + }, + "application/json;odata.metadata=full;odata.streaming=true;IEEE754Compatible=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateMunicipalTaxResource" + } + }, + "application/json;odata.metadata=full;odata.streaming=true;IEEE754Compatible=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateMunicipalTaxResource" + } + }, + "application/json;odata.metadata=full;odata.streaming=false;IEEE754Compatible=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateMunicipalTaxResource" + } + }, + "application/json;odata.metadata=full;odata.streaming=false;IEEE754Compatible=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateMunicipalTaxResource" + } + }, + "application/json;odata.metadata=full;IEEE754Compatible=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateMunicipalTaxResource" + } + }, + "application/json;odata.metadata=full;IEEE754Compatible=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateMunicipalTaxResource" + } + }, + "application/json;odata.metadata=none;odata.streaming=true;IEEE754Compatible=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateMunicipalTaxResource" + } + }, + "application/json;odata.metadata=none;odata.streaming=true;IEEE754Compatible=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateMunicipalTaxResource" + } + }, + "application/json;odata.metadata=none;odata.streaming=false;IEEE754Compatible=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateMunicipalTaxResource" + } + }, + "application/json;odata.metadata=none;odata.streaming=false;IEEE754Compatible=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateMunicipalTaxResource" + } + }, + "application/json;odata.metadata=none;IEEE754Compatible=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateMunicipalTaxResource" + } + }, + "application/json;odata.metadata=none;IEEE754Compatible=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateMunicipalTaxResource" + } + }, + "application/json;odata.streaming=true;IEEE754Compatible=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateMunicipalTaxResource" + } + }, + "application/json;odata.streaming=true;IEEE754Compatible=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateMunicipalTaxResource" + } + }, + "application/json;odata.streaming=false;IEEE754Compatible=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateMunicipalTaxResource" + } + }, + "application/json;odata.streaming=false;IEEE754Compatible=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateMunicipalTaxResource" + } + }, + "application/json;IEEE754Compatible=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateMunicipalTaxResource" + } + }, + "application/json;IEEE754Compatible=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateMunicipalTaxResource" + } + }, + "application/xml": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateMunicipalTaxResource" + } + }, + "text/plain": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateMunicipalTaxResource" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateMunicipalTaxResource" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateMunicipalTaxResource" + } + } + } + }, + "responses": { + "200": { + "description": "Sucesso na alteração da Inscrição Municipal", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.MunicipalTaxResource" + } + } + } + }, + "400": { + "description": "Algum parametro informado não é válido, verificar resposta", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.ErrorsResource" + } + } + } + }, + "404": { + "description": "Inscrição Municipal não encontrada", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.ErrorsResource" + } + } + } + } + } + }, + "delete": { + "tags": [ + "Companies Municipal Taxes" + ], + "summary": "Excluir uma Inscrição Municipal pelo ID", + "description": "### Informações adicionais\r\nUtilize esta requisição para excluir uma Inscrição Municipal pelo ID, cuidado pois esse processo é irreversível.\r\n**Empresa** representa uma pessoa jurídica que precisa processar Documentos Fiscais.\r\n**Inscrição Municipal** representa os dados necessários sobre o cadastro municipal da empresa junto à prefeitura, exigidos para a emissão de notas fiscais de serviços (NFS-e) e para o cumprimento das obrigações tributárias no âmbito municipal.", + "parameters": [ + { + "name": "company_id", + "in": "path", + "description": "ID da Empresa", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "municipal_tax_id", + "in": "path", + "description": "ID da Inscrição Municipal que deverá ser retornado", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "Sucesso na exclusão da Inscrição Municipal" + }, + "400": { + "description": "Algum parametro informado não é válido, verificar resposta", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.ErrorsResource" + } + } + } + }, + "404": { + "description": "Inscrição Municipal não encontrada", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.ErrorsResource" + } + } + } + } + } + } + }, + "/v2/companies/{company_id}/municipaltaxes/{municipal_tax_id}/updateprefecture": { + "patch": { + "tags": [ + "Companies Municipal Taxes" + ], + "summary": "Atualizar status da prefeitura para uma Inscrição Municipal pelo ID", + "description": "### Informações adicionais\r\nUtilize esta requisição para atualizar o status de uma prefeitura relacionada a uma Inscrição Municipal pelo ID.\r\n**Empresa** representa uma pessoa jurídica que precisa processar Documentos Fiscais.\r\n**Inscrição Municipal** representa os dados necessários sobre o cadastro municipal da empresa junto à prefeitura, exigidos para a emissão de notas fiscais de serviços (NFS-e) e para o cumprimento das obrigações tributárias no âmbito municipal.", + "parameters": [ + { + "name": "company_id", + "in": "path", + "description": "ID da Empresa", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "municipal_tax_id", + "in": "path", + "description": "ID da Inscrição Municipal que deverá ser retornado", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "Sucesso na atualização do status da prefeitura da Inscrição Municipal" + }, + "400": { + "description": "Algum parametro informado não é válido, verificar resposta", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.ErrorsResource" + } + } + } + }, + "404": { + "description": "Inscrição Municipal não encontrada", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.ErrorsResource" + } + } + } + } + } + } + }, + "/v2/companies/{company_id}/municipaltaxes/{municipal_tax_id}/series/{serie}": { + "get": { + "tags": [ + "Companies Municipal Taxes" + ], + "summary": "Consultar uma Serie pelo ID", + "description": "### Informações adicionais\r\nUtilize esta requisição para consultar os dados de uma Serie de RPS.\r\n**Empresa** representa uma pessoa jurídica que precisa processar Documentos Fiscais.\r\n**Inscrição Municipal** representa os dados necessários sobre o cadastro municipal da empresa junto à prefeitura, exigidos para a emissão de notas fiscais de serviços (NFS-e) e para o cumprimento das obrigações tributárias no âmbito municipal.\r\n**Serie** representa a série do RPS (Recibo Provisório de Serviços) utilizada para a emissão de notas fiscais de serviços eletrônicas (NFS-e).", + "parameters": [ + { + "name": "company_id", + "in": "path", + "description": "ID da Empresa", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "municipal_tax_id", + "in": "path", + "description": "ID da Inscrição Mnicipal que deverá ser retornado", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "serie", + "in": "path", + "description": "Série do RPS que deverá ser retornado", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Sucesso na consulta da Série", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.SerieResource" + } + } + } + }, + "400": { + "description": "Algum parametro informado não é válido, verificar resposta", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.ErrorsResource" + } + } + } + }, + "404": { + "description": "Inscrição Municipal não encontrada", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.ErrorsResource" + } + } + } + } + } + } + }, + "/v2/companies/{company_id}/statetaxes": { + "get": { + "tags": [ + "Companies State Taxes" + ], + "summary": "Listar as Inscrições Estaduais", + "description": "### Informações adicionais\r\nUtilize esta requisição para listar as inscrições estaduais na empresa para processar __Documentos Fiscais__.\r\n**Empresa** representa uma pessoa jurídica que precisa processar Documentos Fiscais.\r\n**Inscrição Estadual** representa os dados necessários sobre o cadastro Estadual (ICMS) que é preciso para processar Documentos Fiscais na SEFAZ.", + "parameters": [ + { + "name": "company_id", + "in": "path", + "description": "ID da Empresa", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "startingAfter", + "in": "query", + "description": "Id de início do contador (Default: Empty)", + "schema": { + "type": "string" + } + }, + { + "name": "endingBefore", + "in": "query", + "description": "Id final do contador (Default: Empty)", + "schema": { + "type": "string" + } + }, + { + "name": "limit", + "in": "query", + "description": "Limite de resultados na página (Default: 10)", + "schema": { + "type": "integer", + "format": "int32", + "default": 10 + } + } + ], + "responses": { + "200": { + "description": "Sucesso na criação da Inscrição Estadual", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.StateTaxesResource" + } + } + } + }, + "400": { + "description": "Algum parametro informado não é válido, verificar resposta", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.ErrorsResource" + } + } + } + } + } + }, + "post": { + "tags": [ + "Companies State Taxes" + ], + "summary": "Criar uma Inscrição Estadual", + "description": "### Informações adicionais\r\nUtilize esta requisição para criar novas inscrição estadual na empresa para processar __Documentos Fiscais__.\r\n**Empresa** representa uma pessoa jurídica que precisa processar Documentos Fiscais.\r\n**Inscrição Estadual** representa os dados necessários sobre o cadastro Estadual (ICMS) que é preciso para processar Documentos Fiscais na SEFAZ.", + "parameters": [ + { + "name": "company_id", + "in": "path", + "description": "ID da Empresa", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "Dados da Inscrição Estadual a ser criada", + "content": { + "application/json;odata.metadata=minimal;odata.streaming=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateStateTaxResource" + } + }, + "application/json;odata.metadata=minimal;odata.streaming=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateStateTaxResource" + } + }, + "application/json;odata.metadata=minimal": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateStateTaxResource" + } + }, + "application/json;odata.metadata=full;odata.streaming=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateStateTaxResource" + } + }, + "application/json;odata.metadata=full;odata.streaming=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateStateTaxResource" + } + }, + "application/json;odata.metadata=full": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateStateTaxResource" + } + }, + "application/json;odata.metadata=none;odata.streaming=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateStateTaxResource" + } + }, + "application/json;odata.metadata=none;odata.streaming=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateStateTaxResource" + } + }, + "application/json;odata.metadata=none": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateStateTaxResource" + } + }, + "application/json;odata.streaming=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateStateTaxResource" + } + }, + "application/json;odata.streaming=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateStateTaxResource" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateStateTaxResource" + } + }, + "application/json;odata.metadata=minimal;odata.streaming=true;IEEE754Compatible=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateStateTaxResource" + } + }, + "application/json;odata.metadata=minimal;odata.streaming=true;IEEE754Compatible=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateStateTaxResource" + } + }, + "application/json;odata.metadata=minimal;odata.streaming=false;IEEE754Compatible=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateStateTaxResource" + } + }, + "application/json;odata.metadata=minimal;odata.streaming=false;IEEE754Compatible=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateStateTaxResource" + } + }, + "application/json;odata.metadata=minimal;IEEE754Compatible=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateStateTaxResource" + } + }, + "application/json;odata.metadata=minimal;IEEE754Compatible=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateStateTaxResource" + } + }, + "application/json;odata.metadata=full;odata.streaming=true;IEEE754Compatible=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateStateTaxResource" + } + }, + "application/json;odata.metadata=full;odata.streaming=true;IEEE754Compatible=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateStateTaxResource" + } + }, + "application/json;odata.metadata=full;odata.streaming=false;IEEE754Compatible=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateStateTaxResource" + } + }, + "application/json;odata.metadata=full;odata.streaming=false;IEEE754Compatible=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateStateTaxResource" + } + }, + "application/json;odata.metadata=full;IEEE754Compatible=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateStateTaxResource" + } + }, + "application/json;odata.metadata=full;IEEE754Compatible=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateStateTaxResource" + } + }, + "application/json;odata.metadata=none;odata.streaming=true;IEEE754Compatible=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateStateTaxResource" + } + }, + "application/json;odata.metadata=none;odata.streaming=true;IEEE754Compatible=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateStateTaxResource" + } + }, + "application/json;odata.metadata=none;odata.streaming=false;IEEE754Compatible=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateStateTaxResource" + } + }, + "application/json;odata.metadata=none;odata.streaming=false;IEEE754Compatible=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateStateTaxResource" + } + }, + "application/json;odata.metadata=none;IEEE754Compatible=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateStateTaxResource" + } + }, + "application/json;odata.metadata=none;IEEE754Compatible=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateStateTaxResource" + } + }, + "application/json;odata.streaming=true;IEEE754Compatible=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateStateTaxResource" + } + }, + "application/json;odata.streaming=true;IEEE754Compatible=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateStateTaxResource" + } + }, + "application/json;odata.streaming=false;IEEE754Compatible=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateStateTaxResource" + } + }, + "application/json;odata.streaming=false;IEEE754Compatible=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateStateTaxResource" + } + }, + "application/json;IEEE754Compatible=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateStateTaxResource" + } + }, + "application/json;IEEE754Compatible=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateStateTaxResource" + } + }, + "application/xml": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateStateTaxResource" + } + }, + "text/plain": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateStateTaxResource" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateStateTaxResource" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateStateTaxResource" + } + } + } + }, + "responses": { + "200": { + "description": "Sucesso na criação da Inscrição Estadual", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.StateTaxResource" + } + } + } + }, + "400": { + "description": "Algum parametro informado não é válido, verificar resposta", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.ErrorsResource" + } + } + } + } + } + } + }, + "/v2/companies/{company_id}/statetaxes/{state_tax_id}": { + "get": { + "tags": [ + "Companies State Taxes" + ], + "summary": "Consultar uma Inscrição Estadual pelo ID", + "description": "### Informações adicionais\r\nUtilize esta requisição para consultar os dados de uma empresas pelo ID.\r\n**Empresa** representa uma pessoa jurídica que precisa processar Documentos Fiscais.\r\n**Inscrição Estadual** representa os dados necessários sobre o cadastro Estadual (ICMS) que é preciso para processar Documentos Fiscais na SEFAZ.", + "parameters": [ + { + "name": "company_id", + "in": "path", + "description": "ID da Empresa", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "state_tax_id", + "in": "path", + "description": "ID da Inscrição Estadual que deverá ser retornado", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Sucesso na consulta da Inscrição Estadual", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.StateTaxResource" + } + } + } + }, + "400": { + "description": "Algum parametro informado não é válido, verificar resposta", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.ErrorsResource" + } + } + } + }, + "404": { + "description": "Inscrição Estadual não encontrada", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.ErrorsResource" + } + } + } + } + } + }, + "put": { + "tags": [ + "Companies State Taxes" + ], + "summary": "Alterar uma Inscrição Estadual pelo ID", + "description": "### Informações adicionais\r\nUtilize esta requisição para alterar os dados de uma Inscrição Estadual pelo ID.\r\n**Empresa** representa uma pessoa jurídica que precisa processar Documentos Fiscais.\r\n**Inscrição Estadual** representa os dados necessários sobre o cadastro Estadual (ICMS) que é preciso para processar Documentos Fiscais na SEFAZ.", + "parameters": [ + { + "name": "company_id", + "in": "path", + "description": "ID da Empresa", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "state_tax_id", + "in": "path", + "description": "ID da Inscrição Estadual que deverá ser retornado", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "Dados da Inscrição Estadual a ser alterada", + "content": { + "application/json;odata.metadata=minimal;odata.streaming=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateStateTaxResource" + } + }, + "application/json;odata.metadata=minimal;odata.streaming=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateStateTaxResource" + } + }, + "application/json;odata.metadata=minimal": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateStateTaxResource" + } + }, + "application/json;odata.metadata=full;odata.streaming=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateStateTaxResource" + } + }, + "application/json;odata.metadata=full;odata.streaming=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateStateTaxResource" + } + }, + "application/json;odata.metadata=full": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateStateTaxResource" + } + }, + "application/json;odata.metadata=none;odata.streaming=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateStateTaxResource" + } + }, + "application/json;odata.metadata=none;odata.streaming=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateStateTaxResource" + } + }, + "application/json;odata.metadata=none": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateStateTaxResource" + } + }, + "application/json;odata.streaming=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateStateTaxResource" + } + }, + "application/json;odata.streaming=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateStateTaxResource" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateStateTaxResource" + } + }, + "application/json;odata.metadata=minimal;odata.streaming=true;IEEE754Compatible=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateStateTaxResource" + } + }, + "application/json;odata.metadata=minimal;odata.streaming=true;IEEE754Compatible=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateStateTaxResource" + } + }, + "application/json;odata.metadata=minimal;odata.streaming=false;IEEE754Compatible=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateStateTaxResource" + } + }, + "application/json;odata.metadata=minimal;odata.streaming=false;IEEE754Compatible=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateStateTaxResource" + } + }, + "application/json;odata.metadata=minimal;IEEE754Compatible=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateStateTaxResource" + } + }, + "application/json;odata.metadata=minimal;IEEE754Compatible=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateStateTaxResource" + } + }, + "application/json;odata.metadata=full;odata.streaming=true;IEEE754Compatible=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateStateTaxResource" + } + }, + "application/json;odata.metadata=full;odata.streaming=true;IEEE754Compatible=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateStateTaxResource" + } + }, + "application/json;odata.metadata=full;odata.streaming=false;IEEE754Compatible=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateStateTaxResource" + } + }, + "application/json;odata.metadata=full;odata.streaming=false;IEEE754Compatible=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateStateTaxResource" + } + }, + "application/json;odata.metadata=full;IEEE754Compatible=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateStateTaxResource" + } + }, + "application/json;odata.metadata=full;IEEE754Compatible=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateStateTaxResource" + } + }, + "application/json;odata.metadata=none;odata.streaming=true;IEEE754Compatible=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateStateTaxResource" + } + }, + "application/json;odata.metadata=none;odata.streaming=true;IEEE754Compatible=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateStateTaxResource" + } + }, + "application/json;odata.metadata=none;odata.streaming=false;IEEE754Compatible=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateStateTaxResource" + } + }, + "application/json;odata.metadata=none;odata.streaming=false;IEEE754Compatible=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateStateTaxResource" + } + }, + "application/json;odata.metadata=none;IEEE754Compatible=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateStateTaxResource" + } + }, + "application/json;odata.metadata=none;IEEE754Compatible=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateStateTaxResource" + } + }, + "application/json;odata.streaming=true;IEEE754Compatible=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateStateTaxResource" + } + }, + "application/json;odata.streaming=true;IEEE754Compatible=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateStateTaxResource" + } + }, + "application/json;odata.streaming=false;IEEE754Compatible=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateStateTaxResource" + } + }, + "application/json;odata.streaming=false;IEEE754Compatible=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateStateTaxResource" + } + }, + "application/json;IEEE754Compatible=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateStateTaxResource" + } + }, + "application/json;IEEE754Compatible=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateStateTaxResource" + } + }, + "application/xml": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateStateTaxResource" + } + }, + "text/plain": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateStateTaxResource" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateStateTaxResource" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateStateTaxResource" + } + } + } + }, + "responses": { + "200": { + "description": "Sucesso na alteração da Inscrição Estadual", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.StateTaxResource" + } + } + } + }, + "400": { + "description": "Algum parametro informado não é válido, verificar resposta", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.ErrorsResource" + } + } + } + }, + "404": { + "description": "Inscrição Estadual não encontrada", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.ErrorsResource" + } + } + } + } + } + }, + "delete": { + "tags": [ + "Companies State Taxes" + ], + "summary": "Excluir uma Inscrição Estadual pelo ID", + "description": "### Informações adicionais\r\nUtilize esta requisição para excluir uma Inscrição Estadual pelo ID, cuidado pois esse processo é irreversível.\r\n**Empresa** representa uma pessoa jurídica que precisa processar Documentos Fiscais.\r\n**Inscrição Estadual** representa os dados necessários sobre o cadastro Estadual (ICMS) que é preciso para processar Documentos Fiscais na SEFAZ.", + "parameters": [ + { + "name": "company_id", + "in": "path", + "description": "ID da Empresa", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "state_tax_id", + "in": "path", + "description": "ID da Inscrição Estadual que deverá ser retornado", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "Sucesso na exclusão da Inscrição Estadual" + }, + "400": { + "description": "Algum parametro informado não é válido, verificar resposta", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.ErrorsResource" + } + } + } + }, + "404": { + "description": "Inscrição Estadual não encontrada", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.ErrorsResource" + } + } + } + } + } + } + }, + "/v2/companies/{company_id}/statetaxes/{state_tax_id}/switch-authorizer": { + "post": { + "tags": [ + "Companies State Taxes" + ], + "summary": "Alterar o ambiente de uma Inscrição Estadual pelo ID", + "description": "### Informações adicionais\r\nUtilize esta requisição para definir o ambiente de autorização das Notas Fiscais de uma Inscrição Estadual pelo ID.", + "parameters": [ + { + "name": "company_id", + "in": "path", + "description": "ID da Empresa", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "state_tax_id", + "in": "path", + "description": "ID da Inscrição Estadual que deverá ser retornado", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "", + "content": { + "application/json;odata.metadata=minimal;odata.streaming=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.StateTaxSwitchAuthorizerRequest" + } + }, + "application/json;odata.metadata=minimal;odata.streaming=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.StateTaxSwitchAuthorizerRequest" + } + }, + "application/json;odata.metadata=minimal": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.StateTaxSwitchAuthorizerRequest" + } + }, + "application/json;odata.metadata=full;odata.streaming=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.StateTaxSwitchAuthorizerRequest" + } + }, + "application/json;odata.metadata=full;odata.streaming=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.StateTaxSwitchAuthorizerRequest" + } + }, + "application/json;odata.metadata=full": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.StateTaxSwitchAuthorizerRequest" + } + }, + "application/json;odata.metadata=none;odata.streaming=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.StateTaxSwitchAuthorizerRequest" + } + }, + "application/json;odata.metadata=none;odata.streaming=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.StateTaxSwitchAuthorizerRequest" + } + }, + "application/json;odata.metadata=none": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.StateTaxSwitchAuthorizerRequest" + } + }, + "application/json;odata.streaming=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.StateTaxSwitchAuthorizerRequest" + } + }, + "application/json;odata.streaming=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.StateTaxSwitchAuthorizerRequest" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.StateTaxSwitchAuthorizerRequest" + } + }, + "application/json;odata.metadata=minimal;odata.streaming=true;IEEE754Compatible=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.StateTaxSwitchAuthorizerRequest" + } + }, + "application/json;odata.metadata=minimal;odata.streaming=true;IEEE754Compatible=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.StateTaxSwitchAuthorizerRequest" + } + }, + "application/json;odata.metadata=minimal;odata.streaming=false;IEEE754Compatible=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.StateTaxSwitchAuthorizerRequest" + } + }, + "application/json;odata.metadata=minimal;odata.streaming=false;IEEE754Compatible=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.StateTaxSwitchAuthorizerRequest" + } + }, + "application/json;odata.metadata=minimal;IEEE754Compatible=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.StateTaxSwitchAuthorizerRequest" + } + }, + "application/json;odata.metadata=minimal;IEEE754Compatible=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.StateTaxSwitchAuthorizerRequest" + } + }, + "application/json;odata.metadata=full;odata.streaming=true;IEEE754Compatible=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.StateTaxSwitchAuthorizerRequest" + } + }, + "application/json;odata.metadata=full;odata.streaming=true;IEEE754Compatible=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.StateTaxSwitchAuthorizerRequest" + } + }, + "application/json;odata.metadata=full;odata.streaming=false;IEEE754Compatible=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.StateTaxSwitchAuthorizerRequest" + } + }, + "application/json;odata.metadata=full;odata.streaming=false;IEEE754Compatible=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.StateTaxSwitchAuthorizerRequest" + } + }, + "application/json;odata.metadata=full;IEEE754Compatible=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.StateTaxSwitchAuthorizerRequest" + } + }, + "application/json;odata.metadata=full;IEEE754Compatible=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.StateTaxSwitchAuthorizerRequest" + } + }, + "application/json;odata.metadata=none;odata.streaming=true;IEEE754Compatible=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.StateTaxSwitchAuthorizerRequest" + } + }, + "application/json;odata.metadata=none;odata.streaming=true;IEEE754Compatible=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.StateTaxSwitchAuthorizerRequest" + } + }, + "application/json;odata.metadata=none;odata.streaming=false;IEEE754Compatible=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.StateTaxSwitchAuthorizerRequest" + } + }, + "application/json;odata.metadata=none;odata.streaming=false;IEEE754Compatible=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.StateTaxSwitchAuthorizerRequest" + } + }, + "application/json;odata.metadata=none;IEEE754Compatible=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.StateTaxSwitchAuthorizerRequest" + } + }, + "application/json;odata.metadata=none;IEEE754Compatible=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.StateTaxSwitchAuthorizerRequest" + } + }, + "application/json;odata.streaming=true;IEEE754Compatible=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.StateTaxSwitchAuthorizerRequest" + } + }, + "application/json;odata.streaming=true;IEEE754Compatible=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.StateTaxSwitchAuthorizerRequest" + } + }, + "application/json;odata.streaming=false;IEEE754Compatible=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.StateTaxSwitchAuthorizerRequest" + } + }, + "application/json;odata.streaming=false;IEEE754Compatible=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.StateTaxSwitchAuthorizerRequest" + } + }, + "application/json;IEEE754Compatible=false": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.StateTaxSwitchAuthorizerRequest" + } + }, + "application/json;IEEE754Compatible=true": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.StateTaxSwitchAuthorizerRequest" + } + }, + "application/xml": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.StateTaxSwitchAuthorizerRequest" + } + }, + "text/plain": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.StateTaxSwitchAuthorizerRequest" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.StateTaxSwitchAuthorizerRequest" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.StateTaxSwitchAuthorizerRequest" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.StateTaxSwitchAuthorizerResponse" + } + } + } + }, + "400": { + "description": "Algum parâmetro informado não é válido, verificar resposta", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.ErrorsResource" + } + } + } + }, + "404": { + "description": "Inscrição Estadual não encontrada", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.ErrorsResource" + } + } + } + }, + "204": { + "description": "Sucesso na exclusão da Inscrição Estadual" + } + } + } + }, + "/v1/companies/{companyIdOrTaxNumber}": { + "get": { + "tags": [ + "Companies V1" + ], + "summary": "Obter os detalhes de uma empresa", + "parameters": [ + { + "name": "companyIdOrTaxNumber", + "in": "path", + "description": "ID da empresa ou Inscrição Federal (CNPJ)", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Sucesso na requisição", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CompanySingleResourceV1" + } + } + } + }, + "400": { + "description": "Algum parametro informado não é válido" + }, + "401": { + "description": "API Key da conta não é valida" + }, + "500": { + "description": "Erro no processamento" + } + } + } + }, + "/v1/companies": { + "get": { + "tags": [ + "Companies V1" + ], + "summary": "Obter os detalhes de todas as empresas da conta", + "parameters": [ + { + "name": "pageCount", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 10 + } + }, + { + "name": "pageIndex", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 1 + } + }, + { + "name": "search", + "in": "query", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Sucesso na requisição", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CompanyCollectionResourceV1" + } + } + } + }, + "400": { + "description": "Algum parametro informado não é válido" + }, + "401": { + "description": "API Key da conta não é valida" + }, + "500": { + "description": "Erro no processamento" + } + } + }, + "post": { + "tags": [ + "Companies V1" + ], + "summary": "Criar uma empresa", + "requestBody": { + "description": "Dados da empresa", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CompanyResourceV1" + } + } + } + }, + "responses": { + "201": { + "description": "Sucesso na criação da empresa", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CompanySingleResourceV1" + } + } + } + }, + "400": { + "description": "Algum parametro informado não é válido" + }, + "401": { + "description": "API Key da conta não é valida" + }, + "409": { + "description": "Já existe uma empresa com o CNPJ informado" + }, + "500": { + "description": "Erro no processamento" + } + } + } + }, + "/v1/companies/{companyId}": { + "put": { + "tags": [ + "Companies V1" + ], + "summary": "Atualizar uma empresa", + "parameters": [ + { + "name": "companyId", + "in": "path", + "description": "ID da empresa", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "Dados da empresa", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CompanyResourceV1" + } + } + } + }, + "responses": { + "200": { + "description": "Sucesso na atualização da empresa", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CompanySingleResourceV1" + } + } + } + }, + "400": { + "description": "Algum parametro informado não é válido" + }, + "401": { + "description": "API Key da conta não é valida" + }, + "500": { + "description": "Erro no processamento" + } + } + }, + "delete": { + "tags": [ + "Companies V1" + ], + "summary": "Deletar uma empresa", + "parameters": [ + { + "name": "companyId", + "in": "path", + "description": "ID da empresa", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "Sucesso na deleção da empresa" + }, + "400": { + "description": "Algum parametro informado não é válido" + }, + "401": { + "description": "API Key da conta não é valida" + }, + "500": { + "description": "Erro no processamento" + } + } + } + } + }, + "components": { + "schemas": { + "DFeTech.TaxPayers.Domain.Entities.Address": { + "type": "object", + "properties": { + "state": { + "type": "string", + "nullable": true + }, + "city": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Domain.Entities.CityBase" + }, + "district": { + "type": "string", + "nullable": true + }, + "additionalInformation": { + "type": "string", + "nullable": true + }, + "streetPrefix": { + "type": "string", + "nullable": true + }, + "street": { + "type": "string", + "nullable": true + }, + "number": { + "type": "string", + "nullable": true + }, + "postalCode": { + "type": "string", + "nullable": true + }, + "country": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "DFeTech.TaxPayers.Domain.Entities.ApiEnvironment": { + "enum": [ + "Development", + "Production", + "Staging" + ], + "type": "string" + }, + "DFeTech.TaxPayers.Domain.Entities.CertificateStatus": { + "enum": [ + "None", + "Active", + "Inactive", + "Overdue", + "Pending" + ], + "type": "string" + }, + "DFeTech.TaxPayers.Domain.Entities.CityBase": { + "type": "object", + "properties": { + "code": { + "type": "string", + "nullable": true + }, + "name": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "DFeTech.TaxPayers.Domain.Entities.CityExtended": { + "type": "object", + "properties": { + "code": { + "type": "string", + "nullable": true + }, + "name": { + "type": "string", + "nullable": true + }, + "country": { + "type": "string", + "nullable": true + }, + "state": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "DFeTech.TaxPayers.Domain.Entities.CompanyFiscalStatus": { + "enum": [ + "None", + "Active", + "CityNotSupported", + "Pending", + "Inactive" + ], + "type": "string" + }, + "DFeTech.TaxPayers.Domain.Entities.EnvironmentType": { + "enum": [ + "None", + "Production", + "Test" + ], + "type": "string" + }, + "DFeTech.TaxPayers.Domain.Entities.FederalTaxDeterminationBy": { + "enum": [ + "NotInformed", + "Default", + "SimplesNacional" + ], + "type": "string" + }, + "DFeTech.TaxPayers.Domain.Entities.LegalNature": { + "enum": [ + "None", + "EmpresaPublica", + "SociedadeEconomiaMista", + "SociedadeAnonimaAberta", + "SociedadeAnonimaFechada", + "SociedadeEmpresariaLimitada", + "SociedadeEmpresariaEmNomeColetivo", + "SociedadeEmpresariaEmComanditaSimples", + "SociedadeEmpresariaEmComanditaporAcoes", + "SociedadeemContaParticipacao", + "Empresario", + "Cooperativa", + "ConsorcioSociedades", + "GrupoSociedades", + "SociedadeEstrangeiraNoBrasil", + "EmpresaBinacionalArgentinoBrasileira", + "EmpresaDomiciliadaExterior", + "ClubeFundoInvestimento", + "SociedadeSimplesPura", + "SociedadeSimplesLimitada", + "SociedadeSimplesEmNomeColetivo", + "SociedadeSimplesEmComanditaSimples", + "EmpresaBinacional", + "ConsorcioEmpregadores", + "ConsorcioSimples", + "EireliNaturezaEmpresaria", + "EireliNaturezaSimples", + "SociedadeUnipessoaldeAdvogados", + "CooperativaDeConsumo", + "EmpresaSimplesDeInovacao", + "InvestidorNaoResidente", + "ServicoNotarial", + "FundacaoPrivada", + "ServicoSocialAutonomo", + "CondominioEdilicio", + "ComissaoConciliacaoPrevia", + "EntidadeMediacaoArbitragem", + "PartidoPolitico", + "EntidadeSindical", + "EstabelecimentoBrasilFundacaoAssociacaoEstrangeiras", + "FundacaoAssociacaoDomiciliadaExterior", + "OrganizacaoReligiosa", + "ComunidadeIndigena", + "FundoPrivado", + "OrgaoDirecaoNacionalPartidoPolitico", + "OrgaoDirecaoRegionalPartidoPolitico", + "OrgaoDirecaoLocalPartidoPolitico", + "ComiteFinanceiroDePartidoPolitico", + "FrentePlebiscitariaOuReferendaria", + "OrganizacaoSocial", + "DemaisCondominios", + "PlanoBeneficiosPrevidenciaComplementarFechada", + "AssociacaoPrivada", + "EmpresaIndividualImobiliaria", + "SeguradoEspecial", + "ContribuinteIndividual", + "CandidatoCargoPoliticoEletivo", + "Leiloeiro", + "ProdutorRural", + "OrganizacaoInternacional", + "RepresentacaoDiplomaticaEstrangeira", + "OutrasInstituicoesExtraterritoriais" + ], + "type": "string" + }, + "DFeTech.TaxPayers.Domain.Entities.MunicipalTaxDeterminationBy": { + "enum": [ + "NotInformed", + "Default", + "SimplesNacional" + ], + "type": "string" + }, + "DFeTech.TaxPayers.Domain.Entities.MunicipalTaxFiscalStatus": { + "enum": [ + "None", + "Active", + "CityNotSupported", + "Pending", + "Inactive" + ], + "type": "string" + }, + "DFeTech.TaxPayers.Domain.Entities.SecurityCredential": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int32" + }, + "code": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "DFeTech.TaxPayers.Domain.Entities.SpecialTaxRegime": { + "enum": [ + "Nenhum", + "MicroempresaMunicipal", + "Estimativa", + "SociedadeDeProfissionais", + "Cooperativa", + "MicroempreendedorIndividual", + "MicroempresarioEmpresaPequenoPorte", + "Automatico" + ], + "type": "string" + }, + "DFeTech.TaxPayers.Domain.Entities.StateCode": { + "enum": [ + "NA", + "RO", + "AC", + "AM", + "RR", + "PA", + "AP", + "TO", + "MA", + "PI", + "CE", + "RN", + "PB", + "PE", + "AL", + "SE", + "BA", + "MG", + "ES", + "RJ", + "SP", + "PR", + "SC", + "RS", + "MS", + "MT", + "GO", + "DF", + "EX" + ], + "type": "string" + }, + "DFeTech.TaxPayers.Domain.Entities.StateTaxProcessingAuthorizer": { + "enum": [ + "Normal", + "EPEC" + ], + "type": "string" + }, + "DFeTech.TaxPayers.Domain.Entities.StateTaxType": { + "enum": [ + "Default", + "NFe", + "NFCe" + ], + "type": "string" + }, + "DFeTech.TaxPayers.Domain.Entities.Status": { + "enum": [ + "None", + "Active", + "Inactive" + ], + "type": "string" + }, + "DFeTech.TaxPayers.Domain.Entities.TaxRegime": { + "enum": [ + "None", + "LucroReal", + "LucroPresumido", + "SimplesNacional", + "SimplesNacionalExcessoSublimite", + "MicroempreendedorIndividual", + "Isento" + ], + "type": "string" + }, + "DFeTech.TaxPayers.Resources.AddressResource": { + "required": [ + "city", + "country", + "district", + "number", + "postalCode", + "state", + "street" + ], + "type": "object", + "properties": { + "state": { + "minLength": 1, + "type": "string", + "description": "Estado, ex.: SP, RJ, AC, padrão ISO 3166-2 ALFA 2." + }, + "city": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CityBaseResource" + }, + "district": { + "minLength": 1, + "type": "string", + "description": "Bairro do Endereço" + }, + "additionalInformation": { + "type": "string", + "description": "Complemento do Endereço, ex.: AP 2, BL A.", + "nullable": true + }, + "street": { + "minLength": 1, + "type": "string", + "description": "Logradouro do Endereço" + }, + "number": { + "minLength": 1, + "type": "string", + "description": "Número do Endereço. Usar S/N para \"sem número\"." + }, + "postalCode": { + "minLength": 1, + "type": "string", + "description": "Cód. Endereço Postal (CEP)" + }, + "country": { + "minLength": 1, + "type": "string", + "description": "País, ex.: BRA, ARG, USA, padrão ISO 3166-1 ALFA-3." + } + }, + "additionalProperties": false + }, + "DFeTech.TaxPayers.Resources.CertificateMetadataResource": { + "type": "object", + "properties": { + "certificate": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CertificateMetadataResourceItem" + } + }, + "additionalProperties": false, + "description": "Certificado" + }, + "DFeTech.TaxPayers.Resources.CertificateMetadataResourceItem": { + "type": "object", + "properties": { + "taxPayerId": { + "type": "string", + "description": "Identificador do contribuinte", + "nullable": true + }, + "thumbprint": { + "type": "string", + "description": "A impressão digital do certificado", + "nullable": true + }, + "taxId": { + "type": "string", + "description": "Documento do contribuinte", + "nullable": true + }, + "subject": { + "type": "string", + "description": "Nome do certificado (subject distinguished name)", + "nullable": true + }, + "validUntil": { + "type": "string", + "description": "Data no horário local após o qual um certificado não é mais válido", + "format": "date-time" + }, + "modifiedOn": { + "type": "string", + "description": "Data de modificação", + "format": "date-time", + "nullable": true + }, + "status": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Domain.Entities.CertificateStatus" + } + }, + "additionalProperties": false, + "description": "Certificado" + }, + "DFeTech.TaxPayers.Resources.CertificatesMetadataResource": { + "type": "object", + "properties": { + "certificates": { + "type": "array", + "items": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CertificateMetadataResourceItem" + }, + "description": "Certificado", + "nullable": true + } + }, + "additionalProperties": false, + "description": "Certificado" + }, + "DFeTech.TaxPayers.Resources.CityBaseResource": { + "required": [ + "code", + "name" + ], + "type": "object", + "properties": { + "code": { + "minLength": 1, + "type": "string", + "description": "Cód. do Município, segundo o Tabela de Municípios do IBGE" + }, + "name": { + "minLength": 1, + "type": "string", + "description": "Nome do Município" + } + }, + "additionalProperties": false + }, + "DFeTech.TaxPayers.Resources.CompaniesResource": { + "type": "object", + "properties": { + "companies": { + "type": "array", + "items": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CompanyResourceItem" + }, + "description": "Lista de Empresa", + "nullable": true + } + }, + "additionalProperties": false, + "description": "Empresas" + }, + "DFeTech.TaxPayers.Resources.CompanyCertificateV1": { + "type": "object", + "properties": { + "thumbprint": { + "type": "string", + "description": "Thumbprint certificado", + "nullable": true + }, + "modifiedOn": { + "type": "string", + "description": "Certificado alterado em", + "format": "date-time", + "nullable": true + }, + "expiresOn": { + "type": "string", + "description": "Certificado expira em", + "format": "date-time", + "nullable": true + }, + "status": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Domain.Entities.CertificateStatus" + } + }, + "additionalProperties": false + }, + "DFeTech.TaxPayers.Resources.CompanyCollectionResourceV1": { + "type": "object", + "properties": { + "totalResults": { + "type": "integer", + "format": "int64", + "nullable": true + }, + "totalPages": { + "type": "integer", + "format": "int32", + "nullable": true + }, + "page": { + "type": "integer", + "format": "int32", + "nullable": true + }, + "companies": { + "type": "array", + "items": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CompanyResourceV1" + }, + "nullable": true + } + }, + "additionalProperties": false + }, + "DFeTech.TaxPayers.Resources.CompanyResource": { + "type": "object", + "properties": { + "company": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CompanyResourceItem" + } + }, + "additionalProperties": false, + "description": "Empresa" + }, + "DFeTech.TaxPayers.Resources.CompanyResourceItem": { + "required": [ + "address", + "federalTaxNumber", + "name", + "taxRegime" + ], + "type": "object", + "properties": { + "name": { + "minLength": 1, + "type": "string", + "description": "Razão Social" + }, + "accountId": { + "type": "string", + "description": "Identificador da conta", + "nullable": true + }, + "tradeName": { + "type": "string", + "description": "Nome Fantasia", + "nullable": true + }, + "federalTaxNumber": { + "type": "integer", + "description": "Número de Inscrição Federal (CNPJ)", + "format": "int64" + }, + "municipalTaxNumber": { + "type": "string", + "description": "Número da Inscrição Municipal", + "nullable": true + }, + "taxRegime": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Domain.Entities.TaxRegime" + }, + "address": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.AddressResource" + }, + "id": { + "type": "string", + "description": "Identificador (gerado automaticamente)", + "nullable": true + }, + "stateTaxes": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Lista de Inscrição Estadual", + "nullable": true + }, + "municipalTaxes": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Lista de Inscrição Municipal", + "nullable": true + }, + "status": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Domain.Entities.Status" + }, + "createdOn": { + "type": "string", + "description": "Data de criação", + "format": "date-time" + }, + "modifiedOn": { + "type": "string", + "description": "Data de modificação", + "format": "date-time", + "nullable": true + } + }, + "additionalProperties": false, + "description": "Dados da Empresa" + }, + "DFeTech.TaxPayers.Resources.CompanyResourceV1": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Identificação", + "nullable": true + }, + "name": { + "type": "string", + "description": "Nome ou Razão Social", + "nullable": true + }, + "tradeName": { + "type": "string", + "description": "Nome fantasia", + "nullable": true + }, + "federalTaxNumber": { + "type": "integer", + "description": "CNPJ ou CPF", + "format": "int64", + "nullable": true + }, + "email": { + "type": "string", + "description": "Email", + "nullable": true + }, + "address": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Domain.Entities.Address" + }, + "openningDate": { + "type": "string", + "description": "Data abertura da empresa", + "format": "date-time", + "nullable": true + }, + "taxRegime": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Domain.Entities.TaxRegime" + }, + "specialTaxRegime": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Domain.Entities.SpecialTaxRegime" + }, + "legalNature": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Domain.Entities.LegalNature" + }, + "companyRegistryNumber": { + "type": "integer", + "description": "Número de Inscricação na Junta Comercial", + "format": "int64", + "nullable": true + }, + "regionalTaxNumber": { + "type": "integer", + "description": "Número de Inscricação na SEFAZ (IE)", + "format": "int64", + "nullable": true + }, + "municipalTaxNumber": { + "type": "string", + "description": "Número de Inscricação na Prefeitura (CCM)", + "nullable": true + }, + "rpsSerialNumber": { + "type": "string", + "description": "RPS número serie", + "nullable": true + }, + "rpsNumber": { + "type": "integer", + "description": "RPS número", + "format": "int64", + "nullable": true + }, + "lastRpsSent": { + "type": "integer", + "description": "RPS número", + "format": "int64", + "nullable": true + }, + "issRate": { + "type": "number", + "description": "Alíquota do ISS para Simples Nacional", + "format": "double", + "nullable": true + }, + "environment": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Domain.Entities.ApiEnvironment" + }, + "fiscalStatus": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Domain.Entities.MunicipalTaxFiscalStatus" + }, + "federalTaxDetermination": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Domain.Entities.FederalTaxDeterminationBy" + }, + "municipalTaxDetermination": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Domain.Entities.MunicipalTaxDeterminationBy" + }, + "loginName": { + "type": "string", + "description": "Login para acesso ao sistema", + "nullable": true + }, + "loginPassword": { + "type": "string", + "description": "Senha para acesso ao sistema", + "nullable": true + }, + "authIssueValue": { + "type": "string", + "description": "Código de autenticação gerado pela prefeitura", + "nullable": true + }, + "certificate": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CompanyCertificateV1" + }, + "createdOn": { + "type": "string", + "description": "Data de criação", + "format": "date-time", + "nullable": true + }, + "modifiedOn": { + "type": "string", + "description": "Data da última modificação", + "format": "date-time", + "nullable": true + }, + "status": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Domain.Entities.Status" + } + }, + "additionalProperties": false + }, + "DFeTech.TaxPayers.Resources.CompanySingleResourceV1": { + "type": "object", + "properties": { + "companies": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CompanyResourceV1" + } + }, + "additionalProperties": false + }, + "DFeTech.TaxPayers.Resources.CreateCompanyResource": { + "type": "object", + "properties": { + "company": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateCompanyResourceItem" + } + }, + "additionalProperties": false, + "description": "Dados para Criar Empresa" + }, + "DFeTech.TaxPayers.Resources.CreateCompanyResourceItem": { + "required": [ + "address", + "federalTaxNumber", + "name", + "taxRegime" + ], + "type": "object", + "properties": { + "name": { + "minLength": 1, + "type": "string", + "description": "Razão Social" + }, + "accountId": { + "type": "string", + "description": "Identificador da conta", + "nullable": true + }, + "tradeName": { + "type": "string", + "description": "Nome Fantasia", + "nullable": true + }, + "federalTaxNumber": { + "type": "integer", + "description": "Número de Inscrição Federal (CNPJ)", + "format": "int64" + }, + "municipalTaxNumber": { + "type": "string", + "description": "Número da Inscrição Municipal", + "nullable": true + }, + "taxRegime": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Domain.Entities.TaxRegime" + }, + "address": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.AddressResource" + } + }, + "additionalProperties": false, + "description": "Criar Empresa" + }, + "DFeTech.TaxPayers.Resources.CreateMunicipalTaxResource": { + "type": "object", + "properties": { + "municipalTax": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateMunicipalTaxResourceItem" + } + }, + "additionalProperties": false + }, + "DFeTech.TaxPayers.Resources.CreateMunicipalTaxResourceItem": { + "type": "object", + "properties": { + "city": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Domain.Entities.CityExtended" + }, + "taxNumber": { + "type": "string", + "description": "Inscrição Municipal", + "nullable": true + }, + "environment": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Domain.Entities.ApiEnvironment" + }, + "specialTaxRegime": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Domain.Entities.SpecialTaxRegime" + }, + "email": { + "type": "string", + "description": "Email", + "nullable": true + }, + "legalNature": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Domain.Entities.LegalNature" + }, + "companyRegistryNumber": { + "type": "integer", + "description": "Registro da Empresa na Junta Comercial", + "format": "int64", + "nullable": true + }, + "regionalTaxNumber": { + "type": "integer", + "description": "Número Região", + "format": "int64", + "nullable": true + }, + "issRate": { + "type": "number", + "description": "ALíquota do ISS (%)", + "format": "double", + "nullable": true + }, + "federalTaxDetermination": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Domain.Entities.FederalTaxDeterminationBy" + }, + "municipalTaxDetermination": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Domain.Entities.MunicipalTaxDeterminationBy" + }, + "loginName": { + "type": "string", + "description": "Login para acesso ao sistema", + "nullable": true + }, + "loginPassword": { + "type": "string", + "description": "Senha para acesso ao sistema", + "nullable": true + }, + "authIssueValue": { + "type": "string", + "description": "Código de autenticação gerado pela prefeitura", + "nullable": true + }, + "rpsNumber": { + "type": "integer", + "description": "Número do RPS", + "format": "int64" + }, + "lastRpsSent": { + "type": "integer", + "description": "Último RPS enviado", + "format": "int64" + }, + "rpsSerialNumber": { + "type": "string", + "description": "Série do RPS", + "nullable": true + } + }, + "additionalProperties": false + }, + "DFeTech.TaxPayers.Resources.CreateStateTaxProcessingDetailsResource": { + "type": "object", + "properties": { + "switchAuthorizerStrategy": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.StateTaxProcessingSwitchAuthorizerStrategyResource" + } + }, + "additionalProperties": false + }, + "DFeTech.TaxPayers.Resources.CreateStateTaxResource": { + "type": "object", + "properties": { + "stateTax": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateStateTaxResourceItem" + } + }, + "additionalProperties": false + }, + "DFeTech.TaxPayers.Resources.CreateStateTaxResourceItem": { + "required": [ + "code", + "taxNumber", + "type" + ], + "type": "object", + "properties": { + "code": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Domain.Entities.StateCode" + }, + "environmentType": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Domain.Entities.EnvironmentType" + }, + "taxNumber": { + "minLength": 1, + "type": "string", + "description": "Inscrição Estadual" + }, + "specialTaxRegime": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Domain.Entities.SpecialTaxRegime" + }, + "serie": { + "type": "integer", + "description": "Serie para a emissão NFe", + "format": "int32", + "nullable": true + }, + "number": { + "type": "integer", + "description": "Número para a emissão NFe", + "format": "int64", + "nullable": true + }, + "securityCredential": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Domain.Entities.SecurityCredential" + }, + "type": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Domain.Entities.StateTaxType" + }, + "processingDetails": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateStateTaxProcessingDetailsResource" + } + }, + "additionalProperties": false + }, + "DFeTech.TaxPayers.Resources.ErrorResource": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "description": "Código do erro", + "format": "int32", + "nullable": true, + "readOnly": true + }, + "message": { + "type": "string", + "description": "Mensagem contendo os detalhes do erro", + "nullable": true + } + }, + "additionalProperties": false, + "description": "Erro" + }, + "DFeTech.TaxPayers.Resources.ErrorsResource": { + "type": "object", + "properties": { + "errors": { + "type": "array", + "items": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.ErrorResource" + }, + "description": "Lista de Erros", + "nullable": true, + "readOnly": true + } + }, + "additionalProperties": false, + "description": "Lista de Erros" + }, + "DFeTech.TaxPayers.Resources.MunicipalTaxResource": { + "type": "object", + "properties": { + "municipalTax": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.MunicipalTaxResourceItem" + } + }, + "additionalProperties": false, + "description": "Inscrição Municipal" + }, + "DFeTech.TaxPayers.Resources.MunicipalTaxResourceItem": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Identificador (gerado automaticamente)", + "nullable": true + }, + "companyId": { + "type": "string", + "description": "Código da Empresa", + "nullable": true + }, + "accountId": { + "type": "string", + "description": "Código da Conta", + "nullable": true + }, + "status": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Domain.Entities.Status" + }, + "city": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Domain.Entities.CityExtended" + }, + "taxNumber": { + "type": "string", + "description": "Inscrição Municipal", + "nullable": true + }, + "specialTaxRegime": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Domain.Entities.SpecialTaxRegime" + }, + "email": { + "type": "string", + "description": "Email", + "nullable": true + }, + "legalNature": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Domain.Entities.LegalNature" + }, + "companyRegistryNumber": { + "type": "integer", + "description": "Registro da Empresa na Junta Comercial", + "format": "int64", + "nullable": true + }, + "regionalTaxNumber": { + "type": "integer", + "description": "Número Região", + "format": "int64", + "nullable": true + }, + "rpsSerialNumber": { + "type": "string", + "description": "Série do RPS", + "nullable": true + }, + "rpsNumber": { + "type": "integer", + "description": "Número do RPS", + "format": "int64" + }, + "lastRpsSent": { + "type": "integer", + "description": "Número do último RPS", + "format": "int64" + }, + "issRate": { + "type": "number", + "description": "ALíquota do ISS (%)", + "format": "double", + "nullable": true + }, + "environment": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Domain.Entities.ApiEnvironment" + }, + "fiscalStatus": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Domain.Entities.CompanyFiscalStatus" + }, + "federalTaxDetermination": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Domain.Entities.FederalTaxDeterminationBy" + }, + "municipalTaxDetermination": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Domain.Entities.MunicipalTaxDeterminationBy" + }, + "loginName": { + "type": "string", + "description": "Login para acesso ao sistema", + "nullable": true + }, + "loginPassword": { + "type": "string", + "description": "Senha para acesso ao sistema", + "nullable": true + }, + "authIssueValue": { + "type": "string", + "description": "Código de autenticação gerado pela prefeitura", + "nullable": true + }, + "rpsSerialNumbers": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Todas as séries para esta Inscrição Municipal", + "nullable": true + } + }, + "additionalProperties": false, + "description": "Dados da Inscrição Municipal" + }, + "DFeTech.TaxPayers.Resources.SerieResource": { + "type": "object", + "properties": { + "serie": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.SerieResourceItem" + } + }, + "additionalProperties": false + }, + "DFeTech.TaxPayers.Resources.SerieResourceItem": { + "type": "object", + "properties": { + "rpsNumber": { + "type": "integer", + "description": "Próximo número de RPS", + "format": "int64" + }, + "lastRpsSent": { + "type": "integer", + "description": "Último RPS enviado", + "format": "int64" + } + }, + "additionalProperties": false, + "description": "Dados da Serie" + }, + "DFeTech.TaxPayers.Resources.StateTaxProcessingAuthorizerResource": { + "enum": [ + "Normal", + "EPEC" + ], + "type": "string" + }, + "DFeTech.TaxPayers.Resources.StateTaxProcessingSwitchAuthorizerStrategyResource": { + "enum": [ + "Manual", + "StateTaxAuthorityStatusUnavailable" + ], + "type": "string" + }, + "DFeTech.TaxPayers.Resources.StateTaxResource": { + "type": "object", + "properties": { + "stateTax": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.StateTaxResourceItem" + } + }, + "additionalProperties": false, + "description": "Inscrição Estadual" + }, + "DFeTech.TaxPayers.Resources.StateTaxResourceItem": { + "required": [ + "code", + "taxNumber", + "type" + ], + "type": "object", + "properties": { + "code": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Domain.Entities.StateCode" + }, + "environmentType": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Domain.Entities.EnvironmentType" + }, + "taxNumber": { + "minLength": 1, + "type": "string", + "description": "Inscrição Estadual" + }, + "specialTaxRegime": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Domain.Entities.SpecialTaxRegime" + }, + "serie": { + "type": "integer", + "description": "Serie para a emissão NFe", + "format": "int32", + "nullable": true + }, + "number": { + "type": "integer", + "description": "Número para a emissão NFe", + "format": "int64", + "nullable": true + }, + "securityCredential": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Domain.Entities.SecurityCredential" + }, + "type": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Domain.Entities.StateTaxType" + }, + "processingDetails": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateStateTaxProcessingDetailsResource" + }, + "id": { + "type": "string", + "description": "Identificador (gerado automaticamente)", + "nullable": true + }, + "companyId": { + "type": "string", + "description": "Código da Empresa", + "nullable": true + }, + "accountId": { + "type": "string", + "description": "Código da Conta", + "nullable": true + }, + "status": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Domain.Entities.Status" + }, + "series": { + "type": "array", + "items": { + "type": "integer", + "format": "int32" + }, + "description": "Todas as séries para esta Inscrição Estadual", + "nullable": true + }, + "createdOn": { + "type": "string", + "description": "Data de criação", + "format": "date-time" + }, + "modifiedOn": { + "type": "string", + "description": "Data de modificação", + "format": "date-time", + "nullable": true + } + }, + "additionalProperties": false, + "description": "Dados da Inscrição Estadual" + }, + "DFeTech.TaxPayers.Resources.StateTaxSwitchAuthorizerRequest": { + "type": "object", + "properties": { + "authorizer": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.StateTaxProcessingAuthorizerResource" + }, + "reason": { + "type": "string", + "description": "Motivo da contingência", + "nullable": true + } + }, + "additionalProperties": false + }, + "DFeTech.TaxPayers.Resources.StateTaxSwitchAuthorizerResponse": { + "type": "object", + "properties": { + "fromAuthorizer": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Domain.Entities.StateTaxProcessingAuthorizer" + }, + "toAuthorizer": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Domain.Entities.StateTaxProcessingAuthorizer" + }, + "reason": { + "type": "string", + "description": "Motivo da contingência", + "nullable": true + }, + "modifiedOn": { + "type": "string", + "description": "Data e hora da modificação", + "format": "date-time" + } + }, + "additionalProperties": false + }, + "DFeTech.TaxPayers.Resources.StateTaxesResource": { + "type": "object", + "properties": { + "stateTaxes": { + "type": "array", + "items": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.StateTaxResourceItem" + }, + "description": "Lista de Inscrições Estaduais", + "nullable": true + } + }, + "additionalProperties": false, + "description": "Inscrições Estaduais" + }, + "DFeTech.TaxPayers.Resources.UpdateCompanyResource": { + "type": "object", + "properties": { + "company": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateCompanyResourceItem" + } + }, + "additionalProperties": false, + "description": "Dados para Alterar Empresa" + }, + "DFeTech.TaxPayers.Resources.UpdateCompanyResourceItem": { + "required": [ + "address", + "federalTaxNumber", + "name", + "taxRegime" + ], + "type": "object", + "properties": { + "name": { + "minLength": 1, + "type": "string", + "description": "Razão Social" + }, + "accountId": { + "type": "string", + "description": "Identificador da conta", + "nullable": true + }, + "tradeName": { + "type": "string", + "description": "Nome Fantasia", + "nullable": true + }, + "federalTaxNumber": { + "type": "integer", + "description": "Número de Inscrição Federal (CNPJ)", + "format": "int64" + }, + "municipalTaxNumber": { + "type": "string", + "description": "Número da Inscrição Municipal", + "nullable": true + }, + "taxRegime": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Domain.Entities.TaxRegime" + }, + "address": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.AddressResource" + }, + "id": { + "type": "string", + "description": "Identificador (gerado automaticamente)", + "nullable": true + } + }, + "additionalProperties": false, + "description": "Alterar Empresa" + }, + "DFeTech.TaxPayers.Resources.UpdateMunicipalTaxResource": { + "type": "object", + "properties": { + "municipalTax": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateMunicipalTaxResourceItem" + } + }, + "additionalProperties": false + }, + "DFeTech.TaxPayers.Resources.UpdateMunicipalTaxResourceItem": { + "type": "object", + "properties": { + "city": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Domain.Entities.CityExtended" + }, + "taxNumber": { + "type": "string", + "description": "Inscrição Municipal", + "nullable": true + }, + "specialTaxRegime": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Domain.Entities.SpecialTaxRegime" + }, + "email": { + "type": "string", + "description": "Email", + "nullable": true + }, + "legalNature": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Domain.Entities.LegalNature" + }, + "companyRegistryNumber": { + "type": "integer", + "description": "Registro da Empresa na Junta Comercial", + "format": "int64", + "nullable": true + }, + "regionalTaxNumber": { + "type": "integer", + "description": "Número Região", + "format": "int64", + "nullable": true + }, + "rpsNumber": { + "type": "integer", + "description": "Número do RPS", + "format": "int64" + }, + "lastRpsSent": { + "type": "integer", + "description": "Número do último RPS", + "format": "int64" + }, + "issRate": { + "type": "number", + "description": "ALíquota do ISS (%)", + "format": "double", + "nullable": true + }, + "federalTaxDetermination": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Domain.Entities.FederalTaxDeterminationBy" + }, + "municipalTaxDetermination": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Domain.Entities.MunicipalTaxDeterminationBy" + }, + "loginName": { + "type": "string", + "description": "Login para acesso ao sistema", + "nullable": true + }, + "loginPassword": { + "type": "string", + "description": "Senha para acesso ao sistema", + "nullable": true + }, + "authIssueValue": { + "type": "string", + "description": "Código de autenticação gerado pela prefeitura", + "nullable": true + }, + "environment": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Domain.Entities.ApiEnvironment" + }, + "rpsSerialNumber": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "DFeTech.TaxPayers.Resources.UpdateStateTaxResource": { + "type": "object", + "properties": { + "stateTax": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.UpdateStateTaxResourceItem" + } + }, + "additionalProperties": false + }, + "DFeTech.TaxPayers.Resources.UpdateStateTaxResourceItem": { + "required": [ + "code", + "taxNumber", + "type" + ], + "type": "object", + "properties": { + "code": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Domain.Entities.StateCode" + }, + "environmentType": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Domain.Entities.EnvironmentType" + }, + "taxNumber": { + "minLength": 1, + "type": "string", + "description": "Inscrição Estadual" + }, + "specialTaxRegime": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Domain.Entities.SpecialTaxRegime" + }, + "serie": { + "type": "integer", + "description": "Serie para a emissão NFe", + "format": "int32", + "nullable": true + }, + "number": { + "type": "integer", + "description": "Número para a emissão NFe", + "format": "int64", + "nullable": true + }, + "securityCredential": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Domain.Entities.SecurityCredential" + }, + "type": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Domain.Entities.StateTaxType" + }, + "processingDetails": { + "$ref": "#/components/schemas/DFeTech.TaxPayers.Resources.CreateStateTaxProcessingDetailsResource" + } + }, + "additionalProperties": false + }, + "Microsoft.OData.Edm.EdmContainerElementKind": { + "enum": [ + "None", + "EntitySet", + "ActionImport", + "FunctionImport", + "Singleton" + ], + "type": "string" + }, + "Microsoft.OData.Edm.EdmExpressionKind": { + "enum": [ + "None", + "BinaryConstant", + "BooleanConstant", + "DateTimeOffsetConstant", + "DecimalConstant", + "FloatingConstant", + "GuidConstant", + "IntegerConstant", + "StringConstant", + "DurationConstant", + "Null", + "Record", + "Collection", + "Path", + "If", + "Cast", + "IsOf", + "FunctionApplication", + "LabeledExpressionReference", + "Labeled", + "PropertyPath", + "NavigationPropertyPath", + "DateConstant", + "TimeOfDayConstant", + "EnumMember", + "AnnotationPath" + ], + "type": "string" + }, + "Microsoft.OData.Edm.EdmSchemaElementKind": { + "enum": [ + "None", + "TypeDefinition", + "Term", + "Action", + "EntityContainer", + "Function" + ], + "type": "string" + }, + "Microsoft.OData.Edm.EdmTypeKind": { + "enum": [ + "None", + "Primitive", + "Entity", + "Complex", + "Collection", + "EntityReference", + "Enum", + "TypeDefinition", + "Untyped", + "Path" + ], + "type": "string" + }, + "Microsoft.OData.Edm.IEdmEntityContainer": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "readOnly": true + }, + "schemaElementKind": { + "$ref": "#/components/schemas/Microsoft.OData.Edm.EdmSchemaElementKind" + }, + "namespace": { + "type": "string", + "nullable": true, + "readOnly": true + }, + "elements": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Microsoft.OData.Edm.IEdmEntityContainerElement" + }, + "nullable": true, + "readOnly": true + } + }, + "additionalProperties": false + }, + "Microsoft.OData.Edm.IEdmEntityContainerElement": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "readOnly": true + }, + "containerElementKind": { + "$ref": "#/components/schemas/Microsoft.OData.Edm.EdmContainerElementKind" + }, + "container": { + "$ref": "#/components/schemas/Microsoft.OData.Edm.IEdmEntityContainer" + } + }, + "additionalProperties": false + }, + "Microsoft.OData.Edm.IEdmExpression": { + "type": "object", + "properties": { + "expressionKind": { + "$ref": "#/components/schemas/Microsoft.OData.Edm.EdmExpressionKind" + } + }, + "additionalProperties": false + }, + "Microsoft.OData.Edm.IEdmModel": { + "type": "object", + "properties": { + "schemaElements": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Microsoft.OData.Edm.IEdmSchemaElement" + }, + "nullable": true, + "readOnly": true + }, + "vocabularyAnnotations": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Microsoft.OData.Edm.Vocabularies.IEdmVocabularyAnnotation" + }, + "nullable": true, + "readOnly": true + }, + "referencedModels": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Microsoft.OData.Edm.IEdmModel" + }, + "nullable": true, + "readOnly": true + }, + "declaredNamespaces": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "readOnly": true + }, + "directValueAnnotationsManager": { + "$ref": "#/components/schemas/Microsoft.OData.Edm.Vocabularies.IEdmDirectValueAnnotationsManager" + }, + "entityContainer": { + "$ref": "#/components/schemas/Microsoft.OData.Edm.IEdmEntityContainer" + } + }, + "additionalProperties": false + }, + "Microsoft.OData.Edm.IEdmSchemaElement": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "readOnly": true + }, + "schemaElementKind": { + "$ref": "#/components/schemas/Microsoft.OData.Edm.EdmSchemaElementKind" + }, + "namespace": { + "type": "string", + "nullable": true, + "readOnly": true + } + }, + "additionalProperties": false + }, + "Microsoft.OData.Edm.IEdmType": { + "type": "object", + "properties": { + "typeKind": { + "$ref": "#/components/schemas/Microsoft.OData.Edm.EdmTypeKind" + } + }, + "additionalProperties": false + }, + "Microsoft.OData.Edm.IEdmTypeReference": { + "type": "object", + "properties": { + "isNullable": { + "type": "boolean", + "readOnly": true + }, + "definition": { + "$ref": "#/components/schemas/Microsoft.OData.Edm.IEdmType" + } + }, + "additionalProperties": false + }, + "Microsoft.OData.Edm.Vocabularies.IEdmDirectValueAnnotationsManager": { + "type": "object", + "additionalProperties": false + }, + "Microsoft.OData.Edm.Vocabularies.IEdmTerm": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "readOnly": true + }, + "schemaElementKind": { + "$ref": "#/components/schemas/Microsoft.OData.Edm.EdmSchemaElementKind" + }, + "namespace": { + "type": "string", + "nullable": true, + "readOnly": true + }, + "type": { + "$ref": "#/components/schemas/Microsoft.OData.Edm.IEdmTypeReference" + }, + "appliesTo": { + "type": "string", + "nullable": true, + "readOnly": true + }, + "defaultValue": { + "type": "string", + "nullable": true, + "readOnly": true + } + }, + "additionalProperties": false + }, + "Microsoft.OData.Edm.Vocabularies.IEdmVocabularyAnnotatable": { + "type": "object", + "additionalProperties": false + }, + "Microsoft.OData.Edm.Vocabularies.IEdmVocabularyAnnotation": { + "type": "object", + "properties": { + "qualifier": { + "type": "string", + "nullable": true, + "readOnly": true + }, + "term": { + "$ref": "#/components/schemas/Microsoft.OData.Edm.Vocabularies.IEdmTerm" + }, + "target": { + "$ref": "#/components/schemas/Microsoft.OData.Edm.Vocabularies.IEdmVocabularyAnnotatable" + }, + "value": { + "$ref": "#/components/schemas/Microsoft.OData.Edm.IEdmExpression" + }, + "usesDefault": { + "type": "boolean", + "readOnly": true + } + }, + "additionalProperties": false + }, + "Microsoft.OData.ODataEntitySetInfo": { + "type": "object", + "properties": { + "typeAnnotation": { + "$ref": "#/components/schemas/Microsoft.OData.ODataTypeAnnotation" + }, + "url": { + "type": "string", + "format": "uri", + "nullable": true + }, + "name": { + "type": "string", + "nullable": true + }, + "title": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "Microsoft.OData.ODataFunctionImportInfo": { + "type": "object", + "properties": { + "typeAnnotation": { + "$ref": "#/components/schemas/Microsoft.OData.ODataTypeAnnotation" + }, + "url": { + "type": "string", + "format": "uri", + "nullable": true + }, + "name": { + "type": "string", + "nullable": true + }, + "title": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "Microsoft.OData.ODataServiceDocument": { + "type": "object", + "properties": { + "typeAnnotation": { + "$ref": "#/components/schemas/Microsoft.OData.ODataTypeAnnotation" + }, + "entitySets": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Microsoft.OData.ODataEntitySetInfo" + }, + "nullable": true + }, + "singletons": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Microsoft.OData.ODataSingletonInfo" + }, + "nullable": true + }, + "functionImports": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Microsoft.OData.ODataFunctionImportInfo" + }, + "nullable": true + } + }, + "additionalProperties": false + }, + "Microsoft.OData.ODataSingletonInfo": { + "type": "object", + "properties": { + "typeAnnotation": { + "$ref": "#/components/schemas/Microsoft.OData.ODataTypeAnnotation" + }, + "url": { + "type": "string", + "format": "uri", + "nullable": true + }, + "name": { + "type": "string", + "nullable": true + }, + "title": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "Microsoft.OData.ODataTypeAnnotation": { + "type": "object", + "properties": { + "typeName": { + "type": "string", + "nullable": true, + "readOnly": true + } + }, + "additionalProperties": false + } + }, + "securitySchemes": { + "Authorization_Header": { + "type": "apiKey", + "description": "Autenticar usando o cabeçalho HTTP", + "name": "Authorization", + "in": "header" + }, + "Authorization_QueryParam": { + "type": "apiKey", + "description": "Autenticar usando o parâmetro na URL", + "name": "apikey", + "in": "query" + }, + "Authorization_JwtBearer": { + "type": "http", + "description": "Autenticar usando o cabeçalho HTTP", + "scheme": "bearer", + "bearerFormat": "Json Web Token" + } + } + }, + "security": [ + { + "Authorization_Header": [], + "Authorization_QueryParam": [] + }, + { + "Authorization_JwtBearer": [] + } + ], + "tags": [ + { + "name": "Companies", + "description": "Utilize esta requisição para criar novas empresas plataforma para processar Documentos Fiscais. **Empresa** representa uma pessoa jurídica que precisa processar Documentos Fiscais." + }, + { + "name": "Companies Certificates", + "description": "Utilize esta requisição para fazer upload de um **Certificado da ICP-Brasil** do tipo __e-CNPJ A1__ ou __NFE A1__ em uma **Empresa** e vincula-lo para processamentos.\r\n\r\nO **Certificado da ICP-Brasil** funciona como uma identidade virtual, para empresas e pessoas, que permite a identificação segura e inequívoca do autor de uma mensagem ou transação feita em meios eletrônicos, como a web." + }, + { + "name": "Companies State Taxes", + "description": "Está sessão é destinada às **Incrições Estaduais(IE)**. Uma **Incrição Estadual** representa os dados necessários sobre o cadastro Estadual (ICMS) que é preciso para processar Documentos Fiscais na SEFAZ.\r\n\r\nUtilizando as informações abaixo você pode criar novas IEs na empresa para processar **Documentos Fiscais**. Além disso, também é possível listar as IEs por empresa e consultar, alterar e exluir uma IE pelo ID da mesma." + } + ] +} \ No newline at end of file diff --git a/openapi/spec/product-invoice-rtc-v1.yaml b/openapi/spec/product-invoice-rtc-v1.yaml new file mode 100644 index 0000000..a5296e1 --- /dev/null +++ b/openapi/spec/product-invoice-rtc-v1.yaml @@ -0,0 +1,5083 @@ +openapi: 3.0.1 +info: + title: API de Emissão de Nota Fiscal de Produto (NFe/NFCe) - RTC + version: v3 + description: | + ### Introdução + + API para emissão de Notas Fiscais de Produto (NFe/NFCe) de acordo com a legislação vigente e os padrões estabelecidos pelo Governo. + + ### Adequação de Leiaute + + Leiaute NFe/NFCe Reforma Tributária do Consumo - Modelo 55 e 65. + + Esta adequação de leiaute contempla até a Nota Técnica **NT_2025.002_v1.30_RTC_NF-e_IBS_CBS_IS**. + + > **Atenção** + > _Sujeito a alterações mediante notas técnicas e processos de homologação._ +servers: + - url: https://api.nfse.io + description: Nota Fiscal de Produto/Consumidor (RTC). +tags: + - name: Nota Fiscal de Produto/Consumidor (RTC) + description: Operações relacionadas à Nota Fiscal Eletrônica de Produto + +paths: + /v2/companies/:companyId/productinvoices: + post: + tags: + - Nota Fiscal de Produto/Consumidor (RTC) + summary: Envio de dados para emissão de NF-e/NFC-e + description: Envia os dados para a criação e autorização de uma nova Nota Fiscal de Produto. + operationId: createProductInvoice + requestBody: + description: "Objeto JSON contendo todos os dados da NF-e, conforme o schema ProductInvoiceRequest." + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/ProductInvoiceRequest' + examples: + minimalWithIBSCBS: + summary: Exemplo Mínimo com IBS/CBS + value: + operationType: "Outgoing" + destination: "InternalOperation" + printType: "DANFE_NFC_E" + purposeType: "Normal" + consumerType: "FinalConsumer" + presenceType: "Internet" + buyer: + name: "NF-E EMITIDA EM AMBIENTE DE HOMOLOGACAO - SEM VALOR FISCAL" + federalTaxNumber: 11223344000155 + address: + state: "SC" + city: + code: "4205407" + name: "Florianópolis" + district: "Centro" + street: "Rua Teste" + number: "123" + postalCode: "88010000" + country: "BRA" + type: "LegalEntity" + stateTaxNumberIndicator: "TaxPayer" + stateTaxNumber: "123456789" + items: + - description: "NOTA FISCAL EMITIDA EM AMBIENTE DE HOMOLOGACAO - SEM VALOR FISCAL" + ncm: "85171300" + cfop: 5102 + quantity: 1.0 + unitAmount: 150.00 + totalAmount: 150.0 + totalIndicator: true + tax: + icms: + origin: "0" + cst: "00" + pis: + cst: "01" + cofins: + cst: "01" + IBSCBS: + situationCode: "000" + classCode: "000001" + calculationMode: "OfficialService" + payment: + - paymentDetail: + - method: "Cash" + paymentType: "InCash" # Pagamento à vista + amount: 150.00 + intermediateWithDetailedIBSCBS: + summary: Exemplo Intermediário com IBS/CBS detalhado + value: + operationType: "Outgoing" + destination: "InterstateOperation" + printType: "NFeNormalPortrait" + purposeType: "Normal" + consumerType: "Normal" + presenceType: "Internet" + buyer: + name: "Empresa Exemplo Intermediario LTDA" + federalTaxNumber: 11223344000155 + address: + state: "RJ" + city: + code: "3304557" + name: "Rio de Janeiro" + district: "Centro" + street: "Avenida Rio Branco" + number: "1" + postalCode: "20090003" + country: "BRA" + type: "LegalEntity" + stateTaxNumberIndicator: "TaxPayer" + stateTaxNumber: "123456789" + items: + - description: "Produto Exemplo com IBS/CBS Detalhado" + ncm: "84713012" + cfop: 6102 + quantity: 2.0 + unitAmount: 750.00 + totalAmount: 1500.00 + totalIndicator: true + tax: + icms: + origin: "0" + cst: "00" + pis: + cst: "01" + cofins: + cst: "01" + IBSCBS: + situationCode: "101" + classCode: "RT0001" + basis: 1500.00 + state: # IBS Estadual + rate: 8.5 + amount: 127.50 + municipal: # IBS Municipal + rate: 2.0 + amount: 30.00 + cbs: # CBS + rate: 10.5 + amount: 157.50 + totals: + icms: + productAmount: 1500.00 + invoiceAmount: 1500.00 + ibsCbsTotals: + basis: 1500.00 + ibs: + state: { amount: 127.50 } + municipal: { amount: 30.00 } + totalAmount: 157.50 + cbs: + amount: 157.50 + totalInvoiceAmount: 1657.50 + payment: + - paymentDetail: + - method: "InstantPayment" # Pagamento Instantâneo PIX + paymentType: "InCash" + amount: 1500.00 + intermediateWithTaxDetermination: + summary: Exemplo Intermediário com taxDetermination + value: + operationType: "Outgoing" + destination: "InterstateOperation" + printType: "NFeNormalPortrait" + purposeType: "Normal" + consumerType: "Normal" + presenceType: "Internet" + buyer: + name: "Empresa Exemplo com Tax Determination" + federalTaxNumber: 55443322000111 + address: + state: "SP" + city: + code: "3550308" + name: "São Paulo" + district: "Itaim Bibi" + street: "Avenida Brigadeiro Faria Lima" + number: "2000" + postalCode: "01452002" + country: "BRA" + type: "LegalEntity" + stateTaxNumberIndicator: "TaxPayer" + stateTaxNumber: "987654321" + items: + - description: "Produto com cálculo automático de imposto" + ncm: "90064000" + quantity: 5.0 + unitAmount: 300.00 + totalAmount: 1500.00 + totalIndicator: true + taxDetermination: + operationCode: 121 + issuerTaxProfile: "retail" + buyerTaxProfile: "final_consumer_non_icms_contributor" + origin: "0" + acquisitionPurpose: null + tax: + IBSCBS: # Grupo de Informações do IBS e CBS + situationCode: "000" # Código de Situação Tributária do IBS/CBS + classCode: "000001" # Código de Classificação Tributária do IBS/CBS + calculationMode: "OfficialService" + full: + summary: Exemplo Completo (Exaustivo) + value: + id: "nfe-exemplo-completo-12345" + payment: + - paymentDetail: + - method: "CreditCard" + methodDescription: "Cartão de Crédito Master" + paymentType: "Term" + amount: 1500.5 + card: + federalTaxNumber: "12345678000199" + flag: "Mastercard" + authorization: "ABC123456" + integrationPaymentType: "Integrated" + federalTaxNumberRecipient: "98765432000100" # CNPJ do beneficiário do pagamento + idPaymentTerminal: "TERM-PDV-001" + paymentDate: "2025-10-28T10:00:00-03:00" + federalTaxNumberPag: "11223344000155" + statePag: "SP" + - method: "InstantPayment" + methodDescription: "PIX" + paymentType: "InCash" + amount: 523.5 + card: null # Grupo de Cartões + paymentDate: "2025-10-29T11:30:00-03:00" + federalTaxNumberPag: "111222333000144" + statePag: "RJ" + payBack: 0.0 + serie: 1 + number: 987654 + operationOn: "2025-10-28T10:00:00-03:00" + expectedDeliveryOn: "2025-10-30" + operationNature: "Venda de Mercadoria c/ Reforma Tributaria" + operationType: "Outgoing" + destination: "InterstateOperation" + consumptionCityCode: 3550308 + ibsConsumptionCityCode: 3550308 + printType: "NFeNormalPortrait" + purposeType: "Normal" # Finalidade da emissão + debitType: "FinesAndInterest" + creditType: "IbsPresumedCreditAppropriationZfm" + consumerType: "Normal" + presenceType: "Internet" + contingencyOn: "2025-10-28T09:00:00-03:00" + contingencyJustification: "Problemas tecnicos no SEFAZ de origem." + governmentPurchase: + entityType: "state" + rateReduction: 15.5 + operationType: "supply" + buyer: + accountId: "ACC-BUYER-789" + id: "BUYER-001" + name: "Empresa Compradora Exemplo LTDA" + federalTaxNumber: 11223344000155 + email: "compras@comprador.com" + address: + state: "RJ" + city: + code: "3304557" + name: "Rio de Janeiro" + district: "Centro" + additionalInformation: "Sala 1001" + street: "Avenida Rio Branco" + number: "1" + postalCode: "20090003" + country: "BRA" + phone: "21987654321" + type: "LegalEntity" + stateTaxNumberIndicator: "TaxPayer" + tradeName: "Comprador Exemplo" + taxRegime: "LucroReal" + stateTaxNumber: "123456789" + transport: + freightModality: "ByThirdParties" + transportGroup: + accountId: "ACC-TRANSP-456" + id: "TRANSP-001" + name: "Transportadora Veloz S.A." + federalTaxNumber: 55667788000199 + email: "contato@transportadora.com" + address: + state: "SP" + city: + code: "3550308" + name: "São Paulo" + district: "Vila Olimpia" + additionalInformation: "Andar 5" + street: "Rua Funchal" + number: "500" + postalCode: "04551060" + country: "BRA" + phone: "11912345678" + type: "LegalEntity" + stateTaxNumber: "987654321" + transportRetention: null + reboque: + plate: "REB1A23" + uf: "SP" + rntc: "12345678" + wagon: "WAG-001" + ferry: "BAL-001" + volume: + volumeQuantity: 10 + species: "Caixa (CX)" + brand: "MARCA-EXEMPLO" + volumeNumeration: "001-010" + netWeight: 1000.5 + grossWeight: 1100.75 + transportVehicle: + plate: "VEI4B56" + state: "SP" + rntc: "87654321" + sealNumber: "LACRE-98765" + transpRate: + serviceAmount: 350.0 + bcRetentionAmount: 350.0 + icmsRetentionRate: 12.0 + icmsRetentionAmount: 42.0 + cfop: 5352 + cityGeneratorFactCode: 3550308 + additionalInformation: + fisco: "Informacoes de interesse do Fisco (Reforma Tributaria)." + taxpayer: "Informacoes complementares do contribuinte." + xmlAuthorized: [12345678901, 98765432109] + effort: "Safra 2025" + order: "Pedido 123" + contract: "Contrato 456" + taxDocumentsReference: + - documentElectronicInvoice: + accessKey: "41251012345678000199550010009876541234567890" + advancePayment: + - accessKey: "41251012345678000199550010001112221234567890" + taxpayerComments: + - field: "MeuCampoCustom" + text: "Observacao fiscal do meu campo" + - field: "OutroCampo" + text: "Outra observacao" + referencedProcess: + - identifierConcessory: "PROC-JUD-123" + identifierOrigin: 1 + concessionActType: 1 + export: + state: "SP" + office: "Porto de Santos" + local: "Armazem 1 - Alfandega" + items: + - code: "PROD-EXAUSTIVO-001" + codeGTIN: "7891234567890" + description: "Produto de Exemplo Exaustivo com todos os impostos" + ncm: "84713012" + nve: ["AA01", "BB02"] + extipi: "01" + cfop: 5102 + unit: "UN" + quantity: 10.0 + unitAmount: 200.0 + totalAmount: 2000.0 + codeTaxGTIN: "7891234567890" + unitTax: "UN" + quantityTax: 10.0 + taxUnitAmount: 200.0 + freightAmount: 10.0 + insuranceAmount: 5.0 + discountAmount: 15.0 + othersAmount: 2.0 + totalIndicator: true + usedMovableAssetIndicator: false + itemAmount: 2002.0 # Valor total do item + cest: "0100100" + benefit: "RS051500" + tax: + totalTax: 450.5 + icms: + origin: "0" + cst: "00" + csosn: null + baseTaxModality: "3" + baseTax: 2002.0 + baseTaxSTModality: "4" + baseTaxSTReduction: "10.0" + baseTaxST: 2200.0 + baseTaxReduction: 0.0 + basisBenefitCode: "BENEF-ICMS-01" + stRate: 18.0 + stAmount: 396.0 + stMarginAmount: 10.0 + rate: 18.0 + amount: 360.36 + percentual: null + snCreditRate: 0.0 + snCreditAmount: 0.0 + stMarginAddedAmount: "10.0" + stRetentionAmount: "396.0" + baseSTRetentionAmount: "2200.0" + baseTaxOperationPercentual: "100.0" + ufst: "RJ" + amountSTReason: "Valor desonerado ST" + baseSNRetentionAmount: "0.0" + snRetentionAmount: "0.0" + amountOperation: "360.36" + percentualDeferment: "0.0" + baseDeferred: "0.0" + exemptAmount: 0.0 + exemptReason: "Others" + exemptAmountST: 0.0 + exemptReasonST: "Others" + fcpRate: 2.0 + fcpAmount: 40.04 + fcpstRate: 2.0 + fcpstAmount: 44.0 + fcpstRetRate: 2.0 + fcpstRetAmount: 44.0 + baseTaxFCPSTAmount: 2200.0 + substituteAmount: 360.36 + stFinalConsumerRate: 20.0 + effectiveBaseTaxReductionRate: 0.0 + effectiveBaseTaxAmount: 2002.0 + effectiveRate: 18.0 + effectiveAmount: 360.36 + deductionIndicator: "NotDeduct" + ipi: + cst: "50" + classificationCode: "999" + classification: "CL-01" + producerCNPJ: "44556677000188" + stampCode: "SELO-IPI-123" + stampQuantity: 10.0 + base: 2002.0 + rate: 10.0 + unitQuantity: 10.0 + unitAmount: 20.0 + amount: 200.2 + ii: + baseTax: "2002.00" + customsExpenditureAmount: "100.00" + amount: 120.0 + iofAmount: 20.0 + vEnqCamb: 5.0 + pis: + cst: "01" + baseTax: 2002.0 + rate: 1.65 + amount: 33.03 + baseTaxProductQuantity: 10.0 + productRate: 3.303 + cofins: + cst: "01" + baseTax: 2002.0 + rate: 7.6 + amount: 152.15 + baseTaxProductQuantity: 10.0 + productRate: 15.215 + icmsDestination: + vBCUFDest: 2002.0 + pFCPUFDest: 2.0 + pICMSUFDest: 18.0 + pICMSInter: 12.0 + pICMSInterPart: 60.0 + vFCPUFDest: 40.04 + vICMSUFDest: 72.07 + vICMSUFRemet: 48.05 + vBCFCPUFDest: 2002.0 + IS: + situationCode: "101" + classificationCode: "IS0001" + basis: 2002.0 + rate: 5.0 + unitRate: null + unit: null + quantity: null + amount: 100.1 + IBSCBS: + situationCode: "101" + classCode: "RT0001" + donationIndicator: "0" + basis: 2002.0 + state: + rate: 10.0 + deferment: { rate: 1.0, amount: 20.02 } + returnedAmount: { amount: 10.0 } + reduction: { rateReduction: 0.5, effectiveRate: 9.5 } + amount: 190.19 + municipal: + rate: 5.0 + deferment: { rate: 0.5, amount: 10.01 } + returnedAmount: { amount: 5.0 } + reduction: { rateReduction: 0.2, effectiveRate: 4.8 } + amount: 96.1 + ibsTotalAmount: 286.29 + cbs: + rate: 12.0 + deferment: { rate: 1.0, amount: 24.02 } + returnedAmount: { amount: 12.0 } + reduction: { rateReduction: 0.1, effectiveRate: 10.8 } + amount: 216.22 + regularTaxation: + situationCode: "101" + classCode: "RT0001" + stateEffectiveRate: 10.0 + amount: 200.2 + municipalEffectiveRate: 5.0 + municipalAmount: 100.1 + cbsEffectiveRate: 12.0 + cbsAmount: 240.24 + governmentPurchase: + stateRate: 8.0 + stateAmount: 160.16 + municipalRate: 4.0 + municipalAmount: 80.08 + cbsRate: 10.0 + cbsAmount: 200.2 + monophase: + standart: + quantityBasis: 10.0 + ibsAdRemRate: 15.0 + cbsAdRemRate: 25.0 + ibsAmount: 150.0 + cbsAmount: 250.0 + withholding: + quantityBasis: 5.0 + ibsAdRemRate: 10.0 + ibsAmount: 50.0 + cbsAdRemRate: 20.0 + cbsAmount: 100.0 + previouslyWithheld: + quantityBasis: 3.0 + ibsAdRemRate: 5.0 + ibsAmount: 15.0 + cbsAdRemRate: 10.0 + cbsAmount: 30.0 + deferment: + ibsRate: 50.0 + ibsAmount: 75.0 + cbsRate: 50.0 + cbsAmount: 125.0 + ibsAmount: 140.0 + cbsAmount: 255.0 + creditTransfer: + ibsAmount: 50.0 + cbsAmount: 30.0 + operationalPresumedCredit: # Crédito presumido operacional + basis: 1000.0 + classificationCode: "RuralProducerNonTaxpayer" + ibs: { rate: 10.0, amount: 100.0, suspensiveConditionAmount: 10.0 } + cbs: { rate: 5.0, amount: 50.0, suspensiveConditionAmount: 5.0 } + creditReversal: + ibsReversalAmount: 2.0 # Valor do IBS a ser estornado + cbsReversalAmount: 1.0 + zfmPresumedCredit: + classificationCode: "IntermediateGoods" + amount: 123.45 + competenceAdjustment: + assessmentPeriod: "2025-09" + ibsAmount: 5.0 + cbsAmount: 3.0 # Valor de CBS referente ao ajuste de competência + additionalInformation: "Informacao adicional do item." + numberOrderBuy: "PED-COMPRA-555" # Número do pedido de compra + itemNumberOrderBuy: 1 + importControlSheetNumber: "FCI-12345678-ABCD" + vehicleDetail: + operationType: 1 # 1-Venda concessionária + chassis: "9BWZZZ377VT004251" + colorCode: "001" + colorDescription: "PRETO" + enginePower: "150" + engineDisplacement: "1998" + netWeight: "1250.000" + grossWeight: "1500.000" + serialNumber: "123456789" + fuelType: "16" # 16-Álcool/Gasolina + engineNumber: "MOTOR123456789" + maximumTractionCapacity: "0.0000" + wheelBase: "2649" + modelYear: 2026 + manufactureYear: 2025 + paintType: "S" # Sólida + vehicleType: "06" # Automóvel + vehicleSpecies: 1 # Passageiro + vinCondition: "N" # N-Normal + vehicleCondition: 1 # 1-Acabado + brandModelCode: "025104" + denatranColorCode: "11" # 11-PRETA + seatingCapacity: 5 + restrictionType: 0 # 0-Não há + fuelDetail: + codeANP: "210203001" + percentageNG: 50.0 + descriptionANP: "GLP" + percentageGLP: 40.0 # Percentual de GLP derivado do petróleo + percentageNGn: 30.0 + percentageGNi: 20.0 + startingAmount: 100.0 + codif: "CODIF-123" + amountTemp: 10.0 + stateBuyer: "RJ" + cide: { bc: 10.0, rate: 5.0, cideAmount: 0.5 } + pump: { spoutNumber: 1, number: 1, tankNumber: 1, beginningAmount: 1000.0, endAmount: 1010.0, percentageBio: 10.0 } + fuelOrigin: { indImport: 1, cUFOrig: 35, pOrig: 10.0 } + presumedCredit: { code: "BENEF-001", rate: 4.0, amount: 80.08 } + ibsZfmPresumedCreditClassification: "ItAndOtherGoods" + importDeclarations: + - code: "DI-123456" + registeredOn: "2025-10-01T10:00:00-03:00" + customsClearanceName: "Porto de Santos" + customsClearanceState: "SP" + customsClearancedOn: "2025-10-15T10:00:00-03:00" + additions: + - { code: 1, manufacturer: "FAB-CHINA-XYZ", amount: 10.0, drawback: 12345 } + exporter: "EXP-US-99" + internationalTransport: "Maritime" + intermediation: "ByOrder" + acquirerFederalTaxNumber: "11223344000155" + stateThird: "RJ" + exportDetails: + - drawback: "DB-98765" + hintInformation: { registryId: "RE-555444", accessKey: "41251012345678000199550010003334441234567890", quantity: 10.0 } + referencedDFe: + accessKey: "35251098765432000199550010000001231000001234" + itemNumber: 1 + taxDetermination: + operationCode: 120 + issuerTaxProfile: "industry" + buyerTaxProfile: "final_consumer_non_icms_contributor" + origin: "0" + acquisitionPurpose: "43" + purchaseInformation: + commitmentNote: "NE-2025-12345" + purchaseOrder: "PO-2025-67890" + contractNumber: "CT-2025-ABCDE" + totals: + icms: + baseTax: 2002.0 + icmsAmount: 360.36 + icmsExemptAmount: 0.0 + stCalculationBasisAmount: 2200.0 + stAmount: 396.0 + productAmount: 2000.0 + freightAmount: 10.0 + insuranceAmount: 5.0 + discountAmount: 15.0 + iiAmount: 120.0 + ipiAmount: 200.2 + pisAmount: 33.03 + cofinsAmount: 152.15 + othersAmount: 2.0 + invoiceAmount: 2023.5 + fcpufDestinationAmount: 40.04 + icmsufDestinationAmount: 72.07 + icmsufSenderAmount: 48.05 + federalTaxesAmount: 450.5 + fcpAmount: 40.04 + fcpstAmount: 44.0 + fcpstRetAmount: 44.0 + ipiDevolAmount: 0.0 + qBCMono: 10.0 + vICMSMono: 150.0 + qBCMonoReten: 5.0 + vICMSMonoReten: 50.0 + qBCMonoRet: 3.0 + vICMSMonoRet: 15.0 + issqn: + totalServiceNotTaxedICMS: 0.0 + baseRateISS: 0.0 + totalISS: 0.0 + valueServicePIS: 0.0 + valueServiceCOFINS: 0.0 + provisionService: "2025-10-28T10:00:00-03:00" + deductionReductionBC: 0.0 + valueOtherRetention: 0.0 + discountUnconditional: 0.0 + discountConditioning: 0.0 + totalRetentionISS: 0.0 + codeTaxRegime: 1.0 + withheldTaxes: + pisAmount: 10.0 # Valor Retido de PIS + cofinsAmount: 50.0 + csllAmount: 20.0 + irrfBasis: 1000.0 + irrfAmount: 150.0 + socialSecutiryBasis: 1000.0 + socialSecutiryAmount: 110.0 + isTotals: + amount: 100.1 + ibsCbsTotals: + basis: 2002.0 + ibs: + state: { defermentAmount: 20.02, returnedAmount: 10.0, amount: 190.19 } + municipal: { defermentAmount: 10.01, returnedAmount: 5.0, amount: 96.1 } + totalAmount: 286.29 + presumedCreditAmount: 100.0 + presumedCreditConditionalAmount: 10.0 + cbs: + defermentAmount: 24.02 + returnedAmount: 12.0 + amount: 216.22 + presumedCreditAmount: 50.0 + presumedCreditConditionalAmount: 5.0 + monophase: + ibs: { amount: 150.0, withheldAmount: 50.0, previouslyWithheldAmount: 15.0 } # Totais de IBS monofásico + cbs: { amount: 250.0, withheldAmount: 100.0, previouslyWithheldAmount: 30.0 } # Totais de CBS monofásico + totalInvoiceAmount: 2675.04 + billing: + bill: + number: "FAT-987654" + originalAmount: 2000.0 + discountAmount: 15.0 + netAmount: 1985.0 + duplicates: + - { number: "DUP-001", expirationOn: "2025-11-28T00:00:00Z", amount: 1000.0 } + - { number: "DUP-002", expirationOn: "2025-12-28T00:00:00Z", amount: 985.0 } + issuer: + stStateTaxNumber: "9876543210" + transactionIntermediate: + federalTaxNumber: 77889900000144 # CNPJ do Intermediador da Transação + identifier: "Marketplace-ID-555" + delivery: + accountId: "ACC-DELIV-111" + id: "DELIV-001" + name: "Local de Entrega Ltda" + federalTaxNumber: 22334455000166 + email: "entrega@local.com" + address: + state: "MG" + city: { code: "3106200", name: "Belo Horizonte" } + district: "Savassi" + additionalInformation: "Loja B" + street: "Avenida Cristovão Colombo" + number: "300" + postalCode: "30140150" + country: "BRA" + phone: "31911223344" + type: "LegalEntity" + stateTaxNumber: "111222333" + withdrawal: + accountId: "ACC-WITHDR-222" + id: "WITHDR-001" + name: "Local de Retirada Ltda" + federalTaxNumber: 33445566000177 + email: "retirada@local.com" + address: + state: "PR" + city: { code: "4106902", name: "Curitiba" } + district: "Centro Cívico" + additionalInformation: "Galpão 3" + street: "Avenida Cândido de Abreu" + number: "100" + postalCode: "80530000" + country: "BRA" + phone: "41955667788" + type: "LegalEntity" + stateTaxNumber: "444555666" + responses: + '201': + description: NF-e criada com sucesso (aguardando processamento/autorização). + content: + application/json: + schema: + $ref: '#/components/schemas/InvoiceResource' + '400': + description: Erro de validação (Bad Request). O corpo da requisição está inválido. + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorsResource' + '500': + description: Erro interno do servidor. + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResource' +components: + schemas: + ProductInvoiceRequest: + title: "Nota Fiscal de Produto (NFe) Schema" + description: "Schema para validar o objeto ProductInvoiceRequest da API" + type: "object" + required: + - items + - payment + properties: + id: + type: "string" + nullable: true + description: "Identificador único" + payment: + type: "array" + items: + $ref: "#/components/schemas/PaymentResource" + description: "Grupo de Formas de Pagamento (pag)" + serie: + type: "integer" + nullable: true + description: "Série do Documento Fiscal (serie)" + format: "int32" + number: + type: "integer" + nullable: true + description: "Número do Documento Fiscal (nNF)" + format: "int64" + operationOn: + type: "string" + nullable: true + description: "Data e Hora de Saída ou da Entrada da Mercadoria/Produto (dhSaiEnt)\r\n\r\n Data e hora no formato UTC (Universal Coordinated Time): AAAA-MM-DDThh:mm:ssTZD.\r\n" + format: "date-time" + expectedDeliveryOn: + type: "string" + nullable: true + description: "Data da previsão de entrega ou disponibilização do bem (dPrevEntrega). Formato: “AAAA-MM-DD”. Observação: Não informar este campo para a NFC-e." + format: "date" + operationNature: + type: "string" + nullable: true + description: "Descrição da Natureza da Operação (natOp)" + operationType: + description: "Tipo do Documento Fiscal (tpNF)" + $ref: "#/components/schemas/OperationType" + destination: + description: "Identificador de Local de destino da operação (idDest)" + $ref: "#/components/schemas/Destination" + consumptionCityCode: + description: "Código do Município de Ocorrência do Fato Gerador (cMunFG)" + $ref: "#/components/schemas/ConsumptionCityCode" + ibsConsumptionCityCode: + description: "Município de ocorrência do fato gerador do IBS/CBS (cMunFGIBS)" + $ref: "#/components/schemas/IbsConsumptionCityCode" + printType: + description: "Formato de impressão do DANFE (tpImp)" + $ref: "#/components/schemas/PrintType" + purposeType: + description: "Finalidade da emissão da NF-e (finNFe)" + $ref: "#/components/schemas/PurposeType" + debitType: + description: "Tipo de Nota de Débito (tpNFDebito)" + $ref: "#/components/schemas/DebitType" + creditType: + $ref: "#/components/schemas/CreditType" + consumerType: + description: "Indica operação com Consumidor final (indFinal)" + $ref: "#/components/schemas/ConsumerType" + presenceType: + description: "Indicador de presença do comprador no estabelecimento (indPres)" + $ref: "#/components/schemas/ConsumerPresenceType" + contingencyOn: + type: "string" + nullable: true + description: "Data e Hora da entrada em contingência (dhCont)\r\n\r\n Data e hora no formato UTC (Universal Coordinated Time): AAAA-MM-DDThh:mm:ssTZD\r\n" + format: "date-time" + contingencyJustification: + type: "string" + nullable: true + description: "Justificativa da entrada em contingência (xJust)" + governmentPurchase: + description: "Grupo de Compras Governamentais (gCompraGov)" + $ref: "#/components/schemas/GovernmentPurchaseResource" + buyer: + description: "Identificação do Destinatário (dest)" + $ref: "#/components/schemas/BuyerResource" + transport: + description: "Grupo de Informações do Transporte da NF-e (transp)" + $ref: "#/components/schemas/TransportInformationResource" + additionalInformation: + description: "Informações adicionais da NF-e (infAdic)" + $ref: "#/components/schemas/AdditionalInformationResource" + export: + description: "Informações de exportação (exporta)" + $ref: "#/components/schemas/ExportResource" + items: + type: "array" + minItems: 1 + items: + $ref: "#/components/schemas/InvoiceItemResource" + description: "Detalhamento de Produtos e Serviços (det). Pelo menos um item é obrigatório." + totals: + description: "Grupo de Valores Totais da NF-e (total)" + $ref: "#/components/schemas/Total" + billing: + $ref: "#/components/schemas/BillingResource" + issuerTaxSubstitute: + description: "IE do Substituto Tributário da UF de destino da mercadoria, quando houver a retenção do ICMS ST para a UF de destino. (IEST)" + $ref: "#/components/schemas/IssuerFromRequestResource" + transactionIntermediate: + description: "Grupo de Informações do Intermediador da Transação (infIntermed)" + $ref: "#/components/schemas/IntermediateResource" + delivery: + description: "Identificação do Local de entrega (entrega)" + $ref: "#/components/schemas/DeliveryInformationResource" + withdrawal: + description: "Identificação do Local de retirada (retirada)" + $ref: "#/components/schemas/WithdrawalInformationResource" + purchaseInformation: + $ref: "#/components/schemas/PurchaseInformationResource" + additionalProperties: false + ActivityResource: + type: "object" + properties: + data: + description: "Detalhes do Evento" + nullable: true + type: # Nome do Evento gerado + type: "string" + nullable: true + description: "Nome do Evento gerado" + sequence: + type: "integer" + nullable: true + description: "Número sequencial do Evento" + format: "int32" + additionalProperties: false + AdditionResource: + type: "object" + properties: + code: + type: "integer" # Numero da adição + nullable: true + description: "Numero da adição (nAdicao)" + format: "int64" + manufacturer: + type: "string" + nullable: true + description: "Código do fabricante estrangeiro (cFabricante)" + amount: + type: "number" + nullable: true # Valor do desconto do item da DI – Adição + description: "Valor do desconto do item da DI – Adição (vDescDI)" + format: "double" + drawback: + type: "integer" + nullable: true + description: "Número do ato concessório de Drawback (nDraw)" + format: "int64" + additionalProperties: false + description: "Adições (adi)" + AdditionalInformationResource: + type: "object" + properties: + fisco: + type: "string" # Informações Adicionais de Interesse do Fisco + nullable: true + description: "Informações Adicionais de Interesse do Fisco (infAdFisco)" + taxpayer: + type: "string" + nullable: true + description: "Informações Complementares de interesse do Contribuinte (infCpl)" + xmlAuthorized: + type: "array" + nullable: true + items: + type: "integer" + format: "int64" + description: "Pessoas autorizadas para o download do XML da NF-e (autXML). CNPJ ou CPF das pessoas autorizadas. Máximo de 10 autorizações." + effort: + type: "string" + nullable: true # Esforço + description: "Esforço (xCampo)" + order: + type: "string" + nullable: true + description: "Pedido (xCampo)" + contract: + type: "string" + nullable: true + description: "Contrato (xCampo)" + taxDocumentsReference: + type: "array" + nullable: true + items: + $ref: "#/components/schemas/TaxDocumentsReferenceResource" + description: "Documentos Fiscais Referenciados (refECF)" + advancePayment: # Grupo de notas de antecipação de pagamento + type: "array" + nullable: true + items: + $ref: "#/components/schemas/AdvancePaymentItemResource" + description: "Grupo de notas de antecipação de pagamento - Informado para abater as parcelas de antecipação de pagamento, conforme Art. 10. § 4º (gPagAntecipado)." + taxpayerComments: + type: "array" + nullable: true + items: + $ref: "#/components/schemas/TaxpayerCommentsResource" + description: "Observações fiscais (obsCont)" + referencedProcess: # Processos referenciados + type: "array" + nullable: true + items: + $ref: "#/components/schemas/ReferencedProcessResource" + description: "Processos referenciados (procRef)" + additionalProperties: false + AddressResource: + type: "object" + required: + - state + - city + - district + - street + - number + properties: + state: + type: "string" # Estado + description: "Estado, ex.: SP, RJ, AC, padrão ISO 3166-2 ALFA 2." + city: + description: "Cidade do Endereço (cMun)" + $ref: "#/components/schemas/CityResource" + district: + type: "string" + description: "Bairro do Endereço (xBairro)" + additionalInformation: + type: "string" + nullable: true + description: "Complemento do Endereço, ex.: AP 2, BL A. (xCpl)" + street: + type: "string" + description: "Logradouro do Endereço (xLgr)" + number: # Número do Endereço + type: "string" + description: "Número do Endereço. Usar S/N para \"sem número\". (nro)" + postalCode: + type: "string" + nullable: true + description: "Cód. Endereço Postal (CEP)" + country: + type: "string" # País + nullable: true + description: "País, ex.: BRA, ARG, USA, padrão ISO 3166-1 ALFA-3. (xPais). Opcional — se omitido, assume BRA." + default: "BRA" + phone: + type: "string" + nullable: true + description: "Telefone (fone)" + additionalProperties: false + description: "Dados do Endereço" + AdvancePaymentItemResource: + type: "object" + properties: + accessKey: # Chave de Acesso da NF-e para antecipação de pagamento + type: "string" + nullable: true + additionalProperties: false + description: "Grupo de notas de antecipação de pagamento - Informado para abater as parcelas de antecipação de pagamento, conforme Art. 10. § 4º (gPagAntecipado)." + AuthorizationResource: + type: "object" + properties: + receiptOn: + type: "string" + nullable: true # Data e hora do recebimento + description: "Data e hora do recebimento (dhRecbto)" + format: "date-time" + accessKey: + type: "string" + nullable: true + description: "Chave de Acesso da NF-e (chNFe)" + message: + type: "string" + nullable: true # Mensagem da SEFAZ + description: + type: string + description: "Mensagem da SEFAZ (xMotivo)" + additionalProperties: false + BillResource: + type: "object" + properties: + number: + type: "string" # Número da Fatura + nullable: true + description: "Número da Fatura (nFat)" + originalAmount: + type: "number" + nullable: true + description: "Valor Original da Fatura (vOrig)" + format: "double" + discountAmount: + type: "number" + nullable: true # Valor do desconto + description: "Valor do desconto (vDesc)" + format: "double" + netAmount: + type: "number" + nullable: true + description: "Valor Líquido da Fatura (vLiq)" + format: "double" + additionalProperties: false + BillingResource: + type: "object" + properties: + bill: + description: "Grupo Fatura (fat)" + $ref: "#/components/schemas/BillResource" + duplicates: + type: "array" + nullable: true + items: + $ref: "#/components/schemas/DuplicateResource" + description: "Grupo Duplicata (dup)" + additionalProperties: false # Grupo Cobrança (cobr) + BuyerResource: + type: "object" + required: + - name + - federalTaxNumber + - address + properties: + accountId: + type: "string" + nullable: true + description: "Identificador da Conta" + id: + type: "string" + nullable: true + description: "Identificação" + name: + type: "string" + description: "Nome ou Razão Social (xNome)" + federalTaxNumber: + type: "integer" # O valor padrão de consumerType é determinado com base neste campo. Se for um CNPJ, o padrão será "Normal"; se for um CPF, será "FinalConsumer". + description: "CNPJ ou CPF (CNPJ/CPF)" + format: "int64" + email: + type: "string" + nullable: true # Email — opcional pelo leiaute SEFAZ + description: "Email (email)" + address: + description: "Dados do Endereço (enderDest)" + $ref: "#/components/schemas/AddressResource" + type: + description: "Tipo da pessoa (indIEDest)" + $ref: "#/components/schemas/PersonType" + stateTaxNumberIndicator: + description: "Indicador Inscrição Estadual (indIEDest)" + $ref: "#/components/schemas/ReceiverStateTaxIndicator" + tradeName: + type: "string" # Nome fantasia + nullable: true + description: "Nome fantasia (xFant)" + taxRegime: + description: "Regime de tributação (CRT)" + $ref: "#/components/schemas/TaxRegime" + stateTaxNumber: + type: "string" + nullable: true # Inscrição Estadual + description: "Inscrição Estadual (IE)" + additionalProperties: false + description: "Identificação do Destinatário da Nota Fiscal eletrônica - Grupo Obrigatório para a NF-e (modelo 55) e opcional para NFC-e (modelo 65)." + CBSTaxResource: + type: "object" + description: "Grupo de Informações da CBS" + properties: + rate: + type: "number" # Alíquota da CBS (em percentual) + nullable: true + description: "Alíquota da CBS (em percentual) (pCBS)" + format: "double" + deferment: + description: "Grupo de Informações do Diferimento (gDif)" + $ref: "#/components/schemas/DefermentTaxResource" + returnedAmount: + description: "Grupo de Informações da Devolução de Tributos (gDevTrib)" + $ref: "#/components/schemas/ReturnedTaxResource" + reduction: + description: "Grupo de informações da redução da alíquota (gRed)" + $ref: "#/components/schemas/ReductionTaxResource" + amount: + type: "number" + nullable: true # Valor total da CBS + description: "Valor total da CBS (vCBS)." + format: "double" + additionalProperties: false + CIDEResource: + type: "object" + properties: + bc: + type: "number" # BC da CIDE + nullable: true + description: "BC da CIDE (qBCProd)" + format: "double" + rate: + type: "number" + nullable: true + description: "Valor da alíquota da CIDE (vAliqProd)" + format: "double" + cideAmount: + type: "number" # Valor da CIDE + nullable: true + description: "Valor da CIDE (vCIDE)" + format: "double" + additionalProperties: false + CardResource: + type: "object" + properties: + federalTaxNumber: + type: "string" # CNPJ da Credenciadora de cartão de crédito e/ou débito + nullable: true + description: "CNPJ da Credenciadora de cartão de crédito e/ou débito (CNPJ)" + flag: + description: "Bandeira da operadora de cartão de crédito e/ou débito (tBand)" + $ref: "#/components/schemas/FlagCard" + authorization: + type: "string" + nullable: true # Número de autorização da operação cartão de crédito e/ou débito + description: "Número de autorização da operação cartão de crédito e/ou débito (cAut)" + integrationPaymentType: + description: "Tipo de Integração para pagamento (tpIntegra)" + $ref: "#/components/schemas/IntegrationPaymentType" + federalTaxNumberRecipient: + type: "string" # CNPJ do beneficiário do pagamento + nullable: true + idPaymentTerminal: + type: "string" + nullable: true + additionalProperties: false + CityResource: + type: "object" + required: + - code + - name + properties: + code: + type: "string" # Cód. do Município + description: "Cód. do Município, segundo o Tabela de Municípios do IBGE (cMun)" + name: + type: "string" + description: "Nome do Município (xMun)" + additionalProperties: false + CofinsTaxResource: + type: "object" + required: + - cst + properties: + cst: + type: "string" # Código de Situação Tributária da COFINS + description: "Código de Situação Tributária da COFINS (CST)" + baseTax: + type: "number" + nullable: true + description: "Valor da Base de Cálculo da COFINS (vBC)" + format: "double" + rate: + type: "number" + nullable: true # Alíquota da COFINS (em percentual) + description: "Alíquota da COFINS (em percentual) (pCOFINS)" + format: "double" + amount: + type: "number" + nullable: true + description: "Valor da COFINS (vCOFINS)" + format: "double" + baseTaxProductQuantity: + type: "number" + nullable: true # Quantidade Vendida + description: "Quantidade Vendida (qBCProd)" + format: "double" + productRate: + type: "number" + nullable: true + description: "Alíquota da COFINS (em reais) (vAliqProd)" + format: "double" + additionalProperties: false + description: "Grupo do COFINS\r\n\r\nID: S01\r\nPai: M01\r\n\r\n Obs: Informar apenas um dos grupos S02, S03, S04 ou S04\r\n com base valor atribuído ao campo S06 – CST do COFINS\r\n" + CompetenceAdjustmentResource: + type: "object" + description: "Ajuste de Competência (gAjusteCompet)" + properties: + assessmentPeriod: + type: "string" + nullable: true # Ano e mês referência do período de apuração + description: "Ano e mês referência do período de apuração (AAAA-MM) (competApur)" + ibsAmount: + type: "number" + nullable: true + description: "Valor de IBS referente ao ajuste de competência (vIBS)" + format: "double" + cbsAmount: + type: "number" + nullable: true # Valor de CBS referente ao ajuste de competência + description: "Valor de CBS referente ao ajuste de competência (vCBS)" + format: "double" + additionalProperties: false + ConsumerPresenceType: + type: "string" + description: | + Indicador de Presença (indPres). + Valores possíveis: + - `None`: Não se aplica (ex: nota complementar ou de ajuste) + - `Presence`: Operação presencial + - `Internet`: Operação não presencial, pela Internet + - `Telephone`: Operação não presencial, Teleatendimento + - `Delivery`: NFC-e em operação com entrega a domicílio + - `OthersNonPresenceOperation`: Operação não presencial, outros + enum: + - "None" + - "Presence" + - "Internet" + - "Telephone" + - "Delivery" + - "OthersNonPresenceOperation" + default: "Internet" + ConsumerType: + type: "string" + description: | + Indica operação com Consumidor final (indFinal). O valor padrão é determinado pelo `federalTaxNumber` do `buyer`: `Normal` para CNPJ, `FinalConsumer` para CPF. + Valores possíveis: + - `FinalConsumer`: Consumidor final + - `Normal`: Operação normal + enum: + - "FinalConsumer" + - "Normal" + default: "Normal" # O valor padrão é "Normal" para CNPJ e "FinalConsumer" para CPF. + + ConsumptionCityCode: + type: "integer" + nullable: true + description: "Código do Município de ocorrência do fato gerador do IBS/CBS (cMunFGIBS). Informar o município de ocorrência do fato gerador do IBS/CBS. Este campo só é preenchido quando \"indPres = 5 (Operação presencial, fora do estabelecimento)\" e não incluir o endereço do destinatário (Grupo: E05) ou local de entrega (Grupo: G01)." + format: "int64" + ContingencyDetails: + type: "object" + properties: + authorizer: + $ref: "#/components/schemas/StateTaxProcessingAuthorizer" + startedOn: + type: "string" + description: "Data e hora do início da contingência" + format: "date-time" + reason: + type: "string" # Justificativa da entrada em contingência + nullable: true + description: "Justificativa da entrada em contingência" + additionalProperties: false + CreditReversalResource: + type: "object" + description: "Estorno de Crédito (gEstornoCred)" + properties: + ibsReversalAmount: + type: "number" # Valor do IBS a ser estornado + nullable: true + description: "Valor do IBS a ser estornado (vIBSEstCred)" + format: "double" + cbsReversalAmount: + type: "number" + nullable: true + description: "Valor da CBS a ser estornada (vCBSEstCred)" + format: "double" + additionalProperties: false + CreditReversalTotalsResource: + type: "object" + description: "Estorno de Crédito (gEstornoCred)" + properties: + ibsReversalAmount: + type: "number" + nullable: true + description: "Valor do IBS a ser estornado (vIBSEstCred)" + format: "double" + cbsReversalAmount: + type: "number" + nullable: true + description: "Valor da CBS a ser estornada (vCBSEstCred)" + format: "double" + additionalProperties: false + CreditTransferTaxResource: + type: "object" + description: "Informações de Transferências de Crédito (gTransfCred)" + properties: + ibsAmount: + type: "number" # Valor de IBS a transferir + nullable: true + description: "Valor de IBS a transferir (vIBS)." + format: "double" + cbsAmount: + type: "number" + nullable: true + description: "Valor de CBS a transferir (vCBS)." + format: "double" + additionalProperties: false + CreditType: + type: "string" + nullable: true + description: | + Tipo de Nota de Crédito (tpNFCredito). + Valores possíveis: + - `FinesAndInterest`: Multa e juros + - `IbsPresumedCreditAppropriationZfm`: Apropriação de crédito presumido de IBS sobre o saldo devedor na ZFM + - `ReturnDeliveryRefusedOrNotFound`: Retorno por recusa na entrega ou não localização do destinatário + - `ValueReduction`: Redução de valores + - `TransferCreditSuccession`: Transferência de crédito na sucessão + enum: + - "FinesAndInterest" + - "IbsPresumedCreditAppropriationZfm" + - "ReturnDeliveryRefusedOrNotFound" + - "ValueReduction" + - "TransferCreditSuccession" + DebitType: + type: "string" + nullable: true + description: | + Tipo de Nota de Débito (tpNFDebito). + Valores possíveis: + - `TransferCreditsToCooperatives`: Transferência de créditos para cooperativas + - `CancelCreditsExemptImmuneSales`: Cancelamento de créditos por vendas isentas/imunes + - `UnprocessedInvoicesDebits`: Débitos de faturas não processadas no cálculo + - `FinesAndInterest`: Multas e juros + - `TransferInheritanceCredit`: Transferência de crédito de herança + - `AdvancePayment`: Pagamento antecipado + - `InventoryLoss`: Perda de estoque + - `SnDisqualification`: Desenquadramento do Simples Nacional + enum: + - "TransferCreditsToCooperatives" + - "CancelCreditsExemptImmuneSales" + - "UnprocessedInvoicesDebits" + - "FinesAndInterest" + - "TransferInheritanceCredit" + - "AdvancePayment" + - "InventoryLoss" + - "SnDisqualification" + DefermentTaxResource: + type: "object" + description: "Grupo de Informações do Diferimento" + properties: + rate: + type: "number" # Percentual do diferimento + nullable: true + description: "Percentual do diferimento" + format: "double" + amount: + type: "number" + nullable: true + description: "Valor do Diferimento" + format: "double" + additionalProperties: false + DeliveryInformationResource: + type: "object" + properties: + accountId: + type: "string" + nullable: true + description: "Identificador da Conta" + id: + type: "string" + nullable: true + description: "Identificação" + name: + type: "string" + nullable: true + description: "Nome ou Razão Social (xNome)" + federalTaxNumber: + type: "integer" + nullable: true # CNPJ ou CPF + description: "CNPJ ou CPF" + format: "int64" + email: + type: "string" + nullable: true + description: "Email" + address: + $ref: "#/components/schemas/AddressResource" + type: + $ref: "#/components/schemas/PersonType" + stateTaxNumber: + type: "string" # Inscrição Estadual + nullable: true + description: "Inscrição Estadual (IE)" + additionalProperties: false + description: "Identificação do Local de entrega (entrega)" + Destination: + type: "string" + enum: + - "None" + - "InternalOperation" + - "InterstateOperation" + - "InternationalOperation" + default: "InternalOperation" # Operação interna + description: | + Identificador de local de destino da operação (idDest). + Valores possíveis: + - `None`: Não definido + - `InternalOperation`: Operação interna + - `InterstateOperation`: Operação interestadual + - `InternationalOperation`: Operação com o exterior + DisablementResource: + type: "object" + properties: + environment: + $ref: "#/components/schemas/EnvironmentType" + serie: + type: "integer" # Série + description: "Série" + format: "int32" + state: + $ref: "#/components/schemas/StateCode" + beginNumber: + type: "integer" + description: "Número inicial" + format: "int32" + lastNumber: + type: "integer" # Número final + description: "Número final (usar o mesmo número inicial se for apenas um número)" + format: "int32" + reason: + type: "string" + nullable: true + description: "Motivo da inutilização" + additionalProperties: false + description: "Dados para inutilizar números de nota fiscal" + DocumentElectronicInvoiceResource: + type: "object" + properties: + accessKey: + type: "string" # Chave de Acesso + nullable: true + description: "Chave de Acesso (refNFe)" + additionalProperties: false + DocumentInvoiceReferenceResource: + type: "object" + properties: + state: + type: "number" # Código da UF + nullable: true + description: "Código da UF (cUF)" + format: "double" + yearMonth: + type: "string" + nullable: true + description: "Ano / Mês (AAMM)" + federalTaxNumber: + type: "string" + nullable: true # CNPJ + description: "CNPJ (CNPJ)" + model: + type: "string" + nullable: true + description: "Modelo (mod)" + series: + type: "string" + nullable: true + description: "Série (serie)" + number: + type: "string" + nullable: true # Número + description: "Número (nNF)" + additionalProperties: false + DuductionIndicator: + type: "string" + enum: + - "NotDeduct" + - "Deduce" + description: | + Indicador de dedução do ICMS desonerado do valor total da NF-e (indDeducao). + Valores possíveis: + - `NotDeduct`: O valor do ICMS desonerado (vICMSDeson) não deduz do valor total da NF-e + - `Deduce`: O valor do ICMS desonerado (vICMSDeson) deduz do valor total da NF-e + DuplicateResource: + type: "object" + properties: + number: + type: "string" + nullable: true + description: "Número da Duplicata (nDup)" + expirationOn: + type: "string" + nullable: true # Data de vencimento + description: "Data de vencimento (dVenc)" + format: "date-time" + amount: + type: "number" + nullable: true + description: "Valor da duplicata (vDup)" + format: "double" + additionalProperties: false + EconomicActivityResource: + type: "object" + properties: + type: + $ref: "#/components/schemas/EconomicActivityType" + code: + type: "integer" # Código da Atividade da Empresa + nullable: true + description: "Código da Atividade da Empresa" + format: "int32" + additionalProperties: false + EconomicActivityType: + type: "string" + enum: + - "Main" + - "Secondary" + description: | + Tipo de Atividade Econômica. + Valores possíveis: + - `Main`: Principal + - `Secondary`: Secundária + EnvironmentType: + type: "string" + description: | + Tipo de Ambiente (tpAmb). + Valores possíveis: + - `None`: Não definido + - `Production`: Produção + - `Test`: Homologação (Teste) + enum: + - "None" + - "Production" + - "Test" + ErrorResource: + type: "object" + properties: + code: + type: "integer" + nullable: true + format: "int32" + message: + type: "string" + nullable: true + additionalProperties: false + ErrorsResource: + type: "object" + properties: + errors: + type: "array" + nullable: true + items: + $ref: "#/components/schemas/ErrorResource" + readOnly: true + additionalProperties: false + ExemptReason: + type: "string" + enum: + - "Agriculture" + - "Others" + - "DevelopmentEntities" + description: | + Motivo da desoneração. Campo será preenchido quando o campo anterior estiver preenchido. + Valores possíveis: + - `Agriculture`: Produtos agropecuários + - `Others`: Outros + - `DevelopmentEntities`: Órgãos de fomento e desenvolvimento + ExportDetailResource: + type: "object" + properties: + drawback: + type: "string" + nullable: true + description: "Número do ato concessório de Drawback (nDraw)" + hintInformation: + $ref: "#/components/schemas/ExportHintResource" # Informações de exportação + additionalProperties: false + ExportHintResource: + type: "object" + properties: + registryId: + type: "string" + nullable: true + description: "Número do Registro de Exportação (nRE)" + accessKey: + type: "string" # Chave de Acesso da NF-e recebida para exportação + nullable: true + description: "Chave de Acesso da NF-e recebida para exportação (chNFe)" + quantity: + type: "number" + nullable: true + description: "Quantidade do item realmente exportado (qExport)" + format: "double" + additionalProperties: false + ExportResource: + type: "object" + properties: + state: + $ref: "#/components/schemas/StateCode" # Sigla da UF + office: + type: "string" + nullable: true + description: "Descrição do Local de Embarque ou de transposição de fronteira (xLocExporta)" + local: + type: "string" + nullable: true + description: "Informações Complementares de interesse do Contribuinte (xLocDespacho)" + additionalProperties: false + FileResource: + type: "object" + properties: + uri: + type: "string" + nullable: true + description: "Endereço Absoluto URI para o arquivo" + additionalProperties: false # Arquivo + description: "Arquivo" + FlagCard: + type: "string" + enum: + - "None" + - "Visa" + - "Mastercard" + - "AmericanExpress" + - "Sorocred" + - "DinersClub" + - "Elo" + - "Hipercard" + - "Aura" + - "Cabal" + - "Alelo" + - "BanesCard" + - "CalCard" + - "Credz" + - "Discover" + - "GoodCard" + - "GreenCard" + - "Hiper" + - "JCB" + - "Mais" + - "MaxVan" + - "Policard" + - "RedeCompras" + - "Sodexo" + - "ValeCard" + - "Verocheque" + - "VR" + - "Ticket" + - "Other" + description: | + Bandeira da operadora de cartão de crédito e/ou débito (tBand). + Valores possíveis: + - `None`: Não definido + - `Visa`: Visa + - `Mastercard`: Mastercard + - `AmericanExpress`: American Express + - `Sorocred`: Sorocred + - `DinersClub`: Diners Club + - `Elo`: Elo + - `Hipercard`: Hipercard + - `Aura`: Aura + - `Cabal`: Cabal + - `Alelo`: Alelo + - `BanesCard`: Banescard + - `CalCard`: Calcard + - `Credz`: Credz + - `Discover`: Discover + - `GoodCard`: Good Card + - `GreenCard`: Green Card + - `Hiper`: Hiper + - `JCB`: JCB + - `Mais`: Mais + - `MaxVan`: Maxvan + - `Policard`: Policard + - `RedeCompras`: Rede Compras + - `Sodexo`: Sodexo + - `ValeCard`: ValeCard + - `Verocheque`: Verocheque + - `VR`: VR + - `Ticket`: Ticket + - `Other`: Outros + FuelOriginResource: + type: "object" + properties: + indImport: + type: "integer" + nullable: true + description: "Indicador de importação (indImport)" + format: "int32" + cUFOrig: + type: "integer" # Código da UF + nullable: true + description: "Código da UF (cUFOrig)" + format: "int32" + pOrig: + type: "number" + nullable: true + description: "Percentual originário para a UF (pOrig)" + format: "double" + additionalProperties: false + VehicleDetailResource: + type: "object" + description: "Detalhamento de Veículos novos (veicProd) - grupo J01" + properties: + operationType: + type: "integer" + nullable: true + description: | + Tipo da Operação (tpOp). + Valores possíveis: + - `0`: Outros + - `1`: Venda concessionária + - `2`: Faturamento direto para consumidor final + - `3`: Venda direta para grandes consumidores + format: "int32" + enum: [0, 1, 2, 3] + chassis: + type: "string" + nullable: true + description: "Chassi do veículo - VIN (chassi). 17 caracteres alfanuméricos" + minLength: 17 + maxLength: 17 + pattern: "^[A-Z0-9]+$" + colorCode: + type: "string" + nullable: true + description: "Cor do veículo - código de cada montadora (cCor)" + maxLength: 4 + colorDescription: + type: "string" + nullable: true + description: "Descrição da Cor (xCor)" + maxLength: 40 + enginePower: + type: "string" + nullable: true + description: "Potência máxima do motor em cavalo vapor - CV (pot)" + maxLength: 4 + engineDisplacement: + type: "string" + nullable: true + description: "Capacidade volumétrica do motor em centímetros cúbicos - CC (cilin)" + maxLength: 4 + netWeight: + type: "string" + nullable: true + description: "Peso Líquido (pesoL)" + maxLength: 9 + grossWeight: + type: "string" + nullable: true + description: "Peso Bruto (pesoB)" + maxLength: 9 + serialNumber: + type: "string" + nullable: true + description: "Serial - série (nSerie)" + maxLength: 9 + fuelType: + type: "string" + nullable: true + description: "Tipo de combustível - Tabela RENAVAM (tpComb). Ex: 01-Álcool; 02-Gasolina; 03-Diesel; 16-Álcool/Gas.; 17-Gas./Álcool/GNV; 18-Gasolina/Elétrico" + maxLength: 2 + engineNumber: + type: "string" + nullable: true + description: "Número do motor (nMotor)" + maxLength: 21 + maximumTractionCapacity: + type: "string" + nullable: true + description: "Capacidade Máxima de Tração em toneladas - 4 casas decimais (CMT)" + maxLength: 9 + wheelBase: + type: "string" + nullable: true + description: "Distância entre eixos (dist)" + maxLength: 4 + modelYear: + type: "integer" + nullable: true + description: "Ano Modelo de Fabricação (anoMod). Formato: 4 dígitos" + format: "int32" + manufactureYear: + type: "integer" + nullable: true + description: "Ano de Fabricação (anoFab). Formato: 4 dígitos" + format: "int32" + paintType: + type: "string" + nullable: true + description: "Tipo de pintura (tpPint)" + maxLength: 1 + vehicleType: + type: "string" + nullable: true + description: "Tipo de veículo - Tabela RENAVAM (tpVeic)" + maxLength: 2 + vehicleSpecies: + type: "integer" + nullable: true + description: "Espécie de veículo - Tabela RENAVAM (espVeic)" + format: "int32" + vinCondition: + type: "string" + nullable: true + description: | + Condição do VIN — chassi (VIN). + Valores possíveis: + - `R`: Remarcado (chassi regravado pelo DETRAN) + - `N`: Normal (chassi original de fábrica) + enum: ["R", "N"] + vehicleCondition: + type: "integer" + nullable: true + description: | + Condição do veículo (condVeic). + Valores possíveis: + - `1`: Acabado (veículo pronto para uso) + - `2`: Inacabado (veículo sem algum componente essencial) + - `3`: Semi-acabado (veículo parcialmente montado, ex: chassis-cabina) + format: "int32" + enum: [1, 2, 3] + brandModelCode: + type: "string" + nullable: true + description: "Código Marca Modelo - Tabela RENAVAM (cMod)" + maxLength: 6 + denatranColorCode: + type: "string" + nullable: true + description: "Código da Cor DENATRAN (cCorDENATRAN). 01-AMARELO; 02-AZUL; 03-BEGE; 04-BRANCA; 05-CINZA; 06-DOURADA; 07-GRENA; 08-LARANJA; 09-MARROM; 10-PRATA; 11-PRETA; 12-ROSA; 13-ROXA; 14-VERDE; 15-VERMELHA; 16-FANTASIA" + maxLength: 2 + seatingCapacity: + type: "integer" + nullable: true + description: "Quantidade máxima de passageiros sentados, inclusive motorista (lota)" + format: "int32" + restrictionType: + type: "integer" + nullable: true + description: | + Tipo de restrição sobre o veículo (tpRest). + Valores possíveis: + - `0`: Não há restrição (veículo livre para venda e transferência) + - `1`: Alienação Fiduciária (veículo dado como garantia em financiamento) + - `2`: Arrendamento Mercantil (veículo em contrato de leasing) + - `3`: Reserva de Domínio (vendedor mantém a propriedade até quitação total) + - `4`: Penhor de Veículos (veículo dado como garantia em empréstimo) + - `9`: Outras restrições + format: "int32" + enum: [0, 1, 2, 3, 4, 9] + additionalProperties: false + FuelResource: + type: "object" + properties: + codeANP: + type: "string" # Código de produto da ANP + nullable: true + description: "Código de produto da ANP (cProdANP)" + percentageNG: + type: "number" + nullable: true + description: "Percentual de Gás Natural para o produto GLP (cProdANP=210203001) (pMixGN)" + format: "double" + descriptionANP: + type: "string" + nullable: true # Descrição do produto conforme ANP + description: "Descrição do produto conforme ANP (descANP)" + percentageGLP: + type: "number" + nullable: true + description: "Percentual do GLP derivado do petróleo no produto GLP (cProdANP=210203001) (pGLP)" + format: "double" + percentageNGn: + type: "number" + nullable: true # Percentual de Gás Natural Nacional + description: "Percentual de Gás Natural Nacional – GLGNn para o produto GLP (cProdANP= 210203001) (pGNn)" + format: "double" + percentageGNi: + type: "number" + nullable: true + description: "Percentual \r\nde Gás Natural Importado – GLGNi para o produto GLP (cProdANP= 210203001) (pGNi)" + format: "double" + startingAmount: + type: "number" + nullable: true # Valor de partida + description: "Valor de partida (cProdANP=210203001) (vPart)" + format: "double" + codif: + type: "string" + nullable: true + description: "Código de autorização / registro do CODIF (CODIF)" + amountTemp: + type: "number" + nullable: true # Quantidade de combustível faturada à temperatura ambiente + description: "Quantidade de combustível faturada à temperatura ambiente (qTemp)" + format: "double" + stateBuyer: + type: "string" + nullable: true + description: "Sigla da UF de consumo (UFCons)" + cide: + $ref: "#/components/schemas/CIDEResource" + pump: + $ref: "#/components/schemas/PumpResource" + fuelOrigin: + $ref: "#/components/schemas/FuelOriginResource" + additionalProperties: false + GovernmentPurchaseEntityType: + type: "string" + nullable: true + description: | + Tipo de ente governamental (tpEnteGov). Para administração pública direta e suas autarquias e fundações. + Valores possíveis: + - `Union`: União + - `State`: Estado + - `FederalDistrict`: Distrito Federal + - `Municipality`: Município + enum: + - "Union" + - "State" + - "FederalDistrict" + - "Municipality" + GovernmentPurchaseOperationType: + type: "string" + nullable: true + description: | + Tipo de operação com o ente governamental (tpOperGov). + Valores possíveis: + - `Supply`: Fornecimento + - `Payment`: Recebimento do pagamento + - `SupplyThenPay`: Fornecimento e pagamento simultâneo + - `PayForPastSupply`: Pagamento por fornecimento passado + - `SupplyAfterPay`: Fornecimento após pagamento + - `PayNowSupplyLater`: Pagamento agora, fornecimento depois + - `SupplyAndPayNow`: Fornecimento e pagamento agora + enum: + - "Supply" + - "Payment" + - "SupplyThenPay" + - "PayForPastSupply" + - "SupplyAfterPay" + - "PayNowSupplyLater" + - "SupplyAndPayNow" + GovernmentPurchaseResource: + type: "object" + description: "Grupo de Compras Governamentais (gCompraGov). Grupo opcional. Informar apenas para compras governamentais." + properties: + entityType: + $ref: "#/components/schemas/GovernmentPurchaseEntityType" # Tipo de ente governamental + rateReduction: + type: "number" + nullable: true + description: "Percentual de redução de alíquota em compra governamental (pRedutor). Conforme o art. 472/370 da LC 214/2025." + format: "double" + operationType: + $ref: "#/components/schemas/GovernmentPurchaseOperationType" + additionalProperties: false + GovernmentPurchaseTaxResource: + type: "object" + description: "Grupo de informações sobre a composição do valor do IBS e CBS nas compras governamentais. Informar apenas para compras governamentais." + properties: + stateRate: + type: "number" # Alíquota IBS UF em compras governamentais + nullable: true + description: "Alíquota IBS UF em compras governamentais (pAliqIBSUF)." + format: "double" + stateAmount: + type: "number" + nullable: true + description: "Valor IBS UF em compras governamentais (vTribIBSUF)." + format: "double" + municipalRate: + type: "number" # Alíquota IBS Município em compras governamentais + nullable: true + description: "Alíquota IBS Município em compras governamentais (pAliqIBSMun)." + format: "double" + municipalAmount: + type: "number" + nullable: true + description: "Valor IBS Município em compras governamentais (vTribIBSMun)." + format: "double" + cbsRate: + type: "number" # Alíquota CBS em compras governamentais + nullable: true + description: "Alíquota CBS em compras governamentais (pAliqCBS)." + format: "double" + cbsAmount: + type: "number" + nullable: true + description: "Valor CBS em compras governamentais (vTribCBS)" + format: "double" + additionalProperties: false + IBSCBSTaxResource: + type: "object" + description: "Grupo de Informações do IBS e CBS" + properties: + situationCode: + type: "string" + nullable: true # Código de Situação Tributária do IBS/CBS + description: "Código de Situação Tributária do IBS/CBS (CST). Campo opcional. Se preenchido, será considerado; caso contrário, será definido com base no valor informado no campo `classCode`. Consulte a tabela de referência de `situationCode` (CST) disponível na nossa documentação funcional." + maxLength: 3 + classCode: + type: "string" + nullable: true + description: "Código de Classificação Tributária do IBS/CBS (cClassTrib). Consulte a tabela de referência [cClassTrib](https://nfe.io/docs/documentacao/reforma-tributaria/conceitos-funcionais/tabelas-de-referencia/tabela-referencia-cst-classificacao-tributaria-ibs-cbs/) disponível na nossa documentação funcional." + maxLength: 6 + calculationMode: + type: "string" + nullable: true + description: | + Modo de cálculo do IBS/CBS. + Valores possíveis: + - `Manual`: Preenchimento manual dos campos de tributo. + - `OfficialService`: Utiliza o serviço oficial para calcular IBS/CBS. Quando ativo, apenas `situationCode` e `classCode` são necessários; os demais campos de tributo são ignorados na entrada e preenchidos pelo serviço. + enum: + - "Manual" + - "OfficialService" + default: "Manual" + donationIndicator: + type: "string" + nullable: true # Indica a natureza da operação de doação + description: "Indica a natureza da operação de doação, orientando a apuração e a geração de débitos ou estornos conforme o cenário (indDoacao). Informar quando for doação." + maxLength: 1 + basis: + type: "number" + nullable: true + description: "Base de cálculo antes de reduções para cálculo do tributo bruto. Este campo é opcional. Se enviado, será considerado; caso contrário, será calculado." + format: "double" + state: + $ref: "#/components/schemas/IBSStateTaxResource" + municipal: # Grupo de Informações do IBS para o município + $ref: "#/components/schemas/IBSMunicipalTaxResource" + ibsTotalAmount: + type: "number" + nullable: true + description: "Total do IBS (vIBSTot = vIBSUF + vIBSMun). Quando houver crédito presumido com o indicador \"IndDeduzCredPres=1\", o valor de vCredPres deve ser deduzido deste total." + format: "double" + cbs: + $ref: "#/components/schemas/CBSTaxResource" + regularTaxation: # Tributação regular hipotética + description: "Tributação regular hipotética caso condição resolutória/suspensiva não se aplique." + $ref: "#/components/schemas/RegularTaxationResource" + governmentPurchase: + description: "Grupo de informações sobre a composição do valor do IBS e CBS nas compras governamentais." + $ref: "#/components/schemas/GovernmentPurchaseTaxResource" + monophase: + description: "Grupo de Informações do IBS e CBS sobre transações monofásicas" + $ref: "#/components/schemas/MonophaseIBSCBSTaxResource" + creditTransfer: # Grupo de Informações sobre Transferências de Crédito + description: "Grupo de Informações sobre Transferências de Crédito" + $ref: "#/components/schemas/CreditTransferTaxResource" + operationalPresumedCredit: # Informações sobre o crédito presumido operacional + description: "Informações sobre o crédito presumido operacional (gCredPresOper)." + $ref: "#/components/schemas/OperationalPresumedCreditResource" + creditReversal: + description: "Estorno de Crédito (gEstornoCred). Observação: a obrigatoriedade ou vedação do preenchimento deste grupo está condicionada ao indicador “ind_gEstornoCred” da tabela de cClassTrib do IBS e da CBS." + $ref: "#/components/schemas/CreditReversalResource" + zfmPresumedCredit: + description: "Informações sobre o crédito presumido de IBS para fornecimentos da ZFM (gCredPresIBSZFM)." + $ref: "#/components/schemas/ZfmPresumedCreditResource" # Informações sobre o crédito presumido de IBS para fornecimentos da ZFM + additionalProperties: false + ICMSTotal: + type: "object" + properties: + baseTax: + type: "number" + nullable: true + description: "Base de Cálculo do ICMS (vBC)" + format: "double" + icmsAmount: + type: "number" + nullable: true + description: "Valor Total do ICMS (vICMS)" + format: "double" + icmsExemptAmount: + type: "number" + nullable: true + description: "Valor ICMS Total desonerado (vICMSDeson)" + format: "double" + stCalculationBasisAmount: + type: "number" + nullable: true + description: "Base de Cálculo do ICMS Substituição Tributária (vBCST)" + format: "double" + stAmount: + type: "number" + nullable: true + description: "Valor Total do ICMS ST (vST)" + format: "double" + productAmount: + type: "number" + description: "Valor Total dos produtos e serviços (vProd)" # Valor Total dos produtos e serviços + format: "double" + freightAmount: + type: "number" + nullable: true + description: "Valor Total do Frete (vFrete)" + format: "double" + insuranceAmount: + type: "number" + nullable: true # Valor Total do Seguro + description: "Valor Total do Seguro (vSeg)" + format: "double" + discountAmount: + type: "number" + nullable: true + description: "Valor Total do Desconto (vDesc)" + format: "double" + iiAmount: + type: "number" + nullable: true + description: "Valor Total do Imposto de Importação (vII)" + format: "double" + ipiAmount: + type: "number" + nullable: true + description: "Valor Total do IPI (vIPI)" + format: "double" + pisAmount: + type: "number" + nullable: true + description: "Valor do PIS (vPIS)" + format: "double" + cofinsAmount: + type: "number" + nullable: true + description: "Valor do COFINS (vCOFINS)" + format: "double" + othersAmount: + type: "number" + nullable: true + description: "Outras Despesas acessórias (vOutro)" + format: "double" + invoiceAmount: + type: "number" + description: "Valor Total da NF-e (vNF)" # Valor Total da NF-e + format: "double" + fcpufDestinationAmount: + type: "number" + nullable: true + description: "Valor Total ICMS FCP UF Destino" + format: "double" + icmsufDestinationAmount: + type: "number" + nullable: true + description: "Valor Total ICMS Interestadual UF Destino" + format: "double" + icmsufSenderAmount: + type: "number" + nullable: true + description: "Valor Total ICMS Interestadual UF Rem." + format: "double" + federalTaxesAmount: + type: "number" + description: "Valor aproximado total de tributos federais, estaduais e municipais. (vTotTrib)" + format: "double" + fcpAmount: + type: "number" + nullable: true + description: "Valor Total do FCP - Valor do ICMS relativo ao Fundo de Combate à Pobreza (vFCP)" + format: "double" + fcpstAmount: + type: "number" + nullable: true + description: "Valor Total do FCP retido por ST - Valor do ICMS relativo ao Fundo de Combate à Pobreza (vFCPST) retido por substituição tributária." + format: "double" + fcpstRetAmount: + type: "number" + nullable: true + description: "Valor Total do FCP retido por anteriormente por ST - Valor do ICMS relativo ao Fundo de Combate à Pobreza (vFCP) retido anteriormente por substituição tributária." + format: "double" + ipiDevolAmount: + type: "number" + nullable: true + description: "Valor total do IPI devolvido (vIPIDevol)" + format: "double" + qBCMono: + type: "number" + nullable: true # Valor total da quantidade tributada do ICMS monofásico próprio + format: "double" + vICMSMono: + type: "number" + nullable: true + description: "Valor total do ICMS monofásico próprio (vICMSMono)." + format: "double" + qBCMonoReten: + type: "number" + nullable: true # Valor total da quantidade tributada do ICMS monofásico sujeito a retenção + description: "Valor total da quantidade tributada do ICMS monofásico sujeito a retenção (qBCMonoReten)." + format: "double" + vICMSMonoReten: + type: "number" + nullable: true + description: "Valor total da quantidade tributada do ICMS monofásico retido anteriormente(vICMSMonoReten)" + format: "double" + qBCMonoRet: + type: "number" + nullable: true # Valor total do ICMS monofásico retido anteriormente + description: "Valor total do ICMS monofásico retido anteriormente (vICMSMonoRet)" + format: "double" + vICMSMonoRet: + type: "number" + nullable: true + description: "Valor total do ICMS monofásico retido anteriormente (vICMSMonoRet)" + format: "double" + additionalProperties: false + description: "Grupo de Valores Totais referentes ao ICMS" + ICMSTotalResource: + type: "object" + properties: + baseTax: + type: "number" + nullable: true + description: "Base de Cálculo do ICMS (vBC)" + format: "double" + icmsAmount: + type: "number" + nullable: true + description: "Valor Total do ICMS (vICMS)" + format: "double" + icmsExemptAmount: + type: "number" + nullable: true + description: "Valor ICMS Total desonerado (vICMSDeson)" + format: "double" + stCalculationBasisAmount: + type: "number" + nullable: true + description: "Base de Cálculo do ICMS Substituição Tributária (vBCST)" + format: "double" + stAmount: + type: "number" + nullable: true + description: "Valor Total do ICMS ST (vST)" + format: "double" + productAmount: + type: "number" + nullable: true + description: "Valor Total dos produtos e serviços (vProd)" + format: "double" + freightAmount: + type: "number" + nullable: true + description: "Valor Total do Frete (vFrete)" + format: "double" + insuranceAmount: + type: "number" + nullable: true # Valor Total do Seguro + description: "Valor Total do Seguro (vSeg)" + format: "double" + discountAmount: + type: "number" + nullable: true + description: "Valor Total do Desconto (vDesc)" + format: "double" + iiAmount: + type: "number" + nullable: true + description: "Valor Total do Imposto de Importação (vII)" + format: "double" + ipiAmount: + type: "number" + nullable: true + description: "Valor Total do IPI (vIPI)" + format: "double" + pisAmount: + type: "number" + nullable: true + description: "Valor do PIS (vPIS)" + format: "double" + cofinsAmount: + type: "number" + nullable: true + description: "Valor do COFINS (vCOFINS)" + format: "double" + othersAmount: + type: "number" + nullable: true + description: "Outras Despesas acessórias (vOutro)" + format: "double" + invoiceAmount: + type: "number" + nullable: true + description: "Valor Total da NF-e (vNF)" + format: "double" + fcpufDestinationAmount: + type: "number" + nullable: true + description: "Valor Total ICMS FCP UF Destino (vFCPUFDest)" + format: "double" + icmsufDestinationAmount: + type: "number" + nullable: true + description: "Valor Total ICMS Interestadual UF Destino (vICMSUFDest)" + format: "double" + icmsufSenderAmount: + type: "number" + nullable: true + description: "Valor Total ICMS Interestadual UF Remetente (vICMSUFRemet)" + format: "double" + federalTaxesAmount: + type: "number" + nullable: true # Valor aproximado total de tributos federais, estaduais e municipais + description: "Valor aproximado total de tributos federais, estaduais e municipais.\r\n(vTotTrib)" + format: "double" + fcpAmount: + type: "number" + nullable: true + description: "Valor Total do FCP - Valor do ICMS relativo ao Fundo de Combate à Pobreza (vFCP)" + format: "double" + fcpstAmount: + type: "number" + nullable: true + description: "Valor Total do FCP retido por ST - Valor do ICMS relativo ao Fundo de Combate à Pobreza (vFCPST) retido por substituição tributária." + format: "double" + fcpstRetAmount: + type: "number" + nullable: true + description: "Valor Total do FCP retido por anteriormente por ST - Valor do ICMS relativo ao Fundo de Combate à Pobreza retido anteriormente por substituição tributária (vFCPSTRet)" + format: "double" + ipiDevolAmount: + type: "number" + nullable: true + description: "Valor total do IPI devolvido (vIPIDevol)" + format: "double" + qBCMono: + type: "number" + nullable: true # Valor total da quantidade tributada do ICMS monofásico próprio + description: "Valor total da quantidade tributada do ICMS monofásico próprio (qBCMono)" + format: "double" + vICMSMono: + type: "number" + nullable: true + description: "Valor total do ICMS monofásico próprio (vICMSMono)." + format: "double" + qBCMonoReten: + type: "number" + nullable: true # Valor total da quantidade tributada do ICMS monofásico sujeito a retenção + description: "Valor total da quantidade tributada do ICMS monofásico sujeito a retenção (qBCMonoReten)." + format: "double" + vICMSMonoReten: + type: "number" + nullable: true + description: "Valor total da quantidade tributada do ICMS monofásico retido anteriormente(vICMSMonoReten)" + format: "double" + qBCMonoRet: + type: "number" + nullable: true # Valor total da quantidade tributada do ICMS monofásico retido anteriormente + description: "Valor total da quantidade tributada do ICMS monofásico retido anteriormente(qBCMonoRet)" + format: "double" + vICMSMonoRet: + type: "number" + nullable: true + description: "Valor total do ICMS monofásico retido anteriormente (vICMSMonoRet)" + format: "double" + additionalProperties: false + description: "Grupo de Valores Totais referentes ao ICMS" + ICMSUFDestinationTaxResource: + type: "object" + properties: + vBCUFDest: + type: "number" + nullable: true # Valor da Base de Cálculo do ICMS na UF de destino + description: "Valor da Base de Cálculo do ICMS na UF de destino (vBCUFDest)" + format: "double" + pFCPUFDest: + type: "number" + nullable: true + description: "Percentual adicional inserido na alíquota interna da UF de destino, relativo ao Fundo de Combate à Pobreza (FCP) naquela UF (pFCPUFDest)" + format: "double" + pICMSUFDest: + type: "number" + nullable: true # Alíquota adotada nas operações internas na UF de destino para o produto / mercadoria + description: "Alíquota adotada nas operações internas na UF de destino para o produto / mercadoria (pICMSUFDest)" + format: "double" + pICMSInter: + type: "number" + nullable: true + description: "Alíquota interestadual das UF envolvidas (pICMSInter)" + format: "double" + pICMSInterPart: + type: "number" + nullable: true # Percentual de ICMS Interestadual para a UF de destino + description: "Percentual de ICMS Interestadual para a UF de destino (pICMSInterPart)" + format: "double" + vFCPUFDest: + type: "number" + nullable: true + description: "Valor do ICMS relativo ao Fundo de Combate à Pobreza (FCP) da UF de destino (vFCPUFDest" + format: "double" + vICMSUFDest: + type: "number" + nullable: true # Valor do ICMS Interestadual para a UF de destino + description: "Valor do ICMS \r\nInterestadual para a UF de destino (vICMSUFDest)" + format: "double" + vICMSUFRemet: + type: "number" + nullable: true + description: "Valor do ICMS Interestadual para a UF do remetente (vICMSUFRemet)" + format: "double" + vBCFCPUFDest: + type: "number" + nullable: true # Valor da BC FCP na UF de destino + description: "Valor da BC FCP na UF de destino (vBCFCPUFDest)" + format: "double" + additionalProperties: false + description: "Grupo de Tributação do ICMS de Destino da UF" + IBSMunicipalTaxResource: + type: "object" + description: "Grupo de Informações do IBS para o município" + properties: + rate: + type: "number" # Alíquota IBS (Município) + nullable: true + description: "Alíquota IBS (Município) (%)." + format: "double" + deferment: + $ref: "#/components/schemas/DefermentTaxResource" + returnedAmount: + $ref: "#/components/schemas/ReturnedTaxResource" + reduction: + $ref: "#/components/schemas/ReductionTaxResource" + amount: + type: "number" + nullable: true # Valor do IBS do Município + description: "Valor do IBS do Município (vIBSMun)." + format: "double" + additionalProperties: false + IBSStateTaxResource: + type: "object" + description: "Grupo de Informações do IBS para a UF" + properties: + rate: + type: "number" # Alíquota do IBS de competência das UF + nullable: true + description: "Alíquota do IBS de competência das UF (em percentual). Alíquota vigente do IBS da UF.Preencher de acordo com a tabela de alíquotas do IBS e CBS." + format: "double" + deferment: + $ref: "#/components/schemas/DefermentTaxResource" + returnedAmount: + $ref: "#/components/schemas/ReturnedTaxResource" + reduction: + $ref: "#/components/schemas/ReductionTaxResource" + amount: + type: "number" + nullable: true # Valor do IBS da UF + description: "Valor do IBS da UF (vIBSUF)." + format: "double" + additionalProperties: false + IITaxResource: + type: "object" + properties: + baseTax: + type: "string" # Valor BC do Imposto de Importação + nullable: true + description: "Valor BC do Imposto de Importação (vBC)" + customsExpenditureAmount: + type: "string" + nullable: true + description: "Valor despesas aduaneiras (vDespAdu)" + amount: + type: "number" + nullable: true + description: "Valor Imposto de Importação (vII)" + format: "double" + iofAmount: + type: "number" + nullable: true # Valor Imposto sobre Operações Financeiras + description: "Valor Imposto sobre Operações Financeiras (vIOF)" + format: "double" + vEnqCamb: + type: "number" + nullable: true + description: "Valor dos encargos cambiais" + format: "double" + additionalProperties: false + description: "Grupo do Imposto de Importação\r\n\r\nId: P01\r\nPai: O01" + IPITaxResource: + type: "object" + properties: + cst: + type: "string" + nullable: true # Código da situação tributária do IPI + description: "Código da situação tributária do IPI (CST)" + classificationCode: + type: "string" + nullable: true + description: "Código de Enquadramento Legal do IPI (cEnq)" + classification: + type: "string" + nullable: true + description: "Classe de enquadramento do IPI para Cigarros e Bebidas (clEnq)" + producerCNPJ: + type: "string" # CNPJ do produtor da mercadoria + nullable: true + description: "CNPJ do produtor da mercadoria, quando diferente do emitente. Somente para os casos de exportação direta ou indireta (CNPJProd)" + stampCode: + type: "string" + nullable: true + description: "Código do selo de controle IPI (cSelo)" + stampQuantity: + type: "number" + nullable: true # Quantidade de selo de controle + description: "Quantidade de selo de controle (qSelo)" + format: "double" + base: + type: "number" + nullable: true + description: "Valor da BC do IPI (vBC)" + format: "double" + rate: + type: "number" + nullable: true # Alíquota do IPI + description: "Alíquota do IPI (pIPI)" + format: "double" + unitQuantity: + type: "number" + nullable: true + description: "Quantidade total na unidade padrão para tributação (somente para os produtos tributados por unidade) (qUnid)" + format: "double" + unitAmount: + type: "number" + nullable: true # Valor por Unidade Tributável + description: "Valor por Unidade Tributável (vUnid)" + format: "double" + amount: + type: "number" + nullable: true + description: "Valor IPI (vIPI)" + format: "double" + additionalProperties: false + description: "\r\nGrupo do IPI\r\n\r\nInformar apenas quando \r\no item for sujeito ao IPI\r\n\r\nID: O01\r\n\r\nPai: M01" + ISSQNTotal: + type: "object" + properties: + totalServiceNotTaxedICMS: + type: "number" + nullable: true # Valor Total Serv.Não Tributados p/ ICMS + description: "Valor Total Serv.Não Tributados p/ ICMS (vServ)" + format: "double" + baseRateISS: + type: "number" + nullable: true + description: "Base de Cálculo do ISS (vBC)" + format: "double" + totalISS: + type: "number" + nullable: true # Valor Total do ISS + description: "Valor Total do ISS (vISS)" + format: "double" + valueServicePIS: + type: "number" + nullable: true + description: "Valor do PIS sobre Serviços (vPIS)" + format: "double" + valueServiceCOFINS: + type: "number" + nullable: true # Valor da COFINS sobre Serviços + description: "Valor da COFINS sobre Serviços (vCOFINS)" + format: "double" + provisionService: + type: "string" + nullable: true + description: "Data Prestação Serviço (dCompet)" + format: "date-time" + deductionReductionBC: + type: "number" + nullable: true # Valor Dedução para Redução da BC + description: "Valor Dedução para Redução da BC (vDeducao)" + format: "double" + valueOtherRetention: + type: "number" + nullable: true + description: "Valor Outras Retenções (vOutro)" + format: "double" + discountUnconditional: + type: "number" + nullable: true # Valor Desconto Incondicionado + description: "Valor Desconto Incondicionado (vDescIncond)" + format: "double" + discountConditioning: + type: "number" + nullable: true + description: "Valor Desconto Condicionado (vDescCond)" + format: "double" + totalRetentionISS: + type: "number" + nullable: true # Valor Total Retenção ISS + description: "Valor Total Retenção ISS (vISSRet)" + format: "double" + codeTaxRegime: + type: "number" + nullable: true + description: "Código Regime Tributação (cRegTrib)" + format: "double" + additionalProperties: false + ISSQNTotalResource: + type: "object" + properties: + totalServiceNotTaxedICMS: + type: "number" + nullable: true # Valor Total Serv.Não Tributados p/ ICMS + description: "Valor Total Serv.Não Tributados p/ ICMS (vServ)" + format: "double" + baseRateISS: + type: "number" + nullable: true + description: "Base de Cálculo do ISS (vBC)" + format: "double" + totalISS: + type: "number" + nullable: true # Valor Total do ISS + description: "Valor Total do ISS (vISS)" + format: "double" + valueServicePIS: + type: "number" + nullable: true + description: "Valor do PIS sobre Serviços (vPIS)" + format: "double" + valueServiceCOFINS: + type: "number" + nullable: true # Valor da COFINS sobre Serviços + description: "Valor da COFINS sobre Serviços (vCOFINS)" + format: "double" + provisionService: + type: "string" + nullable: true + description: "Data Prestação Serviço (dCompet)" + format: "date-time" + deductionReductionBC: + type: "number" + nullable: true # Valor Dedução para Redução da BC + description: "Valor Dedução para Redução da BC (vDeducao)" + format: "double" + valueOtherRetention: + type: "number" + nullable: true + description: "Valor Outras Retenções (vOutro)" + format: "double" + discountUnconditional: + type: "number" + nullable: true # Valor Desconto Incondicionado + description: "Valor Desconto Incondicionado (vDescIncond)" + format: "double" + discountConditioning: + type: "number" + nullable: true + description: "Valor Desconto Condicionado (vDescCond)" + format: "double" + totalRetentionISS: + type: "number" + nullable: true # Valor Total Retenção ISS + description: "Valor Total Retenção ISS (vISSRet)" + format: "double" + codeTaxRegime: + type: "number" + nullable: true + description: "Código Regime Tributação (cRegTrib)" + format: "double" + additionalProperties: false + ISTaxResource: + type: "object" + description: "Imposto Seletivo (IS). Informe quando o item estiver sujeito ao IS." + properties: + situationCode: + type: "string" # CST do Imposto Seletivo (IS) + nullable: true + description: "CST do Imposto Seletivo (IS). Código de Situação Tributária de 3 dígitos (CST). Consulte a tabela de referência." + classificationCode: + type: "string" + nullable: true + description: "Código de Classificação Tributária do IS (cClassTribIS), 6 dígitos. Consulte a tabela de referência." + basis: + type: "number" + nullable: true # Base de cálculo do IS + description: "Base de cálculo do IS (vBCIS). 15 posições, 13 inteiros e 2 decimais." + format: "double" + rate: + type: "number" + nullable: true + description: "Alíquota do IS em percentual (pIS). Até 4 casas decimais." + format: "double" + unitRate: + type: "number" + nullable: true # Alíquota específica por unidade + description: "Alíquota específica por unidade (pISEspec), em R$ por unidade tributável." + format: "double" + unit: + type: "string" + nullable: true + description: "Unidade tributável usada quando houver alíquota específica por unidade (uTrib)." + quantity: + type: "number" + nullable: true # Quantidade tributável + description: "Quantidade tributável usada quando houver alíquota específica por unidade (qTrib). Até 4 casas decimais." + format: "double" + amount: + type: "number" + nullable: true + description: "Valor do Imposto Seletivo (vIS). 15 posições, 13 inteiros e 2 decimais." + format: "double" + additionalProperties: false + IbsConsumptionCityCode: + type: "integer" + nullable: true + description: "Código do Município de ocorrência do fato gerador do IBS/CBS (cMunFGIBS). Informar o município de ocorrência do fato gerador do IBS/CBS. Este campo só é preenchido quando \"indPres = 5 (Operação presencial, fora do estabelecimento)\" e não incluir o endereço do destinatário (Grupo: E05) ou local de entrega (Grupo: G01)." + format: "int64" # Código do Município de ocorrência do fato gerador do IBS/CBS + IbsZfmPresumedCreditClassification: + type: "string" + nullable: true + description: | + Classificação para cálculo do crédito presumido de IBS para fornecimentos da ZFM (tpCredPresIBSZFM). + Valores possíveis: + - `NoPresumedCredit`: Sem Crédito Presumido + - `FinalConsumptionGoods`: Bens de consumo final (55%) + - `CapitalGoods`: Bens de capital (75%) + - `IntermediateGoods`: Bens intermediários (90,25%) + - `ItAndOtherGoods`: Bens de informática e outros definidos em legislação (100%) + enum: + - "NoPresumedCredit" + - "FinalConsumptionGoods" + - "CapitalGoods" + - "IntermediateGoods" + - "ItAndOtherGoods" + IcmsTaxResource: # Grupo do ICMS da Operação própria e ST (ICMS) + type: "object" + description: | + Grupo do ICMS da Operação própria e ST (ICMS). + **Regra de obrigatoriedade**: deve-se informar **exatamente um** entre `cst` (Regime Normal) ou `csosn` (Simples Nacional). A SEFAZ não aceita ambos preenchidos ou nenhum dos dois. + required: + - origin + properties: + origin: + type: "string" + description: "Origem da mercadoria (orig).\n0 - Nacional, exceto as indicadas nos códigos 3, 4, 5 e 8;\n1 - Estrangeira - Importação direta, exceto a indicada no código 6;\n2 - Estrangeira - Adquirida no mercado interno, exceto a indicada no código 7;\n3 - Nacional, mercadoria ou bem com Conteúdo de Importação superior a 40% e inferior ou igual a 70%;\n4 - Nacional, cuja produção tenha sido feita em conformidade com os processos produtivos básicos de que tratam as legislações citadas nos Ajustes;\n5 - Nacional, mercadoria ou bem com Conteúdo de Importação inferior ou igual a 40%;\n6 - Estrangeira - Importação direta, sem similar nacional, constante em lista da CAMEX e gás natural;\n7 - Estrangeira - Adquirida no mercado interno, sem similar nacional, constante lista CAMEX e gás natural;\n8 - Nacional, mercadoria ou bem com Conteúdo de Importação superior a 70%;" + cst: + type: "string" + nullable: true + description: "Tributação do ICMS (CST) — obrigatório quando o emitente é do Regime Normal. Mutuamente exclusivo com `csosn`." + csosn: + type: "string" + nullable: true # Código de Situação da Operação – Simples Nacional + description: "101- Tributada pelo Simples Nacional com permissão de crédito (v.2.0) (CSOSN). Código de Situação da Operação – Simples Nacional" + baseTaxModality: + type: "string" + nullable: true + description: "Modalidade de determinação da BC do ICMS (modBC).\nMargem Valor Agregado (%) = 0\nPauta (valor) = 1\nPreço Tabelado Máximo (valor) = 2\nValor da Operação = 3" + baseTax: + type: "number" + nullable: true # Valor da BC do ICMS + description: "Valor da BC do ICMS (vBC)" + format: "double" + baseTaxSTModality: + type: "string" + nullable: true + description: "Modalidade de determinação da BC do ICMS ST (modBCST)" + baseTaxSTReduction: + type: "string" + nullable: true # Percentual da Redução de BC do ICMS ST + description: "Percentual da Redução de BC do ICMS ST (pRedBCST)" + baseTaxST: + type: "number" + nullable: true + description: "Valor da BC do ICMS ST (vBCST)" + format: "double" + baseTaxReduction: + type: "number" + nullable: true # Percentual da Redução de BC + description: "Percentual da Redução de BC (pRedBC)" + format: "double" + basisBenefitCode: + type: "string" + nullable: true + description: "Código de Benefício na UF para Redução da Base de Cálculo (cBenefRBC). Tamanho 8 ou 10, quando exigido pela UF (NT 2019.001). Código de Benefício Fiscal utilizado pelo Estado, aplicado ao item quando houver RBC. Observação: Deve ser utilizado o mesmo código utilizado na EFD e demais declarações nos Estados que o exigem. (Incluído na NT 2019.001)" + stRate: + type: "number" + nullable: true # Alíquota do imposto do ICMS ST + description: "Alíquota do imposto do ICMS ST (pICMSST)" + format: "double" + stAmount: + type: "number" + nullable: true + description: "Valor do ICMS ST (vICMSST)" + format: "double" + stMarginAmount: + type: "number" + nullable: true # Percentual da margem de valor Adicionado do ICMS ST + description: "Percentual da margem de valor Adicionado do ICMS ST (pMVAST)" + format: "double" + rate: + type: "number" + nullable: true + description: "Alíquota do imposto (pICMS)" + format: "double" + amount: + type: "number" + nullable: true # Valor do ICMS + description: "Valor do ICMS (vICMS). O valor do ICMS desonerado será informado apenas nas operações:\na) com produtos beneficiados com a desoneração condicional do ICMS.\nb) destinadas à SUFRAMA, informando-se o valor que seria devido se não houvesse isenção.\nc) de venda a órgãos da administração pública direta e suas fundações e autarquias com isenção do ICMS. (NT 2011/004)" + format: "double" + percentual: + type: "number" + nullable: true + description: "Percentual da Redução de BC (pICMS)" + format: "double" + snCreditRate: + type: "number" + nullable: true # Alíquota aplicável de cálculo do crédito (Simples Nacional) + description: "Alíquota aplicável de cálculo do crédito (Simples Nacional) (pCredSN)." + format: "double" + snCreditAmount: + type: "number" + nullable: true + description: "Valor crédito do ICMS que pode ser aproveitado nos termos do art. 23 da LC 123 Simples Nacional (vCredICMSSN)" + format: "double" + stMarginAddedAmount: + type: "string" + nullable: true # Percentual da margem de valor Adicionado do ICMS ST + description: "Percentual da margem de valor Adicionado do ICMS ST (pMVAST)" + stRetentionAmount: + type: "string" + nullable: true + description: "Valor do ICMS ST retido (vICMSSTRet)" + baseSTRetentionAmount: + type: "string" + nullable: true # Valor da BC do ICMS ST retido + description: "Valor da BC do ICMS ST retido (vBCSTRet)" + baseTaxOperationPercentual: + type: "string" + nullable: true + description: "Percentual da BC operação própria (pBCOp). Percentual para determinação do valor da Base de Cálculo da operação própria (v2.0)" + ufst: + type: "string" + nullable: true # UF para qual é devido o ICMS ST + description: "UF para qual é devido o ICMS ST (UFST). Sigla da UF para qual é devido o ICMS ST da operação (v2.0)" + amountSTReason: + type: "string" + nullable: true + description: "Motivo Desoneração ICMS (motDesICMS)" + baseSNRetentionAmount: + type: "string" + nullable: true # Valor da BC do ICMS ST retido + description: "Valor da BC do ICMS ST retido (vBCSTRet)" + snRetentionAmount: + type: "string" + nullable: true + description: "Valor do ICMS ST retido (vICMSSTRet)" + amountOperation: + type: "string" + nullable: true # Valor do ICMS da Operação + description: "Valor do ICMS da Operação (vICMSOp)" + percentualDeferment: + type: "string" + nullable: true + description: "Percentual do Diferimento (pDif)" + baseDeferred: + type: "string" + nullable: true # Valor do ICMS Diferido + description: "Valor do ICMS Diferido (vICMSDif)" + exemptAmount: + type: "number" + nullable: true + description: "Valor ICMS Desonerado (vICMSDeson)" + format: "double" + exemptReason: + description: "Motivo da desoneração do ICMS (motDesICMS)" + $ref: "#/components/schemas/ExemptReason" + exemptAmountST: # Valor ICMS Desonerado + type: "number" + nullable: true + description: "Valor ICMS Desonerado (vICMSSTDeson)" + format: "double" + exemptReasonST: + description: "Motivo da desoneração do ICMS ST (motDesICMSST)" + $ref: "#/components/schemas/ExemptReason" + fcpRate: + type: "number" + nullable: true # Percentual do FCP + description: "Percentual do FCP - Valor do ICMS relativo ao Fundo de Combate à Pobreza (pFCP)" + format: "double" + fcpAmount: + type: "number" + nullable: true + description: "Valor Total do FCP - Valor do ICMS relativo ao Fundo de Combate à Pobreza (vFCP)" + format: "double" + fcpstRate: + type: "number" + nullable: true # Percentual do FCP retido por ST + description: "Percentual do FCP retido por ST - Valor do ICMS relativo ao Fundo de Combate à Pobreza retido por substituição tributária (pFCPST)" + format: "double" + fcpstAmount: + type: "number" + nullable: true + description: "Valor Total do FCP retido por ST - Valor do ICMS relativo ao Fundo de Combate à Pobreza retido por substituição tributária (vFCPST)" + format: "double" + fcpstRetRate: + type: "number" + nullable: true # Percentual do FCP retido por anteriormente por ST + description: "Percentual do FCP retido por anteriormente por ST - Valor do ICMS relativo ao Fundo de Combate à Pobreza retido anteriormente por substituição tributária (pFCPSTRet)" + format: "double" + fcpstRetAmount: + type: "number" + nullable: true + description: "Valor Total do FCP retido por anteriormente por ST - Valor do ICMS relativo ao Fundo de Combate à Pobreza retido anteriormente por substituição tributária (vFCPSTRet)" + format: "double" + baseTaxFCPSTAmount: + type: "number" + nullable: true # Informar o valor da Base de Cálculo do FCP + description: "Informar o valor da Base de Cálculo do FCP (vBCFCPST)" + format: "double" + substituteAmount: + type: "number" + nullable: true + description: "Valor do ICMS próprio do Substituto (tag: vICMSSubstituto)" + format: "double" + stFinalConsumerRate: + type: "number" + nullable: true # Alíquota suportada pelo Consumidor Final + description: "N26a - Alíquota suportada pelo Consumidor Final (pST). Deve ser informada a alíquota do cálculo do ICMS-ST, já incluso o FCP caso incida sobre a mercadoria" + format: "double" + effectiveBaseTaxReductionRate: + type: "number" + nullable: true + description: "N34 - Percentual de redução da base de cálculo efetiva, caso estivesse submetida ao regime comum de tributação (pRedBCEfet)" + format: "double" + effectiveBaseTaxAmount: + type: "number" + nullable: true # Valor da base de cálculo efetiva + description: "N35 - Valor da base de cálculo efetiva, caso estivesse submetida ao regime comum de tributação (vBCEfet)" + format: "double" + effectiveRate: + type: "number" + nullable: true + description: "N36 - Alíquota do ICMS efetiva, caso estivesse submetida ao regime comum de tributação (pICMSEFET)" + format: "double" + effectiveAmount: + type: "number" + nullable: true # Valor do ICMS efetivo + description: "N37 - Valor do ICMS efetivo, caso estivesse submetida ao regime comum de tributação (vICMSEFET)" + format: "double" + deductionIndicator: + description: "Indicador de dedução do ICMS desonerado (indDeducao)" + $ref: "#/components/schemas/DuductionIndicator" + additionalProperties: false + ImportDeclarationResource: + type: "object" + properties: + code: + type: "string" + nullable: true # Número do Documento de Importação da DI/DSI/DA + description: "Número do Documento de Importação da DI/DSI/DA (nDI)" + registeredOn: + type: "string" + nullable: true + description: "Data de Registro da DI/DSI/DA (dDI)" + format: "date-time" + customsClearanceName: + type: "string" + nullable: true # Local de desembaraço + description: "Local de desembaraço (xLocDesemb)" + customsClearanceState: + description: "Sigla da UF onde ocorreu o Desembaraço Aduaneiro (UFDesemb)" + $ref: "#/components/schemas/StateCode" + customsClearancedOn: + type: "string" + nullable: true + description: "Data do Desembaraço Aduaneiro (dDesemb)" + format: "date-time" + additions: + type: "array" + nullable: true + items: + $ref: "#/components/schemas/AdditionResource" + description: "Adições (adi)" + exporter: # Código do exportador + type: "string" + nullable: true + description: "Código do exportador (cExportador)" + internationalTransport: + description: "Via de transporte internacional informada na Declaração de Importação (DI) (tpViaTransp)" + $ref: "#/components/schemas/InternationalTransportType" + afrmmAmount: + type: "number" + nullable: true + description: "Valor Adicional ao frete para renovação de marinha mercante (vAFRMM)" + format: "double" + intermediation: + description: "Forma de importação quanto a intermediação (tpIntermedio)" + $ref: "#/components/schemas/IntermediationType" + acquirerFederalTaxNumber: + type: "string" # CNPJ/CPF do adquirente ou do encomendante + nullable: true + description: "CNPJ/CPF do adquirente ou do encomendante (CNPJ ou CPF)" + stateThird: + type: "string" + nullable: true + description: "Sigla da UF do adquirente ou do encomendante (UFTerceiro)" + additionalProperties: false + description: "Declaração Importação (DI)" + IntegrationPaymentType: + type: "string" + enum: + - "Integrated" + - "NotIntegrated" + description: | + Tipo de Integração para pagamento (tpIntegra). + Valores possíveis: + - `Integrated`: 1 - Pagamento integrado com o sistema de automação da empresa (ex: equipamento TEF, Comércio Eletrônico) + - `NotIntegrated`: 2 - Pagamento não integrado com o sistema de automação da empresa (ex: equipamento POS) + IntermediateResource: + type: "object" + properties: + federalTaxNumber: + type: "integer" + nullable: true + description: "CNPJ do Intermediador da Transação (agenciador, plataforma de delivery, marketplace e similar) de serviços e de negócios (CNPJ)." + format: "int64" + identifier: + type: "string" # Identificador cadastrado no intermediador + nullable: true + description: "Identificador cadastrado no intermediador (idCadIntTran)" + additionalProperties: false + description: "Grupo de Informações do Intermediador da Transação (infIntermed)" + IntermediationType: + type: "string" + enum: + - "None" + - "ByOwn" + - "ImportOnBehalf" + - "ByOrder" + description: | + Forma de importação quanto a intermediação (tpIntermedio). + Valores possíveis: + - `None`: Não definido + - `ByOwn`: Importação por conta própria + - `ImportOnBehalf`: Importação por conta e ordem de terceiro + - `ByOrder`: Importação por encomenda + InternationalTransportType: + type: "string" + description: | + Via de transporte internacional (tpViaTransp). + Valores possíveis: + - `None`: Não definido + - `Maritime`: Marítima + - `River`: Fluvial + - `Lake`: Lacustre + - `Airline`: Aérea + - `Postal`: Postal + - `Railway`: Ferroviária + - `Highway`: Rodoviária + - `Network`: Conduto / Rede Transmissão + - `Own`: Meios Próprios + - `Ficta`: Entrada / Saída ficta + - `Courier`: Serviço de Courrier + - `Handcarry`: Em mãos + enum: + - "None" + - "Maritime" + - "River" + - "Lake" + - "Airline" + - "Postal" + - "Railway" + - "Highway" + - "Network" + - "Own" + - "Ficta" + - "Courier" + - "Handcarry" + InvoiceEventsResource: + type: "object" + properties: + events: + type: "array" + nullable: true + items: + $ref: "#/components/schemas/ActivityResource" + description: "Lista de Eventos ocorridos na Nota Fiscal" + hasMore: # Identificador de possibilidade de mais itens + type: "boolean" + nullable: true + description: "Identificador de possibilidade de mais itens." + id: + type: "string" + nullable: true + description: "Identificação" + accountId: + type: "string" + nullable: true # Identificador da Conta + description: "Identificador da Conta" + companyId: + type: "string" + nullable: true + description: "Identificador da Empresa" + additionalProperties: false + InvoiceEventsResourceBase: + type: "object" + properties: + events: + type: "array" + nullable: true + items: + $ref: "#/components/schemas/ActivityResource" + description: "Lista de Eventos ocorridos na Nota Fiscal" + hasMore: # Identificador de possibilidade de mais itens + type: "boolean" + nullable: true + description: "Identificador de possibilidade de mais itens." + additionalProperties: false + InvoiceItemResource: + type: "object" + required: + - code + - description + - ncm + - unit + - unitTax + - quantity + - unitAmount + - totalAmount + - tax + properties: + code: + type: "string" + description: "Código do produto ou serviço (cProd)" + codeGTIN: + type: "string" + nullable: true + description: "GTIN (Global Trade Item Number) do produto, antigo código EAN ou código de barras (cEAN)" + description: + type: "string" + description: "Descrição do produto ou serviço (xProd)" + ncm: + type: "string" + description: "Código NCM com 8 dígitos ou 2 dígitos (gênero) (NCM)" + nve: + type: "array" + nullable: true # Nomenclatura de Valor Aduaneiro e Estatístico + items: + type: "string" + description: "Nomenclatura de Valor Aduaneiro e Estatístico (NVE)" + extipi: + type: "string" + nullable: true + description: "Código Exceção da Tabela de IPI (EXTIPI)" + cfop: + type: "integer" + nullable: true # Código Fiscal de Operações e Prestações + description: "Código Fiscal de Operações e Prestações (CFOP)" + format: "int64" + unit: + type: "string" + description: "Unidade Comercial (uCom)" + quantity: + type: "number" + description: "Quantidade Comercial (qCom)" + format: "double" + unitAmount: + type: "number" + description: "Valor Unitário de Comercialização (vUnCom)" + format: "double" + totalAmount: + type: "number" + description: "Valor Total Bruto dos Produtos ou Serviços (vProd)" + format: "double" + codeTaxGTIN: + type: "string" + nullable: true + description: "GTIN (Global Trade Item Number) da unidade tributável, antigo código EAN ou código de barras (cEANTrib)" + unitTax: + type: "string" + description: "Unidade Tributável (uTrib)" + quantityTax: + type: "number" + nullable: true + description: "Quantidade Tributável (qTrib)" + format: "double" + taxUnitAmount: + type: "number" + nullable: true # Valor Unitário de tributação + description: "Valor Unitário de tributação (vUnTrib)" + format: "double" + freightAmount: + type: "number" + nullable: true + description: "Valor Total do Frete (vFrete)" + format: "double" + insuranceAmount: + type: "number" + nullable: true # Valor Total do Seguro + description: "Valor Total do Seguro (vSeg)" + format: "double" + discountAmount: + type: "number" + nullable: true + description: "Valor do Desconto (vDesc)" + format: "double" + othersAmount: + type: "number" + nullable: true # Outras despesas acessórias + description: "Outras despesas acessórias (vOutro)" + format: "double" + totalIndicator: + type: "boolean" + nullable: true + description: "Indica se valor do Item (vProd) entra no valor total da NF-e (vProd) (indTot)" + default: true # Mantido o default anterior + usedMovableAssetIndicator: + description: "Indica fornecimento de bem móvel usado (indBemMovelUsado)" + $ref: "#/components/schemas/UsedMovableAssetIndicator" + cest: + type: "string" # CEST - Código especificador da substituição tributária + nullable: true + description: "CEST - Código especificador da substituição tributária (CEST)" + tax: + description: "Tributos incidentes no Produto ou Serviço (imposto)" + $ref: "#/components/schemas/InvoiceItemTaxResource" + additionalInformation: + type: "string" + nullable: true + description: "Informações Adicionais do Produto (infAdProd)" + itemAmount: + type: "number" + nullable: true # Valor total do item + description: "Valor total do item (vItem)." + format: "double" + maximum: 99999999999.99 + minimum: 0 + numberOrderBuy: + type: "string" + nullable: true + description: "Número do pedido de compra (xPed)" + itemNumberOrderBuy: + type: "integer" + nullable: true # Item do Pedido de Compra + description: "Item do Pedido de Compra (nItemPed)" + format: "int32" + importControlSheetNumber: + type: "string" + nullable: true + description: "Número de controle da FCI - Ficha de Conteúdo de Importação (nFCI)" + vehicleDetail: + description: | + Detalhamento de Veículos novos (veicProd). Preencher exclusivamente quando o item da nota fiscal é um veículo 0 km. + **Mutuamente exclusivo** com `fuelDetail` e `medicineDetail` — cada item da nota pode conter no máximo um grupo de produto específico. + $ref: "#/components/schemas/VehicleDetailResource" + fuelDetail: + description: "Detalhamento de Combustível (comb)" + $ref: "#/components/schemas/FuelResource" # Detalhamento de Combustível + benefit: + type: "string" + nullable: true + description: "Código de Benefício Fiscal na UF aplicado ao item (cBenef)" + presumedCredit: + description: "Informações de Crédito Presumido por item (gCred)" + $ref: "#/components/schemas/PresumedCreditResource" + ibsZfmPresumedCreditClassification: + description: "Classificação conforme percentuais definidos no art. 450, § 1º, da LC 214/25 para o cálculo do crédito presumido (tpCredPresIBSZFM)" # Classificação conforme percentuais definidos no art. 450, § 1º, da LC 214/25 para o cálculo do crédito presumido + $ref: "#/components/schemas/IbsZfmPresumedCreditClassification" + importDeclarations: + type: "array" + nullable: true + items: + $ref: "#/components/schemas/ImportDeclarationResource" + description: "Declaração Importação (DI)" + exportDetails: + type: "array" + nullable: true + items: + $ref: "#/components/schemas/ExportDetailResource" + description: "Grupo de informações de exportação para o item (detExport)" + referencedDFe: # Documento Fiscal Eletrônico Referenciado + $ref: "#/components/schemas/ReferencedDFeResource" + taxDetermination: + description: "Grupo de informações fiscais para o cálculo automático de impostos (infFiscal). Se este grupo for preenchido, o grupo `tax.IBSCBS` será calculado automaticamente. Se não for preenchido, o cálculo automático do grupo `tax.IBSCBS` torna-se opcional." + $ref: "#/components/schemas/TaxDeterminationResource" + additionalProperties: false + description: "Grupo do detalhamento de Produtos e Serviços da NF-e\r\n" + InvoiceItemTaxResource: + type: "object" + required: + - icms + properties: + totalTax: + type: "number" + nullable: true # Valor aproximado total de tributos federais, estaduais e municipais — opcional (calculado automaticamente quando não informado) + description: "Valor aproximado total de tributos federais, estaduais e municipais (vTotTrib)" + format: "double" + icms: + description: "Grupo do ICMS da Operação própria e ST (ICMS)" + $ref: "#/components/schemas/IcmsTaxResource" + ipi: + description: "Grupo do IPI (IPI)" + $ref: "#/components/schemas/IPITaxResource" + ii: + description: "Grupo do Imposto de Importação (II)" + $ref: "#/components/schemas/IITaxResource" + pis: + description: "Grupo do PIS (PIS)" + $ref: "#/components/schemas/PISTaxResource" + cofins: + description: "Grupo do COFINS (COFINS)" + $ref: "#/components/schemas/CofinsTaxResource" + icmsDestination: + description: "Grupo de Tributação do ICMS de Destino da UF (ICMSUFDest)" # Grupo de Tributação do ICMS de Destino da UF + $ref: "#/components/schemas/ICMSUFDestinationTaxResource" + IS: + description: "Imposto Seletivo (IS)" + $ref: "#/components/schemas/ISTaxResource" + IBSCBS: + description: "Grupo de Informações do IBS e CBS" + $ref: "#/components/schemas/IBSCBSTaxResource" + competenceAdjustment: # Ajuste de Competência (gAjusteCompet) + $ref: "#/components/schemas/CompetenceAdjustmentResource" + description: "Ajuste de Competência (gAjusteCompet)" + additionalProperties: false + InvoiceItemsResource: + type: "object" + properties: + accountId: + type: "string" + nullable: true + description: "Identificador da Conta" + companyId: + type: "string" + nullable: true + description: "Identificador da Empresa" + id: + type: "string" + nullable: true + description: "Identificador da Nota Fiscal" + items: + type: "array" + nullable: true + items: + $ref: "#/components/schemas/InvoiceItemResource" + description: "Detalhamento de Produtos e Serviços (det) - Lista de Items da Nota Fiscal" + hasMore: + type: "boolean" + nullable: true + description: "Identifica se existem mais items a serem consultados" + additionalProperties: false + InvoiceResource: + type: "object" + properties: + id: + type: "string" + nullable: true + description: "Identificador único" + serie: + type: "integer" + nullable: true + description: "Série do Documento Fiscal (serie)" + format: "int32" + number: + type: "integer" + nullable: true + description: "Número do Documento Fiscal (nNF)" + format: "int64" + status: + description: "Status da NF-e (cStat)" + $ref: "#/components/schemas/InvoiceStatus" + authorization: + description: "Informações do Protocolo de Autorização (protNFe)" + $ref: "#/components/schemas/AuthorizationResource" + contingencyDetails: + description: "Detalhes da contingência (dhCont)" + $ref: "#/components/schemas/ContingencyDetails" + operationNature: + type: "string" + nullable: true + description: "Descrição da Natureza da Operação (natOp)" + createdOn: + type: "string" + nullable: true + description: "Data de criação" + format: "date-time" + modifiedOn: + type: "string" + nullable: true + description: "Data de modificação" + format: "date-time" + operationOn: + type: "string" + nullable: true + description: "Data e Hora de Saída ou da Entrada da Mercadoria/Produto (dhSaiEnt). Data e hora no formato UTC (Universal Coordinated Time): AAAA-MM-DDThh:mm:ssTZD." + format: "date-time" + operationType: + description: "Tipo do Documento Fiscal (tpNF)" + $ref: "#/components/schemas/OperationType" + environmentType: + description: "Tipo de Ambiente (tpAmb)" + $ref: "#/components/schemas/EnvironmentType" + purposeType: + description: "Finalidade da emissão da NF-e (finNFe)" + $ref: "#/components/schemas/PurposeType" + issuer: + description: "Identificação do Emitente (emit)" + $ref: "#/components/schemas/IssuerResource" + buyer: + description: "Identificação do Destinatário (dest)" + $ref: "#/components/schemas/BuyerResource" + totals: + description: "Grupo de Valores Totais da NF-e (total)" + $ref: "#/components/schemas/TotalResource" + transport: + description: "Grupo de Informações do Transporte da NF-e (transp)" + $ref: "#/components/schemas/TransportInformationResource" + additionalInformation: + description: "Informações adicionais da NF-e (infAdic)" + $ref: "#/components/schemas/AdditionalInformationResource" + export: + description: "Informações de exportação (exporta)" + $ref: "#/components/schemas/ExportResource" + billing: + description: "Grupo Cobrança (cobr)" + $ref: "#/components/schemas/BillingResource" + payment: + type: "array" + items: + $ref: "#/components/schemas/PaymentResource" + description: "Grupo de Formas de Pagamento (pag)" + transactionIntermediate: + description: "Grupo de Informações do Intermediador da Transação (infIntermed)" + $ref: "#/components/schemas/IntermediateResource" + delivery: + description: "Identificação do Local de entrega (entrega)" + $ref: "#/components/schemas/DeliveryInformationResource" + withdrawal: + description: "Identificação do Local de retirada (retirada)" + $ref: "#/components/schemas/WithdrawalInformationResource" + lastEvents: + description: "Últimos eventos da NF-e (eventos)" + $ref: "#/components/schemas/InvoiceEventsResourceBase" + additionalProperties: false + InvoiceStatus: + type: "string" + enum: + - "None" + - "Created" + - "Processing" + - "Issued" + - "IssuedContingency" + - "Cancelled" + - "Disabled" + - "IssueDenied" + - "Error" + description: | + Status da NF-e. + Valores possíveis: + - `None`: Não definido + - `Created`: Criada + - `Processing`: Processando + - `Issued`: Emitida + - `IssuedContingency`: Emitida em Contingência + - `Cancelled`: Cancelada + - `Disabled`: Inutilizada + - `IssueDenied`: Emissão Denegada + - `Error`: Erro + InvoiceWithoutEventsResource: + type: "object" + properties: + id: + type: "string" + nullable: true + description: "Identificador único" + serie: + type: "integer" + nullable: true + description: "Série do Documento Fiscal (serie)" + format: "int32" + number: + type: "integer" + nullable: true + description: "Número do Documento Fiscal (nNF)" + format: "int64" + status: + description: "Status da NF-e (cStat)" + $ref: "#/components/schemas/InvoiceStatus" + authorization: + description: "Informações do Protocolo de Autorização (protNFe)" + $ref: "#/components/schemas/AuthorizationResource" + contingencyDetails: + description: "Detalhes da contingência (dhCont)" + $ref: "#/components/schemas/ContingencyDetails" + operationNature: + type: "string" + nullable: true + description: "Descrição da Natureza da Operação (natOp)" + createdOn: + type: "string" + nullable: true + description: "Data de criação" + format: "date-time" + modifiedOn: + type: "string" + nullable: true + description: "Data de modificação" + format: "date-time" + operationOn: + type: "string" + nullable: true + description: "Data e Hora de Saída ou da Entrada da Mercadoria/Produto (dhSaiEnt). Data e hora no formato UTC (Universal Coordinated Time): AAAA-MM-DDThh:mm:ssTZD." + format: "date-time" + operationType: + description: "Tipo do Documento Fiscal (tpNF)" + $ref: "#/components/schemas/OperationType" + environmentType: + description: "Tipo de Ambiente (tpAmb)" + $ref: "#/components/schemas/EnvironmentType" + purposeType: + description: "Finalidade da emissão da NF-e (finNFe)" + $ref: "#/components/schemas/PurposeType" + issuer: + description: "Identificação do Emitente (emit)" + $ref: "#/components/schemas/IssuerResource" + buyer: + description: "Identificação do Destinatário (dest)" + $ref: "#/components/schemas/BuyerResource" + totals: + description: "Grupo de Valores Totais da NF-e (total)" + $ref: "#/components/schemas/TotalResource" + transport: + description: "Grupo de Informações do Transporte da NF-e (transp)" + $ref: "#/components/schemas/TransportInformationResource" + additionalInformation: + description: "Informações adicionais da NF-e (infAdic)" + $ref: "#/components/schemas/AdditionalInformationResource" + export: + description: "Informações de exportação (exporta)" + $ref: "#/components/schemas/ExportResource" + billing: + description: "Grupo Cobrança (cobr)" + $ref: "#/components/schemas/BillingResource" + payment: + type: "array" + items: + $ref: "#/components/schemas/PaymentResource" + description: "Grupo de Formas de Pagamento (pag)" + transactionIntermediate: + description: "Grupo de Informações do Intermediador da Transação (infIntermed)" + $ref: "#/components/schemas/IntermediateResource" + delivery: + description: "Identificação do Local de entrega (entrega)" + $ref: "#/components/schemas/DeliveryInformationResource" + withdrawal: + description: "Identificação do Local de retirada (retirada)" + $ref: "#/components/schemas/WithdrawalInformationResource" + additionalProperties: false + IssuerFromRequestResource: + type: "object" + properties: + stStateTaxNumber: + type: "string" # IE do Substituto Tributário da UF de destino da mercadoria + nullable: true + description: "IE do Substituto Tributário da UF de destino da mercadoria, quando houver a retenção do ICMS ST para a UF de destino. (IEST)" + additionalProperties: false + IssuerResource: + type: "object" + properties: + accountId: + type: "string" + nullable: true # Identificador da Conta + description: "Identificador da Conta" + id: + type: "string" + nullable: true + description: "Identificação" + name: + type: "string" + nullable: true + description: "Nome ou Razão Social (xNome)" + federalTaxNumber: + type: "integer" + nullable: true # O valor padrão de consumerType é determinado com base neste campo. Se for um CNPJ, o padrão será "Normal"; se for um CPF ou NIF, será "FinalConsumer". + description: "CNPJ ou CPF (CNPJ/CPF)" + format: "int64" + email: + type: "string" + nullable: true + description: "Email (email)" + address: + description: "Dados do Endereço (enderEmit)" + $ref: "#/components/schemas/AddressResource" + type: + description: "Tipo da pessoa (indIEDest)" + $ref: "#/components/schemas/PersonType" + tradeName: + type: "string" + nullable: true + description: "Nome Fantasia (xFant)" + openningDate: + type: "string" + nullable: true + description: "Data abertura da empresa (dIniAtiv)" + format: "date-time" + taxRegime: + description: "Regime de tributação (CRT)" + $ref: "#/components/schemas/TaxRegime" + specialTaxRegime: + description: "Regime especial de tributação (indRegTrib)" + $ref: "#/components/schemas/SpecialTaxRegime" + legalNature: + description: "Natureza jurídica (natJuridica)" + $ref: "#/components/schemas/LegalNature" + economicActivities: + type: "array" + nullable: true + items: + $ref: "#/components/schemas/EconomicActivityResource" + description: "Atividades da Empresa (CNAE)" + companyRegistryNumber: + type: "integer" + nullable: true + description: "Número de Inscrição na Junta Comercial (nIRE)" + format: "int64" + regionalTaxNumber: + type: "integer" + nullable: true + description: "Número de Inscrição na SEFAZ (IE)" + format: "int64" + regionalSTTaxNumber: + type: "integer" + nullable: true + description: "Inscrição Estadual do Substituto Tributário (IEST)" + format: "int64" + municipalTaxNumber: + type: "string" + nullable: true + description: "Número de Inscrição na Prefeitura (IM/CCM)" + stStateTaxNumber: + type: "string" + nullable: true + description: "IE do Substituto Tributário (IEST)" + additionalProperties: false + description: "Grupo de identificação do emitente da NF-e" + LegalNature: + type: "string" + enum: + - "EmpresaPublica" + - "SociedadeEconomiaMista" + - "SociedadeAnonimaAberta" + - "SociedadeAnonimaFechada" + - "SociedadeEmpresariaLimitada" + - "SociedadeEmpresariaEmNomeColetivo" + - "SociedadeEmpresariaEmComanditaSimples" + - "SociedadeEmpresariaEmComanditaporAcoes" + - "SociedadeemContaParticipacao" + - "Empresario" + - "Cooperativa" + - "ConsorcioSociedades" + - "GrupoSociedades" + - "EmpresaDomiciliadaExterior" + - "ClubeFundoInvestimento" + - "SociedadeSimplesPura" + - "SociedadeSimplesLimitada" + - "SociedadeSimplesEmNomeColetivo" + - "SociedadeSimplesEmComanditaSimples" + - "EmpresaBinacional" + - "ConsorcioEmpregadores" + - "ConsorcioSimples" + - "EireliNaturezaEmpresaria" + - "EireliNaturezaSimples" + - "ServicoNotarial" + - "FundacaoPrivada" + - "ServicoSocialAutonomo" + - "CondominioEdilicio" + - "ComissaoConciliacaoPrevia" + - "EntidadeMediacaoArbitragem" + - "PartidoPolitico" + - "EntidadeSindical" + - "EstabelecimentoBrasilFundacaoAssociacaoEstrangeiras" + - "FundacaoAssociacaoDomiciliadaExterior" + - "OrganizacaoReligiosa" + - "ComunidadeIndigena" + - "FundoPrivado" + - "AssociacaoPrivada" + description: | + Código da Natureza Jurídica da empresa (natJuridica), conforme tabela do IBGE. + Valores possíveis: + - `EmpresaPublica`: Empresa Pública + - `SociedadeEconomiaMista`: Sociedade de Economia Mista + - `SociedadeAnonimaAberta`: Sociedade Anônima Aberta + - `SociedadeAnonimaFechada`: Sociedade Anônima Fechada + - `SociedadeEmpresariaLimitada`: Sociedade Empresária Limitada (LTDA) + - `SociedadeEmpresariaEmNomeColetivo`: Sociedade Empresária em Nome Coletivo + - `SociedadeEmpresariaEmComanditaSimples`: Sociedade Empresária em Comandita Simples + - `SociedadeEmpresariaEmComanditaporAcoes`: Sociedade Empresária em Comandita por Ações + - `SociedadeemContaParticipacao`: Sociedade em Conta de Participação + - `Empresario`: Empresário Individual + - `Cooperativa`: Cooperativa + - `ConsorcioSociedades`: Consórcio de Sociedades + - `GrupoSociedades`: Grupo de Sociedades + - `EmpresaDomiciliadaExterior`: Empresa Domiciliada no Exterior + - `ClubeFundoInvestimento`: Clube/Fundo de Investimento + - `SociedadeSimplesPura`: Sociedade Simples Pura + - `SociedadeSimplesLimitada`: Sociedade Simples Limitada + - `SociedadeSimplesEmNomeColetivo`: Sociedade Simples em Nome Coletivo + - `SociedadeSimplesEmComanditaSimples`: Sociedade Simples em Comandita Simples + - `EmpresaBinacional`: Empresa Binacional + - `ConsorcioEmpregadores`: Consórcio de Empregadores + - `ConsorcioSimples`: Consórcio Simples + - `EireliNaturezaEmpresaria`: EIRELI de Natureza Empresária + - `EireliNaturezaSimples`: EIRELI de Natureza Simples + - `ServicoNotarial`: Serviço Notarial e Registral (Cartórios) + - `FundacaoPrivada`: Fundação Privada + - `ServicoSocialAutonomo`: Serviço Social Autônomo + - `CondominioEdilicio`: Condomínio Edilício + - `ComissaoConciliacaoPrevia`: Comissão de Conciliação Prévia + - `EntidadeMediacaoArbitragem`: Entidade de Mediação e Arbitragem + - `PartidoPolitico`: Partido Político + - `EntidadeSindical`: Entidade Sindical + - `EstabelecimentoBrasilFundacaoAssociacaoEstrangeiras`: Estabelecimento no Brasil de Fundação ou Associação Estrangeiras + - `FundacaoAssociacaoDomiciliadaExterior`: Fundação ou Associação Domiciliada no Exterior + - `OrganizacaoReligiosa`: Organização Religiosa + - `ComunidadeIndigena`: Comunidade Indígena + - `FundoPrivado`: Fundo Privado + - `AssociacaoPrivada`: Associação Privada + MonophaseDefermentTaxResource: + type: "object" + description: "Deferimento na tributação monofásica (para biocombustíveis)." + properties: + ibsRate: + type: "number" + nullable: true + description: "Percentual de diferimento do IBS monofásico (pDifIBS)." + format: "double" + ibsAmount: + type: "number" + nullable: true + description: "Valor do diferimento IBS monofásico (vIBSMonoDif)." + format: "double" + cbsRate: + type: "number" + nullable: true + description: "Percentual de diferimento do CBS monofásico (pDifCBS)." + format: "double" + cbsAmount: + type: "number" + nullable: true + description: "Valor do diferimento CBS monofásico (vCBSMonoDif)." + format: "double" + additionalProperties: false + MonophaseIBSCBSTaxResource: + type: "object" + description: "Grupo de Informações do IBS e CBS sobre transações monofásicas" + properties: + standart: + description: "Informações de tributação monofásica padrão para IBS/CBS (gMonofasicoPadrao)." + $ref: "#/components/schemas/MonophaseStandardTaxResource" + withholding: + description: "Tributação monofásica sujeita à retenção (gMonofasicoRetido)." + $ref: "#/components/schemas/MonophaseWithholdingTaxResource" + previouslyWithheld: + description: "Tributação monofásica previamente retida (gMonofasicoRetidoAnt)." + $ref: "#/components/schemas/MonophasePreviouslyWithheldTaxResource" + deferment: + description: "Deferimento na tributação monofásica (para biocombustíveis) (gMonofasicoDif)." + $ref: "#/components/schemas/MonophaseDefermentTaxResource" + ibsAmount: + type: "number" + nullable: true + description: "Total de IBS monofásico do item (vTotIBSMonoItem)." + format: "double" + cbsAmount: + type: "number" + nullable: true + description: "Total de CBS monofásico do item (vTotCBSMonoItem)." + format: "double" + additionalProperties: false + MonophasePreviouslyWithheldTaxResource: + type: "object" + description: "Tributação monofásica previamente retida." + properties: + quantityBasis: + type: "number" + nullable: true + description: "Quantidade base previamente retida (qBCMonoRet)." + format: "double" + ibsAdRemRate: + type: "number" + nullable: true + description: "Alíquota ad rem IBS previamente retida (adRemIBSRet)." + format: "double" + ibsAmount: + type: "number" + nullable: true + description: "Valor IBS previamente retido (vIBSMonoRet)." + format: "double" + cbsAdRemRate: + type: "number" + nullable: true + description: "Alíquota ad rem CBS previamente retida (adRemCBSRet)." + format: "double" + cbsAmount: + type: "number" + nullable: true + description: "Valor CBS previamente retido (vCBSMonoRet)." + format: "double" + additionalProperties: false + MonophaseStandardTaxResource: + type: "object" + description: "Informações de tributação monofásica padrão para IBS/CBS." + properties: + quantityBasis: + type: "number" + nullable: true + description: "Quantidade base tributada na monofásica (qBCMono)." + format: "double" + ibsAdRemRate: + type: "number" + nullable: true + description: "Alíquota ad rem IBS (adRemIBS)." + format: "double" + cbsAdRemRate: + type: "number" + nullable: true + description: "Alíquota ad rem CBS (adRemCBS)." + format: "double" + ibsAmount: + type: "number" + nullable: true + description: "Valor IBS monofásico (vIBSMono)." + format: "double" + cbsAmount: + type: "number" + nullable: true + description: "Valor CBS monofásico (vCBSMono)." + format: "double" + additionalProperties: false + MonophaseWithholdingTaxResource: + type: "object" + description: "Tributação monofásica sujeita à retenção." + properties: + quantityBasis: + type: "number" + nullable: true + description: "Quantidade base tributada na retenção monofásica (qBCMonoReten)." + format: "double" + ibsAdRemRate: + type: "number" + nullable: true + description: "Alíquota ad rem IBS retida (adRemIBSReten)." + format: "double" + ibsAmount: + type: "number" + nullable: true + description: "Valor IBS monofásico retido (vIBSMonoReten)." + format: "double" + cbsAdRemRate: + type: "number" + nullable: true + description: "Alíquota ad rem CBS retida (adRemCBSReten)." + format: "double" + cbsAmount: + type: "number" + nullable: true + description: "Valor CBS monofásico retido (vCBSMonoReten)." + format: "double" + additionalProperties: false + OperationalPresumedCreditResource: + type: "object" + description: "Informações sobre o crédito presumido de IBS para fornecimentos (gCredPresOper)." + properties: + basis: + type: "number" + nullable: true + description: "Valor da Base de Cálculo do Crédito Presumido da Operação (vBC)" + format: "double" + classificationCode: + description: "Código de Classificação do Crédito Presumido (cCredPres)." + $ref: "#/components/schemas/PresumedCreditClassificationCode" + ibs: + description: "Grupo de Informações do Crédito Presumido referente ao IBS (gIBSCredPres)" + $ref: "#/components/schemas/PresumedCreditDetailsResource" + cbs: + description: "Grupo de Informações do Crédito Presumido referente a CBS (gCBSCredPres)" + $ref: "#/components/schemas/PresumedCreditDetailsResource" + additionalProperties: false + OperationType: + type: "string" + enum: + - "Outgoing" + - "Incoming" # Entrada + description: | + Tipo de Operação (tpNF). + Valores possíveis: + - `Outgoing`: Saída + - `Incoming`: Entrada + default: "Outgoing" + PISTaxResource: + type: "object" + properties: + cst: + type: "string" + nullable: true # Código de Situação Tributária do PIS + description: "Código de Situação Tributária do PIS (CST)" + baseTax: + type: "number" + nullable: true + description: "Valor da Base de Cálculo do PIS (vBC)" + format: "double" + rate: + type: "number" + nullable: true # Alíquota do PIS (em percentual) + description: "Alíquota do PIS (em percentual) (pPIS)" + format: "double" + amount: + type: "number" + nullable: true + description: "Valor do PIS (vPIS)" + format: "double" + baseTaxProductQuantity: + type: "number" + nullable: true # Quantidade Vendida + description: "Quantidade Vendida (qBCProd)" + format: "double" + productRate: + type: "number" + nullable: true + description: "Alíquota do PIS (em reais) (vAliqProd)" + format: "double" + additionalProperties: false + description: "Grupo do PIS" + PaymentDetailResource: + type: "object" + required: + - method + - amount + properties: + method: + description: "Forma de pagamento (tPag)" + $ref: "#/components/schemas/PaymentMethod" # Forma de pagamento + methodDescription: + type: "string" + nullable: true + description: "Descrição do meio de pagamento (xPag)" + paymentType: + description: "Indicador da forma de pagamento (indPag)" + $ref: "#/components/schemas/PaymentType" + amount: + type: "number" + description: "Valor do Pagamento (vPag)" + format: "double" + card: + description: "Grupo de Cartões (card)" + $ref: "#/components/schemas/CardResource" + paymentDate: + type: "string" + nullable: true + description: "Data do pagamento (dPag) - Data e hora no formato UTC (Universal Coordinated Time): AAAA-MM-DDThh:mm:ssTZD" + format: "date-time" + federalTaxNumberPag: + type: "string" # CNPJ transacional do pagamento + nullable: true + description: "CNPJ transacional do pagamento (CNPJPag)" + statePag: + type: "string" + nullable: true + description: "UF do CNPJ do estabelecimento onde o pagamento foi processado/transacionado/recebido (UFPag)" + additionalProperties: false + PaymentMethod: + type: "string" + enum: + - "Cash" + - "Cheque" + - "CreditCard" + - "DebitCard" + - "StoreCredict" + - "FoodVouchers" + - "MealVouchers" + - "GiftVouchers" + - "FuelVouchers" + - "MercantileDuplicate" + - "BankBill" + - "BankDeposit" + - "InstantPayment" + - "WireTransfer" + - "Cashback" + - "StaticInstantPayment" + - "StoreCredit" + - "ElectronicPaymentNotInformed" + - "WithoutPayment" + - "Others" + description: | + Forma de pagamento (tPag). + Valores possíveis: + - `Cash`: 01 - Dinheiro + - `Cheque`: 02 - Cheque + - `CreditCard`: 03 - Cartão de Crédito + - `DebitCard`: 04 - Cartão de Débito + - `StoreCredict`: 05 - Crédito Loja + - `FoodVouchers`: 10 - Vale Alimentação + - `MealVouchers`: 11 - Vale Refeição + - `GiftVouchers`: 12 - Vale Presente + - `FuelVouchers`: 13 - Vale Combustível + - `MercantileDuplicate`: 14 - Duplicata Mercantil + - `BankBill`: 15 - Boleto Bancário + - `BankDeposit`: 16 - Depósito Bancário + - `InstantPayment`: 17 - Pagamento Instantâneo (PIX) + - `WireTransfer`: 18 - Transferência bancária, Carteira Digital + - `Cashback`: 19 - Programa de fidelidade, Cashback, Crédito Virtual + - `StaticInstantPayment`: 20 - Pagamento Instantâneo (PIX) Estático + - `StoreCredit`: 21 - Crédito em Loja + - `ElectronicPaymentNotInformed`: 22 - Pagamento Eletrônico Não Informado + - `WithoutPayment`: 90 - Sem Pagamento + - `Others`: 99 - Outros + PaymentResource: + type: "object" + required: + - paymentDetail + properties: + paymentDetail: + type: "array" + items: + $ref: "#/components/schemas/PaymentDetailResource" + description: "YA01a - Grupo Detalhamento da Forma de Pagamento (detPag) VERSÃO 4.00" + payBack: + type: "number" + nullable: true + description: "Valor do troco (vTroco) VERSÃO 4.00" + format: "double" + additionalProperties: false + PaymentType: + type: "string" + enum: + - "InCash" + - "Term" + description: | + Indicador da forma de pagamento (indPag). + Valores possíveis: + - `InCash`: Pagamento à vista + - `Term`: Pagamento a prazo + PersonType: + type: "string" + enum: + - "Undefined" + - "NaturalPerson" + - "LegalEntity" + - "Company" + - "Customer" + description: | + Tipo de Pessoa. + Valores possíveis: + - `Undefined`: Não definido + - `NaturalPerson`: Pessoa Física + - `LegalEntity`: Pessoa Jurídica + - `Company`: Empresa + - `Customer`: Cliente + PresumedCreditClassificationCode: + type: "string" + nullable: true + enum: + - "RuralProducerNonTaxpayer" + - "TacPfTransportServiceNonTaxpayer" + - "RecyclingFromIndividual" + - "UsedMovableGoodsFromIndividualForResale" + - "OptionalRegimeForCooperative" + description: | + Código de Classificação do Crédito Presumido (cCredPres). + Valores possíveis: + - `RuralProducerNonTaxpayer`: Produtor rural não contribuinte + - `TacPfTransportServiceNonTaxpayer`: TAC Pessoa Física não contribuinte do serviço de transporte + - `RecyclingFromIndividual`: Reciclagem de pessoa física + - `UsedMovableGoodsFromIndividualForResale`: Bens móveis usados de pessoa física para revenda + - `OptionalRegimeForCooperative`: Regime opcional para cooperativa + PresumedCreditDetailsResource: + type: "object" + description: "Grupo de Informações do Crédito Presumido" + properties: + rate: + type: "number" + nullable: true + description: "Percentual do Crédito Presumido (pCredPres)" + format: "double" + amount: + type: "number" # Valor do Crédito Presumido + nullable: true + description: "Valor do Crédito Presumido (vCredPres)" + format: "double" + suspensiveConditionAmount: + type: "number" + nullable: true + description: "Valor do Crédito Presumido em condição suspensiva. (vCredPresCondSus)" + format: "double" + additionalProperties: false + PresumedCreditResource: + type: "object" + description: "Informações de Crédito Presumido por item (gCred). Preenchimento conforme exigência da UF (NT 2019.001)." + properties: + code: + type: "string" + nullable: true # Código do Benefício de Crédito Presumido na UF + description: "Código do Benefício de Crédito Presumido na UF (cCredPresumido). Use o mesmo código utilizado na EFD/declarações da UF. Tamanho 8 ou 10." + rate: + type: "number" + nullable: true + description: "Percentual do Crédito Presumido (pCredPresumido). Use fração: 0.04 = 4%. Até 4 casas decimais." + format: "double" + amount: + type: "number" + nullable: true # Valor do Crédito Presumido + description: "Valor do Crédito Presumido (vCredPresumido)." + format: "double" + additionalProperties: false + PrintType: + type: "string" + enum: + - "None" + - "NFeNormalPortrait" + - "NFeNormalLandscape" + - "NFeSimplified" + - "DANFE_NFC_E" + - "DANFE_NFC_E_MSG_ELETRONICA" + description: | + Formato de impressão do DANFE (tpImp). + Valores possíveis: + - `None`: Não definido + - `NFeNormalPortrait`: DANFE normal, Retrato + - `NFeNormalLandscape`: DANFE normal, Paisagem + - `NFeSimplified`: DANFE Simplificado + - `DANFE_NFC_E`: DANFE NFC-e + - `DANFE_NFC_E_MSG_ELETRONICA`: DANFE NFC-e em mensagem eletrônica + ProductInvoicesResource: + type: "object" + properties: + productInvoices: + type: "array" + nullable: true + items: + $ref: "#/components/schemas/InvoiceWithoutEventsResource" + description: "Lista de Notas Fiscais Eletrônicas (NF-e)" + hasMore: # Identificador de possibilidade de mais itens + type: "boolean" + description: "Identificador de possibilidade de mais itens." + additionalProperties: false + description: "Notas Fiscais Eletrônicas (NF-e)" + PumpResource: + type: "object" + properties: + spoutNumber: + type: "integer" + nullable: true # Número de identificação do bico utilizado no abastecimento + description: "Número de identificação do bico utilizado no abastecimento (nBico)" + format: "int32" + number: + type: "integer" + nullable: true + description: "Número de identificação da bomba ao qual o bico está interligado (nBomba)" + format: "int32" + tankNumber: + type: "integer" + nullable: true # Número de identificação do tanque + description: "Número de identificação do tanque ao qual o bico está interligado (nTanque)" + format: "int32" + beginningAmount: + type: "number" + nullable: true + description: "Valor do Encerrante no início do abastecimento (vEncIni)" + format: "double" + endAmount: + type: "number" + nullable: true # Valor do Encerrante no final do abastecimento + description: "Valor do Encerrante no final do abastecimento (vEncFin)" + format: "double" + percentageBio: + type: "number" + nullable: true + description: "Percentual do índice de mistura do Biodiesel (B100) no Óleo Diesel B instituído pelo órgão regulamentador" + format: "double" + additionalProperties: false + PurchaseInformationResource: + type: "object" + description: "Grupo Compra. Additional purchase information (compra)" + properties: + commitmentNote: + type: "string" + nullable: true # Nota de Empenho + description: "Nota de Empenho. Identification of the Note of Commitment, when dealing with public purchases (xNEmp)" + minLength: 1 + maxLength: 22 + purchaseOrder: + type: "string" + nullable: true + description: "Pedido. (xPed)" + minLength: 1 + maxLength: 60 + contractNumber: # Contrato + type: "string" + nullable: true + description: "Contrato. (xcont)" + minLength: 1 + maxLength: 60 + additionalProperties: false + PurposeType: + type: "string" + enum: + - "None" + - "Normal" + - "Complement" + - "Adjustment" + - "Devolution" # Devolução + default: "Normal" + description: | + Finalidade da emissão da NF-e (finNFe). + Valores possíveis: + - `None`: Não definido + - `Normal`: NF-e normal + - `Complement`: NF-e complementar + - `Adjustment`: NF-e de ajuste + - `Devolution`: Devolução de mercadoria + QueueEventResource: + type: "object" + properties: + reason: + type: "string" + nullable: true # Justificativa da carta de correção + description: "Justificativa da carta de correção\r\nO Texto deve \r\nconter no mínimo 15 e no máximo 1.000 caracteres\r\n(os quais não poderão conter acentos e/ou caracteres especiais)" + additionalProperties: false + ReboqueResource: + type: "object" + properties: + plate: + type: "string" + nullable: true + description: "Placa do Veiculo (placa)" + uf: + type: "string" + nullable: true # UF Veiculo Reboque + description: "UF Veiculo Reboque (UF)" + rntc: + type: "string" + nullable: true + description: "Registro Nacional de Transportador de Carga (ANTT) (RNTC)" + wagon: + type: "string" + nullable: true + description: "Identificação do Vagão (vagao)" + ferry: + type: "string" + nullable: true # Identificação da Balsa + description: "Identificação da Balsa (balsa)" + additionalProperties: false + description: "Grupo Reboque" + ReceiverStateTaxIndicator: + type: "string" + enum: + - "None" + - "TaxPayer" + - "Exempt" + - "NonTaxPayer" + description: | + Indicador da IE do Destinatário (indIEDest). + Valores possíveis: + - `None`: Não definido + - `TaxPayer`: Contribuinte ICMS (informar a IE do destinatário) + - `Exempt`: Contribuinte isento de Inscrição no cadastro de Contribuintes do ICMS + - `NonTaxPayer`: Não Contribuinte, que pode ou não possuir Inscrição Estadual no Cadastro de Contribuintes do ICMS + ReductionTaxResource: + type: "object" + description: "Grupo de informações da redução da alíquota" + properties: + rateReduction: + type: "number" + nullable: true + description: "Percentual da redução de alíquota" + format: "double" + effectiveRate: + type: "number" # Alíquota Efetiva que será aplicada a Base de Cálculo + nullable: true + description: "Alíquota Efetiva que será aplicada a Base de Cálculo (em percentual)" + format: "double" + additionalProperties: false + ReferencedProcessResource: + type: "object" + properties: + identifierConcessory: + type: "string" # Identificador do ato concessório + nullable: true + description: "Identificador do ato concessório (identificadorAtoConcessorio)." + identifierOrigin: + type: "integer" + nullable: true + format: "int32" + description: "Identificador de origem do processo (identificadorOrigemProcesso)." + concessionActType: + type: "integer" + nullable: true # Tipo do ato concessório + format: "int32" + description: "Tipo do ato concessório (tipoAtoConcessorio)." + additionalProperties: false + ReferencedDFeResource: + type: "object" + nullable: true + description: "Documento Fiscal Eletrônico Referenciado (DFeReferenciado). Grupo para referenciamento de itens de outro DF-e." + properties: + accessKey: + type: "string" + description: "Chave de acesso do DF-e referenciado (chaveAcesso)." + maxLength: 44 + minLength: 44 + pattern: "^[0-9]{44}$" + itemNumber: # Número do item do documento referenciado + type: "integer" + nullable: true + description: "Número do item do documento referenciado (nItem). Corresponde ao atributo “nItem” do elemento “det” do documentooriginal." + maximum: 999 + minimum: 1 + format: "int32" + required: + - "accessKey" + additionalProperties: false + ISTotalsResource: + type: "object" + nullable: true + description: "Grupo de totais do Imposto Seletivo. (ISTot)" + properties: + amount: + type: "number" # Total do imposto seletivo + nullable: true + description: "Total do imposto seletivo. (vIS)" + format: "double" + additionalProperties: false + IBSCBSTotalsResource: + type: "object" + nullable: true + description: "Totais da NF-e com IBS e CBS. (IBSCBSTot)" + properties: + basis: + type: "number" + nullable: true # Valor total da Base de Cálculo do IBS e da CBS + description: "Valor total da Base de Cálculo do IBS e da CBS. (vBCIBSCBS)" + format: "double" + ibs: + description: "Grupo total do IBS (gIBS)" + $ref: "#/components/schemas/IBSTotalsResource" + cbs: + description: "Grupo total da CBS (gCBS)" + $ref: "#/components/schemas/CBSTotalsResource" + monophase: + description: "Grupo de totais da tributação monofásica." # Grupo de totais da tributação monofásica + $ref: "#/components/schemas/MonophaseTotalsResource" + creditReversal: + description: "Grupo total do Estorno de Crédito (gEstornoCred)" + $ref: "#/components/schemas/CreditReversalTotalsResource" + additionalProperties: false + IBSTotalsResource: + type: "object" + nullable: true + description: "Grupo total do IBS (gIBS)" + properties: + state: + description: "Informações do IBS da UF." + $ref: "#/components/schemas/IBSStateTotalsResource" + municipal: + description: "Informações do IBS do Município." # Informações do IBS do Município + $ref: "#/components/schemas/IBSMunicipalTotalsResource" + totalAmount: + type: "number" + nullable: true + description: "Valor total do IBS (vIBS)" + format: "double" + presumedCreditAmount: + type: "number" + nullable: true + description: "Valor total do crédito presumido (vCredPres)" + format: "double" + presumedCreditConditionalAmount: + type: "number" + nullable: true + description: "Valor total do crédito presumido em condição suspensiva. (vCredPresCondSus)" + format: "double" + additionalProperties: false + IBSStateTotalsResource: + type: "object" + nullable: true + description: "Informações do IBS da UF (gIBSUF)" + properties: + defermentAmount: + type: "number" + nullable: true # Valor total do diferimento + description: "Valor total do diferimento (vDif)" + format: "double" + returnedAmount: + type: "number" + nullable: true + description: "Valor total de devolução de tributos (vDevTrib)" + format: "double" + amount: + type: "number" + nullable: true # Valor total do IBS da UF + description: "Valor total do IBS da UF. (vIBSUF)" + format: "double" + additionalProperties: false + IBSMunicipalTotalsResource: + type: "object" + nullable: true + description: "Informações do IBS do Município (gIBSMun)" + properties: + defermentAmount: + type: "number" + nullable: true # Valor total do diferimento + description: "Valor total do diferimento (vDif)" + format: "double" + returnedAmount: + type: "number" + nullable: true + description: "Valor total de devolução de tributos (vDevTrib)" + format: "double" + amount: + type: "number" + nullable: true # Valor total do IBS do Município + description: "Valor total do IBS do Município. (vIBSMun)" + format: "double" + additionalProperties: false + CBSTotalsResource: + type: "object" + nullable: true + description: "Grupo total da CBS (gCBS)" + properties: + defermentAmount: + type: "number" + nullable: true # Valor total do diferimento + description: "Valor total do diferimento (vDif)" + format: "double" + returnedAmount: + type: "number" + nullable: true + description: "Valor total de devolução de tributos (vDevTrib)" + format: "double" + amount: + type: "number" + nullable: true # Valor total da CBS + description: "Valor total da CBS (vCBS)" + format: "double" + presumedCreditAmount: + type: "number" + nullable: true # Valor total do crédito presumido + description: "Valor total do crédito presumido (vCredPres)" + format: "double" + presumedCreditConditionalAmount: + type: "number" + nullable: true + description: "Valor total do crédito presumido em condição suspensiva. (vCredPresCondSus)" + format: "double" + additionalProperties: false + MonophaseTotalsResource: + type: "object" + nullable: true + description: "Grupo de totais da tributação monofásica." + properties: + ibs: + description: "Totais do IBS monofásico" + $ref: "#/components/schemas/MonophaseIBSTotalsResource" # Totais do IBS monofásico + cbs: + description: "Totais da CBS monofásica" + $ref: "#/components/schemas/MonophaseCBSTotalsResource" + additionalProperties: false + RegularTaxationResource: + type: "object" + description: "Tributação regular hipotética caso condição resolutória/suspensiva não se aplique. Informar como a tributação seria aplicada se a condição resolutória/suspensiva não for atendida." + properties: + situationCode: + type: "string" + nullable: true # CST regular + description: "CST regular (CSTReg), 3 dígitos. Use tabela CST do IBS/CBS" + classCode: + type: "string" + nullable: true + description: "Classificação tributária regular (cClassTribReg), 6 dígitos. Use tabela cClassTrib" + stateEffectiveRate: + type: "number" + nullable: true # Alíquota efetiva IBS UF + description: "Alíquota efetiva IBS UF (pAliqEfetRegIBSUF)." + format: "double" + amount: + type: "number" + nullable: true + description: "IBS UF regular (vTribRegIBSUF)." + format: "double" + municipalEffectiveRate: + type: "number" + nullable: true # Alíquota efetiva IBS Município + description: "Alíquota efetiva IBS Município (pAliqEfetRegIBSMun)." + format: "double" + municipalAmount: + type: "number" + nullable: true + description: "IBS Município regular (vTribRegIBSMun)." + format: "double" + cbsEffectiveRate: + type: "number" + nullable: true # Alíquota efetiva CBS + description: "Alíquota efetiva CBS (pAliqEfetRegCBS)." + format: "double" + cbsAmount: + type: "number" + nullable: true + description: "CBS regular (vTribRegCBS)." + format: "double" + additionalProperties: false + MonophaseIBSTotalsResource: + type: "object" + nullable: true + description: "Totais do IBS monofásico" + properties: + amount: + type: "number" + nullable: true # Total do IBS monofásico + description: "Total do IBS monofásico. (vIBSMono)" + format: "double" + withheldAmount: + type: "number" + nullable: true + description: "Total do IBS monofásico sujeito a retenção. (vIBSMonoReten)" + format: "double" + previouslyWithheldAmount: + type: "number" + nullable: true # Total do IBS monofásico retido anteriormente + description: "Total do IBS monofásico retido anteriormente. (vIBSMonoRet)" + format: "double" + additionalProperties: false + MonophaseCBSTotalsResource: + type: "object" + nullable: true + description: "Totais da CBS monofásica" + properties: + amount: + type: "number" + nullable: true # Total da CBS monofásica + description: "Total da CBS monofásica. (vCBSMono)" + format: "double" + withheldAmount: + type: "number" + nullable: true + description: "Total da CBS monofásica sujeita a retenção. (vCBSMonoReten)" + format: "double" + previouslyWithheldAmount: + type: "number" + nullable: true # Total da CBS monofásica retida anteriormente + description: "Total da CBS monofásica retida anteriormente. (vCBSMonoRet)" + format: "double" + additionalProperties: false + RequestCancellationResource: + type: "object" + properties: + accountId: + type: "string" + nullable: true # Identificador da Conta + companyId: + type: "string" + nullable: true + productInvoiceId: + type: "string" + nullable: true + reason: + type: "string" + nullable: true # Motivo do cancelamento + additionalProperties: false + ReturnedTaxResource: + type: "object" + description: "Grupo de Informações da Devolução de Tributos" + properties: + amount: + type: "number" + nullable: true # Valor do tributo devolvido + description: "Valor do tributo devolvido (vDevTrib)" + format: "double" + additionalProperties: false + ShippingModality: + type: "string" + enum: + - "ByIssuer" + - "ByReceiver" + - "ByThirdParties" + - "OwnBySender" + - "OwnByBuyer" + - "Free" # Transporte grátis + default: "Free" + description: | + Modalidade do frete (modFrete). + Valores possíveis: + - `ByIssuer`: Contratação do Frete por conta do Remetente (CIF) + - `ByReceiver`: Contratação do Frete por conta do Destinatário (FOB) + - `ByThirdParties`: Contratação do Frete por conta de Terceiros + - `OwnBySender`: Transporte Próprio por conta do Remetente + - `OwnByBuyer`: Transporte Próprio por conta do Destinatário + - `Free`: Sem Ocorrência de Transporte + SpecialTaxRegime: + type: "string" + enum: + - "Nenhum" + - "MicroempresaMunicipal" + - "Estimativa" + - "SociedadeDeProfissionais" + - "MicroempreendedorIndividual" + - "MicroempresarioEmpresaPequenoPorte" + - "Automatico" + description: | + Regime especial de tributação. + Valores possíveis: + - `Nenhum`: Nenhum + - `MicroempresaMunicipal`: Microempresa Municipal + - `Estimativa`: Estimativa + - `SociedadeDeProfissionais`: Sociedade de Profissionais + - `MicroempreendedorIndividual`: Microempreendedor Individual (MEI) + - `MicroempresarioEmpresaPequenoPorte`: Microempresário e Empresa de Pequeno Porte (ME/EPP) + - `Automatico`: Automático + StateCode: + type: "string" + enum: + - "NA" + - "RO" + - "AC" + - "AM" + - "RR" + - "PA" + - "AP" + - "TO" + - "MA" + - "PI" + - "CE" + - "RN" + - "PB" + - "PE" + - "AL" + - "SE" + - "BA" + - "MG" + - "ES" + - "RJ" + - "SP" + - "PR" + - "SC" + - "RS" + - "MS" + - "MT" + - "GO" + - "DF" + - "EX" # Exterior + StateTaxProcessingAuthorizer: + type: "string" + enum: + - "Normal" + - "EPEC" + description: | + Autorizador de Processamento de Imposto Estadual. + Valores possíveis: + - `Normal`: Autorizador Normal + - `EPEC`: Autorizador de Evento Prévio de Emissão em Contingência + TaxCouponInformationResource: + type: "object" + properties: + modelDocumentFiscal: + type: "string" + nullable: true + description: "Modelo de Documento Fiscal (mod)" + orderECF: + type: "string" + nullable: true # Número de Ordem Sequencial do ECF + description: "Número de Ordem Sequencial do ECF (nECF)" + orderCountOperation: + type: "integer" + nullable: true + description: "Número do Contador de Ordem de Operação (nCOO)" + format: "int32" + additionalProperties: false + TaxDeterminationResource: + type: "object" + description: "Grupo de informações fiscais para o cálculo automático de impostos. Se este grupo for preenchido, o grupo `tax.IBSCBS` será calculado automaticamente. Se não for preenchido, o cálculo automático do grupo `tax.IBSCBS` torna-se opcional." + properties: + operationCode: + type: "integer" + nullable: true # Código interno para determinação de natureza de operação + description: "Código interno para determinação de natureza de operação" + format: "int32" + issuerTaxProfile: + type: "string" + nullable: true + description: "Perfil fiscal do vendedor (origem) - usado para o cálculo automático de impostos" + buyerTaxProfile: + type: "string" + nullable: true + description: "Perfil fiscal do comprador (destino) - usado para o cálculo automático de impostos" + origin: + type: "string" + nullable: true # Origem da mercadoria + description: "Origem da mercadoria" + acquisitionPurpose: + type: "string" + nullable: true + description: "Finalidade de aquisição - usado para o \r\ncálculo automático de impostos" + additionalProperties: false + TaxDocumentsReferenceResource: + type: "object" + properties: + taxCouponInformation: + $ref: "#/components/schemas/TaxCouponInformationResource" + documentInvoiceReference: + $ref: "#/components/schemas/DocumentInvoiceReferenceResource" + documentElectronicInvoice: + $ref: "#/components/schemas/DocumentElectronicInvoiceResource" + additionalProperties: false + TaxRegime: + type: "string" + enum: + - "None" + - "LucroReal" + - "LucroPresumido" + - "SimplesNacional" + - "SimplesNacionalExcessoSublimite" + - "MicroempreendedorIndividual" + - "Isento" + description: | + Código de Regime Tributário (CRT). + Valores possíveis: + - `None`: Não definido + - `LucroReal`: Lucro Real + - `LucroPresumido`: Lucro Presumido + - `SimplesNacional`: Simples Nacional + - `SimplesNacionalExcessoSublimite`: Simples Nacional – excesso de sublimite de receita bruta + - `MicroempreendedorIndividual`: Microempreendedor Individual - MEI + - `Isento`: Isento + TaxpayerCommentsResource: + type: "object" + properties: + field: + type: "string" + nullable: true + description: "Campo (xCampo)" + text: + type: "string" + nullable: true + description: "Texto (xTexto)" + additionalProperties: false + Total: + type: "object" + properties: + icms: + $ref: "#/components/schemas/ICMSTotal" + issqn: # Grupo de Valores Totais referentes ao ISSQN + $ref: "#/components/schemas/ISSQNTotal" + withheldTaxes: + description: "Grupo Retenções de Tributos (retTrib)" + $ref: "#/components/schemas/TotalsWithholdings" + isTotals: + description: "Grupo de totais do Imposto Seletivo. (ISTot)" + $ref: "#/components/schemas/ISTotalsResource" + ibsCbsTotals: + description: "Totais da NF-e com IBS e CBS. (IBSCBSTot)" + $ref: "#/components/schemas/IBSCBSTotalsResource" # Totais da NF-e com IBS e CBS + totalInvoiceAmount: + type: "number" + nullable: true + description: "Valor total da NF-e com IBS / CBS / IS (vNFTot)" + format: "double" + additionalProperties: false + TotalResource: + type: "object" + properties: + icms: + $ref: "#/components/schemas/ICMSTotalResource" + issqn: + $ref: "#/components/schemas/ISSQNTotalResource" + additionalProperties: false + TransportGroupResource: + type: "object" + properties: + accountId: + type: "string" + nullable: true # Identificador da Conta + description: "Identificador da Conta" + id: + type: "string" + nullable: true + description: "Identificação" + name: + type: "string" + nullable: true + description: "Nome ou Razão Social (xNome)" + federalTaxNumber: + type: "integer" + nullable: true # CNPJ ou CPF + description: "CNPJ ou CPF" + format: "int64" + email: + type: "string" + nullable: true + description: "Email" + address: + $ref: "#/components/schemas/AddressResource" + type: + $ref: "#/components/schemas/PersonType" + stateTaxNumber: + type: "string" # Inscrição Estadual do Transportador + nullable: true + description: "Inscrição Estadual do Transportador (IE)" + transportRetention: + type: "string" + nullable: true + description: "Grupo de Retenção do ICMS do transporte" + additionalProperties: false + description: "Grupo Transportador" + TransportInformationResource: + type: "object" + properties: + freightModality: + $ref: "#/components/schemas/ShippingModality" + transportGroup: + $ref: "#/components/schemas/TransportGroupResource" + reboque: + $ref: "#/components/schemas/ReboqueResource" + volume: + $ref: "#/components/schemas/VolumeResource" # Volumes + transportVehicle: + $ref: "#/components/schemas/TransportVehicleResource" + sealNumber: + type: "string" + nullable: true + description: "Número dos Lacres" + transpRate: + $ref: "#/components/schemas/TransportRateResource" + additionalProperties: false + description: "Grupo de Informações do Transporte da NF-e\r\nId: X01 Pai: A1" + TransportRateResource: + type: "object" + properties: + serviceAmount: + type: "number" + nullable: true # Valor do Serviço + description: "Valor do Serviço (vServ)" + format: "double" + bcRetentionAmount: + type: "number" + nullable: true + description: "BC da Retenção do ICMS (vBCRet)" + format: "double" + icmsRetentionRate: + type: "number" + nullable: true # Alíquota da Retenção + description: "Alíquota da Retenção (pICMSRet) //Change to Rate" + format: "double" + icmsRetentionAmount: + type: "number" + nullable: true + description: "Valor do ICMS Retido (vICMSRet)" + format: "double" + cfop: + type: "integer" + nullable: true # CFOP de Serviço de Transporte + description: "CFOP de Serviço de Transporte (CFOP)" + format: "int64" + cityGeneratorFactCode: + type: "integer" + nullable: true + description: "Código do Municipio de ocorrencia do fato gerador do ICMS do Transporte (cMunFG)" + format: "int64" + additionalProperties: false + TransportVehicleResource: + type: "object" + properties: + plate: + type: "string" + nullable: true # Placa do Veiculo + description: "Placa do Veiculo (placa)" + state: + type: "string" + nullable: true + description: "Sigla da UF (UF)" + rntc: + type: "string" + nullable: true + description: "Registro Nacional de Transportador de Carga (ANTT) (RNTC)" + additionalProperties: false + description: "Grupo Veiculo" + UsedMovableAssetIndicator: # Indica fornecimento de bem móvel usado + type: "boolean" + nullable: false + description: "Indica fornecimento de bem móvel usado (indBemMovelUsado). true = bem usado; false = bem novo." + VolumeResource: + type: "object" + properties: + volumeQuantity: + type: "integer" + nullable: true # Quantidade de volumes transportados + description: "Quantidade de volumes transportados (qVol)" + format: "int32" + species: + type: "string" + nullable: true + description: "Espécie dos volumes transportados (esp)" + brand: + type: "string" + nullable: true + description: "Marca dos Volumes Transportados (marca)" + volumeNumeration: + type: "string" + nullable: true # Numeração dos Volumes Transportados + description: "Numeração dos Volumes Transportados (nVol)" + netWeight: + type: "number" + nullable: true + description: "Peso Liquido(em Kg) (pesoL)" + format: "double" + grossWeight: + type: "number" + nullable: true + description: "Peso Bruto(em Kg) (pesoB)" + format: "double" + additionalProperties: false + description: "Volumes\r\nId:X26" + WithdrawalInformationResource: + type: "object" + properties: + accountId: + type: "string" + nullable: true # Identificador da Conta + description: "Identificador da Conta" + id: + type: "string" + nullable: true + description: "Identificação" + name: + type: "string" + nullable: true # Nome ou Razão Social + description: "Nome ou Razão Social (xNome)" + federalTaxNumber: + type: "integer" + nullable: true # CNPJ ou CPF + description: "CNPJ ou CPF" + format: "int64" + email: + type: "string" + nullable: true + description: "Email" + address: + $ref: "#/components/schemas/AddressResource" + type: + $ref: "#/components/schemas/PersonType" + stateTaxNumber: + type: "string" # Inscrição Estadual + nullable: true + description: "Inscrição Estadual (IE)" + additionalProperties: false + description: "Identificação do Local de retirada (retirada)" + TotalsWithholdings: + type: "object" + nullable: true + description: "Grupo Retenções de Tributos (retTrib)" + properties: + pisAmount: + type: "number" + nullable: true # Valor Retido de PIS + description: "Valor Retido de PIS (vRetPIS)" + format: "double" + maximum: 9999999999999.99 + minimum: 0 + cofinsAmount: + type: "number" + nullable: true + description: "Valor Retido de COFINS (vRetCOFINS)" + format: "double" + maximum: 9999999999999.99 + minimum: 0 + csllAmount: # Valor Retido de CSLL + type: "number" + nullable: true + description: "Valor Retido de CSLL (vRetCSLL)" + format: "double" + maximum: 9999999999999.99 + minimum: 0 + irrfBasis: + type: "number" + nullable: true + description: "Base de Cálculo do IRRF (vBCIRRF)" + format: "double" + maximum: 9999999999999.99 + minimum: 0 + irrfAmount: # Valor Retido do IRRF + type: "number" + nullable: true + description: "Valor Retido do IRRF (vIRRF)" + format: "double" + maximum: 9999999999999.99 + minimum: 0 + socialSecutiryBasis: + type: "number" + nullable: true + description: "Base de Cálculo da Retenção da Previdência Social (vBCRetPrev)" + format: "double" + maximum: 9999999999999.99 + minimum: 0 + socialSecutiryAmount: # Valor da Retenção da Previdência Social + type: "number" + nullable: true + description: "Valor da Retenção da Previdência Social (vRetPrev)" + format: "double" + maximum: 9999999999999.99 + minimum: 0 + additionalProperties: false + ZfmPresumedCreditResource: + type: "object" + description: "Informações sobre o crédito presumido de IBS para fornecimentos da ZFM (gCredPresIBSZFM)." + properties: + classificationCode: + description: "Tipo de crédito presumido (tpCredPresIBSZFM)." + $ref: "#/components/schemas/IbsZfmPresumedCreditClassification" # Tipo de crédito presumido + amount: + type: "number" + nullable: true + description: "Valor do crédito presumido ZFM (vCredPresIBSZFM). Obrigatório para notas de crédito com tpNFCredito = 02." + format: "double" + additionalProperties: false diff --git a/openapi/spec/service-invoice-rtc-v1.yaml b/openapi/spec/service-invoice-rtc-v1.yaml new file mode 100644 index 0000000..0c25fd5 --- /dev/null +++ b/openapi/spec/service-invoice-rtc-v1.yaml @@ -0,0 +1,1727 @@ +openapi: 3.0.1 +info: + title: API de Emissão de Nota Fiscal de Serviços Eletrônica (NFS-e) - RTC + version: v3 + description: | + ### Introdução + API para envio de dados de RPS/DPS para emissão de NFS-e, alinhada com o padrão nacional e complementada com campos específicos do Web Service da Prefeitura de São Paulo (NF-e/NFS-e). + + ### Adequação de Leiaute + + Nesta adequação de leiaute, foram considerados os leiautes do **Ambiente Nacional**, **São Paulo e ABRASF**. + + > **Atenção** + > _Sujeito a alterações mediante notas técnicas e processos de homologação._ +servers: + - url: https://api.nfe.io + description: Para realizar testes no ambiente de homologação da prefeitura, é necessário entrar em contato com o suporte para que a empresa seja parametrizada com os dados necessários. +tags: + - name: Nota Fiscal de Serviço (RTC) + description: Operações relacionadas à Nota Fiscal Eletrônica de Serviço + +paths: + /v1/companies/:company_id/serviceinvoices: + post: + tags: + - Nota Fiscal de Serviço (RTC) + summary: Envio de dados para emissão de NFS-e (RPS/DPS) + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/NFSeRequest' + examples: + MinimumExample: + summary: Exemplo Mínimo (Apenas campos obrigatórios + IBSCBS obrigatórios) + value: + borrower: + name: CONSUMIDOR MINIMO LTDA + federalTaxNumber: 191 + cityServiceCode: '4444' + federalServiceCode: '01.01' + description: "Serviço de Consultoria e Assessoria (Exemplo Mínimo V4)." + servicesAmount: 1000.00 + nbsCode: '101010100' + ibsCbs: + operationIndicator: '1005011' + classCode: '000001' + IntermediateExample: + summary: Exemplo Intermediário (Raiz do IBSCBS + subgrupos ibs e cbs) + value: + borrower: + name: CONSUMIDOR INTERMEDIARIO S.A. + federalTaxNumber: 201 + address: + country: BRA + postalCode: "01001-001" + street: AVENIDA PRINCIPAL + number: '500' + district: BROOKLIN + city: + code: '3550308' + state: SP + externalId: NFSE-INT-V4-0002 + cityServiceCode: '4444' + federalServiceCode: '01.02' + description: "Assessoria tributária para reforma de 2026 (Exemplo Intermediário V4)." + servicesAmount: 5000.00 + issuedOn: '2026-01-20T11:30:00-03:00' + taxationType: OutsideCity + location: + country: BRA + postalCode: "13000-000" + street: RUA FORA DA CIDADE + number: '120' + district: INTERIOR + city: + code: '3509502' + state: SP + nbsCode: '102020200' + recipient: + name: RECIPIENTE INTERMEDIARIO LTDA. + federalTaxNumber: 301 + address: + country: BRA + postalCode: "01000-000" + city: + code: '3550308' + state: SP + ibsCbs: + purpose: regular # Este campo tem um default 'regular', então não é estritamente necessário no envio. + isDonation: false + personalUse: false + destinationIndicator: SameAsBuyer + operationIndicator: '1005011' + situationCode: '000' # Este campo é opcional e pode ser derivado do classCode. + classCode: '000001' + basis: 5000.00 + reimbursedResuppliedAmount: 0.00 + ibs: + totalAmount: 100.00 + state: + rate: 0.04 + effectiveRate: 0.03 + amount: 50.00 + municipal: + rate: 0.04 + effectiveRate: 0.03 + amount: 50.00 + cbs: + rate: 0.08 + effectiveRate: 0.07 + amount: 350.00 + CompleteExample: + summary: Exemplo Completo e Exaustivo (100% dos campos) + value: + borrower: + type: LegalEntity # O valor já estava correto. + name: TOMADOR COMPLETO GLOBAL LTDA (EXAUSTIVO) + federalTaxNumber: 12345678000199 + municipalTaxNumber: '999999999999' + stateTaxNumber: '111222333444' + caepf: '12345678901234' + taxRegime: LucroReal + phoneNumber: '5511988887777' + email: tomador.exausto@exemplo.com + noTaxIdReason: NotRequired + address: + country: USA + postalCode: "10001-000" + street: 1st AVENUE + number: '10' + additionalInformation: SUITE 50 FLOOR 10 + district: MANHATTAN + city: + name: NEW YORK + code: '9999999' + state: NY + externalId: NFSE-EXAUSTIVO-V4-0004 + cityServiceCode: '4444' + federalServiceCode: '17.01' + cnaeCode: '6201501' + nbsCode: '117010000' + ncmCode: '85044021' + description: 'Desenvolvimento e licenciamento de software customizado, conforme contrato. Inclui valores de multa, juros, deduções, benefícios, suspensão, etc. (Exemplo Exaustivo V4)' + servicesAmount: 25000.00 + paidAmount: 26000.00 + rpsSerialNumber: A0002 + issuedOn: '2026-01-25T09:15:30-03:00' + accrualOn: '2026-01-01' + rpsNumber: 87654321 + taxationType: Immune + issRate: 0.00 + issTaxAmount: 0.00 + deductionsAmount: 1500.00 + discountUnconditionedAmount: 200.00 + discountConditionedAmount: 100.00 + irAmountWithheld: 250.00 + pisRate: 0.0065 + pisCofinsBaseTax: 25000.00 + pisAmountWithheld: 162.50 + cofinsRate: 0.04 + cofinsAmountWithheld: 1000.00 + csllAmountWithheld: 250.00 + inssAmountWithheld: 2750.00 + issAmountWithheld: 0.00 + ipiRate: 0.05 + ipiAmount: 1250.00 + othersAmountWithheld: 50.00 + additionalInformation: 'Informações adicionais exaustivas para o fisco, incluindo detalhes do contrato XYZ.' + isEarlyInstallmentPayment: true + location: + country: BRA + postalCode: "90000-000" + street: RUA DA PRESTACAO EXAUSTIVA + number: '999' + additionalInformation: 'Bloco Z, Apto 101' + district: FLORESTA NOVA + city: + code: '4314902' + name: PORTO ALEGRE + state: RS + activityEvent: + name: EVENTO CULTURAL COMPLETO + beginOn: '2026-02-10T08:00:00-03:00' + endOn: '2026-02-12T22:00:00-03:00' + Code: EVT-COMPLEX + address: + country: BRA + postalCode: "90000-010" + street: ARENA DO EVENTO + number: SN + district: ARENA DISTRICT + city: + code: '4314902' + state: RS + approximateTax: + source: IBPT + version: '24.1.A' + totalRate: 0.15 + totalAmount: 3750.00 + referenceSubstitution: + id: '99998888777766665555444433332222111100009999' + reason: RejectionBuyerOrIntermediary + lease: + category: RightOfWay + objectType: cables + totalLength: 2500.75 + construction: + propertyFiscalRegistration: '9876543210-FISCAL-OBRA' + workId: + scheme: bra.cei + value: CEI-OBRA-EXAUSTIVA + cibCode: 'CIB-9876543210' + siteAddress: + country: BRA + postalCode: "01000-001" + street: RUA DA OBRA COMPLETA + number: '1000' + district: CENTRO NOVO + city: + code: '3550308' + state: SP + realEstate: + propertyFiscalRegistration: 'SQL-IMOVEL-98765' + cibCode: 'CIB-IMOVEL-54321' + siteAddress: + country: BRA + postalCode: "04571-010" + street: RUA DO IMOVEL EXEMPLO + number: '200' + district: BROOKLIN + city: + code: '3550308' + state: SP + foreignTrade: + serviceMode: ConsumptionAbroad + relationShip: affiliate + currency: 220 + serviceAmountInCurrency: 20000.00 + supportMechanismProvider: ProexFinancing + supportMechanismReceiver: Zpe + temporaryGoods: no + importDeclaration: DI-123456-2026 + exportRegistration: 'RE-EXP-COMPLETO-123' + mdicDelivery: true + additionalInformationGroup: + responsibilityDocumentIdentifier: DOC-RESP-001 + referencedDocument: REF-DOC-002 + order: 'PEDIDO-COMPRA-456' + items: + - item: 'Item A - Detalhe 1' + - item: 'Item B - Detalhe 2' + otherInformation: Outras informações complementares. + intermediary: + type: LegalEntity + name: INTERMEDIARIO COMPLETO S/A + federalTaxNumber: 87654321000100 + municipalTaxNumber: '111111111111' + stateTaxNumber: '999888777666' + taxRegime: SimplesNacional + phoneNumber: '5521977776666' + email: interm.completo@teste.com + address: + country: BRA + postalCode: "20000-000" + street: AV INTERMEDIACAO COMPLETA + number: '123' + district: CENTRO RIO + city: + code: '3304557' + state: RJ + recipient: + name: DESTINATARIO DETALHADO COMPLETO LTDA + federalTaxNumber: 100100100100 + email: dest@detalhe.completo.com + address: + country: BRA + postalCode: "01000-000" + street: RUA DESTINO FINAL + number: '10' + district: CENTRO + city: + code: '3550308' + state: SP + deduction: + documents: + - nfseKey: '11111222223333344444555556666677777888889999900000' + deductionType: Subcontracting + issueDate: '2026-01-10' + deductibleTotal: 1000.00 + usedAmount: 1000.00 + supplier: + type: LegalEntity + name: SUBEMPREITEIRO EXEMPLO LTDA + federalTaxNumber: 10101010000110 + address: + country: BRA + postalCode: "01234-000" + street: RUA DO SUBEMPREITEIRO + number: '10' + district: VILA INDUSTRIAL + city: + code: '3550308' + state: SP + benefit: + id: '35503080100002' + amount: 300.00 + suspension: + reason: Administrative + processNumber: ADM-SUSP-2026-XYZ + immunityType: BooksPressPaper + retentionType: WithheldByIntermediary + approximateTotals: + federal: + rate: 0.12 + amount: 3000.00 + state: + rate: 0.03 + amount: 750.00 + municipal: + rate: 0.00 + amount: 0.00 + rate: 0.15 + amount: 3750.00 + ibsCbs: + purpose: regular + isDonation: false + personalUse: true + leasedMovableAssets: + - ncmCode: '84713012' + description: 'Notebook para locação' + quantity: 5 + - ncmCode: '84716052' + description: 'Monitor para locação' + quantity: 10 + destinationIndicator: DifferentFromBuyer + relatedDocs: + items: + - 'NFSE11112222333344445555666677778888999900001111' + - 'NFSE11112222333344445555666677778888999900002222' + operationIndicator: '4' + operationType: SupplyForPastPay + situationCode: '200' # Este campo é opcional e pode ser derivado do classCode. + classCode: '200045' + basis: 25000.00 + reimbursedResuppliedAmount: 150.00 + ibscbsDeductionReductionAmount: 250.00 + realEstate: + propertyFiscalRegistration: 'SQL-IMOVEL-98765' + cibCode: 'CIB-IMOVEL-54321' + siteAddress: + country: BRA + postalCode: "04571-010" + street: RUA DO IMOVEL EXEMPLO + number: '200' + district: BROOKLIN + city: + code: '3550308' + state: SP + ibs: + totalAmount: 500.00 + state: + rate: 0.05 + rateReduction: 0.1 + effectiveRate: 0.045 + deferment: + rate: 0.5 + amount: 125.00 + returnedAmount: 25.00 + amount: 250.00 + municipal: + rate: 0.05 + rateReduction: 0 + effectiveRate: 0.05 + deferment: + rate: 0 + amount: 0 + returnedAmount: 0 + amount: 250.00 + cbs: + rate: 0.08 + rateReduction: 0.05 + effectiveRate: 0.076 + deferment: + rate: 0.2 + amount: 400.00 + returnedAmount: 50.00 + amount: 2000.00 + regularTaxation: + situationCode: '550' + classCode: '550016' + ibs: + totalAmount: 550.00 + state: + effectiveRate: 0.05 + amount: 275.00 + municipal: + effectiveRate: 0.05 + amount: 275.00 + cbs: + effectiveRate: 0.085 + amount: 2125.00 + presumedCredits: + ibs: + code: '01' + rate: 0.01 + amount: 250.00 + conditionalAmount: 50.00 + cbs: + code: '02' + rate: 0.005 + amount: 125.00 + conditionalAmount: 25.00 + governmentPurchase: + entityType: state + ibs: + totalAmount: 450.00 + state: + rate: 0.04 + amount: 225.00 + municipal: + rate: 0.04 + amount: 225.00 + cbs: + rate: 0.07 + amount: 1750.00 + creditTransfer: + ibsAmount: 100.00 + cbsAmount: 50.00 + thirdPartyReimbursements: + documents: + - otherNationalDfe: + dfeKey: DFE-NACIONAL-CHAVE-EXEMPLO-12345678901234567890 + dfeTypeText: Outro Documento Fiscal Eletronico Nacional + dfeType: '9' + supplier: + name: FORNECEDOR DFE NACIONAL LTDA + federalTaxNumber: 30303030000130 + issueDate: '2026-01-05' + accrualOn: '2026-01-05' + reimbursementType: RealEstateBrokerPassThrough + amount: 150.00 + serviceAmountDetails: + initialChargedAmount: 25000.00 + finalChargedAmount: 26000.00 + fineAmount: 250.00 + interestAmount: 750.00 + responses: + '200': + description: Processamento do RPS/DPS iniciado com sucesso + '400': + description: Dados inválidos na requisição + /v1/companies/:company_id/serviceinvoices/:id/cancellation-xml: + get: + tags: + - Nota Fiscal de Serviço (RTC) + summary: Download do XML do evento de cancelamento da NFS-e (Ambiente Nacional) + description: | + Retorna o XML do **evento de cancelamento** (`e110001`) de uma NFS-e emitida no **Ambiente Nacional (ADN)**. + + > **Atenção** + > Disponível **apenas** para notas do Ambiente Nacional. Provedores de layout municipal/ABRASF não possuem evento de cancelamento próprio e retornam `404`. + + Quando existem o XML enviado (request do `e110001`) e o XML do evento autorizado (retorno do ADN — `procEventoNFSe`), o **retorno autorizado é priorizado**. O XML só está disponível para notas com status `Cancelled`. + parameters: + - name: company_id + in: path + description: ID da empresa + required: true + schema: + type: string + - name: id + in: path + description: ID da Nota Fiscal de Serviço (NFS-e) + required: true + schema: + type: string + responses: + '200': + description: XML do evento de cancelamento retornado com sucesso + content: + application/xml: + schema: + type: string + '404': + description: Não há XML de evento de cancelamento para esta nota (provedor legado, ambiente não Nacional ou nota ainda não cancelada) + +components: + schemas: + NFSeRequest: + type: object + title: Nota Fiscal de Serviço (NFSe) Schema + required: + - cityServiceCode + - description + - servicesAmount + - nbsCode + properties: + externalId: + type: string + description: Identificação única do cliente + cityServiceCode: + type: string + description: Código do serviço no municipio + federalServiceCode: + type: string + description: Código federal do servico (Item da lista de serviço LC 116) (CodigoServico, tpCodigoServico) + maxLength: 5 + description: + type: string + description: Descrição dos serviços (Discriminacao, tpDiscriminacao) + maxLength: 2000 + servicesAmount: + type: number + description: Valor do serviços (ValorServicos - V1.0 SP) - Valor total dos serviços (tpValor) + nbsCode: + type: string + description: Código do NBS no municipio (somente quando necessario na cidade) (NBS, tpCodigoNBS) + maxLength: 9 + cnaeCode: + type: string + description: Código CNAE (somente quando necessario na cidade) + ncmCode: + type: string + description: Código NCM (Nomenclatura Comum do Mercosul) (NCM, tpCodigoNCM) + maxLength: 8 + paidAmount: + type: number + description: Valor dos Serviços pago (Valor Total Recebido, tpValor) + issuedOn: + type: string + format: date-time + description: | + Data da emissão no formato YYYY-MM-DDTHH:MM:SS.SSSSSS-03:00 (dhEmi, DataEmissaoNFe). + **Automático**: Se não for informado, o sistema utilizará a data e hora atuais no momento do processamento. + accrualOn: + type: string + format: date + description: | + Data da competência da prestação do serviço no formato AAAA-MM-DD (Competencia). + **Automático**: Se não for informado, o sistema utilizará a data do campo `issuedOn`. + rpsSerialNumber: + type: string + description: | + Número de Série da RPS (SerieRPS, serie, tpSerieRPS). + **Prioridade**: Se informado, o valor enviado será utilizado. + **Automático**: Se não for informado, o sistema utilizará o valor definido no cadastro da empresa. + maxLength: 5 + rpsNumber: + type: number + description: | + Número da RPS (NumeroRPS, nDPS, tpNumero). + **Prioridade**: Se informado, o valor enviado será utilizado. + **Automático**: Se não for informado, o sistema utilizará o próximo número da sequência automática. + taxationType: + type: string + description: | + **Tipo de Tributação (taxationType)** + + O campo `taxationType` define o regime de tributação do **ISSQN (Imposto Sobre Serviços de Qualquer Natureza)**. Ele é um campo legado, mantido para compatibilidade com os layouts municipais existentes, como o da Prefeitura de São Paulo, e continua sendo fundamental para o cálculo do ISS durante o período de transição da Reforma Tributária. + + **Finalidade Principal (Contexto do ISSQN):** + + Este campo indica ao sistema como o ISSQN deve ser tratado na operação. Com base no valor selecionado (ex: `WithinCity`, `OutsideCity`, `Immune`), o sistema determina se o ISSQN é devido, se a tributação ocorrerá dentro ou fora do município, ou se a operação é isenta ou imune a este imposto específico. + + **Relação com a Reforma Tributária (IBS/CBS):** + + É crucial entender que o `taxationType` **não se aplica aos novos tributos (IBS e CBS)**. Para a apuração do IBS e da CBS, as regras de tributação e o local de incidência são definidos exclusivamente pelos campos dentro do grupo `IbsCbs`, como o `operationIndicator` e o `classCode`. + + **Em resumo:** + * **`taxationType`**: Governa o comportamento do **ISSQN**. + * **`ibsCbs`**: Governa o comportamento do **IBS e da CBS**. + + Durante o período de transição, ambos os sistemas de tributação coexistirão, tornando o preenchimento correto de todos esses campos essencial para a conformidade fiscal da nota. + + --- + + Tipo da tributação (TributacaoRPS, tpTributacaoNFe). + Valores possíveis: + - `None`: Nenhuma + - `WithinCity`: Tributação no município + - `OutsideCity`: Tributação fora do município + - `Export`: Exportação + - `Free`: Isenta + - `Immune`: Imune + - `SuspendedCourtDecision`: Exigibilidade suspensa por decisão judicial + - `SuspendedAdministrativeProcedure`: Exigibilidade suspensa por procedimento administrativo + - `OutsideCityFree`: Isenta e fora do município + - `OutsideCityImmune`: Imune e fora do município + - `OutsideCitySuspended`: Suspensa e fora do município + - `OutsideCitySuspendedAdministrativeProcedure`: Suspensa por processo administrativo e fora do município + - `ObjectiveImune`: Imunidade Objetiva + enum: [None, WithinCity, OutsideCity, Export, Free, Immune, SuspendedCourtDecision, SuspendedAdministrativeProcedure, OutsideCityFree, OutsideCityImmune, OutsideCitySuspended, OutsideCitySuspendedAdministrativeProcedure, ObjectiveImune] + default: WithinCity + issRate: + type: number + description: | + Alíquota do ISS (pAliq, tpAliquota). + **Automático**: Se não for informado, o sistema utilizará o valor definido no cadastro do código de serviço. + issTaxAmount: + type: number + description: | + Valor do ISS (ValorISS, tpValor). + **Automático**: Se não for informado, o sistema realizará o cálculo automático. + deductionsAmount: + type: number + description: | + **Deduções: Simples vs. Estruturado (`deductionsAmount` vs. `deduction`)** + + O layout oferece duas formas de registrar deduções da base de cálculo do ISSQN, visando flexibilidade e conformidade com os diferentes padrões (municipal legado vs. nacional). + + * **`deductionsAmount` (Valor Simples):** + Um campo numérico para informar o **valor total consolidado** das deduções. É uma abordagem direta, compatível com layouts mais antigos que não exigem o detalhamento dos documentos que comprovam a dedução. + + * **`deduction` (Grupo Estruturado):** + Um objeto complexo que permite justificar cada dedução com base em documentos fiscais, alinhando-se ao **padrão da NFS-e Nacional**. Ele detalha a origem de cada valor (chave do documento, tipo, valor, fornecedor). + + **Relação e Recomendação:** + A coexistência dos campos oferece compatibilidade. `deductionsAmount` é uma opção simplificada, enquanto o grupo `deduction` é a forma mais completa e recomendada para garantir rastreabilidade e conformidade fiscal com o padrão nacional. O sistema de emissão priorizará os dados detalhados do grupo `deduction`, se fornecidos. + + **Recomendação de Uso:** + * Use `deductionsAmount` para integrações simples ou quando não há detalhamento dos documentos. + * Use o grupo `deduction` sempre que a informação detalhada dos documentos estiver disponível para garantir a máxima conformidade. + + --- + + Valor de deduções (vDR, tpValor). + discountUnconditionedAmount: + type: number + description: Valor do desconto incondicionado (vDescIncond, tpValor). + discountConditionedAmount: + type: number + description: Valor do desconto condicionado (vDescCond, tpValor). + irAmountWithheld: + type: number + description: | + Valor retido do Imposto de Renda (IR) (vRetIRRF, tpValor). + **Automático**: Se não for informado, o sistema realizará o cálculo automático. + cstPisCofins: + type: string + description: | + Código de Situação Tributária do PIS/COFINS (CST). + Valores: + - `00`: Nenhum + - `01`: Operação Tributável com Alíquota Básica + - `02`: Operação Tributável com Alíquota Diferenciada + - `03`: Operação Tributável com Alíquota por Unidade de Medida de Produto + - `04`: Operação Tributável monofásica - Revenda a Alíquota Zero + - `05`: Operação Tributável por Substituição Tributária + - `06`: Operação Tributável a Alíquota Zero + - `07`: Operação Tributável da Contribuição + - `08`: Operação sem Incidência da Contribuição + - `09`: Operação com Suspensão da Contribuição + enum: ["00", "01", "02", "03", "04", "05", "06", "07", "08", "09"] + pisCofinsBaseTax: + type: number + description: Base de cálculo para o Pis e Cofins (vBCPisCofins, tpValor). + pisRate: + type: number + description: | + Alíquota do PIS (pAliqPis). + **Automático**: Se não for informado, o sistema utilizará o valor definido no cadastro do código de serviço. + pisAmount: + type: number + description: Valor do PIS (vPis, tpValor). Campo utilizado para informar o valor do PIS, porém sem retenção. + pisAmountWithheld: + type: number + description: | + Valor retido do PIS (vPis, tpValor). + **Automático**: Se não for informado, o sistema realizará o cálculo automático. + cofinsRate: + type: number + description: | + Alíquota do COFINS (pAliqCofins). + **Automático**: Se não for informado, o sistema utilizará o valor definido no cadastro do código de serviço. + cofinsAmount: + type: number + description: Valor do COFINS (vCofins, tpValor). Campo utilizado para informar o valor do COFINS, porém sem retenção. + cofinsAmountWithheld: + type: number + description: | + Valor retido do COFINS (vCofins, tpValor). + **Automático**: Se não for informado, o sistema realizará o cálculo automático. + csllAmount: + type: number + description: Valor do CSLL (vCSLL, tpValor). Campo utilizado para informar o valor do CSLL, porém sem retenção. + csllRate: + type: number + description: Alíquota do CSLL (pAliqCSLL). + csllAmountWithheld: + type: number + description: | + Valor retido do CSLL (vRetCSLL, tpValor). + **Automático**: Se não for informado, o sistema realizará o cálculo automático. + inssAmountWithheld: + type: number + description: | + Valor retido do INSS (vRetCP, tpValor). + **Automático**: Se não for informado, o sistema realizará o cálculo automático. + issAmountWithheld: + type: number + description: | + Valor retido do ISS (vISSQN). + **Automático**: Se não for informado, o sistema realizará o cálculo automático quando houver retenção de ISS. + ipiRate: + type: number + description: | + SP - Alíquota IPI (pAliqIPI, tpAliquota). + **Automático**: Se não for informado, o sistema utilizará o valor definido no cadastro do código de serviço. + ipiAmount: + type: number + description: | + SP - Valor IPI (vIPI, tpValor). + **Automático**: Se não for informado, o sistema realizará o cálculo automático. + othersAmountWithheld: + type: number + description: Valor de outras retenções (vOutrasRet, tpValor). + additionalInformation: + type: string + description: | + **Informações Adicionais: Simples vs. Estruturado (`additionalInformation` vs. `additionalInformationGroup`)** + + O layout oferece duas formas de enviar informações complementares, visando flexibilidade e completude. + + * **`additionalInformation` (Texto Simples):** + Um campo de `string` para adicionar qualquer observação em formato de texto livre. É ideal para anotações rápidas e genéricas. + + * **`additionalInformationGroup` (Objeto Estruturado):** + Um objeto que organiza dados complementares em campos específicos, como `responsibilityDocumentIdentifier` (ART/RRT), `registrationWork` (Matrícula de Obra), `order` (Pedido), etc. É a forma mais completa e padronizada de enviar esses dados. + + **Comportamento e Relação:** + Para facilitar a integração, o campo `additionalInformation` funciona como um atalho. Se você preencher apenas este campo, seu conteúdo será automaticamente copiado para `additionalInformationGroup.otherInformation`. Isso garante que todas as informações adicionais fiquem consolidadas na estrutura mais completa, mantendo a consistência interna dos dados. + + **Recomendação:** + * Para observações simples, use `additionalInformation`. + * Para dados específicos como ART, Matrícula de Obra, etc., use os campos dedicados dentro de `additionalInformationGroup`. + additionalInformationGroup: + type: object + description: | + **Informações Adicionais: Simples vs. Estruturado (`additionalInformation` vs. `additionalInformationGroup`)** + + O layout oferece duas formas de enviar informações complementares, visando flexibilidade e completude. + + * **`additionalInformation` (Texto Simples):** + Um campo de `string` para adicionar qualquer observação em formato de texto livre. É ideal para anotações rápidas e genéricas. + + * **`additionalInformationGroup` (Objeto Estruturado):** + Um objeto que organiza dados complementares em campos específicos, como `responsibilityDocumentIdentifier` (ART/RRT), `registrationWork` (Matrícula de Obra), `order` (Pedido), etc. É a forma mais completa e padronizada de enviar esses dados. + + **Comportamento e Relação:** + Para facilitar a integração, o campo `additionalInformation` funciona como um atalho. Se você preencher apenas este campo, seu conteúdo será automaticamente copiado para `additionalInformationGroup.otherInformation`. Isso garante que todas as informações adicionais fiquem consolidadas na estrutura mais completa, mantendo a consistência interna dos dados. + + **Recomendação:** + * Para observações simples, use `additionalInformation`. + * Para dados específicos como ART, Matrícula de Obra, etc., use os campos dedicados dentro de `additionalInformationGroup`. + properties: + responsibilityDocumentIdentifier: + type: string + description: "Identificador do documento de responsabilidade técnica (ART, RRT, etc.) (idDocTec)." + referencedDocument: + type: string + description: "Documento de referência relacionado ao serviço prestado (docRef)." + order: + type: string + description: "Número do pedido/ordem de compra/ordem de serviço (xPed)." + items: + type: array + description: "Grupo de itens do pedido/ordem de compra/ordem de serviço (gItemPed)." + items: + type: object + properties: + item: {type: string, description: "Item do pedido/ordem de compra/ordem de serviço (xItemPed)."} + otherInformation: + type: string + description: "Outras informações complementares (xInfComp)." + isEarlyInstallmentPayment: + type: boolean + description: "Indica se é uma nota fiscal de pagamento parcelado antecipado, realizado antes do fornecimento do serviço. (PagamentoParceladoAntecipado, tpNaoSim)" + immunityType: + type: string + description: | + **Tipo de Imunidade (immunityType)** + + O campo `immunityType` é condicional e deve ser utilizado **exclusivamente quando o campo `taxationType` for definido como `Immune`**. + + Sua finalidade é especificar a base legal que fundamenta a imunidade tributária do **ISSQN (Imposto Sobre Serviços de Qualquer Natureza)** para a operação. Cada valor disponível no campo corresponde a uma alínea específica do Artigo 150, Inciso VI, da Constituição Federal, que trata das limitações ao poder de tributar. + + **Relação com a Reforma Tributária (IBS/CBS):** + + É fundamental destacar que este campo se refere **apenas à imunidade do ISSQN**. As regras de imunidade para os novos tributos (IBS e CBS) são tratadas de forma separada, dentro do grupo `ibsCbs`, através de códigos específicos no campo `classCode` (ex: códigos iniciados com `41xxxx`). + + **Em resumo:** + A correta especificação do `immunityType` é essencial para validar a justificativa legal da não incidência do ISSQN na nota fiscal, garantindo a conformidade perante as autoridades fiscais municipais durante o período de transição. + + --- + + Tipo de imunidade (tpImunidade) — usar apenas quando taxationMode = 'immunity'. + Valores possíveis: + - `unspecified`: Imunidade (tipo não informado na nota de origem) + - `PublicEntitiesMutual`: Patrimônio, renda ou serviços, uns dos outros (CF88, Art 150, VI, a) + - `Temples`: Templos de qualquer culto (CF88, Art 150, VI, b) + - `PartiesUnionsEducationSocialNonprofit`: Patrimônio, renda ou serviços dos partidos políticos, inclusive suas fundações, das entidades sindicais dos trabalhadores, das instituições de educação e de assistência social, sem fins lucrativos (CF88, Art 150, VI, c) + - `BooksPressPaper`: Livros, jornais, periódicos e o papel destinado a sua impressão (CF88, Art 150, VI, d) + - `BrazilianMusicPhonograms`: Fonogramas e videofonogramas musicais produzidos no Brasil (CF88, Art 150, VI, e) + enum: [Unspecified, PublicEntitiesMutual, Temples, PartiesUnionsEducationSocialNonprofit, BooksPressPaper, BrazilianMusicPhonograms] + retentionType: + type: string + description: | + **Tipo de Retenção do ISSQN (retentionType)** + + O campo `retentionType` determina quem é o responsável pelo recolhimento do **ISSQN (Imposto Sobre Serviços de Qualquer Natureza)**. Ele é um dos campos mais importantes para a correta apuração do ISSQN, pois define a transferência da responsabilidade de pagamento do imposto do prestador para outra parte na operação. + + **Finalidade e Opções:** + + * **`notWithheld` (Não Retido):** Esta é a situação padrão. O **prestador** do serviço é o responsável por calcular e recolher o ISSQN devido. + * **`withheldByBuyer` (Retido pelo Tomador):** A responsabilidade pelo recolhimento do ISSQN é transferida para o **tomador** (cliente/comprador) do serviço. O valor do ISSQN é descontado do total a ser pago ao prestador, e o tomador fica encarregado de repassá-lo ao município. + * **`withheldByIntermediary` (Retido pelo Intermediário):** Em operações que contam com um intermediário (como marketplaces ou agências), a responsabilidade pelo recolhimento do ISSQN é transferida para este **intermediário**. + + **Comportamento Automático vs. Manual:** + + Conforme a descrição do campo, ele possui uma lógica de prioridade: + 1. **Manual:** Se um valor for explicitamente enviado na requisição, ele será utilizado. + 2. **Automático:** Se o campo não for enviado (nulo), o sistema aplicará as regras de cálculo automáticas (baseadas na legislação, local da prestação e cadastro dos envolvidos) para definir se a retenção é aplicável e quem é o responsável. + + **Relação com a Reforma Tributária (IBS/CBS):** + + Assim como outros campos legados, o `retentionType` aplica-se **exclusivamente ao ISSQN**. As regras de retenção e recolhimento para os novos tributos (IBS e CBS) serão definidas por legislação complementar específica e tratadas em outros campos do layout. + + --- + + Tipo de retenção do ISSQN (tpRetISSQN). + Valores possíveis: + - `NotWithheld`: Não Retido + - `WithheldByBuyer`: Retido pelo Tomador + - `WithheldByIntermediary`: Retido pelo Intermediário + + Define o tipo de retenção. Prioridade: Se um valor for enviado na integração, ele será utilizado. Automático: Se o campo não for enviado (nulo), o sistema aplicará a regra de cálculo automática para definir a retenção. + enum: [NotWithheld, WithheldByBuyer, WithheldByIntermediary] + default: NotWithheld + borrower: + $ref: '#/components/schemas/partyDefinition' + description: | + Tomador dos serviços. + + **Opcional**: o grupo `borrower` pode ser omitido. Quando informado, aplicam-se validações condicionais aos seus campos: + - `name`: se informado, no máximo 115 caracteres; + - `federalTaxNumber`: se informado (diferente de zero), deve ser um CNPJ/CPF válido e coerente com o `type` (`NaturalPerson` → CPF; `LegalEntity` → CNPJ). Para tomador no exterior (`address.country` ≠ `BRA`), não é validado; + - `phoneNumber`: se informado, entre 7 e 20 dígitos; + - `address`: opcional, mas se um endereço **brasileiro** for parcialmente preenchido, passam a ser obrigatórios `postalCode`, `street`, `city.code` (código IBGE de 7 dígitos), `city.name` e `state` (UF de 2 letras), que devem ser consistentes entre si. + intermediary: + $ref: '#/components/schemas/partyDefinition' + description: | + Grupo de informações relativas ao intermediário do serviço (interm). + + **Opcional**: pode ser omitido; envie apenas quando houver intermediário na operação (por exemplo, quando a retenção dos tributos é feita pelo intermediário, `issWithheldIndicator = WithheldByIntermediary`). Quando informado, segue a mesma estrutura do `borrower` (ex.: `type`, `federalTaxNumber`, `name`). + recipient: + $ref: '#/components/schemas/partyDefinition' + description: | + Representa o **Destinatário Final do Serviço**, quando ele é diferente do tomador. Utiliza a estrutura `partyDefinition`. + + **IMPORTANTE**: Quando este objeto for preenchido, é obrigatório informar o valor `DifferentFromBuyer` na propriedade `destinationIndicator` dentro do grupo `ibsCbs`. + location: + $ref: '#/components/schemas/addressDefinition' + description: Local da Prestação do Serviço (cLocPrestacao, cPaisPrestacao) + activityEvent: + $ref: '#/components/schemas/activityEvent' + ReferenceSubstitution: + $ref: '#/components/schemas/ReferenceSubstitution' + lease: + $ref: '#/components/schemas/lease' + construction: + $ref: '#/components/schemas/construction' + realEstate: + $ref: '#/components/schemas/realEstate' + description: "Grupo de informações de operações relacionadas a bens imóveis, exceto obras. (imovel)" + foreignTrade: + $ref: '#/components/schemas/foreignTrade' + deduction: + $ref: '#/components/schemas/deduction' + benefit: + $ref: '#/components/schemas/benefit' + suspension: + $ref: '#/components/schemas/suspension' + serviceAmountDetails: + $ref: '#/components/schemas/serviceAmountDefinitions' + description: "Detalhes dos valores do serviço, incluindo encargos. (RetornoComplementarIBSCBS)" + ibsCbs: + $ref: '#/components/schemas/ibsCbs' + approximateTax: # A descrição foi movida para dentro do schema referenciado para melhor organização. + $ref: '#/components/schemas/approximateTax' + approximateTotals: + $ref: '#/components/schemas/approximateTotals' + + # ========================================================================= + # Definições de Sub-Esquemas + # ========================================================================= + + addressDefinition: + type: object + description: Definição de endereço reutilizável, alinhada com as estruturas nacionais e complementada para endereços no exterior (tpEnderecoExterior, tpEstadoProvinciaRegiao). + properties: + country: {type: string, default: BRA, description: "Sigla do País (padrão ISO 3166-1). Exemplo: BRA, USD, ARG (country do partyDefinition, cPais da NFS-e NAC).", minLength: 3, maxLength: 3} + postalCode: {type: string, description: "CEP (Exemplo: 99999-999) ou Código de Endereçamento Postal no exterior (cEndPost).", minLength: 9, maxLength: 9} + street: {type: string, description: "Logradouro (xLgr, tpLogradouro)"} + number: {type: string, description: "Número (Exemplo: 185 ou S/N) (nro, tpNumeroEndereco)"} + additionalInformation: {type: string, description: "Complemento (Exemplo: BLC A; APT 10) (xCpl, tpComplementoEndereco)"} + district: {type: string, description: "Bairro (xBairro, tpBairro)"} + city: + type: object + description: Cidade + properties: + code: {type: string, description: "Código do IBGE (cMun, tpCidade)", minLength: 7, maxLength: 7} + name: {type: string, description: "Nome da Cidade (tpNomeCidade) - Usado para endereços no exterior."} + state: {type: string, description: "Estado/UF (tpUF) ou Estado, Província, Região no exterior (xEstProvReg, tpEstadoProvinciaRegiao)", minLength: 2, maxLength: 60} + + partyDefinition: + type: object + description: Definição reutilizável para um participante (Tomador, Intermediário, Fornecedor). Baseado em partyDefinition da NFSe Nacional, com campos alinhados aos tipos de São Paulo (tpInformacoesPessoa, gpCPFCNPJNIF). + properties: + type: + type: string + description: | + Tipo do participante. + Valores possíveis: + - `Undefined`: Não definido + - `NaturalPerson`: Pessoa Física + - `LegalEntity`: Pessoa Jurídica + enum: [Undefined, NaturalPerson, LegalEntity] + name: {type: string, description: "Nome / Razão Social (xNome, tpRazaoSocial), obrigatório quando aplicável (tpRazaoSocialObrigatorio)", maxLength: 115} + federalTaxNumber: {type: number, description: "CNPJ, CPF ou NIF (Número de Identificação Fiscal) (tpCNPJ, tpCPF, tpNIF)"} + municipalTaxNumber: {type: string, description: "Inscrição Municipal (tpInscricaoMunicipal, tamanho 8 ou 12 na v2.0 SP)", maxLength: 12} + stateTaxNumber: {type: string, description: "Inscrição Estadual (Opcional, não se aplica a todos) (tpInscricaoEstadual)", maxLength: 19} + taxRegime: + type: string + description: | + Tipo do Regime Tributário. + Valores possíveis: + - `Isento`: Isento + - `MicroempreendedorIndividual`: Microempreendedor Individual (MEI) + - `SimplesNacional`: Simples Nacional + - `LucroPresumido`: Lucro Presumido + - `LucroReal`: Lucro Real + enum: [Isento, MicroempreendedorIndividual, SimplesNacional, LucroPresumido, LucroReal] + caepf: + type: string + description: "Número do Cadastro de Atividade Econômica da Pessoa Física (CAEPF)." + maxLength: 14 + phoneNumber: {type: string, description: "Telefone", minLength: 7, maxLength: 20} + email: {type: string, description: "Endereço eletrônico (email, tpEmail)"} + noTaxIdReason: + type: string + description: | + Justificativa para ausência de NIF (tpNaoNIF, 0 - Não informado na nota de origem, 1 - Dispensado do NIF, 2 - Não exigência do NIF, além dos padrões nacionais). + Valores possíveis: + - `NotInformedOriginal`: Não informado na nota de origem + - `Exempted`: Dispensado do NIF + - `NotRequired`: Não exigência do NIF + enum: [NotInformedOriginal, Exempted, NotRequired] + maxLength: 500 + address: {$ref: '#/components/schemas/addressDefinition'} + + serviceAmountDefinitions: + type: object + description: "Detalhes dos valores do serviço, incluindo encargos. (tpRetornoComplementarIBSCBS)" + properties: + initialChargedAmount: {type: number, description: "Valor Inicial Cobrado (ValorInicialCobrado) - Valor dos serviços antes de tributos, multa e juros. (Versão 2.0 SP)"} + finalChargedAmount: {type: number, description: "Valor Final Cobrado (ValorFinalCobrado) - Valor total cobrado pela prestação do serviço, incluindo todos os tributos. (Versão 2.0 SP)"} + fineAmount: {type: number, description: "Valor da Multa (ValorMulta) (Versão 2.0 SP)"} + interestAmount: {type: number, description: "Valor dos Juros (ValorJuros) (Versão 2.0 SP)"} + + activityEvent: + type: object + description: Detalhes da atividade do evento (atvEvento, tpAtividadeEvento) + properties: + name: {type: string, description: "Nome do evento (xNomeEvt, tpXNomeEvt)"} + beginOn: {type: string, format: date-time, description: "Data de início do evento no formato YYYY-MM-DDTHH:MM:SS.SSSSSS-03:00 (dtiniEvt)"} + endOn: {type: string, format: date-time, description: "Data do fim do evento no formato YYYY-MM-DDTHH:MM:SS.SSSSSS-03:00 (dtFimEvt)"} + Code: {type: string, description: "Código da atividade do evento"} + address: {$ref: '#/components/schemas/addressDefinition', description: "Endereço do evento (end, tpEnderecoSimplesIBSCBS)"} + + approximateTax: + type: object + description: | + Totais aproximados dos tributos (Lei 12.741/2012). (totTrib) - Define os totais aproximados de tributos. **Prioridade**: Se informado, o valor enviado será utilizado, ignorando o cálculo automático. **Automático**: Caso o campo não seja enviado (nulo), os valores serão calculados automaticamente pelo sistema. + + **Estrutura Simplificada vs. Detalhada (`approximateTax` vs. `approximateTotals`)** + + Ambos os campos, `approximateTax` e `approximateTotals`, servem para atender à Lei 12.741/2012 ("Lei De Olho no Imposto"), que exige a informação da carga tributária aproximada na nota fiscal. A principal diferença entre eles está na **granularidade** da informação. + + * **`approximateTax` (Estrutura Simplificada):** + Permite informar a carga tributária de forma consolidada, através de um percentual (`totalRate`) ou valor monetário (`totalAmount`) totais. É ideal para sistemas que não possuem a decomposição dos tributos por esfera governamental. + + * **`approximateTotals` (Estrutura Detalhada):** + Permite um detalhamento completo, separando os valores por esfera: `federal`, `state` e `municipal`. É a escolha ideal quando o sistema possui essa informação decomposta, oferecendo maior transparência. + + **Flexibilidade e Comportamento Automático:** + A coexistência dos dois campos oferece **flexibilidade**. O `approximateTax` simplifica a integração para sistemas com dados consolidados, enquanto o `approximateTotals` atende a sistemas que desejam fornecer o detalhamento completo. O sistema de emissão priorizará a informação mais detalhada, se disponível. + + **Importante:** Se nenhum dos grupos (`approximateTax` ou `approximateTotals`) for preenchido, o sistema realizará o cálculo automático e preencherá o grupo `approximateTax`. + properties: + source: {type: string, description: "Fonte de informação da carga tributária (tpFonteCargaTributaria)"} + version: {type: string} + totalRate: {type: number, description: "Valor percentual da carga tributária (tpPercentualCargaTributaria)"} + totalAmount: {type: number, description: "Valor da carga tributária total em R$ (ValorCargaTributaria, tpValor)"} + + ReferenceSubstitution: + type: object + description: Grupo de informações relativas à NFS-e a ser substituída + additionalProperties: false + properties: + id: {type: string, pattern: '^[0-9]{44}$', description: "Identificador da NFS-e a ser substituída (chSubstda)."} + reason: + type: string + description: | + Motivo da substituição (equivale ao cMotivo). + Valores possíveis: + - `SnOut`: Desenquadramento de NFS-e do Simples Nacional + - `SnIn`: Enquadramento de NFS-e no Simples Nacional + - `ImmunityAddRetro`: Inclusão Retroativa de Imunidade/Isenção para NFS-e + - `ImmunityRemoveRetro`: Exclusão Retroativa de Imunidade/Isenção para NFS-e + - `RejectionBuyerOrIntermediary`: Rejeição de NFS-e pelo tomador ou pelo intermediário se responsável pelo recolhimento do tributo + - `Other`: Outros + enum: [SnOut, SnIn, ImmunityAddRetro, ImmunityRemoveRetro, RejectionBuyerOrIntermediary, Other] + reasonText: {type: string, maxLength: 500, description: "Descrição quando reason = 'other' (equivale ao xMotivo)."} + + lease: + type: object + description: Grupo de informações relativas a atividades de Locação, sublocação, arrendamento, direito de passagem ou permissão de uso, compartilhado ou não, de ferrovia, rodovia, postes, cabos, dutos e condutos de qualquer natureza. + additionalProperties: false + properties: + category: + type: string + description: | + Categoria do serviço (categ). + Valores possíveis: + - `Lease`: Locação + - `Sublease`: Sublocação + - `Leasehold`: Arrendamento + - `RightOfWay`: Direito de passagem + - `UsePermission`: Permissão de uso + enum: [Lease, Sublease, Leasehold, RightOfWay, UsePermission] + objectType: + type: string + description: | + Objeto da locação/sublocação/arrendamento/etc. (objeto). + Valores possíveis: + - `Railway`: Ferrovia + - `Road`: Rodovia + - `Poles`: Postes + - `Cables`: Cabos + - `Pipelines`: Dutos + - `Conduits`: Condutos + enum: [Railway, Road, Poles, Cables, Pipelines, Conduits] + totalLength: {type: number, description: "Comprimento total de ferrovia/rodovia/cabos/dutos/condutos (extensao)."} + polesCount: {type: integer, minimum: 0, description: "Número total de postes (nPostes)."} + + construction: + type: object + description: Grupo de informações relativas à obras de construção civil e congêneres. Apenas uma das opções (workId, cibCode ou siteAddress) deve ser preenchida. (obra) + additionalProperties: false + properties: + propertyFiscalRegistration: {type: string, maxLength: 60, description: "Inscrição imobiliária fiscal (inscImobFisc), exemplos: SQL ou INCRA. (tpinsclmobFisc)"} + workId: + type: object + additionalProperties: false + description: Identificação da obra (CNO/CEI) — cObra. + properties: + scheme: + type: string + description: | + Tipo de cadastro da obra (CNO/CEI). (tpCObra) - Cadastro Nacional de Obras, ou do Cadastro Específico do INSS. + Valores possíveis: + - `bra.cno`: Cadastro Nacional de Obras + - `bra.cei`: Cadastro Específico do INSS + enum: ["bra.cno", "bra.cei"] + value: {type: string, maxLength: 30, description: "Número da obra no cadastro selecionado. (tpCObra, tpNumero)"} + cibCode: {type: string, maxLength: 30, description: "Código do Cadastro Imobiliário Brasileiro (cCIB). (tpCCIB)"} + siteAddress: {$ref: '#/components/schemas/addressDefinition', description: "Endereço da obra (nacional ou exterior). (siteAddress)"} + encapsulationNumber: + type: string + minLength: 1 + maxLength: 12 + pattern: '^[0-9]{1,12}$' + description: | + Número do encapsulamento da obra (NumeroEncapsulamento). String numérica com 1 a 12 dígitos. + Atualmente utilizado apenas pelo serializador do município de São Paulo (Paulistana): quando informado, é emitido como o elemento XML `` nos blocos `` e ``, imediatamente antes de ``. Zeros à esquerda são preservados. (tpNumero: 1-12 dígitos) + example: "123456" + oneOf: + - required: [workId] + - required: [cibCode] + - required: [siteAddress] + + realEstate: + type: object + description: Grupo de informações de operações relacionadas a bens imóveis, exceto obras. Apenas uma das opções (cibCode ou siteAddress) deve ser preenchida. (imovel) + additionalProperties: false + properties: + propertyFiscalRegistration: {type: string, maxLength: 60, description: "Inscrição imobiliária fiscal (inscImobFisc), exemplos: SQL ou INCRA. (tpinsclmobFisc)"} + cibCode: {type: string, maxLength: 30, description: "Código do Cadastro Imobiliário Brasileiro (cCIB). (tpCCIB)"} + siteAddress: {$ref: '#/components/schemas/addressDefinition', description: "Endereço do imóvel (nacional ou exterior). (siteAddress)"} + oneOf: + - required: [cibCode] + - required: [siteAddress] + + foreignTrade: + type: object + description: Grupo de informações sobre transações entre residentes ou domiciliados no Brasil com residentes ou domiciliados no exterior + additionalProperties: false + properties: + serviceMode: + type: string + description: | + Modo de prestação (mdPrestacao). + Valores possíveis: + - `Unknown`: Desconhecido (tipo não informado na nota de origem) + - `CrossBorder`: Transfronteiriço + - `ConsumptionInBrazil`: Consumo no Brasil + - `TemporaryPersonnel`: Movimento Temporário de Pessoas Físicas + - `ConsumptionAbroad`: Consumo no Exterior + enum: [Unknown, CrossBorder, ConsumptionInBrazil, TemporaryPersonnel, ConsumptionAbroad] + default: unknown + relationShip: + type: string + description: | + Vínculo entre as partes (vincPrest). + Valores possíveis: + - `NoLink`: Sem vínculo com o Tomador/Prestador + - `Controlled`: Controlada + - `Controller`: Controladora + - `Affiliate`: Coligada + - `HeadOffice`: Matriz + - `Branch`: Filial ou sucursal + - `OtherLink`: Outro vínculo + enum: [NoLink, Controlled, Controller, Affiliate, HeadOffice, Branch, OtherLink] + default: NoLink + currency: {type: string, pattern: '^[0-9]+$', description: "Moeda da transação (tabela de moedas do Banco Central do Brasil) — tpMoeda."} + serviceAmountInCurrency: {type: number, description: "Valor do serviço na moeda informada em currency — vServMoeda."} + supportMechanismProvider: + type: string + description: | + Mecanismo de apoio/fomento utilizado pelo prestador (mecAFComexP). + Valores possíveis: + - `Unknown`: Desconhecido + - `None`: Nenhum + - `Acc`: ACC - Adiantamento sobre Contrato de Câmbio + - `Ace`: ACE – Adiantamento sobre Cambiais Entregues + - `BndesEximPostShipServices`: BNDES-Exim Pós-Embarque – Serviços + - `BndesEximPreShipServices`: BNDES-Exim Pré-Embarque - Serviços + - `Fge`: FGE - Fundo de Garantia à Exportação + - `ProexEqualization`: PROEX - EQUALIZAÇÃO + - `ProexFinancing`: PROEX - Financiamento + enum: [Unknown, None, Acc, Ace, BndesEximPostShipServices, BndesEximPreShipServices, Fge, ProexEqualization, ProexFinancing] + default: unknown + supportMechanismReceiver: + type: string + description: | + Mecanismo de apoio/fomento utilizado pelo tomador (mecAFComexT). + Valores possíveis: + - `Unknown`: Desconhecido + - `None`: Nenhum + - `PublicAdminAndInternationalRep`: Adm. Pública e Repr. Internacional + - `LeasesMachineryShipsAircraft`: Alugueis e Arrend. Mercantil de maquinas, equip., embarc. e aeronaves + - `AircraftLeaseAirTransportPublic`: Arrendamento Mercantil de aeronave para empresa de transporte aéreo público + - `ExportAgentsCommission`: Comissão a agentes externos na exportação + - `StorageHandlingTransportAbroad`: Despesas de armazenagem, mov. e transporte de carga no exterior + - `FifaEventsSubsidiary`: Eventos FIFA (subsidiária) + - `FifaEvents`: Eventos FIFA + - `FreightsVesselAircraftRentalsOthers`: Fretes, arrendamentos de embarcações ou aeronaves e outros + - `AeronauticalMaterial`: Material Aeronáutico + - `PromotionGoodsAbroad`: Promoção de Bens no Exterior + - `PromotionBrazilianTourism`: Promoção de Dest. Turísticos Brasileiros + - `PromotionBrazilAbroad`: Promoção do Brasil no Exterior + - `PromotionServicesAbroad`: Promoção Serviços no Exterior + - `Recine`: RECINE + - `Recopa`: RECOPA + - `TrademarksPatentsCultivars`: Registro e Manutenção de marcas, patentes e cultivares + - `Reicomp`: REICOMP + - `Reidi`: REIDI + - `Repenec`: REPENEC + - `Repes`: REPES + - `Retaero`: RETAERO + - `Retid`: RETID + - `RoyaltiesTechnicalAssistance`: Royalties, Assistência Técnica, Científica e Assemelhados + - `ConformityAssessmentWto`: Serviços de avaliação da conformidade vinculados aos Acordos da OMC + - `Zpe`: ZPE + enum: [Unknown, None, PublicAdminAndInternationalRep, LeasesMachineryShipsAircraft, AircraftLeaseAirTransportPublic, ExportAgentsCommission, StorageHandlingTransportAbroad, FifaEventsSubsidiary, FifaEvents, FreightsVesselAircraftRentalsOthers, AeronauticalMaterial, PromotionGoodsAbroad, PromotionBrazilianTourism, PromotionBrazilAbroad, PromotionServicesAbroad, Recine, Recopa, TrademarksPatentsCultivars, Reicomp, Reidi, Repenec, Repes, Retaero, Retid, RoyaltiesTechnicalAssistance, ConformityAssessmentWto, Zpe] + default: unknown + temporaryGoods: + type: string + description: | + Vínculo à movimentação temporária de bens (movTempBens). + Valores possíveis: + - `Unknown`: Desconhecido + - `No`: Não + - `LinkedImportDeclaration`: Vinculada - Declaração de Importação + - `LinkedExportDeclaration`: Vinculada - Declaração de Exportação + enum: [Unknown, No, LinkedImportDeclaration, LinkedExportDeclaration] + importDeclaration: {type: string, maxLength: 60, description: "Número da Declaração de Importação (DI/DSI/DA/DRI-E) averbado — nDI."} + exportRegistration: {type: string, maxLength: 60, description: "Número do Registro de Exportação (RE) averbado — nRE."} + mdicDelivery: {type: boolean, description: "Indicador de envio da NFS-e ao MDIC (mdic)."} + + deduction: + type: object + description: | + Grupo de informações relativas ao valores para dedução/redução do valor da base de cálculo (vDedRed). Dedução/Redução aplicada SOMENTE à base de cálculo do ISSQN. + + **Deduções: Simples vs. Estruturado (`deductionsAmount` vs. `deduction`)** + + O layout oferece duas formas de registrar deduções da base de cálculo do ISSQN, visando flexibilidade e conformidade com os diferentes padrões (municipal legado vs. nacional). + + * **`deductionsAmount` (Valor Simples):** + Um campo numérico para informar o **valor total consolidado** das deduções. É uma abordagem direta, compatível com layouts mais antigos que não exigem o detalhamento dos documentos que comprovam a dedução. + + * **`deduction` (Grupo Estruturado):** + Um objeto complexo que permite justificar cada dedução com base em documentos fiscais, alinhando-se ao **padrão da NFS-e Nacional**. Ele detalha a origem de cada valor (chave do documento, tipo, valor, fornecedor). + + **Relação e Recomendação:** + A coexistência dos campos oferece compatibilidade. `deductionsAmount` é uma opção simplificada, enquanto o grupo `deduction` é a forma mais completa e recomendada para garantir rastreabilidade e conformidade fiscal com o padrão nacional. O sistema de emissão priorizará os dados detalhados do grupo `deduction`, se fornecidos. + + **Recomendação de Uso:** + * Use `deductionsAmount` para integrações simples ou quando não há detalhamento dos documentos. + * Use o grupo `deduction` sempre que a informação detalhada dos documentos estiver disponível para garantir a máxima conformidade. + oneOf: + - required: [rate] + - required: [amount] + - required: [documents] + properties: + rate: {type: number, description: "Percentual padrão de dedução/redução (pDR, %)."} + amount: {type: number, description: "Valor monetário padrão de dedução/redução (vDR, R$)."} + documents: + type: array + minItems: 1 + maxItems: 1000 + items: + $ref: '#/components/schemas/deductionDocument' + + deductionDocument: + type: object + additionalProperties: false + description: | + Documento base que justifica um item de dedução/redução (docDedRed, TCDocDedRed). + Pelo menos **um** identificador deve ser preenchido — `nfseKey`, `nfeKey`, + `municipalNfse`, `fiscalDocumentNumber` ou `nonFiscalDocumentNumber`. Se mais + de um for enviado, o backend aplica a precedência nessa ordem. Quando + `deductionType = Other`, o campo `otherDeductionDescription` passa a ser obrigatório. + required: [deductionType, issueDate, deductibleTotal, usedAmount] + oneOf: + - properties: + deductionType: + type: string + enum: [Other] + required: [otherDeductionDescription] + - properties: + deductionType: + not: + enum: [Other] + anyOf: + - required: [nfseKey] + - required: [nfeKey] + - required: [municipalNfse] + - required: [fiscalDocumentNumber] + - required: [nonFiscalDocumentNumber] + properties: + nfseKey: {type: string, pattern: '^[0-9]{50}$', description: "Chave de acesso da NFS-e nacional, exatamente 50 dígitos (chNFSe, TSChaveNFSe)."} + nfeKey: {type: string, pattern: '^[0-9]{44}$', description: "Chave de acesso da NF-e (produto), exatamente 44 dígitos (chNFe)."} + municipalNfse: + type: object + additionalProperties: false + description: Referência a uma NFS-e municipal no padrão legado (NFSeMun). + required: [cityCode, number, verificationCode] + properties: + cityCode: {type: string, description: "Código IBGE do município emissor (cMunNFSeMun)."} + number: {type: string, maxLength: 15, description: "Número da NFS-e municipal (nNFSeMun)."} + verificationCode: {type: string, maxLength: 9, description: "Código de verificação (cVerifNFSeMun)."} + fiscalDocumentNumber: {type: string, maxLength: 255, description: "Identificador de outro documento fiscal não eletrônico (nDocFisc)."} + nonFiscalDocumentNumber: {type: string, maxLength: 255, description: "Identificador de documento não fiscal, ex: nota de débito interna (nDoc)."} + deductionType: + type: string + description: | + Tipo da dedução/redução (tpDedRed). + Valores aceitos (nome do enum — código numérico — descrição): + - `FoodAndBeverages` (1): Alimentação e bebidas/frigobar + - `Materials` (2): Materiais + - `ConsortiumPassThrough` (5): Repasse consorciado + - `HealthPlanPassThrough` (6): Repasse plano de saúde + - `Services` (7): Serviços + - `Subcontracting` (8): Subempreitada de mão de obra + - `Other` (99): Outras deduções — exige `otherDeductionDescription` + enum: [FoodAndBeverages, Materials, ConsortiumPassThrough, HealthPlanPassThrough, Services, Subcontracting, Other] + otherDeductionDescription: {type: string, maxLength: 150, description: "Obrigatório quando deductionType = 'Other' (xDescOutDed)."} + issueDate: {type: string, format: date, description: "Data de emissão do documento de origem (dtEmiDoc, AAAA-MM-DD)."} + deductibleTotal: {type: number, description: "Valor total dedutível/redutível no documento de origem (vDedutivelRedutivel)."} + usedAmount: {type: number, description: "Valor efetivamente utilizado como dedução nesta NFS-e (vDeducaoReducao). Deve ser ≤ deductibleTotal."} + supplier: {$ref: '#/components/schemas/partyDefinition', description: "Fornecedor do documento (fornec)."} + + benefit: + type: object + description: Benefício Municipal aplicado à base de cálculo do ISSQN (BM). + additionalProperties: false + required: [id] + oneOf: + - required: [amount] + - required: [rate] + properties: + id: {type: string, pattern: '^\d{14}$', description: "Identificador do benefício (nBM: IBGE[7] + tipo[2] + seq[5])."} + amount: {type: number, description: "Redução da BC por valor (vRedBCBM, R$)."} + rate: {type: number, description: "Redução da BC por percentual (pRedBCBM, %)."} + + suspension: + type: object + description: Suspensão da exigibilidade do ISSQN (exigSusp). + additionalProperties: false + required: [reason, processNumber] + properties: + reason: + type: string + description: | + Motivo da suspensão (tpSusp). + Valores possíveis: + - `Judicial`: Exigibilidade Suspensa por Decisão Judicial + - `Administrative`: Exigibilidade Suspensa por Processo Administrativo + enum: [Judicial, Administrative] + processNumber: {type: string, maxLength: 30, description: "Número do processo (nProcesso)."} + + approximateTotals: + type: object + additionalProperties: false + description: | + Totais aproximados dos tributos (Lei 12.741/2012). (totTrib) - Define os totais aproximados de tributos. **Prioridade**: Se informado, o valor enviado será utilizado, ignorando o cálculo automático. **Automático**: Caso o campo não seja enviado (nulo), os valores serão calculados automaticamente pelo sistema. + + **Estrutura Simplificada vs. Detalhada (`approximateTax` vs. `approximateTotals`)** + + Ambos os campos, `approximateTax` e `approximateTotals`, servem para atender à Lei 12.741/2012 ("Lei De Olho no Imposto"), que exige a informação da carga tributária aproximada na nota fiscal. A principal diferença entre eles está na **granularidade** da informação. + + * **`approximateTax` (Estrutura Simplificada):** + Permite informar a carga tributária de forma consolidada, através de um percentual (`totalRate`) ou valor monetário (`totalAmount`) totais. É ideal para sistemas que não possuem a decomposição dos tributos por esfera governamental. + + * **`approximateTotals` (Estrutura Detalhada):** + Permite um detalhamento completo, separando os valores por esfera: `federal`, `state` e `municipal`. É a escolha ideal quando o sistema possui essa informação decomposta, oferecendo maior transparência. + + **Flexibilidade e Comportamento Automático:** + A coexistência dos dois campos oferece **flexibilidade**. O `approximateTax` simplifica a integração para sistemas com dados consolidados, enquanto o `approximateTotals` atende a sistemas mais robustos que desejam fornecer o detalhamento completo. O sistema de emissão priorizará a informação mais detalhada, se disponível. + + **Importante:** Se nenhum dos grupos (`approximateTax` ou `approximateTotals`) for preenchido, o sistema realizará o cálculo automático e preencherá o grupo `approximateTax`. + properties: + federal: + type: object + additionalProperties: false + description: Tributos federais. + properties: + rate: {type: number, minimum: 0, description: "Valor percentual total aproximado dos tributos federais (%). (pTotTribFed)"} + amount: {type: number, minimum: 0, description: "Valor monetário total aproximado dos tributos federais (R$). (vTotTribFed)"} + state: + type: object + additionalProperties: false + description: Tributos estaduais. + properties: + rate: {type: number, minimum: 0, description: "Valor percentual total aproximado dos tributos estaduais (%). (vTotTribEst)"} + amount: {type: number, minimum: 0, description: "Valor monetário total aproximado dos tributos estaduais (R$). (vTotTribEst)"} + municipal: + type: object + additionalProperties: false + description: Tributos municipais. + properties: + rate: {type: number, minimum: 0, description: "Valor percentual total aproximado dos tributos municipais (%). (pTotTribMun)"} + amount: {type: number, minimum: 0, description: "Valor monetário total aproximado dos tributos municipais (R$). (vTotTribMun)"} + rate: {type: number, minimum: 0, description: "Valor percentual total aproximado dos tributos (%)."} + amount: {type: number, minimum: 0, description: "Valor monetário total aproximado dos tributos (R$)."} + + ibsCbs: + type: object + additionalProperties: false + description: Informações referentes ao IBS e à CBS por item (NFSe/infNFSe/DPS/infDPS/IBSCBS). (tpIBSCBS) + required: [classCode, operationIndicator] + properties: + purpose: + type: string + description: | + Finalidade da emissão da NFS-e (finNFe). (tpfinNFSe) - default: 'regular'. + Valores possíveis: + - `regular`: Regular + enum: [regular] + default: "regular" + destinationIndicator: + type: string + description: | + Relação entre destinatário e comprador/tomador indicado na NFS-e (indDest). (tpindDest) - default: 'sameAsBuyer'. Quando o valor for 'differentFromBuyer', o preenchimento do objeto 'recipient' se torna obrigatório. + Valores possíveis: + - `SameAsBuyer`: O destinatário é o mesmo que o comprador/tomador + - `DifferentFromBuyer`: O destinatário é diferente do comprador/tomador + enum: [SameAsBuyer, DifferentFromBuyer] + default: "SameAsBuyer" + basis: {type: number, description: "Base de cálculo antes de reduções para cálculo do tributo bruto (vBC)."} + reimbursedResuppliedAmount: {type: number, description: "Montante relativo a reembolso/repasse/ressarcimento não integrante da BC (vReembRepasse)."} + ibscbsDeductionReductionAmount: + type: number + description: "Valor monetário (R$) total relativo aos valores de dedução e redução da Base de Cálculo do IBS e da CBS referentes às operações de locação, cessão onerosa ou arrendamento de bens imóveis, e serviços médicos (vCalcDedRedIBSCBS)." + isDonation: + type: boolean + nullable: true + description: "Indica se a operação é uma doação (indDoacao). (tpNaoSim)" + personalUse: + type: + - boolean + - 'null' + description: "**Indicador de Uso ou Consumo Pessoal**\n\nIndica se a operação é para uso ou consumo pessoal do adquirente. Este campo é crucial para a aplicação de regras tributárias específicas, como a não geração de crédito de IBS/CBS para o tomador. (indFinal)" + operationIndicator: + type: string + description: | + **Indicador da Operação (operationIndicator / cIndOp)** + + O campo `operationIndicator` (ou `cIndOp` nos schemas XSD) é um código obrigatório que define a natureza e a característica da operação de fornecimento do serviço ou bem, sendo um dos campos mais importantes introduzidos pela Reforma Tributária. + + Sua principal finalidade é determinar o **local de incidência** (onde o imposto é devido) para os novos tributos, o IBS (Imposto sobre Bens e Serviços) e a CBS (Contribuição sobre Bens e Serviços). Diferente do modelo anterior, onde o local da prestação era muitas vezes suficiente, este código traz uma regra específica para cada tipo de operação. + + **Como funciona?** + + Com base no código informado, o sistema tributário nacional identifica se o imposto deve ser recolhido: + * No município onde um imóvel está localizado (para serviços de construção ou sobre imóveis). + * No endereço do estabelecimento do fornecedor. + * No endereço do adquirente (comprador/tomador) do serviço. + * No local onde um evento ocorre. + * Entre outras regras específicas. + + A escolha do código correto é, portanto, fundamental para garantir a conformidade fiscal e o recolhimento do IBS para o estado e município corretos. A seleção de um código inadequado pode levar a apurações de impostos incorretas. + + **Exemplo prático:** + Para um serviço de reforma de um imóvel (`código 020201`), o imposto será devido no município onde o imóvel está localizado, independentemente de onde o prestador ou o tomador do serviço estejam estabelecidos. Já para um serviço de consultoria prestado à distância (`código 100301`), a regra geral aponta para o domicílio do adquirente. + + A lista completa de valores pode ser consultada na Tabela de [operationIndicator](https://nfe.io/docs/documentacao/reforma-tributaria/conceitos-funcionais/documentacao-layout-nfse-rtc/#tabela-de-operationindicator) da documentação funcional. + minLength: 6 + maxLength: 6 + pattern: '^[0-9]{6}$' + operationType: + type: string + description: | + Tipo de Operação com Entes Governamentais ou outros serviços sobre bens imóveis (tpOper). + Valores possíveis: + - `SupplyFirstPayLater`: Fornecimento com pagamento posterior + - `PayForPastSupply`: Recebimento do pagamento com fornecimento já realizado + - `SupplyForPastPay`: Fornecimento com pagamento já realizado + - `PayFirstSupplyLater`: Recebimento do pagamento com fornecimento posterior + - `SupplyPayTogether`: Fornecimento e recebimento do pagamento concomitantes + enum: + - SupplyFirstPayLater + - PayForPastSupply + - SupplyForPastPay + - PayFirstSupplyLater + - SupplyPayTogether + situationCode: + type: string + description: | + Código de Situação Tributária do IBS/CBS. Opcional. + Quando não preenchido, o valor será obtido através dos 3 primeiros caracteres do valor informado no campo `classCode`. + A lista completa de valores pode ser consultada na Tabela de [situationCode](hhttps://nfe.io/docs/documentacao/reforma-tributaria/conceitos-funcionais/documentacao-layout-nfse-rtc/#tabela-de-situationcode) da documentação funcional. + classCode: + type: string + maxLength: 6 + description: | + Código de Classificação Tributária do IBS/CBS. (cClassTrib, tpClassificacaoTributaria) + A lista completa de valores pode ser consultada na Tabela de [classCode](https://nfe.io/docs/documentacao/reforma-tributaria/conceitos-funcionais/documentacao-layout-nfse-rtc/#tabela-de-classcode) da documentação funcional. + relatedDocs: + type: object + description: "Grupo de NFS-e referenciadas (gRefNFSe)." + properties: + items: + type: array + description: "Chaves de NFS-e referenciadas (refNFSe)." + items: + type: string + maxLength: 50 + maxItems: 99 + leasedMovableAssets: + type: array + minItems: 0 + maxItems: 99 + description: "Grupo de informações relativas aos bens móveis objetos de locação (gLocBensMoveis). Grupo só é admitido quando se tratar de locação de bens móveis. cTribNac = 99.04.01." + items: + type: object + properties: + ncmCode: + type: string + maxLength: 8 + description: "Código da Nomenclatura Comum do Mercosul (NCM) do bem móvel objeto da locação (cNCMBemMovel)." + description: + type: string + maxLength: 150 + description: "Descrição do Bem Móvel objeto da locação (xNCMBemMovel)." + quantity: + type: integer + description: "Quantidade do Bem Móvel objeto da locação (qtdNCMBemMovel)." + ibs: + type: object + additionalProperties: false + description: Alíquotas e total do IBS por esfera subnacional. + required: [totalAmount, state, municipal] + properties: + totalAmount: {type: number, description: "Total do IBS (vIBSTot = vIBSUF + vIBSMun)."} + state: + type: object + additionalProperties: false + required: [rate, effectiveRate] + properties: + rate: {type: number, description: "Alíquota IBS (UF) (%) (pAliqIBSUF)."} + rateReduction: {type: number, description: "Redução de alíquota estadual (%).", default: 0} + effectiveRate: {type: number, description: "pAliqEfetUF = rate × (1 − rateReduction). Se rateReduction ausente, usar rate."} + deferment: + type: object + additionalProperties: false + description: Deferimento do IBS na esfera estadual. + properties: + rate: {type: number, description: "Percentual de diferimento (pDif, %)."} + amount: {type: number, multipleOf: 0.01, description: "Valor do diferimento (vDif)."} + returnedAmount: {type: number, multipleOf: 0.01, description: "Valor de imposto devolvido/devolução (vDevTrib) na esfera estadual."} + amount: {type: number, multipleOf: 0.01, description: "Valor do IBS da UF (vIBSUF)."} + municipal: + type: object + additionalProperties: false + required: [rate, effectiveRate] + properties: + rate: {type: number, description: "Alíquota IBS (Município) (%) (pAliqIBSMun)."} + rateReduction: {type: number, description: "Redução de alíquota municipal (%).", default: 0} + effectiveRate: {type: number, description: "pAliqEfetMun = rate × (1 − rateReduction). Se rateReduction ausente, usar rate."} + deferment: + type: object + additionalProperties: false + description: Deferimento do IBS na esfera municipal. + properties: + rate: {type: number, description: "Percentual de diferimento (pDif, %)."} + amount: {type: number, multipleOf: 0.01, description: "Valor do diferimento (vDif)."} + returnedAmount: {type: number, multipleOf: 0.01, description: "Valor de imposto devolvido/devolução (vDevTrib) na esfera municipal."} + amount: {type: number, multipleOf: 0.01, description: "Valor do IBS do Município (vIBSMun)."} + cbs: + type: object + additionalProperties: false + description: Alíquotas da CBS (esfera federal). + required: [rate, effectiveRate] + properties: + rate: {type: number, description: "Alíquota da CBS (%) (pAliqCBS)."} + rateReduction: {type: number, description: "Redução de alíquota da CBS (%).", default: 0} + effectiveRate: {type: number, description: "pAliqEfetCBS = rate × (1 − rateReduction). Se rateReduction ausente, usar rate."} + deferment: + type: object + additionalProperties: false + description: Deferimento da CBS. + properties: + rate: {type: number, description: "Percentual de diferimento (pDif, %)."} + amount: {type: number, multipleOf: 0.01, description: "Valor do diferimento (vDif)."} + returnedAmount: {type: number, multipleOf: 0.01, description: "Valor de imposto devolvido/devolução da CBS (vDevTrib)."} + amount: {type: number, multipleOf: 0.01, description: "Valor total da CBS (vCBS)."} + regularTaxation: + type: object + additionalProperties: false + description: Tributação regular hipotética caso condição resolutória/suspensiva não se aplique. (tpGTribRegular) + properties: + situationCode: {type: string, description: "CST regular (CSTReg), 3 dígitos."} + classCode: {type: string, description: "Classificação tributária regular (cClassTribReg), 6 dígitos."} + ibs: + type: object + additionalProperties: false + properties: + totalAmount: {type: number, description: "Total do IBS (vIBSTotReg)."} + state: + type: object + additionalProperties: false + properties: + effectiveRate: {type: number, description: "Alíquota efetiva IBS UF (pAliqEfetRegIBSUF)."} + amount: {type: number, multipleOf: 0.01, description: "IBS UF regular (vTribRegIBSUF)."} + municipal: + type: object + additionalProperties: false + properties: + effectiveRate: {type: number, description: "Alíquota efetiva IBS Município (pAliqEfetRegIBSMun)."} + amount: {type: number, multipleOf: 0.01, description: "IBS Município regular (vTribRegIBSMun)."} + cbs: + type: object + additionalProperties: false + properties: + effectiveRate: {type: number, description: "Alíquota efetiva CBS (pAliqEfetRegCBS)."} + amount: {type: number, multipleOf: 0.01, description: "CBS regular (vTribRegCBS)."} + presumedCredits: + type: object + additionalProperties: false + description: Créditos presumidos de IBS e CBS quando aplicáveis. + properties: + ibs: + type: object + additionalProperties: false + properties: + code: {type: string, pattern: '^\d{2}$', description: "Código do crédito presumido IBS (cCredPres)."} + rate: {type: number, description: "Percentual do crédito presumido IBS (pCredPres)."} + amount: {type: number, multipleOf: 0.01, description: "Valor do crédito presumido IBS (vCredPres)."} + conditionalAmount: {type: number, multipleOf: 0.01, description: "Valor do crédito presumido IBS sob condição suspensiva (vCredPresCondSus)."} + cbs: + type: object + additionalProperties: false + properties: + code: {type: string, pattern: '^\d{2}$', description: "Código do crédito presumido CBS (cCredPres)."} + rate: {type: number, description: "Percentual do crédito presumido CBS (pCredPres)."} + amount: {type: number, multipleOf: 0.01, description: "Valor do crédito presumido CBS (vCredPres)."} + conditionalAmount: {type: number, multipleOf: 0.01, description: "Valor do crédito presumido CBS sob condição suspensiva (vCredPresCondSus)."} + governmentPurchase: + type: object + additionalProperties: false + description: Composição do valor de IBS e CBS em compras governamentais. + properties: + entityType: + type: string + description: | + Tipo do ente da compra governamental (tpEnteGov). + Valores possíveis: + - `Union`: União + - `State`: Estado + - `FederalDistrict`: Distrito Federal + - `Municipality`: Município + enum: ["Union", "State", "FederalDistrict", "Municipality"] + operationType: + type: string + description: | + Tipo de Operação com Entes Governamentais (tpOperGov). + Valores possíveis: + - `SupplyFirstPayLater`: Fornecimento com pagamento posterior + - `PayForPastSupply`: Pagamento de fornecimento anterior + - `SupplyForPastPay`: Fornecimento com pagamento antecipado + - `SupplyPayTogether`: Fornecimento e pagamento concomitantes + enum: [SupplyFirstPayLater, PayForPastSupply, SupplyForPastPay, SupplyPayTogether] + ibs: + type: object + additionalProperties: false + properties: + totalAmount: {type: number, description: "Total do IBS em compras governamentais (vIBSTotGov)."} + state: + type: object + additionalProperties: false + properties: + rate: {type: number, description: "Alíquota IBS UF em compras governamentais (pAliqIBSUF)."} + amount: {type: number, multipleOf: 0.01, description: "Valor IBS UF em compras governamentais (vTribIBSUF)."} + municipal: + type: object + additionalProperties: false + properties: + rate: {type: number, description: "Alíquota IBS Município em compras governamentais (pAliqIBSMun)."} + amount: {type: number, multipleOf: 0.01, description: "Valor IBS Município em compras governamentais (vTribIBSMun)."} + cbs: + type: object + additionalProperties: false + properties: + rate: {type: number, description: "Alíquota CBS em compras governamentais (pAliqCBS)."} + amount: {type: number, multipleOf: 0.01, description: "Valor CBS em compras governamentais (vTribCBS)."} + creditTransfer: + type: object + additionalProperties: false + description: Transferência de créditos de IBS e CBS. + properties: + ibsAmount: {type: number, multipleOf: 0.01, description: "Valor de IBS a transferir (vIBS)."} + cbsAmount: {type: number, multipleOf: 0.01, description: "Valor de CBS a transferir (vCBS)."} + thirdPartyReimbursements: + type: object + additionalProperties: false + description: Valores de reembolso/repasse/ressarcimento já tributados e aqui referenciados. (tpGrupoReeRepRes) + properties: + documents: + type: array + minItems: 1 + maxItems: 1000 + items: + $ref: '#/components/schemas/thirdPartyReimbursementDocument' + + thirdPartyReimbursementDocument: + type: object + additionalProperties: false + description: Documento utilizado para justificar exclusão da BC de IBS/CBS/ISS. (tpDocumento) + required: [issueDate, accrualOn, reimbursementType, amount] + anyOf: + - required: [nfseKey] + - required: [nfeKey] + - required: [cteKey] + - required: [otherNationalDfe] + - required: [otherFiscalDoc] + - required: [otherDoc] + properties: + nfseKey: {type: string, maxLength: 50, description: "Chave NFS-e (padrão nacional)."} + nfeKey: {type: string, pattern: '^[0-9]{44}$', description: "Chave NF-e (44 dígitos)."} + cteKey: {type: string, pattern: '^[0-9]{44}$', description: "Chave CT-e (44 dígitos)."} + otherNationalDfe: + type: object + additionalProperties: false + description: Outro DF-e presente no repositório nacional. (tpDFeNacional) + required: [dfeKey, dfeTypeText] + properties: + dfeKey: {type: string, maxLength: 50, description: "Chave do DF-e (chDFE)."} + dfeTypeText: {type: string, maxLength: 255, description: "Tipo do DF-e (xTpDFE, xTipoChaveDFe)."} + dfeType: {type: string, maxLength: 1, description: "Documento fiscal a que se refere a chaveDfe (tipoChaveDFE, tpTipoChaveDFE)."} + otherFiscalDoc: + type: object + additionalProperties: false + description: Documento fiscal que não está no repositório nacional (eletrônico ou não). (tpDocFiscalOutro) + required: [issuerCityCode, fiscalDocNumber] + properties: + issuerCityCode: {type: string, description: "Código IBGE do município emissor do documento fiscal (cMunEmiDocFisc). (cMunDocFiscal)"} + fiscalDocNumber: {type: string, maxLength: 255, description: "Número do documento fiscal (nDocFisc). (nDocFiscal, tpNumeroDescricaoDocumento)"} + fiscalDocDescription: {type: string, maxLength: 255, description: "Descrição do documento fiscal (xDocFisc). (xDocFiscal, tpNumeroDescricaoDocumento)"} + otherDoc: + type: object + additionalProperties: false + description: Documento não fiscal. (tpDocOutro) + required: [docNumber, docDescription] + properties: + docNumber: {type: string, maxLength: 255, description: "Número do documento (nDoc). (nDoc, tpNumeroDescricaoDocumento)"} + docDescription: {type: string, maxLength: 255, description: "Descrição do documento (xDoc). (xDoc, tpNumeroDescricaoDocumento)"} + supplier: {$ref: '#/components/schemas/partyDefinition', description: "Fornecedor/emitente do documento referenciado (quando aplicável). (fornec, tpFornecedor)"} + issueDate: {type: string, format: date, description: "Data de emissão (AAAA-MM-DD) (dtEmiDoc)."} + accrualOn: {type: string, format: date, description: "Data de competência (AAAA-MM-DD) (dtCompDoc)."} + reimbursementType: + type: string + description: | + Motivo do reembolso/repasse/ressarcimento (tpReemb). (tpReeRepRes) + Valores possíveis: + - `RealEstateBrokerPassThrough`: Repasse de corretagem de imóveis + - `TravelAgencySupplierPassThrough`: Repasse de valores de fornecedores em agência de viagens + - `AdAgencyExternalProductionReimbursement`: Reembolso de produção externa em agência de publicidade + - `AdAgencyMediaReimbursement`: Reembolso de despesas com mídia em agência de publicidade + - `OtherReimbursement`: Outros reembolsos ou ressarcimentos + enum: [RealEstateBrokerPassThrough, TravelAgencySupplierPassThrough, AdAgencyExternalProductionReimbursement, AdAgencyMediaReimbursement, OtherReimbursement] + reimbursementTypeText: {type: string, maxLength: 150, description: "Obrigatório quando reimbursementType = 'otherReimbursement' (xReembOutros). (tpXTpReeRepRes)"} + amount: {type: number, description: "Valor considerado para exclusão da BC (total ou parcial, R$) (vReemb). (vlrReembRepasse)"} diff --git a/openspec/changes/generate-sdk-from-openapi/README.md b/openspec/changes/generate-sdk-from-openapi/README.md deleted file mode 100644 index 70b15c4..0000000 --- a/openspec/changes/generate-sdk-from-openapi/README.md +++ /dev/null @@ -1,201 +0,0 @@ -# Generate SDK from OpenAPI - Proposta OpenSpec - -Esta proposta implementa geração automática de código TypeScript a partir das especificações OpenAPI existentes no projeto. - -## 📋 Estrutura da Proposta - -``` -openspec/changes/generate-sdk-from-openapi/ -├── proposal.md # Visão geral e objetivos -├── tasks.md # Tarefas detalhadas (5 dias) -├── design.md # Arquitetura e decisões técnicas -└── specs/ # Especificações por capacidade - ├── code-generation/ - │ └── spec.md # Geração de tipos TypeScript - ├── spec-validation/ - │ └── spec.md # Validação de specs OpenAPI - └── build-integration/ - └── spec.md # Integração no pipeline de build -``` - -## 🎯 Objetivo - -Automatizar a geração de tipos TypeScript a partir dos 12 arquivos OpenAPI existentes: -- `nf-servico-v1.yaml` (Nota Fiscal de Serviço) -- `nf-produto-v2.yaml` (Nota Fiscal de Produto) -- `nf-consumidor-v2.yaml` (Nota Fiscal do Consumidor) -- E mais 9 especificações - -## 🚀 Comandos Propostos - -### Geração Manual (Desenvolvedor) -```bash -# Validar specs OpenAPI -npm run validate:spec - -# Gerar tipos TypeScript -npm run generate - -# Modo watch (regenera ao modificar specs) -npm run generate:watch - -# Verificar tipos compilam -npm run typecheck -``` - -### Geração Automática (CI/CD) -```bash -# Build completo (inclui validação + geração) -npm run build - -# No CI/CD, o pipeline rodará: -npm run validate:spec # Falha se specs inválidos -npm run generate # Gera tipos -npm run typecheck # Valida compilação -npm run test # Testa integração -``` - -## 📦 Estrutura de Código Proposta - -``` -src/ -├── generated/ # ⚠️ AUTO-GERADO - NÃO EDITAR -│ ├── index.ts # Re-exports unificados -│ ├── schema.ts # Tipos mesclados (compatibilidade) -│ ├── nf-servico.ts # Tipos de nota fiscal de serviço -│ ├── nf-produto.ts # Tipos de nota fiscal de produto -│ └── ... # Outros specs -│ -└── core/resources/ # ✏️ HANDWRITTEN - Usa tipos gerados - ├── service-invoices.ts # Importa de generated/nf-servico - ├── companies.ts # Importa de generated/companies - └── ... - -scripts/ -├── generate-types.ts # Orquestrador de geração -├── validate-spec.ts # Validador de specs OpenAPI -└── download-openapi.ts # Download de specs (se disponível) -``` - -## 🔄 Fluxo de Trabalho - -### 1. Desenvolvedor modifica spec OpenAPI -```bash -# Editar spec -vim openapi/spec/nf-servico-v1.yaml - -# Regenerar tipos -npm run generate - -# Tipos atualizados em src/generated/nf-servico.ts -``` - -### 2. Atualizar resource handwritten -```typescript -// src/core/resources/service-invoices.ts -import { NfServico } from '@/generated'; - -type ServiceInvoice = NfServico.components['schemas']['ServiceInvoice']; - -export class ServiceInvoicesResource { - async create(data: ServiceInvoice): Promise { - // Tipos sincronizados com OpenAPI! - } -} -``` - -### 3. CI/CD valida e gera automaticamente -```yaml -# .github/workflows/ci.yml -- name: Validate OpenAPI Specs - run: npm run validate:spec - -- name: Generate Types - run: npm run generate - -- name: Type Check - run: npm run typecheck -``` - -## ✨ Benefícios - -### 1. Single Source of Truth -- OpenAPI specs são a fonte de verdade -- Tipos TypeScript sempre sincronizados com API -- Elimina divergências entre documentação e código - -### 2. Redução de Manutenção -- Não precisa atualizar tipos manualmente -- Mudanças na API refletem automaticamente -- Menos código handwritten para manter - -### 3. Cobertura Completa -- 12 specs OpenAPI disponíveis -- Atualmente só 5 resources implementados manualmente -- Geração automática cobre todos os endpoints - -### 4. Validação Contínua -- CI/CD falha se specs inválidos -- Tipos devem compilar antes de merge -- Testes garantem tipos correspondem ao runtime - -## 🔧 Implementação - -### Fase 1: Fundação (Dias 1-2) -- Criar scripts de geração e validação -- Gerar tipos do spec principal (`nf-servico-v1.yaml`) -- Migrar ServiceInvoices resource para tipos gerados - -### Fase 2: Cobertura Completa (Dia 3) -- Gerar tipos de todos os 12 specs -- Criar índice unificado -- Estratégia para conflitos de tipos - -### Fase 3: Automação (Dia 4) -- Integração CI/CD -- Modo watch para desenvolvimento -- Cache para otimizar builds - -### Fase 4: Documentação (Dia 5) -- README atualizado -- Guia de migração -- Exemplos de uso - -## 📝 Validação - -A proposta foi validada com OpenSpec: - -```bash -$ openspec validate generate-sdk-from-openapi --strict -✓ Change 'generate-sdk-from-openapi' is valid -``` - -## 📚 Documentos Relacionados - -- [proposal.md](./proposal.md) - Proposta completa -- [tasks.md](./tasks.md) - Lista de tarefas detalhadas -- [design.md](./design.md) - Decisões arquiteturais -- [specs/](./specs/) - Especificações técnicas por capacidade - -## 🚦 Próximos Passos - -1. **Revisar proposta** - Stakeholders aprovam abordagem? -2. **Esclarecer questões abertas** - Ver seção "Open Questions" em proposal.md -3. **Iniciar implementação** - Seguir tasks.md fase por fase -4. **Feedback contínuo** - Ajustar conforme necessário - -## 🤝 Como Contribuir - -Esta é uma proposta em fase de design. Para aplicá-la: - -```bash -# Quando aprovada, aplicar com OpenSpec: -openspec apply generate-sdk-from-openapi - -# Ou implementar manualmente seguindo tasks.md -``` - ---- - -**Status**: ✅ Proposta validada e pronta para revisão -**Próximo**: Aguardando aprovação para iniciar implementação diff --git a/openspec/changes/generate-sdk-from-openapi/design.md b/openspec/changes/generate-sdk-from-openapi/design.md deleted file mode 100644 index 8e874bd..0000000 --- a/openspec/changes/generate-sdk-from-openapi/design.md +++ /dev/null @@ -1,524 +0,0 @@ -# Design: Generate SDK from OpenAPI Specifications - -**Change ID**: `generate-sdk-from-openapi` -**Status**: Draft - ---- - -## Architecture Overview - -The SDK generation system follows a **hybrid architecture** pattern where machine-generated types provide compile-time safety while handwritten code provides developer experience. - -``` -┌─────────────────────────────────────────────────────────────┐ -│ OpenAPI Specs (Source of Truth) │ -│ openapi/spec/*.yaml │ -│ 12 files: nf-servico, nf-produto, consulta-cnpj, etc. │ -└────────────────────┬────────────────────────────────────────┘ - │ - │ scripts/generate-types.ts - │ (openapi-typescript) - ▼ -┌─────────────────────────────────────────────────────────────┐ -│ Generated Types Layer │ -│ src/generated/ │ -│ ⚠️ AUTO-GENERATED - DO NOT EDIT │ -│ - schema.ts (merged types) │ -│ - nf-servico.ts, nf-produto.ts, etc. (per-spec) │ -└────────────────────┬────────────────────────────────────────┘ - │ - │ import types - ▼ -┌─────────────────────────────────────────────────────────────┐ -│ Handwritten DX Layer │ -│ src/core/resources/ │ -│ ✏️ HANDWRITTEN - Wraps generated types │ -│ - service-invoices.ts (uses NfServico types) │ -│ - companies.ts (uses Company types) │ -└─────────────────────────────────────────────────────────────┘ -``` - ---- - -## Component Design - -### 1. Generation Script (`scripts/generate-types.ts`) - -**Responsibilities**: -- Discover OpenAPI specs in `openapi/spec/` -- Run `openapi-typescript` for each spec -- Generate namespaced type files -- Create unified index with re-exports -- Validate output compiles - -**Key Functions**: - -```typescript -/** - * Main orchestrator for type generation - */ -async function generateTypes(): Promise { - const specs = await discoverSpecs(); - - for (const spec of specs) { - await generateTypesForSpec(spec); - } - - await createUnifiedIndex(specs); - await validateOutput(); -} - -/** - * Discovers all OpenAPI specs in openapi/spec/ - */ -async function discoverSpecs(): Promise { - const files = await fs.readdir('openapi/spec'); - return files - .filter(f => f.endsWith('.yaml') || f.endsWith('.yml')) - .map(parseSpecDefinition); -} - -/** - * Generates TypeScript types for a single spec - */ -async function generateTypesForSpec(spec: SpecDefinition): Promise { - const output = await openApiTS(spec.inputPath, { - // openapi-typescript options - }); - - const wrappedOutput = wrapWithNamespace(output, spec.namespace); - await fs.writeFile(spec.outputPath, wrappedOutput); -} -``` - -**Configuration**: -```typescript -interface GeneratorConfig { - inputDir: string; // 'openapi/spec' - outputDir: string; // 'src/generated' - specs: SpecConfig[]; // Per-spec configuration - globalTypes: string[]; // Types to deduplicate -} - -interface SpecConfig { - input: string; // 'nf-servico-v1.yaml' - output: string; // 'nf-servico.ts' - namespace: string; // 'NfServico' - version: string; // 'v1' -} -``` - ---- - -### 2. Validation Script (`scripts/validate-spec.ts`) - -**Responsibilities**: -- Validate OpenAPI 3.0 schema compliance -- Check for required fields -- Detect breaking changes (optional) -- Report clear errors - -**Key Functions**: - -```typescript -/** - * Validates all OpenAPI specs - */ -async function validateSpecs(): Promise { - const specs = await discoverSpecs(); - const results = await Promise.all( - specs.map(validateSpec) - ); - - return aggregateResults(results); -} - -/** - * Validates a single spec - */ -async function validateSpec(specPath: string): Promise { - const content = await loadYaml(specPath); - - return { - isValid: validateOpenAPISchema(content), - errors: findSchemaErrors(content), - warnings: findWarnings(content), - }; -} -``` - ---- - -### 3. Generated Code Structure (`src/generated/`) - -**Directory Layout**: -``` -src/generated/ -├── index.ts # Unified exports -├── schema.ts # Merged types (backward compat) -├── nf-servico.ts # Service invoice types -├── nf-produto.ts # Product invoice types -├── nf-consumidor.ts # Consumer invoice types -├── companies.ts # Company-related types -├── consulta-cnpj.ts # CNPJ lookup types -├── consulta-cte.ts # CTE lookup types -├── consulta-endereco.ts # Address lookup types -└── ... # Other specs -``` - -**File Template**: -```typescript -/** - * ⚠️ AUTO-GENERATED from openapi/spec/nf-servico-v1.yaml - * Do not edit this file directly. - * - * To regenerate: npm run generate - * Last generated: 2026-01-10T10:30:00Z - */ - -export interface paths { - "/v1/companies/{company_id}/serviceinvoices": { - get: operations["ServiceInvoices_Get"]; - post: operations["ServiceInvoices_Create"]; - }; - // ... -} - -export interface components { - schemas: { - ServiceInvoice: { - id: string; - status: "issued" | "cancelled" | "error"; - // ... - }; - Company: { - id: string; - name: string; - // ... - }; - }; -} - -export interface operations { - ServiceInvoices_Get: { - parameters: { /* ... */ }; - responses: { /* ... */ }; - }; - // ... -} -``` - -**Unified Index** (`src/generated/index.ts`): -```typescript -/** - * Unified exports from all OpenAPI specs - */ - -// Per-spec namespaces -export * as NfServico from './nf-servico'; -export * as NfProduto from './nf-produto'; -export * as NfConsumidor from './nf-consumidor'; -export * as Companies from './companies'; -// ... - -// Common types (using service invoice as canonical) -export type ServiceInvoice = NfServico.components['schemas']['ServiceInvoice']; -export type Company = NfServico.components['schemas']['Company']; -export type LegalPerson = NfServico.components['schemas']['LegalPerson']; -export type NaturalPerson = NfServico.components['schemas']['NaturalPerson']; - -// Backward compatibility alias -export * from './schema'; // merged types from all specs -``` - ---- - -### 4. Handwritten DX Layer Integration - -**Usage Pattern** in `src/core/resources/service-invoices.ts`: - -```typescript -import { HttpClient } from '../http/client'; -import { NfServico } from '../../generated'; - -// Extract specific types from generated namespace -type ServiceInvoice = NfServico.components['schemas']['ServiceInvoice']; -type CreateRequest = NfServico.components['schemas']['ServiceInvoiceCreationObject']; -type ListResponse = NfServico.operations['ServiceInvoices_Get']['responses']['200']['content']['application/json']; - -export class ServiceInvoicesResource { - constructor(private http: HttpClient) {} - - /** - * Create a new service invoice - * @param companyId - Company identifier - * @param data - Invoice creation data - */ - async create( - companyId: string, - data: CreateRequest - ): Promise { - return this.http.post( - `/companies/${companyId}/serviceinvoices`, - data - ); - } - - /** - * List service invoices - */ - async list( - companyId: string, - options?: ListOptions - ): Promise { - const response = await this.http.get( - `/companies/${companyId}/serviceinvoices`, - { params: options } - ); - return response.serviceInvoices; - } -} -``` - ---- - -## Type Conflict Resolution - -### Problem -Multiple OpenAPI specs may define the same entity with slight variations: -- `nf-servico-v1.yaml` defines `Company` with fields A, B, C -- `nf-produto-v2.yaml` defines `Company` with fields A, B, D - -### Solution: Namespace Strategy - -**Approach 1: Full Namespacing (Recommended)** -```typescript -// src/generated/index.ts -export * as NfServico from './nf-servico'; -export * as NfProduto from './nf-produto'; - -// Usage in handwritten code: -import { NfServico, NfProduto } from '@/generated'; - -type ServiceCompany = NfServico.components['schemas']['Company']; -type ProductCompany = NfProduto.components['schemas']['Company']; -``` - -**Approach 2: Canonical Types** -```typescript -// Define canonical version from most complete spec -export type Company = NfServico.components['schemas']['Company']; - -// Aliases for other versions -export type ProductCompany = NfProduto.components['schemas']['Company']; -``` - -**Approach 3: Manual Override File** (fallback) -```typescript -// src/generated/overrides.ts - handwritten -export interface Company { - // Manually merged from all specs - id: string; - name: string; - // ... union of all fields -} -``` - ---- - -## Build Pipeline Integration - -### Local Development -```bash -# Terminal 1: Watch mode for OpenAPI changes -npm run generate:watch - -# Terminal 2: Watch mode for TypeScript compilation -npm run dev -``` - -### CI/CD Pipeline -```yaml -# .github/workflows/ci.yml -name: CI - -on: [push, pull_request] - -jobs: - test: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: '18' - cache: 'npm' - - - name: Install dependencies - run: npm ci - - - name: Validate OpenAPI specs - run: npm run validate:spec - - - name: Generate types - run: npm run generate - - - name: Type check - run: npm run typecheck - - - name: Lint - run: npm run lint - - - name: Test - run: npm run test -- --run - - - name: Build - run: npm run build -``` - ---- - -## Error Handling - -### Generation Errors - -**Invalid OpenAPI Spec**: -``` -❌ Error: Invalid OpenAPI spec at openapi/spec/nf-servico-v1.yaml - Line 42: Missing required field 'operationId' in POST /companies - - Fix: Add operationId to the operation or run 'npm run validate:spec' -``` - -**Type Conflicts**: -``` -⚠️ Warning: Type conflict detected - 'Company' defined in multiple specs with different schemas: - - nf-servico-v1.yaml (3 fields) - - nf-produto-v2.yaml (5 fields) - - Resolution: Using namespaced types (NfServico.Company, NfProduto.Company) -``` - -**Compilation Errors**: -``` -❌ Error: Generated types failed to compile - src/generated/nf-servico.ts:123:45 - error TS2304: Cannot find name 'Foo' - - This likely indicates an issue with the OpenAPI spec. - Check openapi/spec/nf-servico-v1.yaml for references to undefined schemas. -``` - ---- - -## Future Enhancements - -### 1. Runtime Validation with Zod -Generate Zod schemas alongside TypeScript types: -```typescript -// src/generated/nf-servico.zod.ts -import { z } from 'zod'; - -export const ServiceInvoiceSchema = z.object({ - id: z.string(), - status: z.enum(['issued', 'cancelled', 'error']), - // ... -}); -``` - -### 2. API Documentation Generation -Generate Markdown docs from OpenAPI: -```bash -npm run generate:docs -# → Creates docs/api/service-invoices.md from OpenAPI descriptions -``` - -### 3. Mock Server Generation -Generate MSW handlers from OpenAPI: -```typescript -// tests/mocks/generated-handlers.ts -export const handlers = [ - rest.post('/companies/:id/serviceinvoices', (req, res, ctx) => { - // Generated from OpenAPI response examples - return res(ctx.json({ ... })); - }), -]; -``` - -### 4. Client SDK Generation -Full client generation (not just types): -```typescript -// Alternative to handwritten DX layer -const client = createClient({ - baseUrl: 'https://api.nfe.io', - apiKey: 'xxx', -}); - -// Fully typed with autocomplete -const invoice = await client.POST('/companies/{id}/serviceinvoices', { - params: { path: { id: 'company-123' } }, - body: { /* typed */ }, -}); -``` - ---- - -## Decision Log - -### Decision 1: Namespace vs. Merge Strategy -**Decision**: Use namespacing (export * as NfServico) -**Rationale**: -- Preserves all type information -- No data loss from merging -- Clear provenance of types -- Easier to debug conflicts - -**Alternatives Considered**: -- Merge all types: Loses information, hard to resolve conflicts -- Manual override: High maintenance, defeats purpose of generation - ---- - -### Decision 2: Committed vs. Ignored Generated Files -**Decision**: Commit generated files to Git -**Rationale**: -- Easier for contributors (no generation step needed) -- Faster CI (skip generation if specs unchanged) -- Clear diffs show API changes - -**Alternatives Considered**: -- Gitignore generated/: Requires generation on every clone/CI run -- Submodule for generated: Adds complexity - ---- - -### Decision 3: Single vs. Multiple Output Files -**Decision**: Generate one file per OpenAPI spec -**Rationale**: -- Clear mapping spec → generated file -- Better tree-shaking (import only needed specs) -- Easier to track changes - -**Alternatives Considered**: -- Single merged file: Harder to attribute types, larger bundle -- Per-endpoint files: Too many files, complex imports - ---- - -## Open Questions - -1. **Spec versioning**: Should we track specific commits of OpenAPI specs or use tags? -2. **Breaking change detection**: Should generation fail if API has breaking changes? -3. **Type customization**: Allow manual type overrides for edge cases? -4. **Zod integration**: Generate runtime validators now or later? - ---- - -## References - -- [openapi-typescript docs](https://openapi-ts.pages.dev/) -- [OpenAPI 3.0 Specification](https://swagger.io/specification/) -- [TypeScript Handbook - Namespaces](https://www.typescriptlang.org/docs/handbook/namespaces.html) -- AGENTS.md: Project modernization guidelines diff --git a/openspec/changes/generate-sdk-from-openapi/proposal.md b/openspec/changes/generate-sdk-from-openapi/proposal.md deleted file mode 100644 index 383974f..0000000 --- a/openspec/changes/generate-sdk-from-openapi/proposal.md +++ /dev/null @@ -1,212 +0,0 @@ -# Proposal: Generate SDK from OpenAPI Specifications - -**Change ID**: `generate-sdk-from-openapi` -**Status**: Approved -**Created**: 2026-01-10 -**Approved**: 2026-01-10 -**Author**: AI Assistant - ---- - -## Problem Statement - -The NFE.io SDK v3 modernization requires automatic code generation from OpenAPI specifications to maintain type safety and reduce manual maintenance. Currently: - -1. **No code generation infrastructure**: Despite having 12 OpenAPI spec files in `openapi/spec/`, there are no scripts to generate TypeScript types or runtime code from them -2. **Manual resource implementation**: All resources in `src/core/resources/` are handwritten, leading to: - - Type mismatches with actual API - - High maintenance burden when API changes - - No single source of truth for API contracts -3. **Incomplete type coverage**: Only 5 resources manually implemented (Companies, ServiceInvoices, LegalPeople, NaturalPeople, Webhooks) while OpenAPI specs describe many more endpoints -4. **Missing validation tooling**: No automated way to ensure SDK implementation matches OpenAPI specs - -The project already has `openapi-typescript` as a dependency but lacks the scripts and architecture to leverage it effectively. - ---- - -## Goals - -### Primary Goals -1. **Automated type generation**: Generate TypeScript types from all OpenAPI specs in `openapi/spec/` -2. **Manual generation workflow**: Provide CLI commands for developers to regenerate types on-demand -3. **Automatic generation workflow**: Integrate generation into the build pipeline for CI/CD -4. **Validation tooling**: Ensure generated code matches OpenAPI specs and is consumable by handwritten DX layer - -### Secondary Goals -1. **Multi-spec support**: Handle the 12 different OpenAPI spec files (nf-servico-v1.yaml, nf-produto-v2.yaml, etc.) -2. **Incremental adoption**: Allow gradual migration from handwritten to generated types -3. **Developer documentation**: Clear guide on how to add new resources or update existing ones - -### Non-Goals -1. **Full runtime client generation**: Generated code provides types only; handwritten DX layer in `src/core/` wraps them -2. **Breaking existing v2 API**: Generation is for v3 only; v2 code in `lib/` remains untouched -3. **API spec creation**: Assumes OpenAPI specs already exist and are maintained by the API team - ---- - -## Proposed Solution - -### High-Level Approach - -Implement a **hybrid architecture**: -- **Generated layer** (`src/generated/`): Auto-generated TypeScript types and schemas from OpenAPI specs -- **Handwritten DX layer** (`src/core/`): Developer-friendly resource classes that use generated types - -### Architecture Components - -``` -openapi/spec/ # Source of truth - 12 OpenAPI YAML files - ├── nf-servico-v1.yaml - ├── nf-produto-v2.yaml - ├── nf-consumidor-v2.yaml - └── ... - -scripts/ - ├── generate-types.ts # Main generation orchestrator - ├── download-openapi.ts # Download specs from API (if available) - ├── validate-spec.ts # Validate OpenAPI specs - └── merge-specs.ts # Merge multiple specs into unified types - -src/generated/ # ⚠️ AUTO-GENERATED - DO NOT EDIT - ├── schema.ts # All API types from all specs - ├── nf-servico.ts # Types for service invoices - ├── nf-produto.ts # Types for product invoices - ├── companies.ts # Types for companies - └── index.ts # Re-exports all types - -src/core/resources/ # ✏️ HANDWRITTEN - Uses generated types - ├── service-invoices.ts # Imports from src/generated/nf-servico - ├── companies.ts # Imports from src/generated/companies - └── ... -``` - -### Key Scripts - -#### 1. `scripts/generate-types.ts` -Orchestrates the entire generation process: -- Discovers all YAML files in `openapi/spec/` -- Runs `openapi-typescript` for each spec -- Generates combined type index -- Validates output compiles - -#### 2. `scripts/validate-spec.ts` -Validates OpenAPI specs before generation: -- Schema validation (OpenAPI 3.0 compliance) -- Required fields check -- Warns about breaking changes - -#### 3. `scripts/download-openapi.ts` -Optional: Downloads latest specs from API if available: -- Checks if public spec endpoints exist -- Falls back to local files if not available - -### npm Scripts Integration - -```json -{ - "scripts": { - "generate": "tsx scripts/generate-types.ts", - "generate:watch": "tsx watch scripts/generate-types.ts", - "validate:spec": "tsx scripts/validate-spec.ts", - "download:spec": "tsx scripts/download-openapi.ts", - "build": "npm run generate && tsup", - "prebuild": "npm run validate:spec" - } -} -``` - -### Developer Workflows - -#### Manual Generation (Developer) -```bash -# 1. Update OpenAPI spec file -vim openapi/spec/nf-servico-v1.yaml - -# 2. Regenerate types -npm run generate - -# 3. Verify types compile -npm run typecheck - -# 4. Update handwritten resource if needed -vim src/core/resources/service-invoices.ts -``` - -#### Automatic Generation (CI/CD) -```bash -# In GitHub Actions or similar -npm run validate:spec # Fails if specs invalid -npm run generate # Generates fresh types -npm run build # Builds with generated types -npm run test # Tests ensure types match runtime -``` - ---- - -## Implementation Phases - -### Phase 1: Foundation (Week 1) -- Create generation scripts infrastructure -- Generate types from existing `nf-servico-v1.yaml` (main spec) -- Validate generated types compile -- Update one resource (ServiceInvoices) to use generated types - -### Phase 2: Full Coverage (Week 2) -- Generate types from all 12 OpenAPI specs -- Create unified type index -- Migration guide for developers -- CI/CD integration - -### Phase 3: Validation & Polish (Week 3) -- Automated spec validation -- Runtime validation with Zod (optional) -- Documentation and examples -- Developer tooling improvements - ---- - -## Success Criteria - -1. ✅ Running `npm run generate` produces valid TypeScript in `src/generated/` -2. ✅ All 12 OpenAPI specs generate types without errors -3. ✅ `npm run typecheck` passes with generated types -4. ✅ At least one handwritten resource uses generated types -5. ✅ CI/CD pipeline includes generation step -6. ✅ Documentation explains manual and automatic workflows -7. ✅ No breaking changes to existing v3 API surface - ---- - -## Risks and Mitigations - -| Risk | Impact | Mitigation | -|------|--------|-----------| -| OpenAPI specs may be incomplete or outdated | Medium | Validate specs first; document gaps; create manual types as fallback | -| Generated types may not match handwritten DX patterns | Medium | Use adapter pattern; generated types are internal only | -| Multiple specs may have conflicting types | High | Namespace types by spec; provide merge strategy | -| Breaking changes in API specs | Medium | Lock spec versions; validate before regenerating | - ---- - -## Open Questions - -1. **Spec versioning**: Should we pin specific versions of OpenAPI specs or always use latest? -2. **Type conflicts**: How to handle when multiple specs define the same entity differently? -3. **Deprecation strategy**: How to mark deprecated endpoints in generated code? -4. **Runtime validation**: Should we generate Zod schemas alongside TypeScript types? - ---- - -## Dependencies - -- External: `openapi-typescript` (already in devDependencies) -- Internal: None - foundation capability -- Blocks: All future resource implementations should use generated types - ---- - -## Related Changes - -- **Future**: Could enable OpenAPI-first development where specs drive implementation -- **Future**: Runtime validation with Zod schemas generated from OpenAPI -- **Future**: Auto-generated API documentation from OpenAPI specs diff --git a/openspec/changes/generate-sdk-from-openapi/specs/build-integration/spec.md b/openspec/changes/generate-sdk-from-openapi/specs/build-integration/spec.md deleted file mode 100644 index c6f8d26..0000000 --- a/openspec/changes/generate-sdk-from-openapi/specs/build-integration/spec.md +++ /dev/null @@ -1,203 +0,0 @@ -# Spec: Build Pipeline Integration - -**Capability**: `build-integration` -**Related Change**: `generate-sdk-from-openapi` - ---- - -## ADDED Requirements - -### Requirement: Integrate generation into build process - -Type generation SHALL be part of the standard build workflow. - -**Priority**: Critical -**Scope**: Build System - -#### Scenario: Build command includes generation - -**Given** developer runs `npm run build` -**When** build process starts -**Then** OpenAPI spec validation runs first -**And** if validation passes, type generation runs -**And** if generation succeeds, TypeScript compilation runs -**And** if compilation succeeds, bundling with tsup runs -**And** final output is created in `dist/` directory - -#### Scenario: Build fails if specs are invalid - -**Given** an OpenAPI spec is invalid -**When** developer runs `npm run build` -**Then** validation step fails with error message -**And** generation step is skipped -**And** build process exits with code 1 -**And** no files are created in `dist/` - -#### Scenario: Build fails if generated types don't compile - -**Given** generated types from OpenAPI contain TypeScript errors -**When** build process reaches TypeScript compilation -**Then** compilation fails with error message -**And** error indicates which generated file has issues -**And** error suggests checking the OpenAPI spec -**And** build process exits with code 1 - ---- - -### Requirement: Provide separate generation command for development - -Developers SHALL be able to regenerate types without full build. - -**Priority**: High -**Scope**: Developer Experience - -#### Scenario: Developer regenerates types manually - -**Given** developer modifies `openapi/spec/nf-servico-v1.yaml` -**When** developer runs `npm run generate` -**Then** only type generation runs (no build or bundling) -**And** generated types are updated in `src/generated/` -**And** command completes in under 3 seconds -**And** TypeScript editor picks up new types immediately - -#### Scenario: Watch mode for iterative development - -**Given** developer is working on OpenAPI spec changes -**When** developer runs `npm run generate:watch` -**Then** watch process starts and monitors `openapi/spec/**/*.yaml` -**And** any change to specs triggers automatic regeneration -**And** console shows real-time feedback on regeneration status -**And** process continues until manually stopped - ---- - -### Requirement: Integrate with CI/CD pipeline - -Generation SHALL be part of CI/CD workflow with caching for efficiency. - -**Priority**: High -**Scope**: Continuous Integration - -#### Scenario: CI pipeline includes generation steps - -**Given** GitHub Actions workflow file exists at `.github/workflows/ci.yml` -**When** CI pipeline runs on pull request -**Then** pipeline executes in order: -1. Checkout code -2. Setup Node.js with npm cache -3. Install dependencies (`npm ci`) -4. Validate OpenAPI specs (`npm run validate:spec`) -5. Generate types (`npm run generate`) -6. Type check (`npm run typecheck`) -7. Lint (`npm run lint`) -8. Test (`npm run test`) -9. Build (`npm run build`) - -**And** pipeline fails at first error -**And** pipeline artifact includes generated types for debugging - -#### Scenario: CI caches generated types when specs unchanged - -**Given** OpenAPI specs have not changed since last CI run -**And** CI cache contains previously generated types -**When** CI pipeline runs -**Then** generation step detects no changes -**And** generation is skipped -**And** cached types are used -**And** build time is reduced by ~30% - -#### Scenario: CI detects uncommitted generated files - -**Given** developer modified spec but didn't commit generated types -**When** CI runs generation -**Then** generation produces different types than in repository -**And** CI fails with error: -``` -❌ Error: Generated types are out of sync - - OpenAPI specs changed but generated types not updated. - Run locally: npm run generate - Then commit: git add src/generated/ && git commit -``` - ---- - -### Requirement: Support prepublish validation - -Package publishing SHALL validate generated types are current. - -**Priority**: High -**Scope**: Release Process - -#### Scenario: Prepublish checks run before npm publish - -**Given** `package.json` has `"prepublishOnly": "npm run build && npm test -- --run"` -**When** developer runs `npm publish` -**Then** prepublish hook runs build (which includes generation) -**And** tests run to verify types match runtime behavior -**And** if any check fails, publish is aborted -**And** error message explains what failed - ---- - -### Requirement: Provide clean command to remove generated files - -Developers SHALL be able to clean generated files for troubleshooting. - -**Priority**: Medium -**Scope**: Developer Tools - -#### Scenario: Clean command removes all generated artifacts - -**Given** generated types exist in `src/generated/` -**And** build artifacts exist in `dist/` -**When** developer runs `npm run clean` -**Then** all files in `src/generated/` are removed -**And** all files in `dist/` are removed -**And** `.gitkeep` or similar sentinel files are preserved -**And** success message confirms cleanup - -#### Scenario: Regenerate from clean state - -**Given** developer runs `npm run clean` -**When** developer runs `npm run build` -**Then** generation recreates all files from scratch -**And** build completes successfully -**And** resulting types are identical to before clean - ---- - -## MODIFIED Requirements - -### Modified: Build script execution order - -**Change Type**: Enhancement -**Previous Behavior**: Build ran TypeScript compilation directly -**New Behavior**: Build runs validation → generation → compilation → bundling - -#### Scenario: Updated build pipeline sequence - -**Given** package.json scripts are updated -**When** developer runs `npm run build` -**Then** execution order is: -```bash -npm run validate:spec # prebuild hook -npm run generate # part of build -tsc --noEmit # type checking (prebuild) -tsup # bundling (build) -``` - ---- - -## REMOVED Requirements - -_No existing requirements removed by this capability._ - ---- - -## Cross-References - -- **Depends on**: `code-generation` - Integrates generation into build -- **Depends on**: `spec-validation` - Validates before generation -- **Enables**: Reliable releases with type-safe generated code -- **Related**: Future automatic release process with generated changelogs diff --git a/openspec/changes/generate-sdk-from-openapi/specs/code-generation/spec.md b/openspec/changes/generate-sdk-from-openapi/specs/code-generation/spec.md deleted file mode 100644 index 9023763..0000000 --- a/openspec/changes/generate-sdk-from-openapi/specs/code-generation/spec.md +++ /dev/null @@ -1,126 +0,0 @@ -# Spec: Code Generation from OpenAPI - -**Capability**: `code-generation` -**Related Change**: `generate-sdk-from-openapi` - ---- - -## ADDED Requirements - -### Requirement: Generate TypeScript types from OpenAPI specs - -The system SHALL provide automated TypeScript type generation from OpenAPI 3.0 specification files. - -**Priority**: Critical -**Scope**: Foundation - -#### Scenario: Developer runs manual generation command - -**Given** OpenAPI spec files exist in `openapi/spec/` directory -**And** the spec `nf-servico-v1.yaml` contains valid OpenAPI 3.0 schema -**When** developer runs `npm run generate` -**Then** TypeScript type file is created at `src/generated/nf-servico.ts` -**And** the generated file includes type definitions for all schemas, paths, and operations -**And** the generated file contains a warning banner "⚠️ AUTO-GENERATED - DO NOT EDIT" -**And** the command completes with exit code 0 -**And** success message is logged to console - -#### Scenario: Generation handles multiple OpenAPI specs - -**Given** 12 OpenAPI spec files exist in `openapi/spec/`: -- nf-servico-v1.yaml -- nf-produto-v2.yaml -- nf-consumidor-v2.yaml -- consulta-cnpj.yaml -- consulta-cte-v2.yaml -- consulta-endereco.yaml -- consulta-nf-consumidor.yaml -- consulta-nf.yaml -- consulta-nfe-distribuicao-v1.yaml -- cpf-api.yaml -- calculo-impostos-v1.yaml -- nfeio.yaml - -**When** developer runs `npm run generate` -**Then** 12 separate TypeScript files are created in `src/generated/` -**And** each file is named after its source spec (e.g., `nf-servico.ts`, `nf-produto.ts`) -**And** a unified index file `src/generated/index.ts` is created with re-exports -**And** all generated files compile without TypeScript errors - -#### Scenario: Generated types are namespaced to avoid conflicts - -**Given** multiple specs define a `Company` schema with different fields -**When** types are generated -**Then** types are exported in namespaces (e.g., `NfServico.components.schemas.Company`) -**And** the unified index exports namespace modules: `export * as NfServico from './nf-servico'` -**And** common types are aliased for convenience: `export type Company = NfServico.components.schemas.Company` - ---- - -### Requirement: Provide watch mode for development workflow - -The system SHALL support automatic regeneration of types when OpenAPI specs change. - -**Priority**: Medium -**Scope**: Developer Experience - -#### Scenario: Watch mode regenerates on file changes - -**Given** watch mode is started with `npm run generate:watch` -**When** developer modifies `openapi/spec/nf-servico-v1.yaml` -**Then** types are automatically regenerated within 2 seconds -**And** console shows "Regenerating types for nf-servico-v1.yaml..." -**And** console shows "✓ Types regenerated successfully" -**And** TypeScript compiler picks up new types immediately - -#### Scenario: Watch mode debounces rapid changes - -**Given** watch mode is running -**When** developer makes 5 changes to a spec within 1 second -**Then** regeneration is triggered only once -**And** generation waits for 500ms of inactivity before running - ---- - -### Requirement: Generate metadata and provenance information - -Generated files SHALL include metadata about their source and generation timestamp. - -**Priority**: Low -**Scope**: Maintainability - -#### Scenario: Generated file includes source attribution - -**Given** types are generated from `openapi/spec/nf-servico-v1.yaml` -**When** developer opens `src/generated/nf-servico.ts` -**Then** file header contains: -```typescript -/** - * ⚠️ AUTO-GENERATED from openapi/spec/nf-servico-v1.yaml - * Do not edit this file directly. - * - * To regenerate: npm run generate - * Last generated: 2026-01-10T10:30:00Z - * Generator: openapi-typescript@6.7.0 - */ -``` - ---- - -## MODIFIED Requirements - -_No existing requirements modified by this capability._ - ---- - -## REMOVED Requirements - -_No existing requirements removed by this capability._ - ---- - -## Cross-References - -- **Depends on**: `spec-validation` - Must validate specs before generation -- **Enables**: `build-integration` - Generated types used in build pipeline -- **Related**: Future runtime validation with Zod schemas diff --git a/openspec/changes/generate-sdk-from-openapi/specs/spec-validation/spec.md b/openspec/changes/generate-sdk-from-openapi/specs/spec-validation/spec.md deleted file mode 100644 index 405501f..0000000 --- a/openspec/changes/generate-sdk-from-openapi/specs/spec-validation/spec.md +++ /dev/null @@ -1,183 +0,0 @@ -# Spec: OpenAPI Specification Validation - -**Capability**: `spec-validation` -**Related Change**: `generate-sdk-from-openapi` - ---- - -## ADDED Requirements - -### Requirement: Validate OpenAPI specification compliance - -The system SHALL validate OpenAPI 3.0 specification files before code generation. - -**Priority**: Critical -**Scope**: Quality Assurance - -#### Scenario: Validation passes for compliant spec - -**Given** OpenAPI spec file `nf-servico-v1.yaml` is valid OpenAPI 3.0 -**And** spec contains required fields: `openapi`, `info`, `paths`, `servers` -**When** developer runs `npm run validate:spec` -**Then** validation completes successfully -**And** console shows "✓ nf-servico-v1.yaml is valid" -**And** command exits with code 0 - -#### Scenario: Validation fails for non-compliant spec - -**Given** OpenAPI spec file `broken-spec.yaml` exists -**And** spec is missing required `info.version` field -**When** developer runs `npm run validate:spec` -**Then** validation fails with error message: -``` -❌ Error: Invalid OpenAPI spec at openapi/spec/broken-spec.yaml - Missing required field: info.version -``` -**And** command exits with code 1 -**And** CI pipeline fails if running in CI environment - -#### Scenario: Validation detects missing operationId - -**Given** OpenAPI spec has a path operation without `operationId` -**When** validation runs -**Then** error message shows: -``` -❌ Error: openapi/spec/nf-servico-v1.yaml - Line 42: POST /companies/{id}/serviceinvoices missing 'operationId' - - Operation IDs are required for code generation. - Add: operationId: ServiceInvoices_Create -``` - ---- - -### Requirement: Validate all specs in directory - -The system SHALL validate all OpenAPI specs in the `openapi/spec/` directory. - -**Priority**: High -**Scope**: Comprehensive Validation - -#### Scenario: Batch validation of multiple specs - -**Given** 12 OpenAPI spec files exist in `openapi/spec/` -**When** developer runs `npm run validate:spec` -**Then** all 12 specs are validated -**And** validation summary shows: -``` -Validating OpenAPI specs... - -✓ nf-servico-v1.yaml (valid) -✓ nf-produto-v2.yaml (valid) -✓ nf-consumidor-v2.yaml (valid) -✓ consulta-cnpj.yaml (valid) -✓ consulta-cte-v2.yaml (valid) -⚠ consulta-endereco.yaml (1 warning) -✓ consulta-nf-consumidor.yaml (valid) -✓ consulta-nf.yaml (valid) -✓ consulta-nfe-distribuicao-v1.yaml (valid) -✓ cpf-api.yaml (valid) -✓ calculo-impostos-v1.yaml (valid) -✓ nfeio.yaml (valid) - -Results: 11 valid, 0 errors, 1 warning -``` - -**And** command exits with code 0 (warnings don't fail validation) - -#### Scenario: Validation stops on first error in strict mode - -**Given** `--strict` flag is provided -**And** second spec file has validation error -**When** validation runs -**Then** validation stops after first error -**And** subsequent specs are not validated -**And** error message indicates use of `--continue-on-error` flag - ---- - -### Requirement: Provide clear error messages with context - -Validation errors SHALL include file location and remediation guidance. - -**Priority**: High -**Scope**: Developer Experience - -#### Scenario: Error message includes line number and context - -**Given** spec has invalid schema reference at line 125 -**When** validation fails -**Then** error message shows: -``` -❌ Error: openapi/spec/nf-servico-v1.yaml:125 - Invalid schema reference: #/components/schemas/NonExistentType - - Referenced type 'NonExistentType' does not exist in components.schemas - - Did you mean one of these? - - ServiceInvoice - - ServiceInvoiceCreationObject - - InvoiceStatus -``` - -#### Scenario: Warning for deprecated OpenAPI features - -**Given** spec uses deprecated `type: file` for file uploads -**When** validation runs -**Then** warning message shows: -``` -⚠ Warning: openapi/spec/companies.yaml:89 - Using deprecated 'type: file' for file uploads - - OpenAPI 3.0 recommends using: - type: string - format: binary - - This will not cause generation to fail but should be updated. -``` - ---- - -### Requirement: Integrate validation into CI/CD pipeline - -Validation SHALL run automatically in CI/CD before generation and build. - -**Priority**: High -**Scope**: Continuous Integration - -#### Scenario: CI fails on invalid spec - -**Given** GitHub Actions workflow includes validation step -**And** a spec file is invalid -**When** CI pipeline runs -**Then** validation step fails -**And** subsequent steps (generate, build, test) are skipped -**And** PR cannot be merged until specs are fixed - -#### Scenario: Pre-build validation prevents broken releases - -**Given** `package.json` has `"prebuild": "npm run validate:spec"` -**When** developer runs `npm run build` -**Then** validation runs before TypeScript compilation -**And** build fails if any spec is invalid -**And** error message directs to fix specs - ---- - -## MODIFIED Requirements - -_No existing requirements modified by this capability._ - ---- - -## REMOVED Requirements - -_No existing requirements removed by this capability._ - ---- - -## Cross-References - -- **Blocks**: `code-generation` - Must validate before generating code -- **Enables**: `build-integration` - Ensures only valid specs reach production -- **Related**: Future schema migration tools for OpenAPI version upgrades diff --git a/openspec/changes/generate-sdk-from-openapi/tasks.md b/openspec/changes/generate-sdk-from-openapi/tasks.md deleted file mode 100644 index 5687286..0000000 --- a/openspec/changes/generate-sdk-from-openapi/tasks.md +++ /dev/null @@ -1,354 +0,0 @@ -# Tasks: Generate SDK from OpenAPI Specifications - -**Change ID**: `generate-sdk-from-openapi` -**Dependencies**: None (foundation capability) -**Estimated Effort**: 3-5 days - ---- - -## Task Breakdown - -### 🔴 Phase 1: Foundation (Day 1-2) - CRITICAL PATH - -#### Task 1.1: Create base generation script -**Deliverable**: `scripts/generate-types.ts` functional for single spec -**Validation**: Script runs without errors and produces TypeScript file -**Effort**: 4 hours -**Status**: ✅ COMPLETED - -- [x] Create `scripts/generate-types.ts` -- [x] Implement file discovery for `openapi/spec/*.yaml` -- [x] Integrate `openapi-typescript` programmatically -- [x] Generate output to `src/generated/schema.ts` -- [x] Add error handling and logging -- [x] Test with `nf-servico-v1.yaml` (main spec) - -```bash -# Expected outcome: -npm run generate -# → Creates src/generated/schema.ts with types from nf-servico-v1.yaml -``` - ---- - -#### Task 1.2: Create spec validation script -**Deliverable**: `scripts/validate-spec.ts` validates OpenAPI compliance -**Validation**: Catches invalid specs before generation -**Effort**: 3 hours -**Status**: ✅ COMPLETED - -- [x] Create `scripts/validate-spec.ts` -- [x] Validate OpenAPI 3.0 schema compliance (+ Swagger 2.0 detection) -- [x] Check for required fields (paths, info, servers) -- [x] Warn about deprecated features -- [x] Report clear error messages -- [x] Test with all 12 existing specs - -```bash -# Expected outcome: -npm run validate:spec -# → Validates all specs, reports errors or success -``` - ---- - -#### Task 1.3: Update package.json scripts -**Deliverable**: npm commands for generation workflow -**Validation**: Commands work and integrate with build -**Effort**: 1 hour -**Status**: ✅ COMPLETED - -- [x] Add `"generate": "tsx scripts/generate-types.ts"` -- [x] Add `"validate:spec": "tsx scripts/validate-spec.ts"` -- [x] Update `"build": "npm run generate && tsup"` -- [x] Add `"prebuild": "npm run validate:spec"` -- [x] Add `"generate:watch": "tsx watch scripts/generate-types.ts"` for dev - ---- - -#### Task 1.4: Setup generated code structure -**Deliverable**: `src/generated/` directory with proper guards -**Validation**: Generated code compiles and exports correctly -**Effort**: 2 hours -**Status**: ✅ COMPLETED - -- [x] Create `src/generated/` directory -- [x] Add `.gitignore` entry for generated files (optional - can be committed) -- [x] Create `src/generated/index.ts` template -- [x] Add warning banner in generated files: `// ⚠️ AUTO-GENERATED - DO NOT EDIT` -- [x] Configure tsconfig to include `src/generated` -- [x] Verify `npm run typecheck` passes with generated types - ---- - -#### Task 1.5: Generate types from main spec -**Deliverable**: Working TypeScript types from `nf-servico-v1.yaml` -**Validation**: Types used successfully in handwritten code -**Effort**: 2 hours -**Status**: ✅ COMPLETED - -- [x] Run generation on `nf-servico-v1.yaml` -- [x] Verify output structure matches expectations -- [x] Create namespace/module for service invoice types -- [x] Export key types (ServiceInvoice, Company, etc.) -- [x] Document type naming conventions - -```typescript -// Expected types in src/generated/schema.ts: -export interface ServiceInvoice { ... } -export interface Company { ... } -export interface paths { ... } -export interface components { ... } -``` - ---- - -### 🟡 Phase 2: Integration (Day 3) - HIGH PRIORITY - -#### Task 2.1: Update ServiceInvoices resource to use generated types -**Deliverable**: `src/core/resources/service-invoices.ts` uses generated types -**Validation**: TypeScript compilation passes, tests pass -**Effort**: 3 hours -**Depends on**: Task 1.5 -**Status**: ✅ COMPLETED - -- [x] Import types from `src/generated/index` -- [x] Replace handwritten interfaces with generated types in `src/core/types.ts` -- [x] Update method signatures to use generated types -- [x] Add type aliases for backward compatibility (ServiceInvoiceStatus, ServiceInvoiceBorrower) -- [x] Update ServiceInvoices, Companies, LegalPeople, NaturalPeople resources -- [x] TypeScript compilation passes (`npm run typecheck`) - -**Notes**: -- Generated types use `flowStatus` instead of `status` field -- API returns single objects for LegalPeople Get, but arrays for NaturalPeople Get -- Some unit tests need mock data updates to match new field names (flowStatus, etc.) - -```typescript -// After migration: -import type { - ServiceInvoice, - ServiceInvoiceData, - Company, - LegalPerson, - NaturalPerson -} from '../generated/index.js'; -``` - ---- - -#### Task 2.2: Multi-spec generation support -**Deliverable**: Generate types from all 12 OpenAPI specs -**Validation**: 12 separate type files created, no conflicts -**Effort**: 4 hours -**Depends on**: Task 1.1 -**Status**: ✅ COMPLETED - -- [x] Update `generate-types.ts` to process multiple specs -- [x] Generate separate file per spec: - - `nf-servico.ts` ← nf-servico-v1.yaml - - `nf-produto.ts` ← nf-produto-v2.yaml - - `nf-consumidor.ts` ← nf-consumidor-v2.yaml - - etc. (7 of 12 specs generated - 5 Swagger 2.0 specs skipped) -- [x] Create unified `src/generated/index.ts` with re-exports -- [x] Handle type name conflicts with namespacing -- [x] Document which resource uses which spec - ---- - -#### Task 2.3: Create type merge strategy -**Deliverable**: Handle overlapping types across specs -**Validation**: No TypeScript compilation errors from conflicts -**Effort**: 2 hours -**Depends on**: Task 2.2 -**Status**: ✅ COMPLETED - -- [ ] Identify common types across specs (Company, Address, etc.) -- [ ] Choose merge strategy: - - Option A: Namespace per spec (`NfServico.Company`) - - Option B: Use latest version (prefer v2 over v1) - - Option C: Manual override file -- [ ] Document resolution strategy in generated index -- [ ] Create type aliases for common use cases - -```typescript -// Example merge strategy: -// src/generated/index.ts -export * as NfServico from './nf-servico'; -export * as NfProduto from './nf-produto'; - -// Common types (use service invoice as canonical) -export type Company = NfServico.components['schemas']['Company']; -``` - ---- - -### 🟢 Phase 3: Automation (Day 4) - MEDIUM PRIORITY - -#### Task 3.1: CI/CD integration -**Deliverable**: GitHub Actions workflow includes generation -**Validation**: CI fails if specs invalid or types don't compile -**Effort**: 2 hours -**Depends on**: Tasks 1.2, 1.3 -**Status**: ✅ COMPLETED - -- [x] Update `.github/workflows/ci.yml` (or create if missing) -- [x] Add step: `npm run validate:spec` -- [x] Add step: `npm run generate` -- [x] Add step: `npm run typecheck` -- [x] Verify workflow fails on invalid specs -- [x] Cache `node_modules` for faster builds - -```yaml -# .github/workflows/ci.yml -- name: Validate OpenAPI Specs - run: npm run validate:spec - -- name: Generate Types - run: npm run generate - -- name: Type Check - run: npm run typecheck -``` - ---- - -#### Task 3.2: Watch mode for development -**Deliverable**: Auto-regenerate types on spec changes -**Validation**: Developer experience improved -**Effort**: 1 hour -**Status**: ✅ COMPLETED - -- [x] Add `generate:watch` script using `tsx watch` -- [x] Watch `openapi/spec/**/*.yaml` for changes -- [x] Debounce regeneration to avoid thrashing -- [x] Show clear console output on regeneration -- [x] Document in README development workflow - ---- - -#### Task 3.3: Download OpenAPI spec script (optional) -**Deliverable**: `scripts/download-openapi.ts` fetches latest specs -**Validation**: Script downloads or gracefully fails -**Effort**: 2 hours -**Optional**: NFE.io may not expose public spec endpoints -**Status**: ✅ COMPLETED - -- [x] Create `scripts/download-openapi.ts` -- [x] Check known spec URLs (e.g., `https://api.nfe.io/openapi.json`) -- [x] Download and save to `openapi/spec/` -- [x] Add `"download:spec": "tsx scripts/download-openapi.ts"` -- [x] Document in README when to use -- [x] Add error handling if specs not publicly available - ---- - -### 🔵 Phase 4: Documentation & Polish (Day 5) - POLISH - -#### Task 4.1: Developer documentation -**Deliverable**: Clear guide in README and/or docs/ -**Validation**: New contributor can generate types successfully -**Effort**: 2 hours -**Status**: ✅ COMPLETED - -- [x] Document manual generation workflow in README -- [x] Document automatic generation in CI/CD -- [x] Explain `src/generated/` structure -- [x] Show examples of using generated types -- [x] Add troubleshooting section -- [x] Update CONTRIBUTING.md with generation guidelines - ---- - -#### Task 4.2: Migration examples -**Deliverable**: Code examples showing handwritten → generated migration -**Validation**: Developers understand migration path -**Effort**: 2 hours -**Status**: ✅ COMPLETED - -- [x] Create `docs/MIGRATION-TO-GENERATED-TYPES.md` -- [x] Show before/after for ServiceInvoices resource -- [x] Document type import patterns -- [x] Show how to handle type conflicts -- [x] Provide checklist for migrating resources - ---- - -#### Task 4.3: Generator configuration -**Deliverable**: `openapi/generator-config.yaml` for customization -**Validation**: Developers can customize generation behavior -**Effort**: 1 hour -**Status**: ✅ COMPLETED - -- [x] Create `openapi/generator-config.yaml` -- [x] Document configuration options: - - Output directory - - Type naming conventions - - Include/exclude specs - - Type transformations -- [x] Use config in `generate-types.ts` -- [x] Add schema validation for config - -```yaml -# openapi/generator-config.yaml -output: src/generated -specs: - - path: openapi/spec/nf-servico-v1.yaml - output: nf-servico.ts - namespace: NfServico - - path: openapi/spec/nf-produto-v2.yaml - output: nf-produto.ts - namespace: NfProduto -``` - ---- - -#### Task 4.4: Testing and validation -**Deliverable**: Tests verify generation correctness -**Validation**: Test suite ensures types match runtime -**Effort**: 3 hours -**Status**: ✅ COMPLETED - -- [x] Create `tests/unit/generation.test.ts` -- [x] Test: Generated files exist after generation -- [x] Test: Generated types compile -- [x] Test: Key types exported correctly -- [x] Test: Spec validation catches errors -- [x] Add to CI pipeline - ---- - -## Parallelization Opportunities - -**Can be done in parallel**: -- Task 1.1 (generation) and Task 1.2 (validation) - independent scripts -- Task 2.2 (multi-spec) and Task 2.3 (merge strategy) - can develop merge strategy while multi-spec runs -- Task 4.1 (docs) and Task 4.2 (examples) - documentation tasks - -**Must be sequential**: -- Phase 1 must complete before Phase 2 (need working generation) -- Task 2.1 depends on Task 1.5 (need generated types to use them) -- Task 3.1 depends on Tasks 1.2, 1.3 (need scripts to run in CI) - ---- - -## Validation Checklist - -After all tasks complete, verify: - -- [ ] `npm run validate:spec` passes for all 12 specs -- [ ] `npm run generate` creates files in `src/generated/` -- [ ] `npm run typecheck` passes with generated types -- [ ] `npm run build` completes successfully -- [ ] `npm test` passes with at least one resource using generated types -- [ ] CI/CD pipeline includes generation steps -- [ ] README documents both manual and automatic workflows -- [ ] At least one example shows using generated types - ---- - -## Notes - -- **Rollback plan**: If generation fails, handwritten types remain functional -- **Incremental adoption**: Can migrate resources one at a time -- **Future work**: Could generate Zod schemas, runtime validators, or full clients diff --git a/openspec/changes/implement-address-lookup/.openspec.yaml b/openspec/changes/implement-address-lookup/.openspec.yaml deleted file mode 100644 index 71f0dad..0000000 --- a/openspec/changes/implement-address-lookup/.openspec.yaml +++ /dev/null @@ -1,2 +0,0 @@ -schema: spec-driven -created: 2026-01-31 diff --git a/openspec/changes/implement-address-lookup/design.md b/openspec/changes/implement-address-lookup/design.md deleted file mode 100644 index 5dca7ca..0000000 --- a/openspec/changes/implement-address-lookup/design.md +++ /dev/null @@ -1,174 +0,0 @@ -## Context - -O SDK NFE.io v3 atualmente suporta 5 resources (ServiceInvoices, Companies, LegalPeople, NaturalPeople, Webhooks) todos conectados ao mesmo host (`api.nfe.io/v1`) usando uma única API key. - -A API de Consulta de Endereços opera em um host diferente (`address.api.nfe.io/v2`) e pode requerer uma chave de API distinta. Isso cria a necessidade de: - -1. Suportar múltiplos hosts dentro do mesmo client -2. Permitir chaves de API por serviço -3. Manter compatibilidade com o padrão existente de inicialização - -**Stakeholders**: Desenvolvedores que integram com NFE.io para validação/autocompletar de endereços brasileiros. - -**Constraints**: -- Zero runtime dependencies (usar fetch nativo) -- Node.js 18+ (já é requisito do SDK) -- TypeScript strict mode -- Manter padrões existentes de recursos (HttpClient, error handling, retry) - -## Goals / Non-Goals - -**Goals:** - -1. Implementar resource `Addresses` com 3 métodos de consulta (CEP, filtro, termo) -2. Suportar chave de API separada (`addressApiKey`) com fallback para `apiKey` -3. Permitir instanciação do client apenas com `addressApiKey` (sem `apiKey`) -4. Validação lazy de chaves (erro apenas quando resource é usado) -5. Manter 100% compatibilidade com código existente -6. Gerar tipos TypeScript a partir do OpenAPI spec - -**Non-Goals:** - -- Cache de resultados (implementação futura) -- Rate limiting específico para API de endereços (usa o padrão do SDK) -- Suporte a outros serviços NFE.io com hosts separados nesta iteração -- Migração de recursos existentes para o novo padrão de chaves - -## Decisions - -### 1. Arquitetura de HttpClient por Resource - -**Decisão**: O resource `Addresses` terá seu próprio `HttpClient` instanciado com configuração específica. - -**Alternativas consideradas**: -- ~~HttpClient único com troca dinâmica de host~~: Complexo, propenso a erros de estado -- ~~Factory de HttpClients no NfeClient~~: Over-engineering para um caso -- **HttpClient por resource (escolhido)**: Simples, isolado, fácil de testar - -**Implementação**: -```typescript -// Em NfeClient constructor -const addressHttpConfig = buildHttpConfig( - this.resolveAddressApiKey(), - 'https://address.api.nfe.io/v2', - this.config.timeout, - this.config.retryConfig -); -this.addresses = new AddressesResource(new HttpClient(addressHttpConfig)); -``` - -### 2. Resolução de API Key com Fallback Chain - -**Decisão**: Implementar cadeia de fallback para resolver chave da API de endereços. - -**Ordem de resolução**: -1. `config.addressApiKey` (explícito no construtor) -2. `config.apiKey` (fallback para chave principal) -3. `env.NFE_ADDRESS_API_KEY` (variável de ambiente específica) -4. `env.NFE_API_KEY` (variável de ambiente principal) - -**Implementação**: -```typescript -private resolveAddressApiKey(): string | undefined { - return ( - this.config.addressApiKey || - this.config.apiKey || - this.getEnv('NFE_ADDRESS_API_KEY') || - this.getEnv('NFE_API_KEY') - ); -} -``` - -### 3. Validação Lazy de Chaves - -**Decisão**: Não validar chaves no construtor. Validar apenas quando um resource é usado. - -**Alternativas consideradas**: -- ~~Validar no construtor~~: Impede uso isolado de resources -- ~~Validar no primeiro request~~: Atraso confuso no erro -- **Validar na criação do resource (escolhido)**: Erro claro, permite uso isolado - -**Implementação**: -```typescript -// Getter lazy para addresses -private _addresses?: AddressesResource; - -get addresses(): AddressesResource { - if (!this._addresses) { - const apiKey = this.resolveAddressApiKey(); - if (!apiKey) { - throw new ConfigurationError( - 'API key required for Addresses. Set "addressApiKey" or "apiKey" in config, ' + - 'or NFE_ADDRESS_API_KEY/NFE_API_KEY environment variable.' - ); - } - this._addresses = new AddressesResource(this.createHttpClient(apiKey, ADDRESS_BASE_URL)); - } - return this._addresses; -} -``` - -### 4. Estrutura do Resource Addresses - -**Decisão**: Seguir padrão existente dos outros resources, com métodos específicos para cada endpoint. - -**Métodos**: -| Método | Endpoint | Descrição | -|--------|----------|-----------| -| `lookupByPostalCode(cep)` | `GET /v2/addresses/{postalCode}` | Busca por CEP | -| `search(filter)` | `GET /v2/addresses?$filter=...` | Pesquisa por campos | -| `lookupByTerm(term)` | `GET /v2/addresses/{term}` | Pesquisa por termo genérico | - -**Tipos de retorno**: -```typescript -interface AddressLookupResponse { - addresses: Address[]; -} - -interface Address { - state: string; - city: { code: string; name: string }; - district: string; - additionalInformation: string; - streetSuffix: string; - street: string; - number: string; - numberMin: string; - numberMax: string; - postalCode: string; - country: string; -} -``` - -### 5. Backward Compatibility - -**Decisão**: Manter `apiKey` funcionando exatamente como antes para recursos existentes. - -**Garantias**: -- Código existente sem `addressApiKey` continua funcionando -- `apiKey` ainda funciona como fallback para Addresses -- Construtor sem nenhuma chave é permitido (erro lazy) - -## Risks / Trade-offs - -### Riscos - -| Risco | Probabilidade | Impacto | Mitigação | -|-------|---------------|---------|-----------| -| Usuário confuso com duas chaves | Média | Baixo | Documentação clara, fallback automático | -| Erro lazy pode ser inesperado | Baixa | Médio | Mensagem de erro detalhada com instruções | -| Múltiplos HttpClients aumentam memória | Baixa | Baixo | HttpClient é leve, criado sob demanda | - -### Trade-offs - -| Trade-off | Escolha | Justificativa | -|-----------|---------|---------------| -| Simplicidade vs Flexibilidade | Flexibilidade | Suportar múltiplos casos de uso compensa complexidade | -| Validação eager vs lazy | Lazy | Permite uso isolado de resources | -| HttpClient compartilhado vs separado | Separado | Isolamento, sem estado global | - -## Open Questions - -1. **Rate limiting**: A API de endereços tem limites diferentes da API principal? (Assumindo não por enquanto) -2. **Retry behavior**: Erros 429 devem ter backoff diferente? (Usando padrão do SDK) -3. **Caching**: Implementar cache de CEPs consultados? (Fora do escopo desta iteração) diff --git a/openspec/changes/implement-address-lookup/proposal.md b/openspec/changes/implement-address-lookup/proposal.md deleted file mode 100644 index 13262c7..0000000 --- a/openspec/changes/implement-address-lookup/proposal.md +++ /dev/null @@ -1,114 +0,0 @@ -## Why - -O SDK NFE.io v3 atualmente não oferece suporte à API de Consulta de Endereços, que permite buscar informações de endereço por CEP ou termo de pesquisa. Esta API é essencial para aplicações que precisam validar ou autocompletar endereços brasileiros usando dados oficiais dos Correios (DNE) integrados com códigos IBGE. - -## What Changes - -- Adicionar novo resource `Addresses` ao SDK com suporte a: - - Consulta de endereço por CEP (código postal) - - Pesquisa de endereço por filtro de campos - - Pesquisa de endereço por termo genérico -- Gerar tipos TypeScript a partir do OpenAPI spec `consulta-endereco.yaml` -- **Suporte a múltiplas API keys e hosts**: A API de endereços usa um host diferente (`address.api.nfe.io`) e pode requerer uma chave de API distinta da chave principal. O SDK deve permitir: - - Configuração de chave específica para a API de endereços (`addressApiKey`) - - Fallback automático para a chave principal se não especificada - - Leitura de `NFE_ADDRESS_API_KEY` do ambiente como alternativa -- Criar testes unitários e de integração para o novo resource -- Adicionar exemplos de uso na documentação - -## Capabilities - -### New Capabilities - -- `address-lookup`: Capacidade de consultar e pesquisar endereços brasileiros através da API NFE.io. Inclui lookup por CEP, pesquisa por campos filtrados e pesquisa por termo genérico. -- `multi-api-support`: Infraestrutura para suportar múltiplas APIs NFE.io com hosts e chaves distintas. Permite que resources específicos usem configurações diferentes do client principal. - -### Modified Capabilities - -(Nenhuma - esta é uma adição sem modificações em capabilities existentes) - -## Impact - -- **Código**: - - Novo arquivo `src/core/resources/addresses.ts` - - Novos tipos em `src/generated/consulta-endereco.ts` - - Atualização de `src/core/client.ts` para expor o novo resource - - Atualização de `src/core/types.ts` para incluir `addressApiKey` em `NfeConfig` - - Atualização de `src/index.ts` para exportar tipos do Addresses -- **HTTP Client**: - - O resource `Addresses` terá seu próprio `HttpClient` configurado com: - - Base URL: `https://address.api.nfe.io/v2` - - API Key: `addressApiKey` (ou fallback para `apiKey`) - - Reutiliza mesma lógica de retry, timeout e error handling -- **Testes**: - - `tests/unit/resources/addresses.test.ts` - - `tests/integration/addresses.integration.test.ts` -- **Documentação**: - - Atualizar `README.md` com exemplos do Addresses - - Novo exemplo em `examples/address-lookup.js` - - Documentar configuração de múltiplas chaves - -## Design Decision: Múltiplas Chaves de API - -### Opção Escolhida: Chaves por Serviço com Validação Lazy - -**Princípio**: Todas as chaves são opcionais no construtor. Validação acontece apenas quando o resource é usado. - -```typescript -// Cenário 1: Só endereços -const nfe = new NfeClient({ addressApiKey: 'addr-key' }); -await nfe.addresses.lookupByPostalCode('01310-100'); // ✅ Funciona -await nfe.companies.list(); // ❌ ConfigurationError: "API key required for Companies resource" - -// Cenário 2: Só invoices/companies -const nfe = new NfeClient({ apiKey: 'main-key' }); -await nfe.companies.list(); // ✅ Funciona -await nfe.addresses.lookupByPostalCode('01310-100'); // ✅ Fallback para apiKey - -// Cenário 3: Chaves separadas -const nfe = new NfeClient({ - apiKey: 'main-key', - addressApiKey: 'addr-key' -}); -// ✅ Cada resource usa sua chave específica - -// Cenário 4: Mesma chave para tudo -const nfe = new NfeClient({ apiKey: 'universal-key' }); -// ✅ Todos os resources usam a mesma chave -``` - -### Regras de Resolução de Chave - -| Resource | Ordem de Resolução | -|----------|-------------------| -| Addresses | `addressApiKey` → `apiKey` → `env.NFE_ADDRESS_API_KEY` → `env.NFE_API_KEY` | -| Companies, ServiceInvoices, etc | `apiKey` → `env.NFE_API_KEY` | - -### Mudança no Construtor - -O construtor `NfeClient` **não lança erro** se nenhuma chave for fornecida. O erro ocorre apenas quando: -1. Um resource é chamado -2. A chave necessária para aquele resource não está disponível - -```typescript -// Isso NÃO lança erro -const nfe = new NfeClient({ environment: 'production' }); - -// Isso lança ConfigurationError -await nfe.companies.list(); -// Error: "API key required for Companies. Set 'apiKey' in config or NFE_API_KEY environment variable." -``` - -### Alternativas Consideradas - -1. ~~Chave principal obrigatória~~: Impede uso isolado de resources auxiliares -2. ~~Clients separados por API~~: Verboso, experiência ruim -3. ~~Validação no construtor~~: Bloqueia casos de uso válidos -4. **Validação lazy (escolhida)**: Máxima flexibilidade, erros claros no momento certo - -### Variáveis de Ambiente - -| Variável | Descrição | -|----------|-----------| -| `NFE_API_KEY` | Chave principal da API (invoices, companies, people, webhooks) | -| `NFE_ADDRESS_API_KEY` | Chave específica para API de endereços | diff --git a/openspec/changes/implement-address-lookup/specs/address-lookup/spec.md b/openspec/changes/implement-address-lookup/specs/address-lookup/spec.md deleted file mode 100644 index 686e2e6..0000000 --- a/openspec/changes/implement-address-lookup/specs/address-lookup/spec.md +++ /dev/null @@ -1,86 +0,0 @@ -## ADDED Requirements - -### Requirement: Lookup address by postal code -The system SHALL allow users to retrieve address information by providing a Brazilian postal code (CEP). - -#### Scenario: Successful lookup by valid CEP -- **WHEN** user calls `addresses.lookupByPostalCode('01310-100')` -- **THEN** system returns an `AddressLookupResponse` with array of matching addresses containing state, city, district, street, and postal code fields - -#### Scenario: Lookup with CEP without hyphen -- **WHEN** user calls `addresses.lookupByPostalCode('01310100')` -- **THEN** system accepts the format and returns matching addresses - -#### Scenario: CEP not found -- **WHEN** user calls `addresses.lookupByPostalCode('00000-000')` -- **THEN** system throws `NotFoundError` with message indicating no address was found - -#### Scenario: Invalid CEP format -- **WHEN** user calls `addresses.lookupByPostalCode('invalid')` -- **THEN** system throws `ValidationError` with message indicating invalid postal code format - ---- - -### Requirement: Search addresses by filter -The system SHALL allow users to search for addresses using field filters via OData `$filter` query parameter. - -#### Scenario: Successful search by filter -- **WHEN** user calls `addresses.search({ filter: "city eq 'São Paulo'" })` -- **THEN** system returns an `AddressLookupResponse` with matching addresses - -#### Scenario: Empty search results -- **WHEN** user calls `addresses.search({ filter: "street eq 'NonexistentStreet'" })` -- **THEN** system returns an `AddressLookupResponse` with empty addresses array - -#### Scenario: Invalid filter syntax -- **WHEN** user calls `addresses.search({ filter: 'invalid syntax' })` -- **THEN** system throws `BadRequestError` with message from API about invalid filter - ---- - -### Requirement: Lookup address by generic term -The system SHALL allow users to search for addresses using a generic search term. - -#### Scenario: Successful lookup by term -- **WHEN** user calls `addresses.lookupByTerm('Avenida Paulista')` -- **THEN** system returns an `AddressLookupResponse` with addresses matching the term - -#### Scenario: Term not found -- **WHEN** user calls `addresses.lookupByTerm('xyznonexistent123')` -- **THEN** system throws `NotFoundError` with message indicating no addresses found - -#### Scenario: Empty term -- **WHEN** user calls `addresses.lookupByTerm('')` -- **THEN** system throws `ValidationError` with message indicating term is required - ---- - -### Requirement: Address response structure -The system SHALL return addresses with complete location data from Correios DNE integrated with IBGE city codes. - -#### Scenario: Complete address data -- **WHEN** a successful address lookup is performed -- **THEN** each address in the response contains: - - `state`: State abbreviation (e.g., 'SP') - - `city.code`: IBGE city code - - `city.name`: City name - - `district`: District/neighborhood name - - `street`: Street name - - `streetSuffix`: Street type (e.g., 'Avenida', 'Rua') - - `postalCode`: Postal code (CEP) - - `country`: Country code - - `additionalInformation`: Additional address info (optional) - - `number`, `numberMin`, `numberMax`: Number range info (optional) - ---- - -### Requirement: TypeScript type safety -The system SHALL provide complete TypeScript types for all address-related operations. - -#### Scenario: Typed response -- **WHEN** user uses `addresses.lookupByPostalCode()` in TypeScript -- **THEN** the return type is `Promise` with full type inference - -#### Scenario: Typed address object -- **WHEN** user accesses properties of an Address object -- **THEN** TypeScript provides autocomplete and type checking for all address fields diff --git a/openspec/changes/implement-address-lookup/specs/multi-api-support/spec.md b/openspec/changes/implement-address-lookup/specs/multi-api-support/spec.md deleted file mode 100644 index 247295e..0000000 --- a/openspec/changes/implement-address-lookup/specs/multi-api-support/spec.md +++ /dev/null @@ -1,92 +0,0 @@ -## ADDED Requirements - -### Requirement: Per-service API key configuration -The system SHALL allow configuration of separate API keys for different NFE.io services within a single client instance. - -#### Scenario: Configure address-specific API key -- **WHEN** user creates client with `new NfeClient({ addressApiKey: 'addr-key' })` -- **THEN** the `addresses` resource uses `addr-key` for authentication - -#### Scenario: Configure both main and address API keys -- **WHEN** user creates client with `new NfeClient({ apiKey: 'main', addressApiKey: 'addr' })` -- **THEN** the `addresses` resource uses `addr` and other resources use `main` - -#### Scenario: Single API key for all resources -- **WHEN** user creates client with `new NfeClient({ apiKey: 'universal-key' })` -- **THEN** all resources including `addresses` use `universal-key` - ---- - -### Requirement: API key fallback chain -The system SHALL resolve API keys using a defined fallback chain when multiple sources are available. - -#### Scenario: Address API key fallback to main key -- **WHEN** user creates client with `new NfeClient({ apiKey: 'main' })` (no addressApiKey) -- **THEN** the `addresses` resource uses `main` as fallback - -#### Scenario: Environment variable fallback for addresses -- **WHEN** user creates client without keys but `NFE_ADDRESS_API_KEY` is set in environment -- **THEN** the `addresses` resource uses the environment variable value - -#### Scenario: Environment variable fallback chain -- **WHEN** user creates client without keys and only `NFE_API_KEY` is set in environment -- **THEN** the `addresses` resource uses `NFE_API_KEY` as final fallback - -#### Scenario: Explicit key overrides environment -- **WHEN** user creates client with `addressApiKey: 'explicit'` and `NFE_ADDRESS_API_KEY` is set -- **THEN** the `addresses` resource uses `explicit` (config takes precedence) - ---- - -### Requirement: Lazy API key validation -The system SHALL NOT validate API key presence at client construction time; validation occurs only when a resource is accessed. - -#### Scenario: Client creation without any keys -- **WHEN** user creates client with `new NfeClient({ environment: 'production' })` -- **THEN** no error is thrown at construction time - -#### Scenario: Error when accessing resource without key -- **WHEN** user accesses `nfe.addresses` without any API key configured -- **THEN** system throws `ConfigurationError` with message: "API key required for Addresses. Set 'addressApiKey' or 'apiKey' in config, or NFE_ADDRESS_API_KEY/NFE_API_KEY environment variable." - -#### Scenario: Error when accessing main resource without key -- **WHEN** user accesses `nfe.companies` without `apiKey` or `NFE_API_KEY` configured -- **THEN** system throws `ConfigurationError` with message indicating the specific key needed - -#### Scenario: Isolated resource usage -- **WHEN** user creates client with only `addressApiKey` and accesses only `addresses` -- **THEN** operations succeed without requiring `apiKey` - ---- - -### Requirement: Multiple base URL support -The system SHALL support resources that connect to different NFE.io API hosts. - -#### Scenario: Addresses resource uses dedicated host -- **WHEN** `addresses` resource makes HTTP requests -- **THEN** requests are sent to `https://address.api.nfe.io/v2` - -#### Scenario: Main resources use default host -- **WHEN** `companies`, `serviceInvoices`, or other main resources make HTTP requests -- **THEN** requests are sent to `https://api.nfe.io/v1` - -#### Scenario: Shared configuration for timeout and retry -- **WHEN** client is configured with custom `timeout` and `retryConfig` -- **THEN** both `addresses` and main resources use the same timeout and retry settings - ---- - -### Requirement: Resource isolation -The system SHALL ensure that each resource with a different API host has its own isolated HTTP client instance. - -#### Scenario: Separate HTTP clients -- **WHEN** client has both `addresses` and `companies` resources -- **THEN** each resource has its own `HttpClient` instance - -#### Scenario: No cross-contamination of auth -- **WHEN** `addresses` uses `addressApiKey` and `companies` uses `apiKey` -- **THEN** credentials are never mixed between resources - -#### Scenario: Lazy resource initialization -- **WHEN** client is created with `addressApiKey` -- **THEN** the `addresses` HttpClient is only created when `nfe.addresses` is first accessed diff --git a/openspec/changes/implement-address-lookup/tasks.md b/openspec/changes/implement-address-lookup/tasks.md deleted file mode 100644 index b03d2fd..0000000 --- a/openspec/changes/implement-address-lookup/tasks.md +++ /dev/null @@ -1,72 +0,0 @@ -## 1. Types & Generated Code - -- [x] 1.1 Generate TypeScript types from `openapi/spec/consulta-endereco.yaml` using openapi-typescript - - Note: Swagger 2.0 not supported by openapi-typescript v6+, types created manually -- [x] 1.2 Create `src/generated/consulta-endereco.ts` with generated types -- [x] 1.3 Export Address-related types from `src/generated/index.ts` - -## 2. Core Types Extension - -- [x] 2.1 Add `addressApiKey?: string` to `NfeConfig` interface in `src/core/types.ts` -- [x] 2.2 Add `Address` and `AddressLookupResponse` types to `src/core/types.ts` -- [x] 2.3 Add `AddressSearchOptions` type with `filter` field - -## 3. Multi-API Support Infrastructure - -- [x] 3.1 Refactor `NfeClient` constructor to not require `apiKey` (make it optional) -- [x] 3.2 Add `resolveAddressApiKey()` private method with fallback chain logic -- [x] 3.3 Add `resolveMainApiKey()` private method for existing resources -- [x] 3.4 Add `getEnvironmentVariable()` helper method for reading `NFE_ADDRESS_API_KEY` -- [x] 3.5 Convert `serviceInvoices`, `companies`, `legalPeople`, `naturalPeople`, `webhooks` to lazy getters with validation - -## 4. Addresses Resource Implementation - -- [x] 4.1 Create `src/core/resources/addresses.ts` with `AddressesResource` class -- [x] 4.2 Implement `lookupByPostalCode(postalCode: string)` method -- [x] 4.3 Implement `search(options: AddressSearchOptions)` method with `$filter` query param -- [x] 4.4 Implement `lookupByTerm(term: string)` method -- [x] 4.5 Add input validation for postal code format (8 digits, with or without hyphen) -- [x] 4.6 Add input validation for empty term -- [x] 4.7 Export `AddressesResource` from `src/core/resources/index.ts` - -## 5. Client Integration - -- [x] 5.1 Add lazy `addresses` getter to `NfeClient` class -- [x] 5.2 Create separate `HttpClient` instance for addresses with `https://address.api.nfe.io/v2` base URL -- [x] 5.3 Throw `ConfigurationError` with descriptive message when no API key available -- [x] 5.4 Export Address types from `src/index.ts` - -## 6. Unit Tests - -- [x] 6.1 Create `tests/unit/resources/addresses.test.ts` -- [x] 6.2 Test `lookupByPostalCode()` with valid CEP returns correct response -- [x] 6.3 Test `lookupByPostalCode()` with invalid CEP throws `ValidationError` -- [x] 6.4 Test `lookupByTerm()` with empty term throws `ValidationError` -- [x] 6.5 Test `search()` passes filter to query params correctly -- [x] 6.6 Create `tests/unit/client-multikey.test.ts` for multi-API key tests -- [x] 6.7 Test lazy getter throws when no key available -- [x] 6.8 Test fallback chain: `addressApiKey` → `apiKey` → env vars -- [x] 6.9 Test isolated resource usage (only addresses, no apiKey) - -## 7. Integration Tests - -- [x] 7.1 Create `tests/integration/addresses.integration.test.ts` -- [x] 7.2 Test real API call to lookup by postal code (requires test API key) -- [x] 7.3 Test real API call to search by term -- [x] 7.4 Test 404 response handling for non-existent CEP - -## 8. Documentation & Examples - -- [x] 8.1 Create `examples/address-lookup.js` with usage examples -- [x] 8.2 Update `README.md` with Addresses resource documentation -- [x] 8.3 Document `addressApiKey` configuration option -- [x] 8.4 Document environment variables `NFE_ADDRESS_API_KEY` -- [x] 8.5 Add JSDoc comments to all public methods in `AddressesResource` - -## 9. Final Validation - -- [x] 9.1 Run `npm run typecheck` - ensure zero errors -- [x] 9.2 Run `npm run lint` - ensure zero warnings (only pre-existing `any` warnings) -- [x] 9.3 Run `npm test` - ensure all tests pass (322 passing, 47 skipped) -- [x] 9.4 Run `npm run build` - ensure successful build -- [ ] 9.5 Test examples manually against sandbox/production API diff --git a/openspec/changes/implement-companies-resource/design.md b/openspec/changes/implement-companies-resource/design.md deleted file mode 100644 index 21f18d9..0000000 --- a/openspec/changes/implement-companies-resource/design.md +++ /dev/null @@ -1,615 +0,0 @@ -# Design: Implement Companies Resource - -**Change ID**: `implement-companies-resource` -**Status**: Draft - ---- - -## Overview - -This document describes the design for completing the Companies resource implementation in the NFE.io SDK v3. The Companies resource is fundamental to the SDK as it provides the foundation for all company-scoped operations (service invoices, people management, etc.). - ---- - -## Architecture - -### Current State - -``` -src/core/resources/companies.ts (239 lines) -├── Basic CRUD operations (create, list, retrieve, update, remove) -├── Certificate management (uploadCertificate, getCertificateStatus) -└── Simple helpers (findByTaxNumber, getCompaniesWithCertificates, createBatch) - -tests/integration/companies.integration.test.ts (209 lines) -├── Integration tests for basic CRUD -└── Integration tests for certificate operations - -tests/unit/companies.test.ts -└── Basic unit tests (needs expansion) -``` - -### Target State - -``` -src/core/resources/companies.ts (~400 lines) -├── Core CRUD Operations (enhanced) -│ ├── create() - with validation and retry -│ ├── list() - with proper pagination -│ ├── listAll() - auto-pagination helper -│ ├── listIterator() - async iteration support -│ ├── retrieve() - with error handling -│ ├── update() - with partial updates -│ └── remove() - with cascade warnings -│ -├── Certificate Management (complete) -│ ├── uploadCertificate() - with validation and retry -│ ├── getCertificateStatus() - enhanced metadata -│ ├── validateCertificate() - pre-upload validation -│ ├── replaceCertificate() - rotation helper -│ └── checkCertificateExpiration() - warning helper -│ -└── Search & Filters (enhanced) - ├── findByTaxNumber() - exact match - ├── findByName() - pattern matching - ├── getCompaniesWithActiveCertificates() - ├── getCompaniesWithExpiredCertificates() - └── getCompaniesWithExpiringSoonCertificates() - -src/core/utils/certificate-validator.ts (new, ~100 lines) -├── Certificate parsing -├── Format validation (.pfx, .p12) -└── Metadata extraction - -tests/ (comprehensive coverage) -├── unit/companies.test.ts (~500 lines) -├── unit/certificate-validator.test.ts (~200 lines) -└── integration/companies.integration.test.ts (~400 lines) -``` - ---- - -## Component Design - -### 1. CompaniesResource Class - -Main resource class that provides the public API: - -```typescript -export class CompaniesResource { - constructor(private readonly http: HttpClient) {} - - // Core CRUD - async create(data: CreateCompanyData): Promise - async list(options?: PaginationOptions): Promise> - async listAll(): Promise - async *listIterator(): AsyncIterableIterator - async retrieve(companyId: string): Promise - async update(companyId: string, data: Partial): Promise - async remove(companyId: string): Promise - - // Certificate Management - async uploadCertificate(companyId: string, cert: CertificateData): Promise - async getCertificateStatus(companyId: string): Promise - async validateCertificate(file: Buffer, password: string): Promise - async replaceCertificate(companyId: string, cert: CertificateReplacement): Promise - async checkCertificateExpiration(companyId: string, threshold?: number): Promise - - // Search & Filters - async findByTaxNumber(taxNumber: number): Promise - async findByName(namePattern: string): Promise - async getCompaniesWithActiveCertificates(): Promise - async getCompaniesWithExpiredCertificates(): Promise - async getCompaniesWithExpiringSoonCertificates(daysThreshold?: number): Promise -} -``` - -### 2. Type Definitions - -All types imported from generated OpenAPI types: - -```typescript -// From src/generated/index.ts -import type { Company, CompanyData } from '../generated/index.js'; - -// SDK-specific types in src/core/types.ts -export type CreateCompanyData = Omit; - -export interface PaginationOptions { - pageCount?: number; - pageIndex?: number; - // Or cursor-based if API supports - cursor?: string; -} - -export interface ListResponse { - data: T[]; - totalCount?: number; - hasMore?: boolean; - nextCursor?: string; -} - -export interface CertificateData { - file: Buffer | Blob; - password: string; - filename?: string; - onProgress?: (percent: number) => void; -} - -export interface CertificateStatus { - hasCertificate: boolean; - isValid: boolean; - expiresOn?: string; - daysUntilExpiration?: number; - isExpiringSoon?: boolean; // < 30 days - subject?: string; - issuer?: string; -} - -export interface ValidationResult { - valid: boolean; - error?: string; - metadata?: { - subject: string; - issuer: string; - expiresOn: string; - validFrom: string; - }; -} - -export interface CertificateReplacement { - oldPassword?: string; // Optional verification - newFile: Buffer | Blob; - newPassword: string; - newFilename?: string; -} - -export interface ExpirationWarning { - companyId: string; - expiresOn: string; - daysRemaining: number; - message: string; -} -``` - -### 3. Certificate Validator Utility - -Separate module for certificate validation logic: - -```typescript -// src/core/utils/certificate-validator.ts -import { readPkcs12 } from 'node:crypto'; // Node 18+ - -export interface CertificateMetadata { - subject: string; - issuer: string; - validFrom: Date; - validTo: Date; - serialNumber: string; -} - -export class CertificateValidator { - /** - * Validate certificate file and extract metadata - */ - static async validate( - file: Buffer, - password: string - ): Promise<{ valid: boolean; metadata?: CertificateMetadata; error?: string }> { - try { - // Parse PKCS#12 certificate - const pkcs12 = readPkcs12(file, password); - - // Extract certificate - const cert = pkcs12.certificate; - if (!cert) { - return { valid: false, error: 'No certificate found in file' }; - } - - // Parse metadata - const metadata: CertificateMetadata = { - subject: cert.subject, - issuer: cert.issuer, - validFrom: new Date(cert.validFrom), - validTo: new Date(cert.validTo), - serialNumber: cert.serialNumber - }; - - // Check if expired - const now = new Date(); - if (now > metadata.validTo) { - return { valid: false, error: 'Certificate has expired', metadata }; - } - - if (now < metadata.validFrom) { - return { valid: false, error: 'Certificate is not yet valid', metadata }; - } - - return { valid: true, metadata }; - } catch (error) { - return { - valid: false, - error: error instanceof Error ? error.message : 'Invalid certificate or password' - }; - } - } - - /** - * Check if certificate format is supported - */ - static isSupportedFormat(filename: string): boolean { - const ext = filename.toLowerCase().split('.').pop(); - return ext === 'pfx' || ext === 'p12'; - } - - /** - * Calculate days until expiration - */ - static getDaysUntilExpiration(expiresOn: Date): number { - const now = new Date(); - const diff = expiresOn.getTime() - now.getTime(); - return Math.floor(diff / (1000 * 60 * 60 * 24)); - } -} -``` - ---- - -## Error Handling Strategy - -### Error Hierarchy - -```typescript -// Use existing error classes from src/errors/ - -// Validation errors -class ValidationError extends NfeError { - constructor(message: string, field?: string) { - super(message, { field }); - } -} - -// Not found errors -class NotFoundError extends NfeError { - constructor(resourceType: string, id: string) { - super(`${resourceType} not found: ${id}`, { id }); - } -} - -// Certificate errors -class CertificateError extends NfeError { - constructor(message: string, cause?: Error) { - super(message, { cause }); - } -} -``` - -### Error Handling Patterns - -```typescript -// In companies.ts - -async create(data: CreateCompanyData): Promise { - try { - // Pre-flight validation - this.validateCompanyData(data); - - // API call - const response = await this.http.post('/companies', data); - return response.data; - } catch (error) { - // Transform HTTP errors to typed errors - if (error instanceof HttpError) { - if (error.status === 400) { - throw new ValidationError(error.message, error.field); - } - if (error.status === 401) { - throw new AuthenticationError('Invalid API key'); - } - if (error.status === 409) { - throw new ConflictError('Company already exists'); - } - } - throw error; - } -} -``` - ---- - -## Pagination Strategy - -### Manual Pagination - -```typescript -async list(options: PaginationOptions = {}): Promise> { - const { pageCount = 20, pageIndex = 0 } = options; - - const response = await this.http.get>('/companies', { - pageCount, - pageIndex - }); - - return response.data; -} -``` - -### Auto-Pagination Helper - -```typescript -async listAll(): Promise { - const companies: Company[] = []; - let pageIndex = 0; - let hasMore = true; - - while (hasMore) { - const page = await this.list({ pageCount: 100, pageIndex }); - companies.push(...page.data); - - // Check if there are more pages - hasMore = page.hasMore ?? (page.data.length === 100); - pageIndex++; - } - - return companies; -} -``` - -### Async Iterator Pattern - -```typescript -async *listIterator(): AsyncIterableIterator { - let pageIndex = 0; - let hasMore = true; - - while (hasMore) { - const page = await this.list({ pageCount: 100, pageIndex }); - - for (const company of page.data) { - yield company; - } - - hasMore = page.hasMore ?? (page.data.length === 100); - pageIndex++; - } -} - -// Usage: -for await (const company of nfe.companies.listIterator()) { - console.log(company.name); -} -``` - ---- - -## Testing Strategy - -### Unit Testing Approach - -Use Vitest with mock HTTP client: - -```typescript -// tests/unit/companies.test.ts -import { describe, it, expect, vi } from 'vitest'; -import { CompaniesResource } from '../../src/core/resources/companies'; -import { HttpClient } from '../../src/core/http/client'; - -describe('CompaniesResource', () => { - it('should create a company', async () => { - const mockHttp = { - post: vi.fn().mockResolvedValue({ - data: { id: 'company-123', name: 'Test Co' } - }) - } as any; - - const companies = new CompaniesResource(mockHttp); - const result = await companies.create({ name: 'Test Co', ... }); - - expect(result.id).toBe('company-123'); - expect(mockHttp.post).toHaveBeenCalledWith('/companies', expect.any(Object)); - }); -}); -``` - -### Integration Testing Approach - -Use real API with test data cleanup: - -```typescript -// tests/integration/companies.integration.test.ts -import { describe, it, expect, beforeAll, afterEach } from 'vitest'; -import { NfeClient } from '../../src/core/client'; - -describe('Companies Integration', () => { - let client: NfeClient; - const createdIds: string[] = []; - - beforeAll(() => { - client = new NfeClient({ apiKey: process.env.NFE_API_KEY }); - }); - - afterEach(async () => { - // Cleanup test data - for (const id of createdIds) { - try { - await client.companies.remove(id); - } catch {} - } - createdIds.length = 0; - }); - - it('should create and retrieve a company', async () => { - const company = await client.companies.create({ name: 'Test Co', ... }); - createdIds.push(company.id); - - const retrieved = await client.companies.retrieve(company.id); - expect(retrieved.id).toBe(company.id); - }); -}); -``` - ---- - -## Performance Considerations - -### 1. Pagination Performance - -- Default page size: 20 (balances requests vs memory) -- Max page size: 100 (API limit) -- Auto-pagination uses 100 for efficiency - -### 2. Certificate Validation Performance - -- Validation done client-side before upload (saves failed upload attempts) -- Certificate parsing may take 50-100ms (acceptable for infrequent operation) -- Cache certificate status for repeated checks (future optimization) - ---- - -## Security Considerations - -### 1. Certificate Handling - -- Certificates contain private keys - never log full contents -- Passwords should not be logged or stored -- Use secure memory for password handling -- Clear sensitive buffers after use - -### 2. Input Validation - -- Validate CNPJ/CPF format before API call -- Sanitize company names to prevent injection -- Validate email formats -- Check file sizes before upload (prevent DoS) - -### 3. Error Messages - -- Don't expose sensitive information in error messages -- Generic messages for authentication failures -- Detailed validation errors only for development environment - ---- - -## Migration Path from v2 - -### v2 API (callback-based): - -```javascript -nfe.companies.create(companyData, function(err, company) { - if (err) { - console.error(err); - } else { - console.log(company); - } -}); -``` - -### v3 API (async/await): - -```typescript -try { - const company = await nfe.companies.create(companyData); - console.log(company); -} catch (error) { - console.error(error); -} -``` - -### Migration Notes: - -1. All methods return Promises instead of accepting callbacks -2. Error handling via try/catch instead of error-first callbacks -3. Type safety via TypeScript interfaces -4. Method names remain the same (except `delete` → `remove`) - ---- - -## Open Design Questions - -### 1. Certificate Storage - -**Question**: Should we provide local certificate caching/storage helpers? -**Options**: -- A: No, users manage storage themselves -- B: Provide optional encrypted storage utility -- C: Integrate with system keychain - -**Recommendation**: Option A for v1, consider B/C for future versions - -### 2. Rate Limiting Strategy - -**Question**: Should we implement client-side rate limiting? -**Options**: -- A: No client-side limiting, rely on API 429 responses + retry -- B: Track request counts and proactively throttle -- C: Configurable rate limiter with token bucket algorithm - -**Recommendation**: Option A initially, B if users report frequent 429s - -### 3. Batch Operation Errors - -**Question**: How to handle partial failures in batch operations? -**Options**: -- A: Always continue on error, return mixed results -- B: Stop on first error if continueOnError=false -- C: Provide rollback mechanism for partial success - -**Recommendation**: Option B (current design), consider A with transaction support later - ---- - -## Dependencies - -### Internal Dependencies -- `src/core/http/client.ts` - HTTP client with retry -- `src/core/errors/` - Error hierarchy -- `src/core/types.ts` - Type definitions -- `src/generated/` - OpenAPI-generated types - -### External Dependencies -- `node:crypto` - Certificate parsing (Node 18+) -- `form-data` - FormData for certificate upload (if browser FormData insufficient) - -### No New External Dependencies Required -- All functionality achievable with existing dependencies -- Node 18+ provides native crypto APIs -- FormData may need polyfill for Node.js (already in dependencies) - ---- - -## Success Metrics - -### Code Quality -- [ ] Test coverage >90% for companies.ts -- [ ] Zero TypeScript errors -- [ ] Zero linting warnings -- [ ] No `any` types in public API - -### Functionality -- [ ] All 19 tasks completed -- [ ] All CRUD operations working -- [ ] Certificate management complete -- [ ] Helper methods implemented - -### Documentation -- [ ] Complete JSDoc for all public methods -- [ ] API.md updated -- [ ] Migration guide updated -- [ ] Examples validated against real API - -### Performance -- [ ] list() operation < 500ms for 100 companies -- [ ] Certificate upload < 5s for typical certificate - ---- - -## Future Enhancements (Out of Scope) - -1. **Webhook Support**: Notifications for company events (created, updated, certificate expired) -2. **Company Analytics**: Dashboard data, usage statistics -3. **Certificate Auto-Renewal**: Automated certificate rotation before expiration -4. **Advanced Search**: Full-text search, complex filters, saved queries -5. **Audit Trail**: Track all company modifications with user attribution -6. **Multi-Company Operations**: Cross-company reports and analytics - -These are explicitly out of scope for this change but documented for future consideration. diff --git a/openspec/changes/implement-companies-resource/proposal.md b/openspec/changes/implement-companies-resource/proposal.md deleted file mode 100644 index d3c2e5d..0000000 --- a/openspec/changes/implement-companies-resource/proposal.md +++ /dev/null @@ -1,331 +0,0 @@ -# Proposal: Implement Companies Resource - -**Change ID**: `implement-companies-resource` -**Status**: ✅ **COMPLETED** (2026-01-13) -**Created**: 2026-01-11 -**Approved**: 2026-01-13 -**Author**: AI Assistant - ---- - -## Problem Statement - -While the Companies resource has a basic implementation in `src/core/resources/companies.ts`, it requires completion and enhancement to match the production requirements specified in the AGENTS.md roadmap (Sprint 3, item 3). Currently: - -1. **Incomplete implementation**: The existing code has basic CRUD operations but may be missing enterprise features needed for production -2. **Limited certificate management**: Certificate operations need validation, error handling, and status checking improvements -3. **Incomplete testing**: Integration tests exist but may not cover all edge cases and error scenarios -4. **Documentation gaps**: JSDoc exists but needs enhancement with real-world examples and common pitfalls -5. **Type safety concerns**: Need to ensure all operations use generated types from OpenAPI specs - -Companies are fundamental to the NFE.io API since they scope most other resources (ServiceInvoices, LegalPeople, NaturalPeople). A robust Companies implementation is critical for the v3 SDK success. - ---- - -## Goals - -### Primary Goals - -1. **Complete CRUD operations**: Ensure all company management operations (create, list, retrieve, update, remove) are production-ready with proper error handling -2. **Enhanced certificate management**: Implement secure certificate upload/retrieval with validation, expiration checking, and error recovery -3. **Comprehensive testing**: Unit tests for all methods, integration tests covering real API scenarios, error handling tests -4. **Production documentation**: Complete JSDoc with examples, migration guide from v2, and troubleshooting section - -### Secondary Goals - -1. **Performance optimization**: Add caching for certificate status, pagination support for large company lists -2. **Validation helpers**: Pre-flight validation for CNPJ/CPF, required fields, certificate formats -3. **Advanced search**: Filter companies by tax number, name, certificate status -4. **Monitoring hooks**: Events/callbacks for tracking company operations in user applications - -### Non-Goals - -1. **Company analytics**: Advanced reporting/dashboards are out of scope -2. **Multi-tenant architecture**: Each API key scopes companies; no cross-tenant operations -3. **Company onboarding UI**: This is an SDK, not a user interface -4. **Backwards compatibility with v2**: This is v3 only; separate migration path exists - ---- - -## Proposed Solution - -### High-Level Approach - -Enhance the existing `src/core/resources/companies.ts` implementation by: - -1. **Completing core operations**: Ensure all CRUD methods handle edge cases (empty responses, rate limits, validation errors) -2. **Certificate management overhaul**: - - Add certificate validation before upload - - Implement retry logic for transient failures - - Add certificate rotation helpers - - Provide certificate expiration notifications -3. **Comprehensive testing**: - - Unit tests with MSW mocks - - Integration tests against sandbox API - - Edge case coverage (expired certificates, invalid CNPJs, etc.) -4. **Enhanced documentation**: - - Migration examples from v2 - - Common use case recipes - - Error handling patterns - -### Architecture Components - -The Companies resource will maintain the existing architecture pattern but with enhancements: - -```typescript -// src/core/resources/companies.ts -export class CompaniesResource { - // Existing CRUD operations (enhanced) - create() { } - list() { } - retrieve() { } - update() { } - remove() { } - - // Certificate management (enhanced + new methods) - uploadCertificate() { } - getCertificateStatus() { } - validateCertificate() { } // NEW - replaceCertificate() { } // NEW - checkCertificateExpiration() { } // NEW - - // Search/filter helpers (new) - findByTaxNumber() { } - findByName() { } - getCompaniesWithCertificates() { } // NEW: Returns companies with valid certificates - getCompaniesWithExpiringCertificates() { } // NEW: Returns companies with expiring certificates -} -``` - -### Key Features - -#### 1. Enhanced Certificate Management - -```typescript -// Validate certificate before upload -await nfe.companies.validateCertificate(fileBuffer, password); - -// Upload with automatic retry -await nfe.companies.uploadCertificate(companyId, { - file: certificateBuffer, - password: 'secret', - filename: 'certificate.pfx' -}); - -// Check expiration -const status = await nfe.companies.getCertificateStatus(companyId); -if (status.expiresOn && isExpiringSoon(status.expiresOn)) { - console.warn('Certificate expires soon:', status.expiresOn); -} - -// Rotate certificate -await nfe.companies.replaceCertificate(companyId, { - oldPassword: 'old-secret', - newFile: newCertBuffer, - newPassword: 'new-secret' -}); -``` - -#### 2. Advanced Search - -```typescript -// Find by tax number -const company = await nfe.companies.findByTaxNumber(12345678901234); - -// Find by name pattern -const matches = await nfe.companies.findByName('Acme Corp'); - -// Get companies with valid certificates -const withCerts = await nfe.companies.getCompaniesWithCertificates(); - -// Get companies with expiring certificates (within 30 days) -const expiring = await nfe.companies.getCompaniesWithExpiringCertificates(30); -``` - ---- - -## Implementation Phases - -### Phase 1: Core Enhancement (Days 1-2) -**Goal**: Complete and harden existing CRUD operations - -- Enhance error handling for all CRUD methods -- Add input validation (CNPJ format, required fields) -- Implement proper pagination for list() -- Add retry logic for transient failures -- Write unit tests for all CRUD operations - -### Phase 2: Certificate Management (Days 3-4) -**Goal**: Production-ready certificate handling - -- Add certificate validation before upload -- Implement getCertificateStatus() enhancements -- Create validateCertificate() helper -- Add replaceCertificate() for rotation -- Add checkCertificateExpiration() warnings -- Write unit and integration tests for certificates - -### Phase 3: Search & Helpers (Day 5) -**Goal**: Developer convenience methods - -- Implement findByTaxNumber() -- Implement findByName() -- Add getCompaniesWithCertificates() -- Add getCompaniesWithExpiringCertificates() -- Write tests for search methods - -### Phase 4: Documentation & Polish (Days 6-7) -**Goal**: Production-ready documentation - -- Complete JSDoc for all public methods -- Add migration guide from v2 -- Create example recipes for common scenarios -- Write troubleshooting guide -- Final integration test review - ---- - -## Success Criteria - -### Functional Criteria -1. ✅ All CRUD operations handle edge cases gracefully -2. ✅ Certificate upload works with various file formats (.pfx, .p12) -3. ✅ Certificate status accurately reflects expiration and validity -4. ✅ Search methods return accurate results -5. ✅ All methods use generated types from OpenAPI specs - -### Quality Criteria -1. ✅ Unit test coverage > 90% for companies.ts -2. ✅ Integration tests cover all happy paths and common errors -3. ✅ TypeScript compilation passes with strict mode -4. ✅ JSDoc complete for all public methods with examples -5. ✅ No `any` types in public API surface -6. ✅ Linting passes without warnings - -### Documentation Criteria -1. ✅ API.md updated with complete Companies section -2. ✅ Migration guide includes Companies examples -3. ✅ Troubleshooting guide covers common certificate issues -4. ✅ Example code validated against real API - ---- - -## Risks and Mitigations - -| Risk | Impact | Likelihood | Mitigation | -|------|--------|------------|-----------| -| Certificate validation may fail for valid certs | High | Low | Extensive testing with various cert formats; provide override option | -| OpenAPI spec may not match actual API behavior | High | Medium | Validate against real API; document discrepancies; use integration tests | -| Certificate password validation edge cases | Medium | Low | Test with special characters; proper encoding | -| Pagination may not work consistently across environments | Medium | Low | Test against production and sandbox; document differences | - ---- - -## Open Questions - -1. **Certificate formats**: Does the API support formats beyond .pfx/.p12? (e.g., .pem, .jks) -2. **Rate limiting**: What are the actual rate limits for company operations? Should we implement client-side throttling? -3. **Certificate storage**: Does the API store certificates securely? Should we document security best practices? -4. **Company deletion**: Does removing a company cascade delete invoices/people? Should we warn users? -5. **Pagination**: Does list() support cursor-based pagination or only offset-based? - ---- - -## Dependencies - -- **Depends on**: `generate-sdk-from-openapi` change (77/89 tasks complete) - need generated types -- **Blocks**: Service invoice operations require valid companies -- **Related**: LegalPeople and NaturalPeople resources are company-scoped - ---- - -## Related Changes - -- **Completes**: Sprint 3, item 3 from AGENTS.md -- **Enables**: Full service invoice workflows (requires valid company with certificate) -- **Future**: Company webhook events, company analytics dashboard integration - ---- - -## Notes - -- Existing implementation at `src/core/resources/companies.ts` is ~239 lines -- Integration tests exist at `tests/integration/companies.integration.test.ts` (~209 lines) -- v2 implementation at `lib/resources/Companies.js` has simpler interface (~48 lines) -- Certificate upload uses FormData which requires proper handling in Node.js vs browser - ---- - -## Implementation Summary - -**Completed**: 2026-01-13 -**Total Effort**: 7 days as estimated - -### What Was Implemented - -#### Code Artifacts -- ✅ **Companies Resource**: `src/core/resources/companies.ts` (603 lines) - - 7 CRUD methods (create, list, listAll, listIterator, retrieve, update, remove) - - 6 certificate methods (validateCertificate, uploadCertificate, getCertificateStatus, replaceCertificate, checkCertificateExpiration, CertificateValidator) - - 4 search helpers (findByTaxNumber, findByName, getCompaniesWithCertificates, getCompaniesWithExpiringCertificates) - -- ✅ **Certificate Validator**: `src/core/utils/certificate-validator.ts` (new utility) - - Certificate parsing and validation - - Expiration checking - - Format validation (.pfx, .p12) - -- ✅ **Tests**: 40 tests, 100% passing - - Unit tests: `tests/unit/companies.test.ts` - - Certificate tests: `tests/unit/certificate-validator.test.ts` (14 tests) - - Certificate methods: `tests/unit/companies-certificates.test.ts` (13 tests) - - Search tests: `tests/unit/companies-search.test.ts` (13 tests) - - Integration tests: `tests/integration/companies.integration.test.ts` - -- ✅ **Documentation**: ~300 lines added - - API reference: `docs/API.md` (Companies section expanded) - - Migration guide: `MIGRATION.md` (Companies + Certificate Management sections) - -### Deviations from Proposal - -**Minor naming adjustments** (functionality preserved): -- Proposed: `getCompaniesWithActiveCertificates()` and `getCompaniesWithExpiredCertificates()` -- Implemented: `getCompaniesWithCertificates()` and `getCompaniesWithExpiringCertificates(thresholdDays)` -- **Rationale**: More flexible - `getCompaniesWithExpiringCertificates()` accepts custom threshold, better UX - -**Removed per user request**: -- Bulk operations (`createBatch`, `updateBatch`) - Removed in early implementation as requested by user - -### Quality Metrics - -- ✅ TypeScript compilation: 0 errors -- ✅ Linting: 39 pre-existing warnings, 0 new warnings -- ✅ Test coverage: 100% for new code (40/40 tests passing) -- ✅ Integration tests: Ready (require NFE_API_KEY) -- ✅ Build: Successful (dist artifacts generated) -- ✅ JSDoc: Complete for all 17 public methods -- ✅ Type safety: No `any` types in public API - -### Open Questions - Resolved - -1. **Certificate formats**: ✅ Supports .pfx and .p12 (validated in implementation) -2. **Rate limiting**: ✅ Inherited from HTTP client retry logic -3. **Certificate storage**: ✅ Documented security best practices in API.md -4. **Company deletion**: ✅ Documented as `remove()` method (cascade behavior per API) -5. **Pagination**: ✅ Offset-based with pageCount/pageIndex (validated) - -### Production Readiness - -✅ **ALL SUCCESS CRITERIA MET**: -- All CRUD operations handle edge cases gracefully -- Certificate upload works with .pfx/.p12 formats -- Certificate status accurately reflects expiration/validity -- Search methods return accurate results -- All methods use generated types -- Unit test coverage: 100% for new code -- Integration tests ready for real API -- TypeScript strict mode: passing -- JSDoc complete with examples -- No `any` types in public API -- Documentation complete and accurate - -**Status**: 🎉 **PRODUCTION READY - All 19 tasks completed** diff --git a/openspec/changes/implement-companies-resource/specs/certificate-management/spec.md b/openspec/changes/implement-companies-resource/specs/certificate-management/spec.md deleted file mode 100644 index 5b16492..0000000 --- a/openspec/changes/implement-companies-resource/specs/certificate-management/spec.md +++ /dev/null @@ -1,268 +0,0 @@ -# Spec: Certificate Management - -**Capability**: `certificate-management` -**Related Change**: `implement-companies-resource` - ---- - -## ADDED Requirements - -### Requirement: Upload Digital Certificate -**Priority**: CRITICAL -**Rationale**: Companies must have valid digital certificates to issue electronic invoices. Certificate upload is mandatory for invoice operations. - -The SDK MUST provide uploadCertificate() method that validates certificates locally before upload, supports .pfx and .p12 formats, handles FormData for file upload, and provides detailed error messages for validation failures. - -#### Scenario: Upload valid certificate -- **Given** a valid .pfx certificate file and correct password -- **When** the user calls `nfe.companies.uploadCertificate("company-123", { file: buffer, password: "secret" })` -- **Then** the SDK validates the certificate locally first -- **And** uploads the certificate to the NFE.io API -- **And** returns an upload confirmation { uploaded: true } -- **And** the certificate becomes active for the company - -#### Scenario: Reject certificate with wrong password -- **Given** a valid certificate file but incorrect password -- **When** the user calls `nfe.companies.uploadCertificate("company-123", { file: buffer, password: "wrong" })` -- **Then** the SDK throws a CertificateError during local validation -- **And** the error message indicates password verification failed -- **And** no API request is made - -#### Scenario: Reject unsupported certificate format -- **Given** a certificate in unsupported format (e.g., .pem, .jks) -- **When** the user calls `nfe.companies.uploadCertificate("company-123", { file: buffer, password: "secret" })` -- **Then** the SDK throws a ValidationError -- **And** the error message lists supported formats (.pfx, .p12) -- **And** no API request is made - -#### Scenario: Handle expired certificate upload -- **Given** a valid but expired certificate -- **When** the user calls `nfe.companies.uploadCertificate("company-123", { file: buffer, password: "secret" })` -- **Then** the SDK throws a CertificateError -- **And** the error indicates the certificate has expired -- **And** includes the expiration date -- **And** no API request is made - -#### Scenario: Upload certificate with progress tracking -- **Given** a large certificate file (e.g., 5MB) -- **When** the user calls `nfe.companies.uploadCertificate("company-123", { file: buffer, password: "secret", onProgress: callback })` -- **Then** the SDK calls the onProgress callback with upload percentage -- **And** percentages range from 0 to 100 -- **And** the upload completes successfully - ---- - -### Requirement: Validate Certificate Before Upload -**Priority**: HIGH -**Rationale**: Pre-upload validation prevents wasted API calls and provides immediate feedback on certificate issues. - -The SDK MUST provide validateCertificate() method that parses certificates client-side, extracts metadata (subject, issuer, expiration), validates password, checks expiration dates, and returns detailed validation results without making API requests. - -#### Scenario: Validate a valid certificate -- **Given** a valid .pfx certificate and correct password -- **When** the user calls `nfe.companies.validateCertificate(buffer, "secret")` -- **Then** the SDK returns { valid: true, metadata: { subject, issuer, expiresOn, ... } } -- **And** metadata includes certificate details -- **And** no API request is made (client-side only) - -#### Scenario: Detect expired certificate -- **Given** an expired certificate -- **When** the user calls `nfe.companies.validateCertificate(buffer, "secret")` -- **Then** the SDK returns { valid: false, error: "Certificate has expired", metadata: ... } -- **And** includes the expiration date in metadata -- **And** no API request is made - -#### Scenario: Detect not-yet-valid certificate -- **Given** a certificate with validFrom date in the future -- **When** the user calls `nfe.companies.validateCertificate(buffer, "secret")` -- **Then** the SDK returns { valid: false, error: "Certificate is not yet valid", metadata: ... } -- **And** includes the validFrom date in metadata - -#### Scenario: Handle corrupt certificate file -- **Given** a corrupted or invalid file -- **When** the user calls `nfe.companies.validateCertificate(buffer, "secret")` -- **Then** the SDK returns { valid: false, error: "Invalid certificate or password" } -- **And** does not throw an exception (returns error object instead) - ---- - -### Requirement: Get Certificate Status -**Priority**: HIGH -**Rationale**: Users need to check certificate validity and expiration to ensure invoices can be issued. - -The SDK MUST provide getCertificateStatus() method that retrieves certificate information from the API, calculates days until expiration, determines validity status, and identifies expiring-soon certificates (< 30 days). - -#### Scenario: Get status of company with valid certificate -- **Given** a company has an active, valid certificate -- **When** the user calls `nfe.companies.getCertificateStatus("company-123")` -- **Then** the SDK returns detailed status: - - hasCertificate: true - - isValid: true - - expiresOn: "2026-12-31" - - daysUntilExpiration: 354 - - isExpiringSoon: false -- **And** the API request completes in < 1 second - -#### Scenario: Get status of company with expiring certificate -- **Given** a company has a certificate expiring in 15 days -- **When** the user calls `nfe.companies.getCertificateStatus("company-123")` -- **Then** the SDK returns: - - hasCertificate: true - - isValid: true - - expiresOn: "2026-01-26" - - daysUntilExpiration: 15 - - isExpiringSoon: true (< 30 days) - -#### Scenario: Get status of company with expired certificate -- **Given** a company has an expired certificate -- **When** the user calls `nfe.companies.getCertificateStatus("company-123")` -- **Then** the SDK returns: - - hasCertificate: true - - isValid: false - - expiresOn: "2025-12-31" - - daysUntilExpiration: -11 (negative = expired) - - isExpiringSoon: false - -#### Scenario: Get status of company without certificate -- **Given** a company has no certificate uploaded -- **When** the user calls `nfe.companies.getCertificateStatus("company-123")` -- **Then** the SDK returns: - - hasCertificate: false - - isValid: false - - Other fields are undefined or null - ---- - -### Requirement: Replace/Rotate Certificate -**Priority**: MEDIUM -**Rationale**: Certificates expire and must be rotated. A dedicated method simplifies this common operation. - -The SDK MUST provide replaceCertificate() method that validates new certificate, uploads it to replace the existing one, optionally verifies old certificate password, and ensures atomic replacement (old certificate remains if new upload fails). - -#### Scenario: Replace certificate with new one -- **Given** a company has an existing certificate -- **When** the user calls `nfe.companies.replaceCertificate("company-123", { newFile: buffer, newPassword: "new-secret" })` -- **Then** the SDK validates the new certificate -- **And** uploads the new certificate (replacing the old one) -- **And** returns upload confirmation -- **And** the old certificate is no longer active - -#### Scenario: Replace with verification of old certificate -- **Given** a company has an existing certificate -- **When** the user calls `nfe.companies.replaceCertificate("company-123", { oldPassword: "old-secret", newFile: buffer, newPassword: "new-secret" })` -- **Then** the SDK verifies the old password matches (optional safety check) -- **And** proceeds with replacement if verification succeeds -- **And** throws CertificateError if old password is wrong - -#### Scenario: Atomic replacement on API error -- **Given** a company has an existing certificate -- **When** the user calls `nfe.companies.replaceCertificate()` but the API fails -- **Then** the SDK does not delete the old certificate -- **And** throws the appropriate error -- **And** the old certificate remains active - ---- - -### Requirement: Check Certificate Expiration Warning -**Priority**: LOW -**Rationale**: Proactive warnings help users avoid service disruptions from expired certificates. - -The SDK MUST provide checkCertificateExpiration() method that checks certificate expiration against a configurable threshold (default 30 days), returns warning object if expiring soon, and returns null if certificate is valid beyond threshold. - -#### Scenario: Warning for expiring certificate -- **Given** a company has a certificate expiring in 20 days -- **When** the user calls `nfe.companies.checkCertificateExpiration("company-123", 30)` -- **Then** the SDK returns a warning object: - - companyId: "company-123" - - expiresOn: "2026-01-31" - - daysRemaining: 20 - - message: "Certificate expires in 20 days" - -#### Scenario: No warning for valid certificate -- **Given** a company has a certificate expiring in 60 days -- **When** the user calls `nfe.companies.checkCertificateExpiration("company-123", 30)` -- **Then** the SDK returns null (no warning) -- **And** no error is thrown - -#### Scenario: Custom threshold for warnings -- **Given** a company has a certificate expiring in 40 days -- **When** the user calls `nfe.companies.checkCertificateExpiration("company-123", 45)` -- **Then** the SDK returns a warning (40 < 45) -- **When** the user calls `nfe.companies.checkCertificateExpiration("company-123", 30)` -- **Then** the SDK returns null (40 > 30) - ---- - -### Requirement: Batch Certificate Status Check -**Priority**: LOW -**Rationale**: Users managing many companies need efficient bulk certificate checking. - -The SDK MUST provide helper methods (getCompaniesWithActiveCertificates, getCompaniesWithExpiredCertificates, getCompaniesWithExpiringSoonCertificates) that filter companies by certificate status efficiently, batch certificate status checks to avoid rate limits, and return filtered company lists. - -#### Scenario: Get companies with active certificates -- **Given** 100 companies, 60 with valid certificates, 20 expired, 20 without certificates -- **When** the user calls `nfe.companies.getCompaniesWithActiveCertificates()` -- **Then** the SDK returns an array of 60 companies -- **And** each company has hasCertificate=true and isValid=true -- **And** the operation completes in reasonable time (< 30 seconds) - -#### Scenario: Get companies with expired certificates -- **Given** 100 companies, 20 with expired certificates -- **When** the user calls `nfe.companies.getCompaniesWithExpiredCertificates()` -- **Then** the SDK returns an array of 20 companies -- **And** each company has hasCertificate=true and isValid=false - -#### Scenario: Get companies with expiring soon certificates -- **Given** 100 companies, 15 with certificates expiring in < 30 days -- **When** the user calls `nfe.companies.getCompaniesWithExpiringSoonCertificates(30)` -- **Then** the SDK returns an array of 15 companies -- **And** each company's certificate expires within 30 days - ---- - -## MODIFIED Requirements - -None - certificate management is new in v3 with enhanced functionality. - ---- - -## REMOVED Requirements - -None. - ---- - -## Cross-Capability Dependencies - -- **Depends on**: `company-crud-operations` (requires company to exist) -- **Enables**: Service invoice creation (requires valid certificate) -- **Relates to**: Error handling (uses CertificateError, ValidationError) - ---- - -## Security Considerations - -1. **Password Handling**: - - Passwords never logged or stored by SDK - - Passwords transmitted only over HTTPS - - Certificate buffers cleared from memory after upload - -2. **Certificate Validation**: - - Local validation prevents uploading invalid certificates - - Reduces exposure of private keys to API - - Catches errors early before network transmission - -3. **Error Messages**: - - Avoid exposing certificate contents in errors - - Generic messages for authentication failures - - Detailed messages only for validation (no sensitive data) - ---- - -## Notes - -- Certificate formats supported: .pfx, .p12 (PKCS#12) -- Node.js 18+ provides native crypto APIs for certificate parsing -- FormData handling may require `form-data` package in Node.js -- Certificate file size limits should be documented (typical: 1-10MB) -- API may have rate limits on certificate uploads (e.g., 10 per hour) diff --git a/openspec/changes/implement-companies-resource/specs/company-crud-operations/spec.md b/openspec/changes/implement-companies-resource/specs/company-crud-operations/spec.md deleted file mode 100644 index 203e565..0000000 --- a/openspec/changes/implement-companies-resource/specs/company-crud-operations/spec.md +++ /dev/null @@ -1,192 +0,0 @@ -# Spec: Company CRUD Operations - -**Capability**: `company-crud-operations` -**Related Change**: `implement-companies-resource` - ---- - -## ADDED Requirements - -### Requirement: Create Company with Validation -**Priority**: CRITICAL -**Rationale**: Creating companies is the foundation of all NFE.io operations. Without proper validation, users may waste API calls and receive unclear error messages. - -The SDK MUST provide a create() method that accepts company data, validates it client-side, and creates the company via the NFE.io API. The method MUST handle validation errors, authentication errors, and conflict errors gracefully. - -#### Scenario: Successfully create a valid company -- **Given** a valid company data object with required fields (name, federalTaxNumber, address) -- **When** the user calls `nfe.companies.create(data)` -- **Then** the API creates the company and returns a Company object with generated `id` -- **And** the company data includes `createdOn` and `modifiedOn` timestamps -- **And** the returned company matches the input data - -#### Scenario: Reject invalid CNPJ format -- **Given** a company data object with invalid CNPJ (wrong length or invalid check digits) -- **When** the user calls `nfe.companies.create(data)` -- **Then** the SDK throws a ValidationError before making an API call -- **And** the error message explains the CNPJ format requirement -- **And** no API request is made - -#### Scenario: Handle authentication errors gracefully -- **Given** an invalid API key is configured -- **When** the user calls `nfe.companies.create(data)` -- **Then** the SDK throws an AuthenticationError -- **And** the error message indicates the API key is invalid -- **And** the error includes the HTTP status code (401) - -#### Scenario: Handle duplicate company errors -- **Given** a company with the same federalTaxNumber already exists -- **When** the user calls `nfe.companies.create(data)` -- **Then** the SDK throws a ConflictError -- **And** the error message indicates a duplicate company exists -- **And** the error includes the conflicting company ID if available - ---- - -### Requirement: List Companies with Pagination -**Priority**: HIGH -**Rationale**: Users need to retrieve their companies efficiently, especially when they have many companies. Proper pagination prevents memory issues and improves performance. - -The SDK MUST provide list() method with pagination support, listAll() for auto-pagination, and listIterator() for async iteration. Pagination MUST handle page boundaries correctly and prevent duplicate results. - -#### Scenario: List first page of companies -- **Given** the user has at least 10 companies in their account -- **When** the user calls `nfe.companies.list({ pageCount: 10, pageIndex: 0 })` -- **Then** the SDK returns a ListResponse with exactly 10 companies -- **And** the response includes pagination metadata (totalCount, hasMore) -- **And** the companies are in a consistent order (e.g., by creation date) - -#### Scenario: Navigate through pages -- **Given** the user has 50 companies in their account -- **When** the user calls `nfe.companies.list({ pageCount: 20, pageIndex: 0 })` -- **And** then calls `nfe.companies.list({ pageCount: 20, pageIndex: 1 })` -- **And** then calls `nfe.companies.list({ pageCount: 20, pageIndex: 2 })` -- **Then** the SDK returns 20, 20, and 10 companies respectively -- **And** no company appears in multiple pages -- **And** all 50 companies are retrieved across the three pages - -#### Scenario: Auto-paginate all companies -- **Given** the user has 250 companies in their account -- **When** the user calls `nfe.companies.listAll()` -- **Then** the SDK automatically fetches all pages -- **And** returns an array of all 250 companies -- **And** makes exactly 3 API requests (100 per page) - -#### Scenario: Stream companies with async iterator -- **Given** the user has 1000 companies in their account -- **When** the user iterates with `for await (const company of nfe.companies.listIterator())` -- **Then** the SDK yields companies one at a time -- **And** automatically fetches new pages as needed -- **And** the memory usage remains constant regardless of total count - ---- - -### Requirement: Retrieve Company by ID -**Priority**: HIGH -**Rationale**: Users frequently need to fetch a specific company by its ID for display or further operations. - -The SDK MUST provide a retrieve() method that fetches a single company by ID. The method MUST throw NotFoundError for non-existent companies and return complete company data for valid IDs. - -#### Scenario: Retrieve an existing company -- **Given** a company exists with ID "company-123" -- **When** the user calls `nfe.companies.retrieve("company-123")` -- **Then** the SDK returns the Company object -- **And** the returned data matches the company's current state -- **And** includes all fields (name, federalTaxNumber, address, etc.) - -#### Scenario: Handle non-existent company -- **Given** no company exists with ID "invalid-id" -- **When** the user calls `nfe.companies.retrieve("invalid-id")` -- **Then** the SDK throws a NotFoundError -- **And** the error message indicates the company was not found -- **And** the error includes the requested company ID - ---- - -### Requirement: Update Company Information -**Priority**: HIGH -**Rationale**: Companies need to update their information when details change (address, contact info, etc.). - -The SDK MUST provide an update() method that accepts a company ID and partial update data. The method MUST validate updates client-side, support partial updates (only specified fields changed), and return the complete updated company object. - -#### Scenario: Update company name -- **Given** a company exists with ID "company-123" and name "Old Name" -- **When** the user calls `nfe.companies.update("company-123", { name: "New Name" })` -- **Then** the SDK updates the company and returns the updated Company object -- **And** the returned company has name "New Name" -- **And** the modifiedOn timestamp is updated -- **And** other fields remain unchanged - -#### Scenario: Update multiple fields -- **Given** a company exists with ID "company-123" -- **When** the user calls `nfe.companies.update("company-123", { name: "New Name", email: "new@example.com" })` -- **Then** the SDK updates both fields -- **And** returns the complete updated Company object -- **And** all specified fields are updated -- **And** unspecified fields remain unchanged - -#### Scenario: Reject invalid updates -- **Given** a company exists with ID "company-123" -- **When** the user calls `nfe.companies.update("company-123", { federalTaxNumber: "invalid" })` -- **Then** the SDK throws a ValidationError -- **And** the company data remains unchanged -- **And** no API request is made (client-side validation) - ---- - -### Requirement: Delete Company -**Priority**: MEDIUM -**Rationale**: Users need to remove companies that are no longer active. Note: Method named `remove()` to avoid JavaScript keyword conflicts. - -The SDK MUST provide a remove() method that deletes a company by ID. The method MUST handle non-existent companies (NotFoundError), potential cascade deletions, and return deletion confirmation. - -#### Scenario: Successfully delete a company -- **Given** a company exists with ID "company-123" and has no dependent resources -- **When** the user calls `nfe.companies.remove("company-123")` -- **Then** the SDK deletes the company -- **And** returns a deletion confirmation { deleted: true, id: "company-123" } -- **And** subsequent retrieve() calls for this ID throw NotFoundError - -#### Scenario: Handle deletion with dependent resources -- **Given** a company exists with ID "company-123" -- **And** the company has active service invoices -- **When** the user calls `nfe.companies.remove("company-123")` -- **Then** the SDK may throw a ConflictError (if API prevents deletion) -- **Or** successfully deletes with cascade (if API allows) -- **And** the behavior is documented clearly - -#### Scenario: Handle deletion of non-existent company -- **Given** no company exists with ID "invalid-id" -- **When** the user calls `nfe.companies.remove("invalid-id")` -- **Then** the SDK throws a NotFoundError -- **And** the error indicates the company was not found - ---- - -## MODIFIED Requirements - -None - these are new requirements for v3. - ---- - -## REMOVED Requirements - -None - all v2 functionality is preserved with modernization. - ---- - -## Cross-Capability Dependencies - -- **Depends on**: Generated types from `generate-sdk-from-openapi` change -- **Used by**: `service-invoices`, `legal-people`, `natural-people` (all company-scoped) -- **Relates to**: `certificate-management` capability (companies need certificates for invoices) - ---- - -## Notes - -- All CRUD operations use generated types from `src/generated/index.ts` -- Error handling follows the error hierarchy defined in `src/core/errors/` -- Pagination strategy may vary based on actual API implementation (offset vs cursor) -- The `remove()` method is named to avoid the JavaScript `delete` keyword -- All operations support retry for transient failures (5xx, rate limits) diff --git a/openspec/changes/implement-companies-resource/tasks.md b/openspec/changes/implement-companies-resource/tasks.md deleted file mode 100644 index cff0976..0000000 --- a/openspec/changes/implement-companies-resource/tasks.md +++ /dev/null @@ -1,505 +0,0 @@ -# Tasks: Implement Companies Resource - -**Change ID**: `implement-companies-resource` -**Dependencies**: generate-sdk-from-openapi (generated types required) -**Estimated Effort**: 7 days -**Priority**: HIGH (Sprint 3, Critical Path) - ---- - -## Task Organization - -This change is organized into 4 phases with 19 tasks total: -- **Phase 1**: Core Enhancement (6 tasks) - Days 1-2 [x] **COMPLETED** -- **Phase 2**: Certificate Management (7 tasks) - Days 3-4 [x] **COMPLETED** -- **Phase 3**: Search & Helpers (3 tasks) - Day 5 [x] **COMPLETED** -- **Phase 4**: Documentation & Polish (3 tasks) - Days 6-7 [x] **COMPLETED** - -**Overall Status**: [x] **COMPLETED** (All 19 tasks finished) - ---- - -## 🔴 Phase 1: Core Enhancement (Days 1-2) [x] COMPLETED - -### Task 1.1: Enhance CRUD error handling -**Deliverable**: All CRUD methods handle API errors gracefully -**Validation**: Error scenarios throw appropriate typed errors -**Effort**: 3 hours -**Status**: [x] **Completed** - -**Completed Work**: -- [x] HTTP client handles API errors gracefully with retry logic -- [x] All CRUD methods use typed errors (ValidationError, NotFoundError, etc.) -- [x] Retry logic configured for 429 and 5xx errors -- [x] Input validation added with validateCompanyData() - ---- - -### Task 1.2: Add input validation -**Deliverable**: Pre-flight validation for company data -**Validation**: Invalid input rejected before API call -**Effort**: 2 hours -**Status**: [x] **Completed** - -**Completed Work**: -- [x] validateCNPJ() helper (14 digits validation) -- [x] validateCPF() helper (11 digits validation) -- [x] validateCompanyData() validates before API calls -- [x] Email format validation -- [x] Unit tests written and passing - ---- - -### Task 1.3: Implement proper pagination -**Deliverable**: list() method supports pagination properly -**Validation**: Can fetch all companies across multiple pages -**Effort**: 2 hours -**Status**: [x] **Completed** - -**Completed Work**: -- [x] list() supports pageCount and pageIndex -- [x] listAll() auto-paginates through all pages -- [x] listIterator() async generator for memory-efficient streaming -- [x] Tests written and passing - ---- - -### Task 1.4: Add retry logic for CRUD operations -**Deliverable**: Transient failures automatically retry -**Validation**: 5xx and rate limit errors retry with backoff -**Effort**: 2 hours -**Depends on**: HTTP client retry support (from runtime layer) -**Status**: [x] **Completed** - -**Completed Work**: -- [x] HTTP client has built-in retry logic with exponential backoff -- [x] Retry policy configured: maxRetries=3, baseDelay=1000ms -- [x] All CRUD operations inherit retry behavior from HTTP client -- [x] Idempotent operations (GET, PUT, DELETE) automatically retry -- [x] Non-idempotent POST operations use retry cautiously - -**Validation**: -```typescript -// Should retry 5xx errors up to 3 times -// Mock server returns 503 twice, then 200 -const company = await nfe.companies.retrieve('company-id'); -expect(httpClient.requestCount).toBe(3); // 2 retries + 1 success -``` - ---- - -### Task 1.5: Write unit tests for CRUD operations -**Deliverable**: >90% coverage for CRUD methods -**Validation**: All tests pass, coverage report shows gaps -**Effort**: 3 hours -**Status**: [x] **Completed** - -**Completed Work**: -- [x] Mock HTTP client created for isolated testing -- [x] Tests for create() with valid/invalid data -- [x] Tests for list() with various pagination scenarios (pageCount, pageIndex) -- [x] Tests for retrieve() (found and not found cases) -- [x] Tests for update() with partial updates -- [x] Tests for remove() success and error cases -- [x] All tests passing (100%) - -**Files Updated**: -- [x] `tests/unit/companies.test.ts` - ---- - -### Task 1.6: Integration tests for CRUD operations -**Deliverable**: End-to-end CRUD tests against sandbox API -**Validation**: Tests pass against real API -**Effort**: 2 hours -**Depends on**: Task 1.1-1.4 -**Status**: [x] **Completed** - -**Completed Work**: -- [x] Integration tests written in `tests/integration/companies.integration.test.ts` -- [x] Full CRUD lifecycle tests (create → retrieve → update → remove) -- [x] Error scenario tests (invalid auth, bad data) -- [x] Pagination tests with real data scenarios -- [x] Cleanup logic implemented to remove test companies -- [x] Tests require NFE_API_KEY environment variable (expected) - -**Validation**: -```bash -npm run test:integration -- tests/integration/companies -# All tests pass against sandbox API -``` - ---- - -## 🟡 Phase 2: Certificate Management (Days 3-4) - -### Task 2.1: Add certificate validation before upload -**Deliverable**: validateCertificate() method -**Validation**: Detects invalid certificates before upload -**Effort**: 3 hours -**Status**: [x] **Completed** - -**Completed Work**: -- [x] validateCertificate(file, password) helper implemented -- [x] File format validation (.pfx, .p12 supported) -- [x] Certificate parsing with password verification -- [x] Metadata extraction (subject, issuer, expiration dates) -- [x] Detailed validation results with error messages -- [x] CertificateValidator utility class created - -**Validation**: -```typescript -const result = await nfe.companies.validateCertificate( - certificateBuffer, - 'password' -); - -if (result.valid) { - console.log('Expires:', result.expiresOn); -} else { - console.error('Invalid:', result.error); -} -``` - -**Files**: -- Update: `src/core/resources/companies.ts` -- New: `src/core/utils/certificate-validator.ts` (helper) - ---- - -### Task 2.2: Enhance uploadCertificate() with retry -**Deliverable**: Robust certificate upload -**Validation**: Upload succeeds even with transient failures -**Effort**: 2 hours -**Status**: [x] **Completed** - -**Completed Work**: -- [x] Retry logic inherited from HTTP client -- [x] FormData handled properly with multipart/form-data -- [x] Certificate validation before upload (pre-flight check) -- [x] Detailed error messages for validation failures -- [x] Upload method validates certificate expiration and format - -**Validation**: -```typescript -await nfe.companies.uploadCertificate('company-id', { - file: buffer, - password: 'secret', - filename: 'cert.pfx', - onProgress: (percent) => console.log(`${percent}%`) -}); -``` - ---- - -### Task 2.3: Enhance getCertificateStatus() -**Deliverable**: Detailed certificate status information -**Validation**: Returns expiration, validity, and metadata -**Effort**: 2 hours -**Status**: [x] **Completed** - -**Completed Work**: -- [x] API call to GET /companies/{id}/certificate -- [x] Response parsing for certificate details -- [x] Days until expiration calculation -- [x] Certificate validity determination (valid/expired/expiring soon) -- [x] Structured status object returned with all metadata - -**Validation**: -```typescript -const status = await nfe.companies.getCertificateStatus('company-id'); -console.log({ - hasCertificate: status.hasCertificate, - isValid: status.isValid, - expiresOn: status.expiresOn, - daysUntilExpiration: status.daysUntilExpiration, - isExpiringSoon: status.isExpiringSoon // < 30 days -}); -``` - ---- - -### Task 2.4: Implement replaceCertificate() helper -**Deliverable**: Certificate rotation method -**Validation**: Can replace existing certificate seamlessly -**Effort**: 2 hours -**Depends on**: Task 2.1, 2.2 -**Status**: [x] **Completed** - -**Completed Work**: -- [x] replaceCertificate(companyId, { newFile, newPassword }) implemented -- [x] Old certificate verification (optional via getCertificateStatus) -- [x] New certificate validation before upload -- [x] Certificate upload with validation -- [x] New certificate status verification -- [x] Success confirmation returned - -**Validation**: -```typescript -await nfe.companies.replaceCertificate('company-id', { - oldPassword: 'old-secret', // Optional verification - newFile: newCertBuffer, - newPassword: 'new-secret' -}); - -const status = await nfe.companies.getCertificateStatus('company-id'); -expect(status.isValid).toBe(true); -``` - ---- - -### Task 2.5: Add checkCertificateExpiration() warnings -**Deliverable**: Expiration checking helper -**Validation**: Warns about expiring certificates -**Effort**: 1 hour -**Status**: [x] **Completed** - -**Completed Work**: -- [x] checkCertificateExpiration(companyId, daysThreshold = 30) implemented -- [x] Certificate status retrieval -- [x] Days until expiration calculation -- [x] Warning returned if expiring soon -- [x] Custom threshold support - -**Validation**: -```typescript -const warning = await nfe.companies.checkCertificateExpiration( - 'company-id', - 45 // warn if < 45 days -); - -if (warning) { - console.warn(`Certificate expires in ${warning.daysRemaining} days`); -} -``` - ---- - -### Task 2.6: Unit tests for certificate operations -**Deliverable**: >90% coverage for certificate methods -**Validation**: All certificate scenarios tested -**Effort**: 2 hours -**Status**: [x] **Completed** - -**Completed Work**: -- [x] Mock certificate files created (valid, invalid, expired) -- [x] Tests for validateCertificate() with various formats -- [x] Tests for uploadCertificate() success and failure paths -- [x] Tests for getCertificateStatus() parsing -- [x] Tests for getCertificateStatus() parsing -- [x] Tests for replaceCertificate() workflow -- [x] Tests for checkCertificateExpiration() with custom thresholds -- [x] All tests passing (14/14 certificate-validator, 13/13 companies-certificates) - -**Files Created/Updated**: -- [x] `tests/unit/certificate-validator.test.ts` (14 tests) -- [x] `tests/unit/companies-certificates.test.ts` (13 tests) - ---- - -### Task 2.7: Integration tests for certificates -**Deliverable**: E2E certificate management tests -**Validation**: Tests pass against sandbox API with real certificates -**Effort**: 3 hours -**Status**: [x] **Completed** - -**Completed Work**: -- [x] Integration tests written for certificate management -- [x] uploadCertificate() tests with mock files -- [x] getCertificateStatus() tests after upload -- [x] Certificate expiration scenario tests -- [x] replaceCertificate() workflow tests -- [x] Cleanup logic implemented -- [x] Tests require NFE_API_KEY (expected, skipped without key) - -**Notes**: -- Tests ready for real certificates when available -- Currently use mock certificates for validation logic - ---- - -## 🟢 Phase 3: Search & Helpers (Day 5) - -### Task 3.1: Implement search helpers -**Deliverable**: findByTaxNumber() and findByName() -**Validation**: Search returns accurate results -**Effort**: 2 hours -**Status**: [x] **Completed** - -**Completed Work**: -- [x] findByTaxNumber(taxNumber) implemented with exact matching -- [x] findByName(namePattern) implemented with case-insensitive search -- [x] Uses listAll() with client-side filtering -- [x] Returns null if not found (findByTaxNumber) -- [x] Returns array of matches (findByName) -- [x] Optimized with early return when found - -**Validation**: -```typescript -// Find by tax number (exact match) -const company = await nfe.companies.findByTaxNumber(12345678901234); - -// Find by name (pattern matching) -const matches = await nfe.companies.findByName('Acme'); -``` - -**Files**: -- Update: `src/core/resources/companies.ts` - ---- - -### Task 3.2: Implement certificate helper methods -**Deliverable**: Certificate filtering methods -**Validation**: Returns companies matching certificate criteria -**Effort**: 2 hours -**Status**: [x] **Completed** - -**Completed Work**: -- [x] getCompaniesWithCertificates() implemented (returns companies with any certificate) -- [x] getCompaniesWithExpiringCertificates(daysThreshold = 30) implemented -- [x] Certificate status checks for all companies -- [x] Filtering logic for expiring certificates -- [x] Returns detailed company info with certificate status - -**Validation**: -```typescript -// Get all companies with valid certificates -const active = await nfe.companies.getCompaniesWithActiveCertificates(); - -// Get companies needing renewal -const expiringSoon = await nfe.companies.getCompaniesWithExpiringSoonCertificates(45); -``` - ---- - -### Task 3.3: Tests for search and helper methods -**Deliverable**: Tests for all helper methods -**Validation**: Unit and integration tests pass -**Effort**: 2 hours -**Status**: [x] **Completed** - -**Completed Work**: -- [x] Unit tests for findByTaxNumber() (found/not found) -- [x] Unit tests for findByName() (multiple matches, case-insensitive) -- [x] Unit tests for getCompaniesWithCertificates() -- [x] Unit tests for getCompaniesWithExpiringCertificates() -- [x] Integration tests ready for real API -- [x] Edge cases tested (no results, multiple matches, empty list) -- [x] All tests passing (13/13 companies-search.test.ts) - ---- - -## 🟢 Phase 4: Documentation & Polish (Days 6-7) - -### Task 4.1: Complete JSDoc documentation -**Deliverable**: Every public method has complete JSDoc -**Validation**: TypeScript intellisense shows helpful docs -**Effort**: 2 hours -**Status**: [x] **Completed** - -**Completed Work**: -- [x] JSDoc added for all public methods (17 methods) -- [x] Complete @param descriptions with types -- [x] Complete @returns descriptions -- [x] @throws documentation for all error cases -- [x] @example blocks with practical code -- [x] Edge cases documented in descriptions -- [x] TypeScript intellisense fully functional - -**Example**: -```typescript -/** - * Create a new company in the NFE.io system - * - * @param data - Company data (excluding id, createdOn, modifiedOn) - * @returns The created company with generated id - * @throws {ValidationError} If company data is invalid - * @throws {AuthenticationError} If API key is invalid - * @throws {RateLimitError} If rate limit exceeded - * - * @example - * ```typescript - * const company = await nfe.companies.create({ - * name: 'Acme Corp', - * federalTaxNumber: 12345678901234, - * email: 'contact@acme.com', - * // ... - * }); - * ``` - */ -async create(data: Omit): Promise -``` - ---- - -### Task 4.2: Update documentation files -**Deliverable**: API.md and migration guide updated -**Validation**: Documentation accurately reflects implementation -**Effort**: 2 hours -**Status**: [x] **Completed** - -**Completed Work**: -- [x] `docs/API.md` Companies section updated (~200 lines added) -- [x] Examples for all new methods (17 methods documented) -- [x] Common use cases documented (certificate rotation, monitoring) -- [x] Certificate management best practices included -- [x] `MIGRATION.md` updated with v2→v3 examples -- [x] Certificate Management Migration section added -- [x] Monitoring setup examples provided - -**Files Updated**: -- [x] `docs/API.md` (Companies section expanded) -- [x] `MIGRATION.md` (Companies + Certificate sections enhanced) - ---- - -### Task 4.3: Final validation and cleanup -**Deliverable**: Production-ready Companies resource -**Validation**: All checklists pass -**Effort**: 2 hours -**Status**: [x] **Completed** - -**Completed Work**: -- [x] Full test suite executed: 243/267 tests passing (91%) -- [x] Coverage: 40/40 new tests passing (100%) -- [x] Type check: 0 errors (npm run typecheck passed) -- [x] Linter: 39 pre-existing warnings only, 0 new warnings -- [x] Build: Successful (dist/index.js, index.cjs, index.d.ts generated) -- [x] No TODOs or FIXMEs in new code -- [x] Code reviewed and validated - -**Validation Results**: -```bash -[x] npm run typecheck # 0 errors -[x] npm run lint # 39 pre-existing warnings, 0 new -[x] npm test # 243/267 passing (91%), all NEW tests 100% -[x] npm run build # Total errors: 0 - Success -[x] Documentation # 300+ lines added -``` - ---- - -## Summary - -**Total Tasks**: 19 -**Estimated Effort**: 7 days -**Critical Path**: Phases 1-2 must be completed sequentially -**Parallelizable**: Phase 3 can overlap with documentation prep - -**Milestone Checklist**: -- [x] Phase 1 Complete: Core CRUD operations production-ready -- [x] Phase 2 Complete: Certificate management production-ready -- [x] Phase 3 Complete: Helper methods implemented -- [x] Phase 4 Complete: Documentation complete, all tests pass - -**Definition of Done**: -1. [x] All 19 tasks completed -2. [x] Test coverage 100% for new code (40/40 tests passing) -3. [x] All new tests passing (unit + integration ready) -4. [x] TypeScript compilation successful (0 errors) -5. [x] Linting passes (39 pre-existing warnings, 0 new) -6. [x] Documentation complete and accurate (300+ lines) -7. [x] No `any` types in public API -8. [x] Code reviewed and validated - -**🎉 PROJECT COMPLETED - PRODUCTION READY** diff --git a/openspec/changes/implement-service-invoices/README.md b/openspec/changes/implement-service-invoices/README.md deleted file mode 100644 index bf9633c..0000000 --- a/openspec/changes/implement-service-invoices/README.md +++ /dev/null @@ -1,233 +0,0 @@ -# Service Invoices Implementation - Change Summary - -**Change ID**: `implement-service-invoices` -**Status**: ✅ Approved - Ready for Implementation -**Created**: 2026-01-15 -**Approved**: 2026-01-16 - ---- - -## 📋 Quick Summary - -This change implements the complete Service Invoices (Nota Fiscal de Serviço - NFSE) resource for NFE.io SDK v3, covering all CRUD operations, asynchronous invoice processing with polling, email notifications, and binary document downloads (PDF/XML). - -**Estimated Effort**: 3-4 days -**Priority**: 🔴 Critical (Core SDK functionality) - ---- - -## 📁 What's Included - -### Core Documents -- ✅ **[proposal.md](./proposal.md)** - Complete problem statement, solution, risks, and success criteria -- ✅ **[tasks.md](./tasks.md)** - Detailed task breakdown with 6 phases, 13 tasks, dependencies, and validation gates -- ✅ **[design.md](./design.md)** - Architectural decisions, component interactions, and technical approach - -### Capability Specs (3 total) -- ✅ **[service-invoice-operations](./specs/service-invoice-operations/spec.md)** - CRUD operations (8 requirements, 24 scenarios) -- ✅ **[async-invoice-processing](./specs/async-invoice-processing/spec.md)** - Polling and async patterns (8 requirements, 16 scenarios) -- ✅ **[invoice-downloads](./specs/invoice-downloads/spec.md)** - PDF/XML downloads (8 requirements, 21 scenarios) - ---- - -## 🎯 Key Capabilities - -### 1. Service Invoice Operations -**Methods**: `create()`, `list()`, `retrieve()`, `cancel()`, `sendEmail()` -**Features**: -- Type-safe CRUD with discriminated union for 201/202 responses -- Pagination and date filtering for list operations -- Comprehensive error handling (401, 400, 404, 408, 500) -- Company-scoped operations - -### 2. Async Invoice Processing -**Method**: `createAndWait()` -**Features**: -- Automatic polling with exponential backoff -- Configurable timeouts and intervals -- Terminal state detection (Issued, IssueFailed, Cancelled, etc.) -- Reusable polling utility in `src/core/utils/polling.ts` - -### 3. Invoice Downloads -**Methods**: `downloadPdf()`, `downloadXml()` -**Features**: -- Binary data handling with Fetch API -- Returns Node.js Buffer objects -- Support for single and batch downloads -- Retry guidance for 404 (document not ready) - ---- - -## 📊 Requirements Summary - -| Capability | Requirements | Scenarios | Priority | -|------------|--------------|-----------|----------| -| Service Invoice Operations | 8 | 24 | Critical | -| Async Invoice Processing | 8 | 16 | Critical | -| Invoice Downloads | 8 | 21 | High | -| **Total** | **24** | **61** | - | - ---- - -## 🛠 Implementation Approach - -### Phase 1: Foundation (Day 1) -- Validate OpenAPI spec and generate types -- Define core TypeScript types -- Create reusable polling utility - -### Phase 2: Core Implementation (Day 2) -- Implement CRUD operations -- Add createAndWait() with polling -- Implement email operations - -### Phase 3: Document Downloads (Day 2-3) -- PDF download with binary handling -- XML download with binary handling - -### Phase 4: Testing (Day 3) -- Unit tests (>80% coverage) -- Integration tests with MSW -- Error scenario testing - -### Phase 5: Documentation (Day 4) -- Complete examples -- Update API.md and README.md -- JSDoc on all public methods - -### Phase 6: Validation & Release (Day 4) -- Full validation suite -- Update CHANGELOG -- Prepare for PR - ---- - -## ✅ Success Criteria - -- [ ] All 7 API endpoints implemented -- [ ] TypeScript strict mode with no `any` in public APIs -- [ ] Unit test coverage > 80% -- [ ] Integration tests passing -- [ ] All error scenarios tested -- [ ] JSDoc complete on all public methods -- [ ] Examples working -- [ ] `npm run typecheck && npm run lint && npm test` passing - ---- - -## 🔑 Key Design Decisions - -1. **Dual Response Pattern**: `create()` returns `ServiceInvoice | AsyncResponse` (discriminated union) -2. **Convenience Method**: `createAndWait()` provides automatic polling for 99% use case -3. **Reusable Polling**: Generic `poll()` utility in `src/core/utils/polling.ts` -4. **Buffer Returns**: Downloads return Node.js Buffer objects for best DX -5. **New Error Type**: `InvoiceProcessingError` for async failures with context - ---- - -## 📚 API Examples - -### Create and Wait (Recommended) -```typescript -const invoice = await nfe.serviceInvoices.createAndWait('company-id', { - cityServiceCode: '2690', - description: 'Consulting services', - servicesAmount: 1000.00, - borrower: { /* ... */ } -}); -console.log('Issued:', invoice.number); -``` - -### Manual Polling (Advanced) -```typescript -const result = await nfe.serviceInvoices.create('company-id', data); - -if ('location' in result) { - // Poll manually - let invoice = await nfe.serviceInvoices.retrieve('company-id', result.invoiceId); - while (!['Issued', 'IssueFailed'].includes(invoice.flowStatus)) { - await new Promise(resolve => setTimeout(resolve, 2000)); - invoice = await nfe.serviceInvoices.retrieve('company-id', result.invoiceId); - } -} -``` - -### List with Filters -```typescript -const result = await nfe.serviceInvoices.list('company-id', { - issuedBegin: '2026-01-01', - issuedEnd: '2026-01-31', - pageCount: 50 -}); -``` - -### Download PDF -```typescript -const pdf = await nfe.serviceInvoices.downloadPdf('company-id', 'invoice-id'); -await writeFile('./invoice.pdf', pdf); -``` - ---- - -## ⚠️ Known Risks - -1. **Async Processing Complexity** - Mitigated with extensive tests and clear docs -2. **OpenAPI Schema Accuracy** - Will cross-reference with v2 and real API -3. **Brazilian Tax Complexity** - Using generated types + examples with real data -4. **Binary Download Handling** - Using proven Fetch API patterns - ---- - -## 🔗 Dependencies - -- ✅ HTTP client (`src/core/http/client.ts`) - Already exists -- ✅ Error system (`src/core/errors/`) - Already exists, will add InvoiceProcessingError -- ✅ Type definitions (`src/core/types.ts`) - Already exists, will expand -- ⚠️ Polling utility (`src/core/utils/polling.ts`) - **Will create** - ---- - -## 🚫 Out of Scope - -- External invoice endpoints -- Advanced filtering beyond dates -- Batch create operations -- Invoice modification (not supported by API) -- Tax calculation endpoints -- MCP/n8n integration (separate packages) - ---- - -## 📝 Open Questions for Review - -1. **Polling defaults**: Are 120s timeout, 1s initial delay, 10s max delay, 1.5x backoff optimal? -2. **Error recovery**: Should async failures (IssueFailed) allow retry or just throw? -3. **List pagination**: Manual only, or also provide auto-pagination iterator? -4. **Download methods**: Buffer only, or also support streaming to file? -5. **Type generation**: Run `npm run generate` before implementation? - ---- - -## 🚀 Next Steps - -1. ✅ **Stakeholder Review** - Completed -2. ✅ **Approval** - Approved 2026-01-16 -3. ⏭️ **Implementation** - Begin following [tasks.md](./tasks.md) phase by phase -4. ⏭️ **Testing** - Achieve >80% coverage -5. ⏭️ **Documentation** - Complete all docs and examples -6. ⏭️ **PR** - Submit for code review - ---- - -## 📞 Questions or Feedback? - -- Review [proposal.md](./proposal.md) for complete context -- Check [design.md](./design.md) for architectural details -- See [tasks.md](./tasks.md) for implementation breakdown -- Examine spec files in `specs/*/spec.md` for requirement details - ---- - -**Validation**: ✅ `openspec validate implement-service-invoices --strict` passed -**Status**: ✅ Approved -**Last Updated**: 2026-01-16 diff --git a/openspec/changes/implement-service-invoices/design.md b/openspec/changes/implement-service-invoices/design.md deleted file mode 100644 index 7b65adc..0000000 --- a/openspec/changes/implement-service-invoices/design.md +++ /dev/null @@ -1,729 +0,0 @@ -# Design: Implement Service Invoices Resource - -**Change ID**: `implement-service-invoices` -**Status**: Draft -**Created**: 2026-01-15 - ---- - -## Overview - -This document outlines the architectural approach for implementing the Service Invoices resource in the NFE.io SDK v3. The implementation must handle complex Brazilian tax invoice operations including CRUD, asynchronous processing with polling, email notifications, and binary document downloads. - ---- - -## Architectural Context - -### System Boundaries - -``` -┌─────────────────────────────────────────────────────────────┐ -│ NFE.io API │ -│ ┌─────────────────────────────────────────────────────┐ │ -│ │ POST /companies/{id}/serviceinvoices │ │ -│ │ → 201 (immediate) or 202 (async) │ │ -│ │ GET /companies/{id}/serviceinvoices │ │ -│ │ GET /companies/{id}/serviceinvoices/{id} │ │ -│ │ DELETE /companies/{id}/serviceinvoices/{id} │ │ -│ │ PUT /companies/{id}/serviceinvoices/{id}/sendemail │ │ -│ │ GET /companies/{id}/serviceinvoices/{id}/pdf │ │ -│ │ GET /companies/{id}/serviceinvoices/{id}/xml │ │ -│ └─────────────────────────────────────────────────────┘ │ -└─────────────────────────────────────────────────────────────┘ - ▲ - │ HTTPS + Basic Auth - │ -┌─────────────────────────────┴───────────────────────────────┐ -│ NFE.io SDK v3 │ -│ ┌──────────────────────────────────────────────────────┐ │ -│ │ NfeClient │ │ -│ │ └─ serviceInvoices: ServiceInvoicesResource │ │ -│ └──────────────────────────────────────────────────────┘ │ -│ ┌──────────────────────────────────────────────────────┐ │ -│ │ ServiceInvoicesResource │ │ -│ │ • create(companyId, data) │ │ -│ │ • createAndWait(companyId, data, options) │ │ -│ │ • list(companyId, options) │ │ -│ │ • retrieve(companyId, invoiceId) │ │ -│ │ • cancel(companyId, invoiceId) │ │ -│ │ • sendEmail(companyId, invoiceId) │ │ -│ │ • downloadPdf(companyId, invoiceId?) │ │ -│ │ • downloadXml(companyId, invoiceId?) │ │ -│ └──────────────────────────────────────────────────────┘ │ -│ ┌──────────────────────────────────────────────────────┐ │ -│ │ HttpClient (Fetch API) │ │ -│ │ • get(), post(), put(), delete() │ │ -│ │ • Authentication, retry, rate limiting │ │ -│ └──────────────────────────────────────────────────────┘ │ -│ ┌──────────────────────────────────────────────────────┐ │ -│ │ Polling Utility │ │ -│ │ • poll(fn, isComplete, options) │ │ -│ │ • Exponential backoff │ │ -│ │ • Timeout enforcement │ │ -│ └──────────────────────────────────────────────────────┘ │ -│ ┌──────────────────────────────────────────────────────┐ │ -│ │ Error System │ │ -│ │ • NfeError │ │ -│ │ • AuthenticationError, ValidationError │ │ -│ │ • NotFoundError, TimeoutError │ │ -│ │ • InvoiceProcessingError │ │ -│ └──────────────────────────────────────────────────────┘ │ -└─────────────────────────────────────────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────────────────────────────┐ -│ User Application │ -│ • Invoice creation and management │ -│ • Integration with accounting systems │ -│ • Compliance with Brazilian tax regulations │ -└─────────────────────────────────────────────────────────────┘ -``` - ---- - -## Key Design Decisions - -### Decision 1: Dual Response Pattern for create() - -**Problem**: The API returns either 201 (immediate success) or 202 (async processing) for invoice creation. - -**Options Considered**: -1. **Always return ServiceInvoice** - Wait internally for async processing -2. **Return discriminated union** - ServiceInvoice | AsyncResponse -3. **Separate methods** - create() for sync, createAsync() for handling 202 - -**Chosen**: Option 2 - Discriminated union - -**Rationale**: -- Preserves API semantics (201 vs 202) -- TypeScript discriminated unions provide type-safe handling -- Allows advanced users to implement custom polling -- Aligns with v2 behavior where callback receives different shapes - -**Implementation**: -```typescript -type CreateInvoiceResult = ServiceInvoice | AsyncResponse; - -interface AsyncResponse { - status: 'pending'; - location: string; // URL for polling - invoiceId: string; // Extracted from location -} - -// User code -const result = await nfe.serviceInvoices.create('company-id', data); - -if ('location' in result) { - // Type guard: TypeScript knows this is AsyncResponse - const invoice = await nfe.serviceInvoices.retrieve('company-id', result.invoiceId); -} else { - // Type guard: TypeScript knows this is ServiceInvoice - console.log('Immediate success:', result.id); -} -``` - -**Trade-offs**: -- ✅ Pro: Type-safe handling of both scenarios -- ✅ Pro: Flexible - users can choose manual or automatic polling -- ❌ Con: More complex than always-wait approach -- ❌ Con: Users must handle two code paths - ---- - -### Decision 2: Provide createAndWait() Convenience Method - -**Problem**: Most users want simple "create and wait" behavior, not manual polling. - -**Options Considered**: -1. **Only create()** - Users implement polling themselves -2. **Only createAndWait()** - Hide async complexity completely -3. **Both methods** - create() for advanced use, createAndWait() for simplicity - -**Chosen**: Option 3 - Provide both methods - -**Rationale**: -- Most users benefit from automatic polling (DX priority) -- Advanced users may need custom polling logic (flexibility) -- Clear naming indicates behavior (create vs createAndWait) -- Aligns with best practices in other SDKs (Stripe, AWS) - -**Implementation**: -```typescript -class ServiceInvoicesResource { - async create(companyId: string, data: ServiceInvoiceData): Promise { - // Returns raw API response - } - - async createAndWait( - companyId: string, - data: ServiceInvoiceData, - options?: PollingOptions - ): Promise { - const result = await this.create(companyId, data); - - if ('location' in result) { - // Poll until completion - return this.pollUntilComplete(companyId, result.invoiceId, options); - } - - return result; // Already complete - } -} -``` - -**Trade-offs**: -- ✅ Pro: Excellent DX for common case -- ✅ Pro: Flexibility for advanced cases -- ✅ Pro: Clear method naming -- ❌ Con: Two methods to maintain and test -- ❌ Con: Slightly larger API surface - ---- - -### Decision 3: Reusable Polling Utility - -**Problem**: Polling logic is needed for invoice creation, but may be useful elsewhere. - -**Options Considered**: -1. **Inline polling** - Implement directly in createAndWait() -2. **Private method** - pollUntilComplete() in ServiceInvoicesResource -3. **Shared utility** - Generic poll() function in src/core/utils/ - -**Chosen**: Option 3 - Shared utility - -**Rationale**: -- Other resources may need polling (certificate processing, batch operations) -- Better testability (isolated unit tests) -- Follows DRY principle -- Easier to maintain and enhance - -**Implementation**: -```typescript -// src/core/utils/polling.ts -export async function poll(options: { - fn: () => Promise; - isComplete: (result: T) => boolean; - timeout: number; - initialDelay: number; - maxDelay: number; - backoffFactor: number; - onPoll?: (attempt: number, result: T) => void; -}): Promise { - const startTime = Date.now(); - let delay = options.initialDelay; - let attempt = 0; - - while (true) { - attempt++; - const result = await options.fn(); - - if (options.onPoll) { - options.onPoll(attempt, result); - } - - if (options.isComplete(result)) { - return result; - } - - if (Date.now() - startTime + delay > options.timeout) { - throw new TimeoutError('Polling timeout exceeded'); - } - - await sleep(delay); - delay = Math.min(delay * options.backoffFactor, options.maxDelay); - } -} - -// Usage in ServiceInvoicesResource -private async pollUntilComplete( - companyId: string, - invoiceId: string, - options?: PollingOptions -): Promise { - return poll({ - fn: () => this.retrieve(companyId, invoiceId), - isComplete: (invoice) => ['Issued', 'IssueFailed', 'Cancelled', 'CancelFailed'].includes(invoice.flowStatus), - timeout: options?.timeout ?? 120000, - initialDelay: options?.initialDelay ?? 1000, - maxDelay: options?.maxDelay ?? 10000, - backoffFactor: options?.backoffFactor ?? 1.5, - onPoll: options?.onPoll - }); -} -``` - -**Trade-offs**: -- ✅ Pro: Reusable across resources -- ✅ Pro: Easier to test -- ✅ Pro: Configurable and extensible -- ❌ Con: Additional abstraction layer -- ❌ Con: Generic types more complex - ---- - -### Decision 4: Binary Downloads Return Buffer - -**Problem**: PDF and XML downloads are binary data. How should they be returned? - -**Options Considered**: -1. **Return Buffer** - Node.js Buffer object -2. **Return ArrayBuffer** - Web-standard ArrayBuffer -3. **Return string** - Base64-encoded string -4. **Return Stream** - Readable stream for large files - -**Chosen**: Option 1 - Return Buffer - -**Rationale**: -- Node.js Buffer is the de facto standard for binary data in Node -- Easy to write to file: `fs.writeFile(path, buffer)` -- Better developer ergonomics than ArrayBuffer or base64 -- Streaming adds complexity for minimal benefit (invoices rarely > 10MB) -- Can convert Buffer to ArrayBuffer if needed: `buffer.buffer.slice()` - -**Implementation**: -```typescript -async downloadPdf(companyId: string, invoiceId?: string): Promise { - const path = invoiceId - ? `/companies/${companyId}/serviceinvoices/${invoiceId}/pdf` - : `/companies/${companyId}/serviceinvoices/pdf`; - - const response = await this.http.get(path, { - headers: { Accept: 'application/pdf' } - }); - - // Fetch API: Response.arrayBuffer() → Buffer - const arrayBuffer = await response.arrayBuffer(); - return Buffer.from(arrayBuffer); -} -``` - -**Trade-offs**: -- ✅ Pro: Best DX for Node.js users -- ✅ Pro: Easy file I/O -- ✅ Pro: No streaming complexity -- ❌ Con: Loads entire file in memory -- ❌ Con: Not web-compatible (but SDK is Node-only) - ---- - -### Decision 5: Error Hierarchy - -**Problem**: Need typed errors for different failure scenarios. - -**Existing System**: -``` -NfeError (base) - ├─ AuthenticationError (401) - ├─ ValidationError (400) - ├─ NotFoundError (404) - ├─ TimeoutError (408) - └─ [others] -``` - -**Addition Needed**: InvoiceProcessingError for async failures - -**Rationale**: -- Async invoice creation can fail with IssueFailed status -- Need to distinguish from validation errors (400) or server errors (500) -- Should include flowStatus and flowMessage for debugging - -**Implementation**: -```typescript -// src/core/errors/index.ts -export class InvoiceProcessingError extends NfeError { - constructor( - message: string, - public readonly flowStatus: string, - public readonly flowMessage: string, - public readonly invoiceId: string - ) { - super(message, 422); // Unprocessable Entity semantic - this.name = 'InvoiceProcessingError'; - } -} - -// Usage in createAndWait() -if (invoice.flowStatus === 'IssueFailed') { - throw new InvoiceProcessingError( - `Invoice processing failed: ${invoice.flowMessage}`, - invoice.flowStatus, - invoice.flowMessage, - invoice.id - ); -} -``` - -**Trade-offs**: -- ✅ Pro: Type-safe error handling -- ✅ Pro: Contains all relevant context -- ✅ Pro: Users can catch specific error type -- ❌ Con: Adds another error class - ---- - -## Component Interactions - -### Sequence: Create Invoice with Async Processing - -``` -User Code ServiceInvoicesResource HttpClient NFE.io API PollingUtility - │ │ │ │ │ - │─createAndWait(id, data)──>│ │ │ │ - │ │ │ │ │ - │ │─create(id, data)────>│ │ │ - │ │ │ │ │ - │ │ │─POST────────>│ │ - │ │ │ │ │ - │ │ │<─202+Location│ │ - │ │ │ │ │ - │ │<─AsyncResponse───────│ │ │ - │ │ │ │ │ - │ │─poll()──────────────────────────────────────────>│ - │ │ │ │ │ - │ │ │ │ │ - │ │ │ │ │ - │ │<─retrieve(invoiceId)─────────────────────────────│ - │ │ │ │ │ - │ │─retrieve(id, invId)─>│ │ │ - │ │ │ │ │ - │ │ │─GET─────────>│ │ - │ │ │ │ │ - │ │ │<─200+Invoice│ │ - │ │ │ │ {flowStatus: │ - │ │ │ │ WaitingSend} │ - │ │ │ │ │ - │ │<─Invoice─────────────│ │ │ - │ │ │ │ │ - │ │─isComplete?(invoice)────────────────────────────>│ - │ │ │ │ │ - │ │<─false──────────────────────────────────────────-│ - │ │ │ │ │ - │ │ │ │ │ - │ │ │ │ │ - │ │<─retrieve(invoiceId)─────────────────────────────│ - │ │ │ │ │ - │ │─retrieve(id, invId)─>│ │ │ - │ │ │ │ │ - │ │ │─GET─────────>│ │ - │ │ │ │ │ - │ │ │<─200+Invoice│ │ - │ │ │ │ {flowStatus: │ - │ │ │ │ Issued} │ - │ │ │ │ │ - │ │<─Invoice─────────────│ │ │ - │ │ │ │ │ - │ │─isComplete?(invoice)────────────────────────────>│ - │ │ │ │ │ - │ │<─true───────────────────────────────────────────-│ - │ │ │ │ │ - │<─ServiceInvoice───────────│ │ │ │ - │ │ │ │ │ -``` - -### Sequence: Download PDF with Retry - -``` -User Code ServiceInvoicesResource HttpClient NFE.io API - │ │ │ │ - │─downloadPdf(id, invId)────>│ │ │ - │ │ │ │ - │ │─get(path)───────────>│ │ - │ │ │ │ - │ │ │─GET─────────>│ - │ │ │ │ - │ │ │<─404────────-│ - │ │ │ │ - │ │<─NotFoundError───────│ │ - │ │ │ │ - │<─throw NotFoundError───────│ │ │ - │ │ │ │ - │ │ │ │ - │ │ │ │ - │─downloadPdf(id, invId)────>│ │ │ - │ │ │ │ - │ │─get(path)───────────>│ │ - │ │ │ │ - │ │ │─GET─────────>│ - │ │ │ │ - │ │ │<─200+Binary──│ - │ │ │ │ - │ │<─arrayBuffer()───────│ │ - │ │ │ │ - │<─Buffer────────────────────│ │ │ -``` - ---- - -## Data Flow - -### Type Definitions - -```typescript -// src/core/types.ts - -// Core invoice type -export interface ServiceInvoice { - id: string; - environment: 'Development' | 'Production' | 'Staging'; - flowStatus: FlowStatus; - flowMessage?: string; - provider: Provider; - borrower: Borrower; - servicesAmount: number; - number?: string; - issuedOn?: string; - createdOn: string; - modifiedOn: string; - // ... many more fields from OpenAPI -} - -export type FlowStatus = - | 'WaitingCalculateTaxes' - | 'WaitingDefineRpsNumber' - | 'WaitingSend' - | 'WaitingReturn' - | 'WaitingDownload' - | 'Issued' - | 'IssueFailed' - | 'Cancelled' - | 'CancelFailed' - | 'PullFromCityHall'; - -// Input type for create -export interface ServiceInvoiceData { - cityServiceCode: string; - description: string; - servicesAmount: number; - borrower: BorrowerInput; - // ... other fields -} - -// Async response -export interface AsyncResponse { - status: 'pending'; - location: string; - invoiceId: string; -} - -// List response -export interface ListResponse { - serviceInvoices: T[]; - totalResults: number; - totalPages: number; - page: number; - totals?: { - totalAmount: number; - // ... other totals - }; -} - -// Pagination options -export interface PaginationOptions { - pageIndex?: number; - pageCount?: number; - issuedBegin?: string; - issuedEnd?: string; - createdBegin?: string; - createdEnd?: string; - hasTotals?: boolean; -} - -// Polling options -export interface PollingOptions { - timeout?: number; // Default: 120000 (2 minutes) - initialDelay?: number; // Default: 1000 (1 second) - maxDelay?: number; // Default: 10000 (10 seconds) - backoffFactor?: number; // Default: 1.5 - onPoll?: (attempt: number, flowStatus: string) => void; -} -``` - ---- - -## Testing Strategy - -### Unit Tests - -**Target**: > 80% coverage for ServiceInvoicesResource - -```typescript -// tests/unit/core/resources/service-invoices.test.ts -describe('ServiceInvoicesResource', () => { - describe('create()', () => { - it('returns ServiceInvoice on 201', async () => { - const mockHttp = createMockHttpClient(); - mockHttp.post.mockResolvedValue({ - data: { id: '123', flowStatus: 'Issued' } - }); - - const resource = new ServiceInvoicesResource(mockHttp); - const result = await resource.create('company-id', invoiceData); - - expect('location' in result).toBe(false); - expect(result.id).toBe('123'); - }); - - it('returns AsyncResponse on 202', async () => { - const mockHttp = createMockHttpClient(); - mockHttp.post.mockResolvedValue({ - data: { location: '/companies/id/serviceinvoices/abc' } - }); - - const resource = new ServiceInvoicesResource(mockHttp); - const result = await resource.create('company-id', invoiceData); - - expect('location' in result).toBe(true); - expect(result.invoiceId).toBe('abc'); - }); - }); - - describe('createAndWait()', () => { - it('returns immediately on 201', async () => { /* ... */ }); - it('polls until Issued on 202', async () => { /* ... */ }); - it('throws TimeoutError on timeout', async () => { /* ... */ }); - it('throws InvoiceProcessingError on IssueFailed', async () => { /* ... */ }); - }); - - // ... tests for list, retrieve, cancel, sendEmail, downloads -}); -``` - -### Integration Tests - -**Target**: Cover real-world scenarios with MSW - -```typescript -// tests/integration/service-invoices.integration.test.ts -describe('ServiceInvoices Integration', () => { - beforeAll(() => { - setupServer( - http.post('/v1/companies/:id/serviceinvoices', ({ params }) => { - return HttpResponse.json( - { location: `/v1/companies/${params.id}/serviceinvoices/new-123` }, - { status: 202 } - ); - }), - - http.get('/v1/companies/:id/serviceinvoices/:invoiceId', ({ params }) => { - // Simulate state progression - const attempt = getAttempt(params.invoiceId); - const status = attempt === 1 ? 'WaitingSend' : 'Issued'; - - return HttpResponse.json({ - id: params.invoiceId, - flowStatus: status - }); - }) - ); - }); - - it('creates invoice and waits for completion', async () => { - const nfe = new NfeClient({ apiKey: 'test-key' }); - const invoice = await nfe.serviceInvoices.createAndWait('company-id', invoiceData); - - expect(invoice.flowStatus).toBe('Issued'); - }); - - it('handles complete lifecycle', async () => { - const nfe = new NfeClient({ apiKey: 'test-key' }); - - // Create - const invoice = await nfe.serviceInvoices.createAndWait('company-id', data); - - // Retrieve - const retrieved = await nfe.serviceInvoices.retrieve('company-id', invoice.id); - - // Send email - await nfe.serviceInvoices.sendEmail('company-id', invoice.id); - - // Download PDF - const pdf = await nfe.serviceInvoices.downloadPdf('company-id', invoice.id); - expect(pdf).toBeInstanceOf(Buffer); - - // Cancel - await nfe.serviceInvoices.cancel('company-id', invoice.id); - }); -}); -``` - ---- - -## Migration from v2 - -### v2 Pattern -```javascript -const nfe = require('nfe')(apiKey); - -nfe.serviceInvoices.create('company-id', data, function(err, invoice) { - if (err) { - // Handle error - } else if (invoice.flowStatus) { - // 201: Immediate success - } else if (invoice.location) { - // 202: Async processing - } -}); -``` - -### v3 Pattern -```typescript -import { NfeClient } from 'nfe-io'; - -const nfe = new NfeClient({ apiKey }); - -// Recommended: Use createAndWait for simplicity -const invoice = await nfe.serviceInvoices.createAndWait('company-id', data); - -// Advanced: Manual handling -const result = await nfe.serviceInvoices.create('company-id', data); -if ('location' in result) { - // Handle async -} -``` - ---- - -## Security Considerations - -1. **API Key Protection**: Always passed via Authorization header (HTTP client handles) -2. **Rate Limiting**: HTTP client implements rate limiting -3. **Retry Logic**: Exponential backoff prevents API abuse -4. **Timeout Enforcement**: Prevents runaway polling -5. **Binary Data**: No encoding transformation preserves integrity - ---- - -## Performance Considerations - -1. **Polling Overhead**: Exponential backoff reduces API calls - - Average case: 3-5 polls over 5-10 seconds - - Worst case: ~20 polls over 2 minutes -2. **Memory Usage**: Binary downloads load full file (typically < 5MB) -3. **Concurrent Requests**: HttpClient can handle multiple invoices in parallel -4. **Type Safety**: Minimal runtime overhead, compile-time only - ---- - -## Open Questions & Future Work - -### Questions Requiring Clarification -1. **Batch PDF downloads**: Does API actually support downloading all invoices as single PDF? Check v2 behavior. -2. **Polling defaults**: Are 120s timeout and 1.5x backoff optimal? May need tuning based on real usage. -3. **XML parsing**: Should we provide XML parsing utilities or let users handle? -4. **Filtering**: Does list() support additional filters beyond dates? - -### Future Enhancements -1. **Streaming downloads**: For very large invoices (> 10MB) -2. **Webhook integration**: Alternative to polling for async completion -3. **Batch operations**: Create multiple invoices in single call -4. **Cancel with retry**: Auto-retry cancellation if API rate-limited -5. **PDF preview**: Return small preview image before full download - ---- - -## References - -- [OpenAPI Spec](../../openapi/spec/nf-servico-v1.yaml) -- [v2 Implementation](../../lib/resources/ServiceInvoices.js) -- [AGENTS.md](../../AGENTS.md) -- [Project Context](../../openspec/project.md) diff --git a/openspec/changes/implement-service-invoices/proposal.md b/openspec/changes/implement-service-invoices/proposal.md deleted file mode 100644 index c9a0e1e..0000000 --- a/openspec/changes/implement-service-invoices/proposal.md +++ /dev/null @@ -1,241 +0,0 @@ -# Proposal: Implement Service Invoices Resource - -**Change ID**: `implement-service-invoices` -**Status**: ✅ Approved -**Created**: 2026-01-15 -**Approved**: 2026-01-16 -**Author**: AI Assistant - ---- - -## Problem Statement - -The NFE.io SDK v3 requires complete implementation of the Service Invoices (Nota Fiscal de Serviço - NFSE) resource, which is the core functionality of the NFE.io API. While a partial implementation exists in `src/core/resources/service-invoices.ts`, it needs to be completed, thoroughly tested, and fully documented. - -Service invoices represent the primary business capability of NFE.io - allowing companies to issue, manage, and distribute electronic service invoices in compliance with Brazilian tax regulations. The resource must handle: - -1. **Standard CRUD operations** (create, list, retrieve, cancel) -2. **Asynchronous processing patterns** (202 responses with location-based polling) -3. **Email notifications** to invoice recipients -4. **Document downloads** (PDF and XML formats) -5. **Complex validation** of Brazilian tax data -6. **Polling helpers** for async invoice creation completion - -## Current State - -### Existing Implementation -- **Location**: `src/core/resources/service-invoices.ts` (322 lines) -- **Status**: Partially implemented -- **Coverage**: Basic CRUD methods exist, but incomplete error handling, validation, and polling utilities - -### v2 Implementation Reference -- **Location**: `lib/resources/ServiceInvoices.js` (51 lines) -- **Pattern**: BaseResource.extend() with REST method declarations -- **Methods**: create, list, retrieve, cancel, sendemail, downloadPdf, downloadXml - -### OpenAPI Specification -- **Location**: `openapi/spec/nf-servico-v1.yaml` -- **Endpoints**: - - `POST /v1/companies/{company_id}/serviceinvoices` - Create invoice - - `GET /v1/companies/{company_id}/serviceinvoices` - List invoices - - `GET /v1/companies/{company_id}/serviceinvoices/{id}` - Get invoice - - `DELETE /v1/companies/{company_id}/serviceinvoices/{id}` - Cancel invoice - - `PUT /v1/companies/{company_id}/serviceinvoices/{id}/sendemail` - Send email - - `GET /v1/companies/{company_id}/serviceinvoices/{id}/pdf` - Download PDF - - `GET /v1/companies/{company_id}/serviceinvoices/{id}/xml` - Download XML - -### Test Coverage -- **Integration tests**: `tests/integration/service-invoices.integration.test.ts` exists -- **Unit tests**: Missing or incomplete -- **Current coverage**: Unknown, but likely < 50% - -## Proposed Solution - -Complete the Service Invoices resource implementation with three distinct capabilities: - -### Capability 1: Service Invoice Operations -**Scope**: Core CRUD operations and email functionality -**Spec Location**: `specs/service-invoice-operations/spec.md` - -Implement complete CRUD operations with: -- Full TypeScript types from OpenAPI schema -- Comprehensive error handling (validation, authentication, processing errors) -- Input validation using Zod schemas -- Pagination support for list operations -- Filtering by date ranges (issued, created) -- Company-scoped operations - -### Capability 2: Async Invoice Processing -**Scope**: Handling 202 responses and polling mechanisms -**Spec Location**: `specs/async-invoice-processing/spec.md` - -Implement asynchronous invoice creation pattern: -- Detect 202 (Accepted) responses from create operations -- Parse Location header for polling URL -- Provide `createAndWait()` helper for automatic polling -- Configurable polling intervals and timeouts -- Flow status tracking (WaitingCalculateTaxes, WaitingSend, Issued, etc.) -- Proper error handling for failed async operations - -### Capability 3: Invoice Downloads -**Scope**: PDF and XML document downloads -**Spec Location**: `specs/invoice-downloads/spec.md` - -Implement document download operations: -- Download PDF representation of invoice -- Download XML representation of invoice -- Handle binary streams using Fetch API -- Return Buffer objects for Node.js compatibility -- Proper Accept headers for content negotiation -- Error handling for 404 (document not ready) scenarios - -## Success Criteria - -1. **Completeness**: All 7 endpoints from OpenAPI spec implemented -2. **Type Safety**: Full TypeScript types with no `any` in public APIs -3. **Testing**: - - Unit test coverage > 80% - - Integration tests for all operations - - Tests for error scenarios (401, 400, 404, 408, 500) - - Tests for async processing (202 → polling → completion) -4. **Documentation**: - - JSDoc comments on all public methods - - Examples in `examples/` directory - - README section on service invoices -5. **Validation**: - - `npm run typecheck` passes - - `npm run lint` passes - - `npm run test` passes with coverage target met -6. **Backward Compatibility**: Method signatures align with v2 where possible - -## Dependencies - -- **Required**: HTTP client implementation (`src/core/http/client.ts`) -- **Required**: Error system (`src/core/errors/`) -- **Required**: Type definitions (`src/core/types.ts`) -- **Required**: Retry logic for polling (`src/runtime/retry.ts` - if not exists, create) -- **Optional**: Rate limiting for API calls - -## Out of Scope - -1. **External invoice operations** (`/v1/companies/{company_id}/serviceinvoices/external/{id}`) -2. **Advanced filtering** beyond date ranges and basic pagination -3. **Batch operations** (create multiple invoices at once) -4. **Invoice modification** (NFE.io API doesn't support PUT on invoices) -5. **Tax calculation endpoints** (separate API concern) -6. **MCP server integration** (lives in separate @nfe-io/mcp-server package) -7. **n8n nodes integration** (lives in separate @nfe-io/n8n-nodes package) - -## Risks and Mitigations - -### Risk 1: Async Processing Complexity -**Risk**: The 202 → polling → completion flow is complex and error-prone -**Mitigation**: -- Create dedicated polling utilities with extensive tests -- Provide both manual (create) and automatic (createAndWait) approaches -- Document retry/timeout behavior clearly -- Add circuit breaker for runaway polling - -### Risk 2: OpenAPI Schema Accuracy -**Risk**: OpenAPI spec may not reflect actual API behavior -**Mitigation**: -- Cross-reference with v2 implementation behavior -- Test against real API (sandbox environment) -- Document any discrepancies discovered -- Update OpenAPI spec if needed - -### Risk 3: Complex Brazilian Tax Types -**Risk**: Brazilian tax data structures are complex (CNAE codes, tax regimes, etc.) -**Mitigation**: -- Use generated types from OpenAPI as source of truth -- Add validation helper functions where needed -- Reference official documentation in JSDoc comments -- Provide examples with realistic Brazilian data - -### Risk 4: Binary Download Handling -**Risk**: PDF/XML downloads require proper stream handling -**Mitigation**: -- Use Fetch API's arrayBuffer() method -- Return Buffer for Node.js compatibility -- Test with actual file downloads -- Document memory considerations for large files - -## Implementation Notes - -### Key Patterns to Follow - -1. **Company-scoped resources**: All operations require `companyId` as first parameter -2. **Error handling**: Use typed errors from `src/core/errors/` -3. **Async/await**: All methods return Promises, no callbacks -4. **TypeScript strict mode**: No `any` types in public APIs -5. **JSDoc comments**: Required for all public methods with examples - -### Testing Strategy - -1. **Unit tests**: Test each method in isolation with mocked HTTP client -2. **Integration tests**: Test against MSW-mocked API endpoints -3. **Error tests**: Test all error scenarios (401, 400, 404, 408, 500) -4. **Async tests**: Test 202 → polling → completion flow -5. **Download tests**: Test binary data handling for PDF/XML - -### Files to Create/Modify - -**Create**: -- `specs/service-invoice-operations/spec.md` -- `specs/async-invoice-processing/spec.md` -- `specs/invoice-downloads/spec.md` -- `tests/unit/core/resources/service-invoices.test.ts` -- `examples/service-invoice-complete.js` - -**Modify**: -- `src/core/resources/service-invoices.ts` (complete implementation) -- `src/core/types.ts` (add missing types) -- `tests/integration/service-invoices.integration.test.ts` (expand coverage) -- `README.md` (add service invoice section) -- `docs/API.md` (document all methods) - -## Open Questions - -1. **Polling configuration**: What are reasonable defaults for: - - Initial polling delay? (Suggestion: 1 second) - - Max polling delay? (Suggestion: 10 seconds) - - Total timeout? (Suggestion: 120 seconds) - - Exponential backoff factor? (Suggestion: 1.5x) - -2. **Error recovery**: For async failures (IssueFailed, CancelFailed), should we: - - Throw immediately? - - Allow retry with fresh API call? - - Expose flowMessage for user debugging? [x] - -3. **List pagination**: Should we provide: - - Manual pagination (current approach)? - - Auto-pagination iterator? - - Both options? [x] - -4. **Download methods**: Should we support: - - Returning raw Buffer (current)? - - Streaming to file? - - Both options? [x] - -5. **Type generation**: Should we regenerate types from OpenAPI or use existing? - - Decision needed: Run `npm run generate` before implementation - -## Next Steps - -1. **Review and approve** this proposal -2. **Answer open questions** above -3. **Validate OpenAPI spec** accuracy against real API -4. **Create spec deltas** for each capability -5. **Draft tasks.md** with detailed work items -6. **Run validation**: `openspec validate implement-service-invoices --strict` -7. **Begin implementation** following tasks.md - ---- - -## References - -- [OpenAPI Spec - Service Invoices](../../spec/nf-servico-v1.yaml) -- [v2 Implementation](../../lib/resources/ServiceInvoices.js) -- [v3 Partial Implementation](../../src/core/resources/service-invoices.ts) -- [AGENTS.md - Implementation Guidelines](../../AGENTS.md) -- [NFE.io API Documentation](https://nfe.io/docs/) diff --git a/openspec/changes/implement-service-invoices/specs/async-invoice-processing/spec.md b/openspec/changes/implement-service-invoices/specs/async-invoice-processing/spec.md deleted file mode 100644 index 0d2b937..0000000 --- a/openspec/changes/implement-service-invoices/specs/async-invoice-processing/spec.md +++ /dev/null @@ -1,464 +0,0 @@ -# Capability: Async Invoice Processing - -**Capability ID**: `async-invoice-processing` -**Parent Change**: `implement-service-invoices` -**Type**: Core Feature -**Priority**: Critical -**Dependencies**: `service-invoice-operations` - ---- - -## Overview - -This capability handles the asynchronous processing pattern used by NFE.io for invoice creation. When an invoice is created, the API may return a 202 (Accepted) response with a Location header, indicating the invoice is being processed asynchronously. This capability provides both manual polling support and an automatic `createAndWait()` helper. - -## Context - -NFE.io's invoice creation follows this flow: -1. POST /serviceinvoices → 202 Accepted + Location header -2. Invoice enters processing states: WaitingCalculateTaxes → WaitingDefineRpsNumber → WaitingSend → WaitingReturn -3. Eventually reaches terminal state: Issued (success) or IssueFailed (failure) -4. Client must poll GET /serviceinvoices/{id} to check status - -## ADDED Requirements - -### Requirement: ASYNC-001 - Detect Async Response -**Priority**: Critical -**Component**: ServiceInvoicesResource.create() - -The create method MUST correctly identify when the API returns a 202 response and parse the Location header for the invoice ID. - -#### Scenario: Parse 202 response with Location header -```typescript -// API returns 202 with Location: /v1/companies/{company_id}/serviceinvoices/{invoice_id} -const result = await nfe.serviceInvoices.create('company-id', invoiceData); - -assert('location' in result); -assert(result.status === 'pending'); -assert(result.location === '/v1/companies/company-id/serviceinvoices/abc-123'); -assert(result.invoiceId === 'abc-123'); // Extracted from location -``` - -#### Scenario: Extract invoice ID from Location header -```typescript -const result = await nfe.serviceInvoices.create('company-id', invoiceData); - -if ('location' in result) { - const invoiceId = result.invoiceId; // Should be extracted automatically - const invoice = await nfe.serviceInvoices.retrieve('company-id', invoiceId); -} -``` - ---- - -### Requirement: ASYNC-002 - Manual Polling Support -**Priority**: High -**Component**: ServiceInvoicesResource.retrieve() - -The SDK MUST allow developers to manually poll an invoice's status by repeatedly calling retrieve() until a terminal state is reached. - -#### Scenario: Manual polling until Issued -```typescript -const createResult = await nfe.serviceInvoices.create('company-id', invoiceData); - -if ('location' in createResult) { - let invoice: ServiceInvoice; - let attempts = 0; - const maxAttempts = 60; // 60 seconds max - - do { - await new Promise(resolve => setTimeout(resolve, 1000)); // Wait 1 second - invoice = await nfe.serviceInvoices.retrieve('company-id', createResult.invoiceId); - attempts++; - } while ( - !['Issued', 'IssueFailed', 'Cancelled'].includes(invoice.flowStatus) && - attempts < maxAttempts - ); - - if (invoice.flowStatus === 'Issued') { - console.log('Invoice issued:', invoice.id); - } else { - console.error('Issue failed:', invoice.flowMessage); - } -} -``` - -#### Scenario: Track processing states -```typescript -const createResult = await nfe.serviceInvoices.create('company-id', invoiceData); -const states: string[] = []; - -if ('location' in createResult) { - let invoice: ServiceInvoice; - - do { - await new Promise(resolve => setTimeout(resolve, 2000)); - invoice = await nfe.serviceInvoices.retrieve('company-id', createResult.invoiceId); - states.push(invoice.flowStatus); - } while (!['Issued', 'IssueFailed'].includes(invoice.flowStatus)); - - // States progression: WaitingCalculateTaxes → WaitingSend → Issued - assert(states.includes('WaitingSend')); - assert(states[states.length - 1] === 'Issued'); -} -``` - ---- - -### Requirement: ASYNC-003 - Automatic Polling with createAndWait() -**Priority**: Critical -**Component**: ServiceInvoicesResource.createAndWait() - -The SDK MUST provide a convenience method that creates an invoice and automatically polls until completion. - -#### Scenario: Create and wait for immediate success (201) -```typescript -const invoice = await nfe.serviceInvoices.createAndWait('company-id', invoiceData); - -// If API returns 201, createAndWait returns immediately -assert(invoice.flowStatus === 'Issued'); -assert(invoice.id !== undefined); -``` - -#### Scenario: Create and wait for async success (202 → Issued) -```typescript -const invoice = await nfe.serviceInvoices.createAndWait('company-id', invoiceData, { - timeout: 120000, // 2 minutes - initialDelay: 1000, // Start with 1 second - maxDelay: 10000, // Max 10 seconds between polls - backoffFactor: 1.5 // Exponential backoff -}); - -// Polls automatically until Issued -assert(invoice.flowStatus === 'Issued'); -assert(invoice.number !== undefined); // Has invoice number -``` - -#### Scenario: Timeout during polling -```typescript -await expect( - nfe.serviceInvoices.createAndWait('company-id', invoiceData, { - timeout: 5000 // Only 5 seconds - }) -).rejects.toThrow(TimeoutError); - -// Error message indicates polling timeout -``` - -#### Scenario: Invoice processing fails (IssueFailed) -```typescript -await expect( - nfe.serviceInvoices.createAndWait('company-id', invalidInvoiceData) -).rejects.toThrow(InvoiceProcessingError); - -// Error contains flowMessage from API -// error.flowStatus === 'IssueFailed' -// error.flowMessage === 'CNPJ do tomador inválido' (or similar) -``` - ---- - -### Requirement: ASYNC-004 - Polling Configuration -**Priority**: High -**Component**: PollingOptions type, createAndWait() - -The polling mechanism MUST be configurable with sensible defaults. - -#### Scenario: Use default polling configuration -```typescript -// Uses defaults if no options provided -const invoice = await nfe.serviceInvoices.createAndWait('company-id', invoiceData); - -// Default configuration: -// - timeout: 120000 (2 minutes) -// - initialDelay: 1000 (1 second) -// - maxDelay: 10000 (10 seconds) -// - backoffFactor: 1.5 (exponential) -``` - -#### Scenario: Custom polling configuration -```typescript -const invoice = await nfe.serviceInvoices.createAndWait('company-id', invoiceData, { - timeout: 300000, // 5 minutes for complex invoice - initialDelay: 2000, // Wait 2 seconds before first poll - maxDelay: 30000, // Up to 30 seconds between polls - backoffFactor: 2.0, // More aggressive backoff - onPoll: (attempt, status) => { - console.log(`Attempt ${attempt}: ${status}`); - } -}); -``` - -#### Scenario: Polling callback for progress tracking -```typescript -const attempts: number[] = []; -const statuses: string[] = []; - -const invoice = await nfe.serviceInvoices.createAndWait('company-id', invoiceData, { - onPoll: (attempt, flowStatus) => { - attempts.push(attempt); - statuses.push(flowStatus); - } -}); - -// Callback invoked on each poll -assert(attempts.length > 0); -assert(statuses[statuses.length - 1] === 'Issued'); -``` - ---- - -### Requirement: ASYNC-005 - Terminal States Detection -**Priority**: Critical -**Component**: Polling utility, createAndWait() - -The polling mechanism MUST correctly identify terminal states and stop polling. - -#### Scenario: Stop on success state (Issued) -```typescript -const invoice = await nfe.serviceInvoices.createAndWait('company-id', invoiceData); - -assert(invoice.flowStatus === 'Issued'); -// Polling stopped automatically -``` - -#### Scenario: Stop on failure state (IssueFailed) -```typescript -try { - await nfe.serviceInvoices.createAndWait('company-id', invalidData); -} catch (error) { - assert(error instanceof InvoiceProcessingError); - assert(error.flowStatus === 'IssueFailed'); - // Polling stopped on failure state -} -``` - -#### Scenario: Stop on cancellation state (CancelFailed) -```typescript -try { - await nfe.serviceInvoices.createAndWait('company-id', invoiceData); -} catch (error) { - // If invoice enters CancelFailed during creation (rare edge case) - assert(error instanceof InvoiceProcessingError); - assert(['IssueFailed', 'CancelFailed'].includes(error.flowStatus)); -} -``` - -#### Scenario: Continue polling on intermediate states -```typescript -const states: string[] = []; - -await nfe.serviceInvoices.createAndWait('company-id', invoiceData, { - onPoll: (_, status) => states.push(status) -}); - -// These states should NOT stop polling: -const intermediateStates = [ - 'WaitingCalculateTaxes', - 'WaitingDefineRpsNumber', - 'WaitingSend', - 'WaitingReturn', - 'WaitingDownload' -]; - -// At least one intermediate state should appear -assert(states.some(s => intermediateStates.includes(s))); - -// Final state is terminal -const finalState = states[states.length - 1]; -assert(['Issued', 'IssueFailed', 'Cancelled', 'CancelFailed'].includes(finalState)); -``` - ---- - -### Requirement: ASYNC-006 - Exponential Backoff -**Priority**: High -**Component**: Polling utility - -Polling MUST implement exponential backoff to reduce API load while waiting for long-running operations. - -#### Scenario: Delays increase exponentially -```typescript -const delays: number[] = []; -const startTimes: number[] = []; - -await nfe.serviceInvoices.createAndWait('company-id', invoiceData, { - initialDelay: 1000, - maxDelay: 10000, - backoffFactor: 2.0, - onPoll: (attempt) => { - startTimes.push(Date.now()); - } -}); - -// Calculate actual delays between polls -for (let i = 1; i < startTimes.length; i++) { - delays.push(startTimes[i] - startTimes[i - 1]); -} - -// Delays should increase: ~1000ms, ~2000ms, ~4000ms, ~8000ms, max out at 10000ms -if (delays.length > 1) { - assert(delays[1] > delays[0]); // Second delay > first delay -} -if (delays.length > 2) { - assert(delays[2] > delays[1]); // Third delay > second delay -} -``` - -#### Scenario: Delay caps at maxDelay -```typescript -const delays: number[] = []; -const startTimes: number[] = [Date.now()]; - -await nfe.serviceInvoices.createAndWait('company-id', invoiceData, { - initialDelay: 1000, - maxDelay: 5000, // Cap at 5 seconds - backoffFactor: 3.0, // Aggressive backoff - onPoll: () => { - startTimes.push(Date.now()); - } -}); - -for (let i = 1; i < startTimes.length; i++) { - delays.push(startTimes[i] - startTimes[i - 1]); -} - -// No delay should exceed maxDelay (with some tolerance for timing jitter) -delays.forEach(delay => { - assert(delay <= 5500); // 500ms tolerance -}); -``` - ---- - -### Requirement: ASYNC-007 - Error Context -**Priority**: High -**Component**: InvoiceProcessingError - -Errors from async processing MUST include context about the failure (flow status, message, invoice ID). - -#### Scenario: Error includes flowStatus and flowMessage -```typescript -try { - await nfe.serviceInvoices.createAndWait('company-id', invalidData); -} catch (error) { - assert(error instanceof InvoiceProcessingError); - assert(error.flowStatus === 'IssueFailed'); - assert(error.flowMessage !== undefined); - assert(error.flowMessage.length > 0); // Contains reason for failure - assert(error.invoiceId !== undefined); // Can retrieve the failed invoice -} -``` - -#### Scenario: Error allows retrieval of failed invoice -```typescript -try { - await nfe.serviceInvoices.createAndWait('company-id', invalidData); -} catch (error) { - if (error instanceof InvoiceProcessingError) { - // Can retrieve the failed invoice for inspection - const invoice = await nfe.serviceInvoices.retrieve('company-id', error.invoiceId); - assert(invoice.flowStatus === 'IssueFailed'); - assert(invoice.flowMessage === error.flowMessage); - } -} -``` - ---- - -### Requirement: ASYNC-008 - Polling Utility Reusability -**Priority**: Medium -**Component**: src/core/utils/polling.ts - -The polling logic MUST be implemented as a reusable utility that can be used for other async operations beyond invoice creation. - -#### Scenario: Generic polling utility -```typescript -import { poll } from '../utils/polling.js'; - -// Can be used for any async operation -const result = await poll({ - fn: async () => { - const invoice = await nfe.serviceInvoices.retrieve('company-id', 'invoice-id'); - return invoice; - }, - isComplete: (invoice) => ['Issued', 'IssueFailed'].includes(invoice.flowStatus), - timeout: 120000, - initialDelay: 1000, - maxDelay: 10000, - backoffFactor: 1.5 -}); -``` - -#### Scenario: Polling utility handles errors -```typescript -await expect( - poll({ - fn: async () => { - throw new Error('Network error'); - }, - isComplete: () => false, - timeout: 5000 - }) -).rejects.toThrow('Network error'); -``` - ---- - -## MODIFIED Requirements - -None - this is a new capability. - ---- - -## REMOVED Requirements - -None - this is a new capability. - ---- - -## Dependencies - -- **service-invoice-operations**: Requires create() and retrieve() methods -- **Error System**: Requires InvoiceProcessingError and TimeoutError -- **Types**: Requires AsyncResponse, PollingOptions types - ---- - -## Testing Requirements - -### Unit Tests -- Test async response detection (202 vs 201) -- Test Location header parsing -- Test polling with various terminal states -- Test exponential backoff timing -- Test timeout handling -- Test error propagation -- Test onPoll callback invocation -- Coverage > 90% (critical path) - -### Integration Tests -- Test complete async flow (202 → polling → Issued) -- Test async failure flow (202 → polling → IssueFailed) -- Test timeout scenario -- Test with MSW to simulate state transitions - ---- - -## Documentation Requirements - -- Document async processing pattern in API.md -- Document polling configuration options -- Provide example of manual polling -- Provide example of createAndWait() -- Document terminal states -- Document error handling for async failures - ---- - -## Non-Functional Requirements - -- **Performance**: Polling should not make excessive API calls (respect backoff) -- **Reliability**: Timeout must be enforced to prevent infinite loops -- **Developer Experience**: createAndWait() should be the default recommended approach -- **Observability**: onPoll callback allows progress tracking diff --git a/openspec/changes/implement-service-invoices/specs/invoice-downloads/spec.md b/openspec/changes/implement-service-invoices/specs/invoice-downloads/spec.md deleted file mode 100644 index 4bf4c3a..0000000 --- a/openspec/changes/implement-service-invoices/specs/invoice-downloads/spec.md +++ /dev/null @@ -1,444 +0,0 @@ -# Capability: Invoice Downloads - -**Capability ID**: `invoice-downloads` -**Parent Change**: `implement-service-invoices` -**Type**: Core Feature -**Priority**: High -**Dependencies**: `service-invoice-operations` - ---- - -## Overview - -This capability enables downloading service invoices in PDF and XML formats. Once an invoice reaches the "Issued" state, it becomes available for download. These downloads are essential for legal compliance, as Brazilian tax regulations require both electronic (XML) and printed (PDF) representations of fiscal documents. - -## Context - -- **PDF**: Visual representation suitable for printing and customer distribution -- **XML**: Official fiscal document format required for tax authority compliance -- Both formats may not be immediately available after issuance (404 possible) -- Downloads return binary data that must be handled as Buffers in Node.js -- Large invoices (with many line items) can produce multi-MB files - -## ADDED Requirements - -### Requirement: DOWNLOAD-001 - Download PDF by Invoice ID -**Priority**: High -**Component**: ServiceInvoicesResource.downloadPdf() - -The SDK MUST allow developers to download the PDF representation of a specific issued service invoice. - -#### Scenario: Download PDF for issued invoice -```typescript -const pdfBuffer = await nfe.serviceInvoices.downloadPdf('company-id', 'invoice-id'); - -assert(pdfBuffer instanceof Buffer); -assert(pdfBuffer.length > 0); -assert(pdfBuffer[0] === 0x25); // PDF magic number '%' -assert(pdfBuffer[1] === 0x50); // 'P' -assert(pdfBuffer[2] === 0x44); // 'D' -assert(pdfBuffer[3] === 0x46); // 'F' -``` - -#### Scenario: Save PDF to file -```typescript -import { writeFile } from 'fs/promises'; - -const pdf = await nfe.serviceInvoices.downloadPdf('company-id', 'invoice-id'); -await writeFile('./invoice-123.pdf', pdf); - -// File is valid PDF -const stats = await stat('./invoice-123.pdf'); -assert(stats.size > 0); -``` - -#### Scenario: PDF not ready yet (404) -```typescript -// Invoice just issued, PDF still generating -await expect( - nfe.serviceInvoices.downloadPdf('company-id', 'new-invoice-id') -).rejects.toThrow(NotFoundError); - -// Retry after delay -await new Promise(resolve => setTimeout(resolve, 5000)); -const pdf = await nfe.serviceInvoices.downloadPdf('company-id', 'new-invoice-id'); -assert(pdf instanceof Buffer); -``` - -#### Scenario: Download non-existent invoice (404) -```typescript -await expect( - nfe.serviceInvoices.downloadPdf('company-id', 'non-existent-id') -).rejects.toThrow(NotFoundError); -``` - ---- - -### Requirement: DOWNLOAD-002 - Download XML by Invoice ID -**Priority**: High -**Component**: ServiceInvoicesResource.downloadXml() - -The SDK MUST allow developers to download the XML representation of a specific issued service invoice for tax compliance. - -#### Scenario: Download XML for issued invoice -```typescript -const xmlBuffer = await nfe.serviceInvoices.downloadXml('company-id', 'invoice-id'); - -assert(xmlBuffer instanceof Buffer); -assert(xmlBuffer.length > 0); - -const xmlString = xmlBuffer.toString('utf8'); -assert(xmlString.startsWith(' { - parseString(xmlString, (err, result) => { - if (err) reject(err); - else resolve(result); - }); -}); - -assert(parsed !== undefined); -// Contains invoice data in structured XML format -``` - -#### Scenario: Save XML to file -```typescript -const xml = await nfe.serviceInvoices.downloadXml('company-id', 'invoice-id'); -await writeFile('./invoice-123.xml', xml); - -// File is valid XML -const content = await readFile('./invoice-123.xml', 'utf8'); -assert(content.includes(' 0); -// Contains multiple invoices in single PDF -``` - -#### Scenario: Batch PDF with date filters -```typescript -// If API supports filtering (check OpenAPI spec) -const batchPdf = await nfe.serviceInvoices.downloadPdf('company-id', { - issuedBegin: '2026-01-01', - issuedEnd: '2026-01-31' -}); - -assert(batchPdf instanceof Buffer); -// Contains only invoices from January 2026 -``` - ---- - -### Requirement: DOWNLOAD-004 - Binary Data Handling -**Priority**: Critical -**Component**: HTTP client, downloadPdf(), downloadXml() - -Download methods MUST correctly handle binary data using Fetch API and return Node.js Buffers. - -#### Scenario: Use correct Accept headers -```typescript -// Implementation should set: -// - Accept: application/pdf for PDF downloads -// - Accept: application/xml or text/xml for XML downloads - -const pdf = await nfe.serviceInvoices.downloadPdf('company-id', 'invoice-id'); -// HTTP client sent Accept: application/pdf - -const xml = await nfe.serviceInvoices.downloadXml('company-id', 'invoice-id'); -// HTTP client sent Accept: application/xml -``` - -#### Scenario: Handle large files without memory issues -```typescript -// Should work even for large invoices (multiple MB) -const largePdf = await nfe.serviceInvoices.downloadPdf('company-id', 'large-invoice-id'); - -assert(largePdf instanceof Buffer); -// Memory is managed efficiently - no streaming needed for reasonable sizes -``` - -#### Scenario: Binary data integrity preserved -```typescript -const pdf = await nfe.serviceInvoices.downloadPdf('company-id', 'invoice-id'); - -// PDF magic number intact -assert(pdf[0] === 0x25); // '%' -assert(pdf[1] === 0x50); // 'P' -assert(pdf[2] === 0x44); // 'D' -assert(pdf[3] === 0x46); // 'F' - -// No encoding corruption -const pdfString = pdf.toString('utf8'); -assert(!pdfString.includes('�')); // No replacement characters -``` - ---- - -### Requirement: DOWNLOAD-005 - Error Handling -**Priority**: High -**Component**: downloadPdf(), downloadXml() - -Download methods MUST handle all API error scenarios gracefully. - -#### Scenario: Document not found (404) -```typescript -try { - await nfe.serviceInvoices.downloadPdf('company-id', 'non-existent-id'); -} catch (error) { - assert(error instanceof NotFoundError); - assert(error.statusCode === 404); - assert(error.message.includes('not found') || error.message.includes('download')); -} -``` - -#### Scenario: Authentication error (401) -```typescript -const nfe = new NfeClient({ apiKey: 'invalid' }); - -try { - await nfe.serviceInvoices.downloadPdf('company-id', 'invoice-id'); -} catch (error) { - assert(error instanceof AuthenticationError); - assert(error.statusCode === 401); -} -``` - -#### Scenario: Timeout (408) -```typescript -try { - await nfe.serviceInvoices.downloadPdf('company-id', 'invoice-id'); -} catch (error) { - assert(error instanceof TimeoutError); - assert(error.statusCode === 408); -} -``` - -#### Scenario: Server error (500) -```typescript -try { - await nfe.serviceInvoices.downloadXml('company-id', 'invoice-id'); -} catch (error) { - assert(error instanceof NfeError); - assert(error.statusCode === 500); -} -``` - ---- - -### Requirement: DOWNLOAD-006 - Retry Logic for 404 -**Priority**: Medium -**Component**: downloadPdf(), downloadXml() or helper utility - -The SDK MUST provide guidance or utility for retrying downloads when documents are not yet ready (404). - -#### Scenario: Retry helper for PDF generation -```typescript -import { retryUntilAvailable } from '../utils/retry.js'; - -// Helper function that retries 404s with exponential backoff -const pdf = await retryUntilAvailable( - () => nfe.serviceInvoices.downloadPdf('company-id', 'new-invoice-id'), - { - maxRetries: 5, - initialDelay: 2000, - maxDelay: 10000 - } -); - -assert(pdf instanceof Buffer); -``` - -#### Scenario: Manual retry pattern documented -```typescript -// Document this pattern in API docs -async function downloadWithRetry(downloadFn, maxRetries = 3) { - for (let i = 0; i < maxRetries; i++) { - try { - return await downloadFn(); - } catch (error) { - if (error instanceof NotFoundError && i < maxRetries - 1) { - await new Promise(resolve => setTimeout(resolve, 2000 * (i + 1))); - continue; - } - throw error; - } - } -} - -const pdf = await downloadWithRetry( - () => nfe.serviceInvoices.downloadPdf('company-id', 'invoice-id') -); -``` - ---- - -### Requirement: DOWNLOAD-007 - Type Safety -**Priority**: High -**Component**: downloadPdf(), downloadXml() - -Download methods MUST have strict TypeScript types. - -#### Scenario: Return type is Buffer -```typescript -// Single invoice download -const pdf: Buffer = await nfe.serviceInvoices.downloadPdf('company-id', 'invoice-id'); -const xml: Buffer = await nfe.serviceInvoices.downloadXml('company-id', 'invoice-id'); - -// Batch download (if supported) -const batchPdf: Buffer = await nfe.serviceInvoices.downloadPdf('company-id'); -``` - -#### Scenario: Parameters are typed -```typescript -// TypeScript error for wrong types -nfe.serviceInvoices.downloadPdf( - 123, // Error: Expected string - 456 // Error: Expected string -); -``` - ---- - -### Requirement: DOWNLOAD-008 - Documentation -**Priority**: High -**Component**: JSDoc, examples, API.md - -Download methods MUST be fully documented with usage examples. - -#### Scenario: JSDoc includes examples -```typescript -/** - * Download the PDF representation of a service invoice - * - * @param companyId - The ID of the company - * @param invoiceId - The ID of the invoice to download (optional for batch download) - * @returns Buffer containing the PDF file - * @throws {NotFoundError} If the invoice doesn't exist or PDF is not yet available - * @throws {AuthenticationError} If API key is invalid - * @throws {TimeoutError} If the request times out - * - * @example Download single invoice - * ```typescript - * const pdf = await nfe.serviceInvoices.downloadPdf('company-id', 'invoice-id'); - * await writeFile('./invoice.pdf', pdf); - * ``` - * - * @example Retry if not ready - * ```typescript - * let pdf; - * for (let i = 0; i < 3; i++) { - * try { - * pdf = await nfe.serviceInvoices.downloadPdf('company-id', 'invoice-id'); - * break; - * } catch (err) { - * if (err instanceof NotFoundError && i < 2) { - * await new Promise(resolve => setTimeout(resolve, 5000)); - * } else { - * throw err; - * } - * } - * } - * ``` - */ -async downloadPdf(companyId: string, invoiceId?: string): Promise -``` - ---- - -## MODIFIED Requirements - -None - this is a new capability. - ---- - -## REMOVED Requirements - -None - this is a new capability. - ---- - -## Dependencies - -- **HTTP Client**: Must support binary responses (arrayBuffer()) -- **Error System**: Requires NotFoundError, AuthenticationError, TimeoutError -- **Node.js**: Requires Buffer support (built-in) - ---- - -## Testing Requirements - -### Unit Tests -- Test PDF download with mocked binary data -- Test XML download with mocked binary data -- Test 404 error handling -- Test other error scenarios (401, 408, 500) -- Test batch download (if supported) -- Test binary data integrity -- Coverage > 80% - -### Integration Tests -- Test against MSW with actual PDF/XML sample data -- Test saving to file -- Test parsing downloaded XML -- Test retry logic for 404 -- Verify PDF magic numbers - ---- - -## Documentation Requirements - -- Document download methods in API.md -- Provide example of downloading and saving files -- Document retry pattern for 404 errors -- Document memory considerations for large files -- Include examples in examples/service-invoice-complete.js - ---- - -## Non-Functional Requirements - -- **Performance**: Downloads should handle multi-MB files efficiently -- **Memory**: Use streaming if files commonly exceed 10MB (check API behavior) -- **Binary Integrity**: No encoding corruption of binary data -- **Compatibility**: Return Node.js Buffer for maximum compatibility -- **Error Clarity**: 404 errors should indicate whether invoice doesn't exist or file isn't ready yet - ---- - -## Open Questions - -1. **Batch download support**: Does the API actually support batch PDF downloads? Check OpenAPI spec and v2 behavior. -2. **File size limits**: What's the typical size of invoices? Do we need streaming for large files? -3. **Retry guidance**: Should we provide a built-in retry helper or just document the pattern? -4. **Format variations**: Do different cities return different XML schemas? Need to document? -5. **Caching**: Should downloads be cached to avoid redundant API calls? diff --git a/openspec/changes/implement-service-invoices/specs/service-invoice-operations/spec.md b/openspec/changes/implement-service-invoices/specs/service-invoice-operations/spec.md deleted file mode 100644 index f274c0a..0000000 --- a/openspec/changes/implement-service-invoices/specs/service-invoice-operations/spec.md +++ /dev/null @@ -1,446 +0,0 @@ -# Capability: Service Invoice Operations - -**Capability ID**: `service-invoice-operations` -**Parent Change**: `implement-service-invoices` -**Type**: Core Feature -**Priority**: Critical - ---- - -## Overview - -This capability encompasses the core CRUD operations for service invoices (Nota Fiscal de Serviço - NFSE), including creation, listing, retrieval, cancellation, and email notifications. These operations form the foundation of NFE.io's service invoice functionality. - -## ADDED Requirements - -### Requirement: CRUD-001 - Create Service Invoice -**Priority**: Critical -**Component**: ServiceInvoicesResource.create() - -The SDK MUST allow developers to create a new service invoice for a company by providing invoice data that complies with Brazilian tax regulations. - -#### Scenario: Create invoice with immediate success (201) -```typescript -const nfe = new NfeClient({ apiKey: 'xxx' }); -const invoice = await nfe.serviceInvoices.create('company-id', { - cityServiceCode: '2690', - description: 'Serviços de consultoria', - servicesAmount: 1000.00, - borrower: { - type: 'LegalEntity', - name: 'Cliente Exemplo LTDA', - federalTaxNumber: 12345678000199, - email: 'cliente@exemplo.com', - address: { - country: 'BRA', - postalCode: '01310-100', - street: 'Avenida Paulista', - number: '1000', - district: 'Bela Vista', - city: { code: '3550308' }, - state: 'SP' - } - } -}); - -// Returns ServiceInvoice -assert(invoice.id !== undefined); -assert(invoice.flowStatus === 'Issued'); -``` - -#### Scenario: Create invoice with async processing (202) -```typescript -const result = await nfe.serviceInvoices.create('company-id', invoiceData); - -// Returns AsyncResponse -assert(result.status === 'pending'); -assert(result.location !== undefined); // URL for polling -assert(result.invoiceId !== undefined); - -// User can poll manually -const invoice = await nfe.serviceInvoices.retrieve('company-id', result.invoiceId); -``` - -#### Scenario: Validation error (400) -```typescript -await expect( - nfe.serviceInvoices.create('company-id', { /* invalid data */ }) -).rejects.toThrow(ValidationError); -// Error message contains field-specific validation failures -``` - -#### Scenario: Authentication error (401) -```typescript -const nfe = new NfeClient({ apiKey: 'invalid' }); -await expect( - nfe.serviceInvoices.create('company-id', invoiceData) -).rejects.toThrow(AuthenticationError); -``` - ---- - -### Requirement: CRUD-002 - List Service Invoices -**Priority**: Critical -**Component**: ServiceInvoicesResource.list() - -The SDK MUST allow developers to list service invoices for a company with pagination and date filtering. - -#### Scenario: List all invoices with pagination -```typescript -const result = await nfe.serviceInvoices.list('company-id', { - pageIndex: 0, - pageCount: 50 -}); - -assert(result.serviceInvoices.length <= 50); -assert(result.totalResults !== undefined); -assert(result.totalPages !== undefined); -assert(result.page === 0); -``` - -#### Scenario: Filter invoices by issued date range -```typescript -const result = await nfe.serviceInvoices.list('company-id', { - issuedBegin: '2026-01-01', - issuedEnd: '2026-01-31', - pageCount: 100 -}); - -// All returned invoices issued within January 2026 -result.serviceInvoices.forEach(invoice => { - const issuedDate = new Date(invoice.issuedOn); - assert(issuedDate >= new Date('2026-01-01')); - assert(issuedDate <= new Date('2026-01-31')); -}); -``` - -#### Scenario: Filter invoices by creation date range -```typescript -const result = await nfe.serviceInvoices.list('company-id', { - createdBegin: '2026-01-10', - createdEnd: '2026-01-15' -}); - -// All returned invoices created within date range -result.serviceInvoices.forEach(invoice => { - const createdDate = new Date(invoice.createdOn); - assert(createdDate >= new Date('2026-01-10')); - assert(createdDate <= new Date('2026-01-15')); -}); -``` - -#### Scenario: Request totals in list response -```typescript -const result = await nfe.serviceInvoices.list('company-id', { - hasTotals: true -}); - -assert(result.totals !== undefined); -assert(result.totals.totalAmount !== undefined); -``` - -#### Scenario: Empty list result -```typescript -const result = await nfe.serviceInvoices.list('company-id', { - issuedBegin: '2030-01-01', // Future date - issuedEnd: '2030-01-31' -}); - -assert(result.serviceInvoices.length === 0); -assert(result.totalResults === 0); -``` - ---- - -### Requirement: CRUD-003 - Retrieve Service Invoice -**Priority**: Critical -**Component**: ServiceInvoicesResource.retrieve() - -The SDK MUST allow developers to retrieve a specific service invoice by its ID to check its current state and details. - -#### Scenario: Retrieve existing invoice -```typescript -const invoice = await nfe.serviceInvoices.retrieve('company-id', 'invoice-id'); - -assert(invoice.id === 'invoice-id'); -assert(invoice.flowStatus !== undefined); -assert(invoice.provider !== undefined); -assert(invoice.borrower !== undefined); -``` - -#### Scenario: Invoice not found (404) -```typescript -await expect( - nfe.serviceInvoices.retrieve('company-id', 'non-existent-id') -).rejects.toThrow(NotFoundError); -``` - -#### Scenario: Check flow status of invoice -```typescript -const invoice = await nfe.serviceInvoices.retrieve('company-id', 'invoice-id'); - -if (invoice.flowStatus === 'Issued') { - console.log('Invoice successfully issued'); -} else if (invoice.flowStatus === 'IssueFailed') { - console.error('Issue failed:', invoice.flowMessage); -} -``` - ---- - -### Requirement: CRUD-004 - Cancel Service Invoice -**Priority**: High -**Component**: ServiceInvoicesResource.cancel() - -The SDK MUST allow developers to cancel an issued service invoice when needed (before certain time limits per city regulations). - -#### Scenario: Cancel issued invoice -```typescript -const cancelled = await nfe.serviceInvoices.cancel('company-id', 'invoice-id'); - -assert(cancelled.flowStatus === 'Cancelled'); -assert(cancelled.id === 'invoice-id'); -``` - -#### Scenario: Cancel already cancelled invoice -```typescript -// First cancellation succeeds -await nfe.serviceInvoices.cancel('company-id', 'invoice-id'); - -// Second cancellation may return already cancelled status -const result = await nfe.serviceInvoices.cancel('company-id', 'invoice-id'); -assert(result.flowStatus === 'Cancelled'); -``` - -#### Scenario: Cancel non-existent invoice (404) -```typescript -await expect( - nfe.serviceInvoices.cancel('company-id', 'non-existent-id') -).rejects.toThrow(NotFoundError); -``` - -#### Scenario: Cancel fails due to city regulations -```typescript -await expect( - nfe.serviceInvoices.cancel('company-id', 'old-invoice-id') -).rejects.toThrow(InvoiceProcessingError); // CancelFailed status -``` - ---- - -### Requirement: EMAIL-001 - Send Invoice via Email -**Priority**: High -**Component**: ServiceInvoicesResource.sendEmail() - -The SDK MUST allow developers to send the issued invoice to the borrower (customer) via email. - -#### Scenario: Send email successfully -```typescript -const result = await nfe.serviceInvoices.sendEmail('company-id', 'invoice-id'); - -assert(result.sent === true); -``` - -#### Scenario: Send email for non-issued invoice -```typescript -// Invoice not yet issued -await expect( - nfe.serviceInvoices.sendEmail('company-id', 'pending-invoice-id') -).rejects.toThrow(ValidationError); // Can't email non-issued invoice -``` - -#### Scenario: Email send failure -```typescript -const result = await nfe.serviceInvoices.sendEmail('company-id', 'invoice-id'); - -if (!result.sent) { - console.error('Email failed:', result.message); -} -``` - ---- - -### Requirement: ERROR-001 - Comprehensive Error Handling -**Priority**: Critical -**Component**: ServiceInvoicesResource (all methods) - -All methods MUST handle API errors consistently and provide typed error instances. - -#### Scenario: Authentication error (401) -```typescript -const nfe = new NfeClient({ apiKey: 'invalid-key' }); - -try { - await nfe.serviceInvoices.list('company-id'); -} catch (error) { - assert(error instanceof AuthenticationError); - assert(error.statusCode === 401); - assert(error.message.includes('API Key')); -} -``` - -#### Scenario: Validation error (400) -```typescript -try { - await nfe.serviceInvoices.create('company-id', { /* incomplete data */ }); -} catch (error) { - assert(error instanceof ValidationError); - assert(error.statusCode === 400); - assert(error.details !== undefined); // Field-level validation errors -} -``` - -#### Scenario: Server error (500) -```typescript -try { - await nfe.serviceInvoices.retrieve('company-id', 'invoice-id'); -} catch (error) { - assert(error instanceof NfeError); - assert(error.statusCode === 500); -} -``` - -#### Scenario: Timeout error (408) -```typescript -try { - await nfe.serviceInvoices.cancel('company-id', 'invoice-id'); -} catch (error) { - assert(error instanceof TimeoutError); - assert(error.statusCode === 408); - assert(error.message.includes('timeout')); -} -``` - ---- - -### Requirement: TYPE-001 - Type Safety -**Priority**: Critical -**Component**: ServiceInvoicesResource, types.ts - -All public methods MUST have strict TypeScript types with no `any` in public APIs. - -#### Scenario: Method parameters are typed -```typescript -// TypeScript compilation error if wrong types -nfe.serviceInvoices.create( - 123, // Error: Expected string - {} // Error: Missing required fields -); -``` - -#### Scenario: Return types are precise -```typescript -const invoice: ServiceInvoice = await nfe.serviceInvoices.retrieve('id', 'inv'); -// invoice has all fields typed - -const result: ListResponse = await nfe.serviceInvoices.list('id'); -// result.serviceInvoices is ServiceInvoice[] -// result.totalResults is number -``` - -#### Scenario: Discriminated unions for async responses -```typescript -const result = await nfe.serviceInvoices.create('id', data); - -if ('location' in result) { - // TypeScript knows this is AsyncResponse - const location: string = result.location; -} else { - // TypeScript knows this is ServiceInvoice - const flowStatus: string = result.flowStatus; -} -``` - ---- - -### Requirement: DOC-001 - JSDoc Documentation -**Priority**: High -**Component**: ServiceInvoicesResource (all methods) - -All public methods MUST have complete JSDoc comments with descriptions, parameters, return types, examples, and error documentation. - -#### Scenario: Method has JSDoc with example -```typescript -/** - * Create a new service invoice - * - * @param companyId - The ID of the company issuing the invoice - * @param data - Invoice data conforming to Brazilian tax regulations - * @returns Either an issued ServiceInvoice (201) or AsyncResponse for polling (202) - * @throws {ValidationError} If invoice data is invalid - * @throws {AuthenticationError} If API key is invalid - * @throws {NfeError} For other API errors - * - * @example - * ```typescript - * const invoice = await nfe.serviceInvoices.create('company-id', { - * cityServiceCode: '2690', - * description: 'Consulting services', - * servicesAmount: 1000.00, - * borrower: { ... } - * }); - * - * if ('location' in invoice) { - * // Async processing - poll for result - * const final = await nfe.serviceInvoices.retrieve('company-id', invoice.invoiceId); - * } - * ``` - */ -async create(companyId: string, data: ServiceInvoiceData): Promise -``` - ---- - -## MODIFIED Requirements - -None - this is a new capability. - ---- - -## REMOVED Requirements - -None - this is a new capability. - ---- - -## Dependencies - -- **HTTP Client**: Must exist and support GET, POST, PUT, DELETE -- **Error System**: Must have AuthenticationError, ValidationError, NotFoundError, TimeoutError, NfeError -- **Types**: Must have ServiceInvoice, ServiceInvoiceData, ListResponse, AsyncResponse defined - ---- - -## Testing Requirements - -### Unit Tests -- Test all CRUD methods with mocked HTTP client -- Test all error scenarios (401, 400, 404, 408, 500) -- Test pagination logic -- Test date filtering -- Test email sending -- Coverage > 80% - -### Integration Tests -- Test against MSW-mocked API -- Test complete invoice lifecycle -- Test error recovery -- Test edge cases (empty lists, invalid dates, etc.) - ---- - -## Documentation Requirements - -- API.md section documenting all methods -- README.md quick start example -- examples/service-invoice-complete.js with all operations -- JSDoc comments on all public methods - ---- - -## Non-Functional Requirements - -- **Performance**: List operations should handle 1000+ results efficiently -- **Type Safety**: Zero `any` types in public APIs -- **Error Handling**: All API errors must be caught and typed -- **Backwards Compatibility**: Method signatures should align with v2 where practical diff --git a/openspec/changes/implement-service-invoices/tasks.md b/openspec/changes/implement-service-invoices/tasks.md deleted file mode 100644 index bbc1130..0000000 --- a/openspec/changes/implement-service-invoices/tasks.md +++ /dev/null @@ -1,1166 +0,0 @@ -# Tasks: Implement Service Invoices Resource - -**Change ID**: `implement-service-invoices` -**Dependencies**: HTTP client, error system, types -**Estimated Effort**: 3-4 days -**Last Updated**: 2026-01-16 (Phase 1 completed and verified) - ---- - -## Task Sequencing - -Tasks are ordered to deliver incremental user value while respecting dependencies. Tasks marked with ⚡ can be parallelized. - ---- - -## Phase 1: Foundation & Setup (Day 1) [x] COMPLETE - -### Task 1.1: Validate OpenAPI Spec and Generate Types [x] DONE -**Duration**: 1 hour (Completed: 2026-01-16) -**Owner**: SDK Team -**Parallelizable**: No - -**Description**: -Ensure the OpenAPI specification for service invoices is accurate and complete, then generate TypeScript types. - -**Steps**: -1. [x] Run `npm run validate-spec` to check OpenAPI validity -2. [x] Review service invoice schemas in `openapi/spec/nf-servico-v1.yaml` -3. [x] Cross-reference with actual API behavior (check v2 code and samples) -4. [x] Document any discrepancies found -5. [x] Run `npm run generate` to generate types -6. [x] Verify types compile with `npm run typecheck` - -**Validation**: -- [x] `npm run validate-spec` exits with code 0 -- [x] Generated types in `src/generated/` compile successfully (nf-servico-v1.ts: 4598 lines) -- [x] ServiceInvoice type includes all expected fields (via operations['ServiceInvoices_Get']) -- [x] No blocking issues or discrepancies documented - -**Deliverable**: [x] Generated TypeScript types ready for use (`src/generated/nf-servico-v1.ts`) - -**Notes**: Successfully generated 7 of 12 OpenAPI specs. All ServiceInvoice operations available. - ---- - -### Task 1.2: Define Core Service Invoice Types [x] DONE -**Duration**: 2 hours (Completed: 2026-01-16) -**Owner**: SDK Team -**Parallelizable**: Yes (can start while 1.1 generates types) - -**Description**: -Create or update TypeScript type definitions specific to service invoice operations in the handwritten layer. - -**Steps**: -1. [x] Open `src/core/types.ts` -2. [x] Define `FlowStatus` type with all processing states -3. [x] Define `TERMINAL_FLOW_STATES` constant and `isTerminalFlowStatus()` helper -4. [x] Define `ServiceInvoiceAsyncResponse` extending `AsyncResponse` with `invoiceId` -5. [x] Define `ListServiceInvoicesOptions` with pagination and date filters -6. [x] Define `PollingOptions` interface for `createAndWait()` configuration -7. [x] Define `SendEmailResponse` interface -8. [x] Export generated types from `operations['ServiceInvoices_*']` -9. [x] Create type aliases: `ServiceInvoiceData`, `CreateServiceInvoiceData`, etc. -10. [x] Add placeholder types for Company, LegalPerson, NaturalPerson -11. [x] Add JSDoc comments with descriptions -12. [x] Run `npm run typecheck` - -**Validation**: -- [x] All types compile with strict TypeScript -- [x] Types match OpenAPI schema structure (via operations type) -- [x] JSDoc comments include descriptions -- [x] No `any` types in public interfaces -- [x] `npm run typecheck` passes (zero errors) - -**Deliverable**: [x] Complete type definitions in `src/core/types.ts` (320 lines, +127 lines) - -**Key Types Added**: -- `FlowStatus` - All invoice processing states (11 values) -- `ServiceInvoiceAsyncResponse` - 202 response with invoiceId -- `ListServiceInvoicesOptions` - List filters (issuedBegin/End, createdBegin/End, hasTotals) -- `PollingOptions` - Configuration for async operations (timeout, delays, callbacks) -- `SendEmailResponse` - Email operation result -- `ServiceInvoiceData` - Main invoice type from generated operations -- `CreateServiceInvoiceData` - Create request body -- `ServiceInvoiceListResponse` - List operation response -- `ServiceInvoiceSingleResponse` - Single item response - ---- - -### Task 1.3: Create Polling Utility [x] DONE -**Duration**: 2 hours (Completed: 2026-01-16) -**Owner**: SDK Team -**Parallelizable**: Yes - -**Description**: -Implement reusable polling utility for async invoice processing. - -**Steps**: -1. [x] Create `src/core/utils/polling.ts` (244 lines) -2. [x] Implement `poll()` function with: - - Configurable intervals (initialDelay, maxDelay, backoffFactor) - - Total timeout with TimeoutError - - isComplete condition function - - Exponential backoff with jitter -3. [x] Implement `pollWithRetries()` for retry logic on failures -4. [x] Add TypeScript types: `PollingOptions`, `PollingConfig` -5. [x] Add error handling for timeouts with attempt count -6. [x] Create unit tests in `tests/unit/core/utils/polling.test.ts` -7. [x] Test various scenarios (immediate, eventual success, timeout, callbacks) - -**Validation**: -- [x] Unit tests pass: 32 passed, 1 skipped (33 total) -- [x] Exponential backoff works correctly (tested with delays 1000→1500→2250ms) -- [x] Timeout handling tested (throws TimeoutError with attempt count) -- [x] TypeScript types are strict (no any types) -- [x] JSDoc comments complete with examples - -**Deliverable**: [x] Reusable polling utility with comprehensive tests - -**Key Functions Implemented**: -- `poll()` - Core polling with exponential backoff -- `pollWithRetries()` - Polling with retry logic on failures -- `createPollingConfig()` - Config factory with defaults -- `sleep()` - Promise-based delay helper - -**Test Coverage**: 32 test cases covering: -- Immediate completion -- Multiple attempts with exponential backoff -- MaxDelay cap enforcement -- Timeout errors with detailed messages -- onPoll and onError callbacks -- Edge cases (zero timeout, immediate errors) - ---- - -## 📊 Phase 1 Summary - -**Status**: [x] COMPLETE (2026-01-16) -**Total Duration**: ~5 hours (within 1 day estimate) -**Completion Rate**: 3/3 tasks (100%) - -### Accomplishments - -#### Files Created/Modified: -- [x] `src/generated/nf-servico-v1.ts` (4598 lines) - Generated types from OpenAPI -- [x] `src/core/types.ts` (320 lines, +127) - Core type definitions -- [x] `src/core/utils/polling.ts` (244 lines) - Polling utility -- [x] `tests/unit/core/utils/polling.test.ts` - Test suite (32 tests) - -#### Key Deliverables: -1. **Type Generation**: All ServiceInvoice operations typed from OpenAPI spec -2. **Type System**: 9 new types/interfaces for service invoices -3. **Polling Utility**: Reusable async processing with exponential backoff -4. **Test Coverage**: 32 passing tests for polling functionality - -#### Technical Foundation: -- [x] TypeScript compilation: Zero errors -- [x] Generated types: 7 specs processed successfully -- [x] Terminal flow states: 4 states identified and tested -- [x] Polling configuration: Fully customizable (timeout, delays, callbacks) - -### Ready for Phase 2 - -All foundation components are in place: -- [x] Types defined and validated -- [x] Polling utility tested and ready -- [x] Generated schemas accessible via `operations` type -- [x] No blocking issues or technical debt - -**Next Steps**: Implement CRUD operations in Phase 2 (Tasks 2.1-2.3) - -### Phase 1 Verification Checklist - -**Foundation Requirements** (from proposal): -- [x] OpenAPI spec validated and types generated -- [x] Core types defined for service invoices -- [x] Polling utility implemented for async processing -- [x] TypeScript strict mode compilation passing -- [x] Basic test coverage for utilities -- [x] No technical debt or blocking issues - -**Type System Completeness**: -- [x] FlowStatus with all 11 states defined -- [x] Terminal states identified (4 states) -- [x] Async response type with invoiceId -- [x] List options with date filters -- [x] Polling configuration interface -- [x] Generated types properly imported -- [x] Type aliases for convenience - -**Polling Utility Completeness**: -- [x] Core poll() function with exponential backoff -- [x] pollWithRetries() for failure scenarios -- [x] Timeout handling with TimeoutError -- [x] Callback support (onPoll, onError) -- [x] Configurable delays and backoff -- [x] Comprehensive test suite (32 tests) - -**Missing from Phase 1** (deferred to Phase 2): -- ⏸️ Actual CRUD operation implementations (Task 2.1) -- ⏸️ createAndWait() method (Task 2.2) -- ⏸️ sendEmail() implementation (Task 2.3) -- ⏸️ PDF/XML download methods (Phase 3) -- ⏸️ Integration tests for full flow (Phase 4) - -**No items missed** - Phase 1 scope fully completed as planned. - ---- - -## Phase 2: Core Implementation (Day 2) [x] COMPLETE - -### Task 2.1: Implement CRUD Operations [x] DONE -**Duration**: 3 hours (Completed: 2026-01-17) -**Owner**: SDK Team -**Parallelizable**: No (depends on 1.1, 1.2) - -**Description**: -Complete the implementation of create, list, retrieve, and cancel operations. - -**Steps**: -1. [x] Open `src/core/resources/service-invoices.ts` -2. [x] Complete `create()` method: - - Handle 201 (immediate success) response - - Handle 202 (async processing) response with location - - Return discriminated union: `CreateInvoiceResponse` (status: 'immediate' | 'async') - - Extract invoiceId from Location header - - Add proper error handling -3. [x] Complete `list()` method: - - Support pagination (pageIndex, pageCount) - - Support date filters (issuedBegin, issuedEnd, createdBegin, createdEnd) - - Support hasTotals flag - - Return typed `ServiceInvoiceListResponse` -4. [x] Complete `retrieve()` method: - - Simple GET by ID - - Return typed `ServiceInvoiceData` - - Handle 404 with NotFoundError -5. [x] Complete `cancel()` method: - - DELETE by ID - - Return cancelled invoice data -6. [x] Add comprehensive JSDoc comments to all methods with examples -7. [x] Run `npm run typecheck` - -**Validation**: -- [x] All methods have correct signatures -- [x] Error handling covers 400, 401, 408, 500 cases (via HttpClient) -- [x] JSDoc includes parameter descriptions and examples -- [x] TypeScript compilation passes (zero errors) -- [x] Return types match spec (discriminated unions for async responses) - -**Deliverable**: [x] Complete CRUD methods in ServiceInvoicesResource class - -**Key Improvements**: -- Discriminated union for 201/202 responses instead of loose union -- Proper invoiceId extraction from Location header -- Comprehensive JSDoc with usage examples -- Type-safe using generated OpenAPI types - ---- - -### Task 2.2: Implement Async Processing Helper [x] DONE -**Duration**: 2 hours (Completed: 2026-01-17) -**Owner**: SDK Team -**Parallelizable**: No (depends on 2.1, 1.3) - -**Description**: -Add `createAndWait()` method for automatic polling of async invoice creation. - -**Steps**: -1. [x] In `src/core/resources/service-invoices.ts`, add `createAndWait()` method -2. [x] Call `create()` internally -3. [x] If result is discriminated union with status 'async', start polling: - - Extract invoice ID from response (already extracted in create()) - - Use polling utility from Task 1.3 - - Poll with `retrieve()` until flowStatus is terminal (Issued, IssueFailed, etc.) -4. [x] If result is status 'immediate', return invoice immediately -5. [x] Handle timeout with TimeoutError (from polling utility) -6. [x] Handle failure states (IssueFailed, CancelFailed) with InvoiceProcessingError -7. [x] Add JSDoc with polling configuration options -8. [x] Add configurable polling options (timeout, intervals, callbacks) -8. [x] Add configurable polling options (timeout, intervals, callbacks) - -**Validation**: -- [x] Method handles both 201 and 202 responses correctly -- [x] Polling stops on terminal states (via isTerminalFlowStatus) -- [x] Timeout errors are clear and actionable (via polling utility) -- [x] Failed invoices throw InvoiceProcessingError with flowMessage -- [x] JSDoc explains polling behavior with examples - -**Deliverable**: [x] `createAndWait()` method with polling logic - -**Key Features**: -- Uses Phase 1 polling utility (exponential backoff, configurable timeouts) -- Supports onPoll callback for progress tracking -- Discriminates between immediate (201) and async (202) responses -- Throws specific errors for failed processing states - ---- - -### Task 2.3: Implement Email Operations [x] DONE -**Duration**: 1 hour (Completed: 2026-01-17) -**Owner**: SDK Team -**Parallelizable**: Yes (can work while 2.2 is being tested) - -**Description**: -Complete the `sendEmail()` method for sending invoices via email. - -**Steps**: -1. [x] In `src/core/resources/service-invoices.ts`, implement `sendEmail()` method -2. [x] Ensure proper PUT to `/serviceinvoices/{id}/sendemail` -3. [x] Handle response (SendEmailResponse type with sent flag + optional message) -4. [x] Add error handling for 400, 401, 408, 500 (via HttpClient) -5. [x] Add JSDoc with example - -**Validation**: -- [x] Method signature matches spec -- [x] Returns typed SendEmailResponse with sent flag -- [x] Error handling complete (via HttpClient) -- [x] JSDoc includes example - -**Deliverable**: [x] Complete `sendEmail()` method - ---- - -## 📊 Phase 2 Summary - -**Status**: [x] COMPLETE (2026-01-17) -**Total Duration**: ~6 hours (within 1 day estimate) -**Completion Rate**: 3/3 tasks (100%) - -### Accomplishments - -#### Files Modified: -- [x] `src/core/resources/service-invoices.ts` (463 lines, completely refactored) - - All CRUD operations implemented with proper types - - createAndWait() with Phase 1 polling utility integration - - sendEmail() operation - - Convenience methods: getStatus(), createBatch() - - Helper methods: extractInvoiceIdFromLocation() - -#### Key Deliverables: -1. **CRUD Operations**: create, list, retrieve, cancel with discriminated unions -2. **Async Processing**: createAndWait() with exponential backoff polling -3. **Email Operations**: sendEmail() with typed responses -4. **Type Safety**: Full integration with generated OpenAPI types -5. **Developer Experience**: Comprehensive JSDoc with usage examples - -#### Technical Improvements: -- [x] Discriminated unions for 201/202 responses (type-safe) -- [x] Automatic invoiceId extraction from Location header -- [x] Integration with polling utility (exponential backoff, timeouts) -- [x] Terminal state detection (isTerminalFlowStatus) -- [x] Proper error handling (InvoiceProcessingError, NotFoundError) -- [x] Zero TypeScript compilation errors -- [x] Comprehensive JSDoc with practical examples - -### Ready for Phase 3 - -Core functionality complete: -- [x] All CRUD operations working -- [x] Async processing with automatic polling -- [x] Email notifications -- [x] Download operations (PDF/XML implemented in Phase 3) - -**Next Steps**: ~~Implement PDF/XML downloads with binary streaming (Phase 3)~~ ✓ Complete - Proceed to Phase 4 (Testing) - ---- - -## Phase 3: Document Downloads (Day 2-3) [x] COMPLETE - -### Task 3.1: Implement PDF Download [x] DONE -**Duration**: 2 hours (Completed: 2026-01-17) -**Owner**: SDK Team -**Parallelizable**: No (depends on 2.1) - -**Description**: -Complete PDF download functionality using Fetch API for binary data. - -**Steps**: -1. [x] In `src/core/resources/service-invoices.ts`, review `downloadPdf()` method -2. [x] Ensure proper GET to `/serviceinvoices/{id}/pdf` -3. [x] Set Accept header to `application/pdf` -4. [x] Handle Response.arrayBuffer() for binary data (via HttpClient) -5. [x] Convert to Buffer for Node.js compatibility (via HttpClient) -6. [x] Handle 404 (document not ready) via HttpClient error handling -7. [x] Add JSDoc with example and memory warning for large files - -**Validation**: -- [x] Returns Buffer object -- [x] Handles binary data correctly (HttpClient auto-converts arrayBuffer to Buffer) -- [x] 404 errors handled gracefully (via HttpClient's NotFoundError) -- [x] Memory considerations documented in JSDoc remarks -- [x] Method works for both single invoice and batch (ZIP for bulk) - -**Deliverable**: [x] Working `downloadPdf()` method with comprehensive JSDoc - -**Key Features**: -- Returns Buffer for direct file writing or streaming -- Custom Accept: application/pdf header -- Supports bulk download (returns ZIP) -- Comprehensive JSDoc with examples and warnings - ---- - -### Task 3.2: Implement XML Download ⚡ [x] DONE -**Duration**: 1.5 hours (Completed: 2026-01-17) -**Owner**: SDK Team -**Parallelizable**: Yes (similar to 3.1) - -**Description**: -Complete XML download functionality. - -**Steps**: -1. [x] In `src/core/resources/service-invoices.ts`, review `downloadXml()` method -2. [x] Ensure proper GET to `/serviceinvoices/{id}/xml` -3. [x] Set Accept header to `application/xml` -4. [x] Handle Response.arrayBuffer() for binary data (via HttpClient) -5. [x] Convert to Buffer for Node.js compatibility (via HttpClient) -6. [x] Handle 404 (document not ready) via HttpClient error handling -7. [x] Add JSDoc with comprehensive examples - -**Validation**: -- [x] Returns Buffer (can be converted to string with .toString('utf-8')) -- [x] Handles XML data correctly (HttpClient auto-detects content-type) -- [x] 404 errors handled gracefully (via HttpClient's NotFoundError) -- [x] JSDoc complete with examples and remarks - -**Deliverable**: [x] Working `downloadXml()` method with comprehensive JSDoc - -**Key Features**: -- Returns Buffer for flexibility (file or string) -- Custom Accept: application/xml header -- Supports bulk download (returns ZIP) -- JSDoc shows both file writing and string conversion examples - ---- - -## 📊 Phase 3 Summary - -**Status**: [x] COMPLETE (2026-01-17) -**Total Duration**: ~3.5 hours (within 0.5 day estimate) -**Completion Rate**: 2/2 tasks (100%) - -### Accomplishments - -#### Files Modified: -- [x] `src/core/http/client.ts` - Added customHeaders support to GET method - - Updated `get()` method signature to accept optional customHeaders parameter - - Modified `request()` to pass customHeaders through - - Modified `executeRequest()` to use customHeaders - - Updated `buildHeaders()` to merge custom headers (allows Accept header override) - -- [x] `src/core/resources/service-invoices.ts` - Implemented download methods - - `downloadPdf()` - Complete implementation with Buffer return type - - `downloadXml()` - Complete implementation with Buffer return type - - Both support single invoice and bulk download (ZIP) - -#### Key Deliverables: -1. **PDF Download**: downloadPdf() with Accept: application/pdf, returns Buffer -2. **XML Download**: downloadXml() with Accept: application/xml, returns Buffer -3. **Binary Streaming**: HttpClient handles Response.arrayBuffer() → Buffer conversion -4. **Error Handling**: 404 handled via NotFoundError (document not ready) -5. **Documentation**: Comprehensive JSDoc with examples, remarks, and warnings - -#### Technical Improvements: -- [x] HttpClient now supports custom headers (backward compatible) -- [x] Automatic content-type detection for PDF/XML (existing in HttpClient) -- [x] Buffer return type for binary data (file writing or string conversion) -- [x] Bulk download support (returns ZIP files) -- [x] Memory warnings documented for large files -- [x] Zero TypeScript compilation errors - -### Ready for Phase 4 - -Download functionality complete: -- [x] PDF download with proper headers and binary handling -- [x] XML download with proper headers and binary handling -- [x] Supports both single and bulk operations -- [x] Comprehensive error handling via HttpClient -- [x] Full JSDoc documentation with practical examples - -**Next Steps**: Write comprehensive unit and integration tests (Phase 4) - ---- - -## Phase 4: Testing (Day 3) ⏳ IN PROGRESS - -### Task 4.1: Write Unit Tests for All Methods [x] DONE -**Duration**: 4 hours (Completed: 2026-01-17) -**Owner**: SDK Team -**Parallelizable**: No (depends on all implementation tasks) - -**Description**: -Create comprehensive unit tests for the ServiceInvoicesResource class. - -**Steps**: -1. [x] Create/update `tests/unit/core/resources/service-invoices.test.ts` -2. [x] Mock HttpClient for all tests -3. [x] Write tests for `create()`: - - Test 201 response (immediate success) - - Test 202 response (async processing) - - Test invoiceId extraction from Location header - - Test missing Location header error - - Test invalid Location format error -4. [x] Write tests for `list()`: - - Test pagination - - Test date filtering - - Test empty results -5. [x] Write tests for `retrieve()`: - - Test successful retrieval - - Test 404 error -6. [x] Write tests for `cancel()`: - - Test successful cancellation - - Test 404 error -7. [x] Write tests for `createAndWait()`: - - Test immediate success (201) - - Note: Complex polling tests deferred to integration tests -8. [x] Write tests for `sendEmail()`: - - Test successful send - - Test failure response -9. [x] Write tests for `downloadPdf()` and `downloadXml()`: - - Test successful downloads (single and bulk) - - Test 404 (not ready) - - Test binary data handling - - Test string conversion for XML -10. [x] Write tests for `getStatus()`: - - Test Issued state (isComplete=true, isFailed=false) - - Test IssueFailed state (isComplete=true, isFailed=true) - - Test WaitingSend state (isComplete=false, isFailed=false) -11. [x] Write tests for `createBatch()`: - - Test without waiting - - Test with waiting - - Test maxConcurrent option -12. [x] Validate all tests pass with zero errors - -**Test Results**: -- **Total Tests**: 29 tests across 10 test suites -- **Pass Rate**: 29/29 (100%) -- **Test Duration**: 24ms execution, 996ms total -- **Test File**: tests/unit/core/resources/service-invoices.test.ts (597 lines) - -**Test Coverage Summary**: -- `create()`: 5 tests (201/202 responses, ID extraction, error cases) -- `list()`: 4 tests (default, pagination, filters, empty results) -- `retrieve()`: 2 tests (success, 404 error) -- `cancel()`: 2 tests (success, 404 error) -- `sendEmail()`: 2 tests (success, failure) -- `createAndWait()`: 1 test (immediate success - complex polling scenarios deferred to integration tests) -- `downloadPdf()`: 3 tests (single invoice, bulk ZIP, 404 not ready) -- `downloadXml()`: 4 tests (single invoice, bulk ZIP, 404 not ready, Buffer conversion) -- `getStatus()`: 3 tests (Issued=complete, IssueFailed=complete+failed, WaitingSend=incomplete) -- `createBatch()`: 3 tests (no wait, with wait, concurrency control) - -**Bugs Fixed During Testing**: -1. **getStatus() logic error**: isComplete was checking `isTerminalFlowStatus(status) && status === 'Issued'`, should only check `isTerminalFlowStatus(status)` to return true for ALL terminal states (Issued, IssueFailed, Cancelled, etc.) -2. **extractInvoiceIdFromLocation regex**: Pattern was `[a-f0-9-]+` (hex-only), changed to `[a-z0-9-]+` to accept full alphanumeric IDs like "invoice-456" - -**Validation Checklist**: -- [x] All 29 tests passing (100% success rate) -- [x] Zero TypeScript compilation errors -- [x] Zero ESLint warnings -- [x] HttpClient properly mocked for unit test isolation -- [x] All discriminated unions handled correctly -- [x] Error cases covered (404, NotFoundError) -- [x] Binary data handling validated (PDF/XML downloads) -- [x] Batch operations validated -- [x] Production bugs discovered and fixed - -**Completion Date**: 2026-01-17 - ---- - - Test failures -9. Write tests for `downloadPdf()` and `downloadXml()`: - - Test successful downloads - - Test 404 (not ready) - - Test binary data handling -10. Run `npm test` and verify coverage > 80% - -**Validation**: -- [ ] All tests pass -- [ ] Coverage > 80% for service-invoices.ts -- [ ] All error scenarios tested -- [ ] Async polling scenarios tested -- [ ] Mocks properly isolated - -**Deliverable**: Complete unit test suite - ---- - -### Task 4.2: Integration Tests with Real API [x] DONE -**Duration**: 3 hours (Completed: 2026-01-18) -**Owner**: SDK Team -**Parallelizable**: Can start while unit tests are being written - -**Description**: -Integration tests that run against real NFE.io API (requires API key). - -**Steps**: -1. [x] Open `tests/integration/service-invoices.integration.test.ts` -2. [x] Set up integration test infrastructure (skipIfNoApiKey, createIntegrationClient) -3. [x] Test complete invoice lifecycle: - - Create invoice (sync or async) - - Poll until Issued (if async) - - Retrieve invoice - - Send email - - Download PDF - - Download XML - - Cancel invoice -4. [x] Test error scenarios: - - Validation errors (400) - - Not found errors (404) - - Polling timeout -5. [x] Test helper methods: - - createAndWait with auto-polling - - list with pagination -6. [x] Set up cleanup (cancel invoices, delete test company) - -**Test Results**: -- **Total Tests**: 12 integration tests -- **Test File**: tests/integration/service-invoices.integration.test.ts (263 lines) -- **Execution**: Requires NFE_API_KEY environment variable -- **Skip Logic**: Tests automatically skip if no API key configured - -**Test Coverage Summary**: -1. `should create a service invoice (sync)` - Tests 201 immediate creation -2. `should poll invoice until complete (if async)` - Tests 202 + manual polling with pollUntilComplete -3. `should use createAndWait helper` - Tests convenience method with auto-polling -4. `should retrieve invoice by id` - Tests retrieve after creation -5. `should list service invoices` - Tests list with pagination -6. `should cancel service invoice` - Tests cancellation -7. `should send invoice email` - Tests email sending -8. `should download invoice PDF` - Tests PDF download (validates Buffer + %PDF header) -9. `should download invoice XML` - Tests XML download (validates Buffer + 80% -4. Run `npm run build` → must generate dist/ successfully -5. Review build artifacts in dist/ -6. Check exports in dist/index.js and dist/index.d.ts -7. Manually test one example end-to-end -8. Review all JSDoc comments for completeness - -**Validation**: -- [ ] `npm run typecheck` exits 0 -- [ ] `npm run lint` exits 0 -- [ ] `npm test` exits 0 with coverage > 80% -- [ ] `npm run build` exits 0 -- [ ] Exports are correct -- [ ] Example runs successfully -- [ ] JSDoc complete - -**Deliverable**: Fully validated implementation ready for PR - ---- - -### Task 6.2: Update CHANGELOG ⚡ -**Duration**: 30 minutes -**Owner**: SDK Team -**Parallelizable**: Yes - -**Description**: -Document the changes in CHANGELOG.md. - -**Steps**: -1. Open `CHANGELOG-v3.md` -2. Add entry for service invoices implementation -3. List all new methods -4. Note any breaking changes (if any) -5. Link to examples and documentation -6. Follow conventional changelog format - -**Validation**: -- [ ] Entry follows format -- [ ] All changes listed -- [ ] Breaking changes noted (if any) -- [ ] Links work - -**Deliverable**: Updated CHANGELOG-v3.md - ---- - -## Summary - -**Total Estimated Duration**: 3-4 days -**Parallelizable Tasks**: 5 tasks can run in parallel -**Critical Path**: Tasks 1.1 → 1.2 → 2.1 → 2.2 → 4.1 → 6.1 -**Key Deliverables**: -- Complete ServiceInvoicesResource implementation -- Polling utility for async processing -- 80%+ test coverage -- Comprehensive documentation and examples -- Validated build ready for release - -**Dependencies**: -- HTTP client (already exists) -- Error system (already exists) -- Type definitions (Task 1.1/1.2) -- Polling utility (Task 1.3) - -**Validation Gates**: -- Phase 1: Types compile, polling utility tested -- Phase 2: All CRUD methods implemented -- Phase 3: Downloads working -- Phase 4: Tests pass with coverage target -- Phase 5: Documentation complete -- Phase 6: Full validation passes - ---- - -## Risk Mitigation - -**If Task 1.1 finds OpenAPI discrepancies**: -- Document discrepancies -- Update OpenAPI spec if needed -- Verify with real API testing -- May add 1-2 hours to timeline - -**If polling tests are flaky**: -- Use fake timers in tests (vitest/jest) -- Ensure proper cleanup between tests -- Add retry logic to tests if needed - -**If coverage target not met**: -- Identify uncovered branches -- Add missing test cases -- May add 1-2 hours to Task 4.1 - -**If integration tests fail with real API**: -- Use MSW to mock responses -- Document API behavior differences -- Update implementation if needed - diff --git a/package.json b/package.json index 281e3bf..90b3b34 100644 --- a/package.json +++ b/package.json @@ -1,14 +1,22 @@ { "name": "nfe-io", - "version": "v4.0.0", + "version": "5.0.0", "description": "Official NFE.io SDK for Node.js - TypeScript native with zero runtime dependencies", "keywords": [ "nfe", "nfse", + "nfse-nacional", "nota-fiscal", + "nota-fiscal-eletronica", + "nota-fiscal-de-servico", "invoice", + "e-invoice", "brazil", - "typescript" + "nodejs", + "typescript", + "sdk", + "api", + "fiscal" ], "author": { "name": "NFE.io Team", @@ -64,6 +72,7 @@ "test:unit": "vitest tests/unit", "test:integration": "vitest tests/integration", "test:coverage": "vitest --coverage", + "test:types": "vitest --run --typecheck --dir tests/types", "test:ui": "vitest --ui", "validate:openapi": "tsx scripts/validate-openapi-compliance.js", "docs": "typedoc src/index.ts --out docs/api", diff --git a/scripts/generate-types.ts b/scripts/generate-types.ts index ff7f82d..5d2d5cb 100644 --- a/scripts/generate-types.ts +++ b/scripts/generate-types.ts @@ -42,6 +42,21 @@ const config: Omit = { outputDir: resolve(process.cwd(), 'src/generated'), }; +/** + * Specs that are KNOWN to be skipped (legacy Swagger 2.0, hand-typed in + * src/core/types.ts). A skip of one of these is expected and does NOT fail the + * build. A skip of anything NOT on this list is an error (fail-loud) — see + * the `sync-openapi-specs-from-docs` change (review finding B1). Keep this list + * in sync with the hand-typed resources until those specs are converted to 3.x. + */ +const KNOWN_SKIPPED = new Set([ + 'consulta-cnpj', + 'consulta-cpf', + 'consulta-endereco', + 'consulta-nf-consumidor', + 'consulta-nf', +]); + // ============================================================================ // Main Generation Logic // ============================================================================ @@ -61,19 +76,40 @@ async function main(): Promise { // 3. Generate types for each spec console.log('⚙️ Generating TypeScript types...'); const generatedSpecs: SpecConfig[] = []; + const skipped: SpecConfig[] = []; for (const spec of specs) { const wasGenerated = await generateTypesForSpec(spec); if (wasGenerated) { generatedSpecs.push(spec); + } else { + skipped.push(spec); } } + // 3b. Fail-loud BEFORE writing the index / success banner: an UNEXPECTED skip + // (a spec not on the known-skipped allowlist) means types silently vanished. + const unexpected = skipped.filter(s => !KNOWN_SKIPPED.has(s.name)); + if (skipped.length > 0) { + const known = skipped.filter(s => KNOWN_SKIPPED.has(s.name)).map(s => s.name); + if (known.length) console.log(` ℹ️ Known-skipped (hand-typed): ${known.join(', ')}`); + } + if (unexpected.length > 0) { + throw new Error( + `Generation produced no output for ${unexpected.length} unexpected spec(s): ` + + `${unexpected.map(s => s.name).join(', ')}. ` + + `Convert to OpenAPI 3.x, or add to KNOWN_SKIPPED if intentionally hand-typed.` + ); + } + // 4. Create unified index console.log('\n📦 Creating unified index...'); await createUnifiedIndex(generatedSpecs); console.log('\n✅ Type generation completed successfully!'); - console.log(` Generated ${generatedSpecs.length} of ${specs.length} spec file(s)`); + console.log( + ` Generated ${generatedSpecs.length} of ${specs.length} spec file(s)` + + ` (${skipped.length} known-skipped)` + ); console.log(` Output directory: ${config.outputDir}\n`); } catch (error) { @@ -92,8 +128,11 @@ async function main(): Promise { async function discoverSpecs(): Promise { const files = await readdir(config.specDir); + // Non-spec sidecar files that live in openapi/spec/ but must NOT be generated. + const NON_SPEC_FILES = new Set(['SOURCES.json']); const specFiles = files.filter(file => - file.endsWith('.yaml') || file.endsWith('.yml') + !NON_SPEC_FILES.has(file) && + (file.endsWith('.yaml') || file.endsWith('.yml') || file.endsWith('.json')) ); if (specFiles.length === 0) { @@ -101,7 +140,8 @@ async function discoverSpecs(): Promise { } return specFiles.map(file => { - const baseName = basename(file, file.endsWith('.yaml') ? '.yaml' : '.yml'); + const ext = file.endsWith('.yaml') ? '.yaml' : file.endsWith('.yml') ? '.yml' : '.json'; + const baseName = basename(file, ext); const namespace = toNamespace(baseName); return { @@ -268,42 +308,13 @@ function generateTypeAliases(specs: SpecConfig[]): string { return '// No main spec found for type aliases'; } - // NOTE: OpenAPI specs have schemas: never (inline types only) - // So we define placeholder interfaces instead of importing from components['schemas'] - return `// Common types from main spec (${mainSpec.name}) -// Use these for convenience, or use namespaced versions for specificity - -// Since OpenAPI specs don't have separate schemas (schemas: never), -// we define minimal types here for backward compatibility -// These are placeholders - real API responses may have more fields - -export interface ServiceInvoice { - id?: string; - flowStatus?: string; - status?: string; - [key: string]: unknown; -} - -export interface Company { - id?: string; - federalTaxNumber?: number; - name?: string; - [key: string]: unknown; -} - -export interface LegalPerson { - id?: string; - federalTaxNumber?: string | number; - name?: string; - [key: string]: unknown; -} - -export interface NaturalPerson { - id?: string; - federalTaxNumber?: string | number; - name?: string; - [key: string]: unknown; -}`; + // The public domain types (ServiceInvoice, Company, LegalPerson, NaturalPerson) + // live in src/core/types.ts — that is the public surface (the barrel re-exports + // from there, not from this generated index). We deliberately do NOT emit + // placeholder `{ [key: string]: unknown }` interfaces here: they were dead code + // that masked the real types and invited drift. (sync-openapi-specs-from-docs, 0B) + return `// Domain types are defined in src/core/types.ts (the public surface). +// Main spec namespace: ${mainSpec.namespace}. Use namespaced generated types for raw operations/paths.`; } // ============================================================================ diff --git a/skills/nfeio-node-sdk/SKILL.md b/skills/nfeio-node-sdk/SKILL.md index 975fc7f..bb11516 100644 --- a/skills/nfeio-node-sdk/SKILL.md +++ b/skills/nfeio-node-sdk/SKILL.md @@ -65,14 +65,20 @@ All resources are lazy-initialized via property getters on `NfeClient`. No resou | Accessor | Resource | API Host | Scope | Key Operations | |----------|----------|----------|-------|----------------| -| `nfe.serviceInvoices` | NFS-e Service Invoices | api.nfe.io | Company | create, createAndWait, list, retrieve, cancel, sendEmail, downloadPdf/Xml | -| `nfe.companies` | Companies | api.nfe.io | Global | CRUD, uploadCertificate, findByTaxNumber, listAll, listIterator | +| `nfe.serviceInvoices` | NFS-e Service Invoices | api.nfe.io | Company | create, createAndWait, list, retrieve, retrieveByExternalId, cancel, cancelAndWait, sendEmail, downloadPdf/Xml | +| `nfe.serviceInvoicesRtc` | NFS-e RTC (Reforma Tributária) | api.nfe.io | Company | create, createAndWait, retrieve, downloadCancellationXml (leiaute IBS/CBS; RTC selecionado pelo payload) | +| `nfe.companies` | Companies | api.nfe.io | Global | CRUD, exists, uploadCertificate, findByTaxNumber, listAll, listIterator | | `nfe.legalPeople` | Legal People (PJ) | api.nfe.io | Company | CRUD, createBatch, findByTaxNumber | | `nfe.naturalPeople` | Natural People (PF) | api.nfe.io | Company | CRUD, createBatch, findByTaxNumber | -| `nfe.webhooks` | Webhooks | api.nfe.io | Company | CRUD, validateSignature, test | -| `nfe.addresses` | Address Lookup | address.api.nfe.io | Global | lookupByPostalCode, search, lookupByTerm | -| `nfe.productInvoices` | NF-e Product Invoices | api.nfse.io | Company | create, list, retrieve, cancel, downloadPdf/Xml, sendCorrectionLetter | -| `nfe.stateTaxes` | State Tax (IE) | api.nfse.io | Company | CRUD (prerequisite for NF-e issuance) | +| `nfe.webhooks` | Webhooks | api.nfe.io (v2 p/ conta) | Company + Account | company CRUD, validateSignature; conta: listAccountWebhooks/create/retrieve/update/delete, deleteAllAccountWebhooks, pingAccountWebhook, fetchEventTypes | +| `nfe.notifications` | Notifications | api.nfe.io | Company | list, retrieve, delete, sendEmail | +| `nfe.addresses` | Address Lookup | address.api.nfe.io | Global | lookupByPostalCode (só CEP; busca/termo não existem na API real) | +| `nfe.productInvoices` | NF-e Product Invoices | api.nfse.io | Company | create, list (requer environment), retrieve, cancel, downloadPdf/Xml, sendCorrectionLetter | +| `nfe.productInvoicesRtc` | NF-e/NFC-e RTC | api.nfse.io | Company | create (webhook-driven; IBS/CBS/IS) | +| `nfe.consumerInvoices` | NFC-e Consumer Invoices | api.nfse.io | Company | create (webhook-driven), list (requer environment), retrieve, cancel, getItems, getEvents, downloadPdf/Xml/Rejection, disable | +| `nfe.stateTaxes` | State Tax (IE) | api.nfse.io | Company | CRUD, switchAuthorizer (pré-requisito p/ NF-e) | +| `nfe.municipalTaxes` | Municipal Tax (IM) | api.nfse.io | Company | CRUD, updatePrefecture, getSeries (pré-requisito p/ NFS-e) | +| `nfe.certificates` | Certificates (por thumbprint) | api.nfse.io | Company | list, getByThumbprint, deleteByThumbprint (+ variantes v1) | | `nfe.taxCalculation` | Tax Engine | api.nfse.io | Tenant | calculate (ICMS, PIS, COFINS, IPI, II) | | `nfe.taxCodes` | Tax Code Reference | api.nfse.io | Global | listOperationCodes, listAcquisitionPurposes, listIssuer/RecipientTaxProfiles | | `nfe.transportationInvoices` | CT-e Transport | api.nfse.io | Company | enable/disable, retrieve, downloadXml | @@ -349,7 +355,7 @@ Available events: `invoice.created`, `invoice.issued`, `invoice.cancelled`, `inv | Manage people (PF) under company | `nfe.naturalPeople.*` | | Set up webhook notifications | `nfe.webhooks.create(companyId, {...})` | | Manage state tax registrations (IE) | `nfe.stateTaxes.*` | -| Cancel a service invoice | `nfe.serviceInvoices.cancel(companyId, invoiceId)` | +| Cancel a service invoice | `nfe.serviceInvoices.cancelAndWait(companyId, invoiceId)` (async; `cancel()` retorna união discriminada) | | Cancel a product invoice | `nfe.productInvoices.cancel(companyId, invoiceId)` | | Download DANFE PDF | `nfe.serviceInvoices.downloadPdf(companyId, id)` or `nfe.productInvoiceQuery.downloadPdf(accessKey)` | | Send invoice by email | `nfe.serviceInvoices.sendEmail(companyId, invoiceId)` | @@ -363,6 +369,8 @@ Load these when you need detailed method signatures, full type definitions, or s - **`references/product-invoices-and-taxes.md`** — Read when working with NF-e product invoices, state tax registrations (IE), tax calculation, tax codes, CT-e, or inbound NF-e distribution. Contains cursor pagination, NfeProductInvoiceIssueData structure, TaxCalculation request/response, and manifest events. -- **`references/data-services-and-lookups.md`** — Read when working with CNPJ/CPF lookups, address/CEP lookup, NF-e/CFe-SAT query by access key. Contains LegalEntityBasicInfo structure, BrazilianState codes, AddressLookupResponse, and host mapping. +- **`references/data-services-and-lookups.md`** — Read when working with CNPJ/CPF lookups, address/CEP lookup, NF-e/CFe-SAT query by access key. Contains LegalEntityBasicInfo structure, BrazilianState codes, the `Address` shape (postal-code lookup only), and host mapping. + +- **`references/rtc-nfce-and-account-resources.md`** — Read when working with RTC issuance (`serviceInvoicesRtc`/`productInvoicesRtc`), NFC-e (`consumerInvoices`, requires `environment`), municipal tax enrollments, certificates by thumbprint, notifications, or account-scoped webhooks. Contains the v5-new resource signatures plus `cancelAndWait`/`exists`/`switchAuthorizer`. - **`references/error-handling-and-patterns.md`** — Read when implementing error handling, retry strategies, or debugging SDK issues. Contains complete error class hierarchy, type guards, ErrorFactory, RetryConfig, and CertificateValidator. diff --git a/skills/nfeio-node-sdk/references/data-services-and-lookups.md b/skills/nfeio-node-sdk/references/data-services-and-lookups.md index 6fb3426..0c2ac7c 100644 --- a/skills/nfeio-node-sdk/references/data-services-and-lookups.md +++ b/skills/nfeio-node-sdk/references/data-services-and-lookups.md @@ -20,39 +20,25 @@ Resources using `dataApiKey` will fall back to `apiKey` if `dataApiKey` is not c Access via `nfe.addresses`. Global scope (no company ID needed). -### lookupByPostalCode(postalCode): Promise +The live host (`address.api.nfe.io/v2`) supports **postal-code lookup only**. There is no +working free-text/search endpoint, so the SDK exposes `lookupByPostalCode` only. -```typescript -// Both formats accepted: -const result = await nfe.addresses.lookupByPostalCode('01310-100'); -const result = await nfe.addresses.lookupByPostalCode('01310100'); -``` - -Validates CEP format (8 digits, with or without dash). +### lookupByPostalCode(postalCode): Promise
-### search(options?): Promise +Returns the single `Address` (the API's `{ address }` envelope is unwrapped for you). -OData filter expression support: ```typescript -const result = await nfe.addresses.search({ - filter: "city eq 'Sao Paulo' and state eq 'SP'", -}); +// Both formats accepted (normalized to 8 digits): +const address = await nfe.addresses.lookupByPostalCode('01310-100'); +console.log(address.street); // 'Paulista' +console.log(address.city.name); // 'São Paulo' ``` -### lookupByTerm(term): Promise - -Free-text search: -```typescript -const result = await nfe.addresses.lookupByTerm('Avenida Paulista Sao Paulo'); -``` +Validates CEP format (8 digits, with or without dash) before the request. -### AddressLookupResponse +### Address ```typescript -interface AddressLookupResponse { - addresses: Address[]; -} - interface Address { state: string; // UF code (e.g., 'SP') city: { @@ -61,16 +47,19 @@ interface Address { }; district: string; street: string; - streetSuffix: string; - number: string; - numberMin: string; - numberMax: string; + streetSuffix: string; // e.g., 'Avenida' + number: string; // may be a textual range + numberMin?: string; // omitted by the API for some entries + numberMax?: string; additionalInformation: string; - postalCode: string; // Format: '01310100' (no dash) - country: string; + postalCode: string; // returned WITH dash, e.g. '01310-100' + country: string; // ISO 3166-1 alpha-3, e.g. 'BRA' } ``` +> Removed in v5: `addresses.search()` and `addresses.lookupByTerm()` (their endpoints +> return 404 on the live host). Use `lookupByPostalCode`. + --- ## ProductInvoiceQueryResource (NF-e SEFAZ Query) diff --git a/skills/nfeio-node-sdk/references/rtc-nfce-and-account-resources.md b/skills/nfeio-node-sdk/references/rtc-nfce-and-account-resources.md new file mode 100644 index 0000000..8126b9a --- /dev/null +++ b/skills/nfeio-node-sdk/references/rtc-nfce-and-account-resources.md @@ -0,0 +1,116 @@ +# RTC, NFC-e & Account Resources (v5) + +New resources introduced in v5. Signatures are current as of v5.0.0. + +## RTC — Reforma Tributária do Consumo + +The RTC layout (IBS/CBS/IS tax groups) is **selected by the payload** — same endpoints as +regular issuance. NFS-e polls; NF-e/NFC-e is webhook-driven. + +### `nfe.serviceInvoicesRtc` (NFS-e RTC · api.nfe.io · Company) +```typescript +create(companyId, data: NFSeRtcRequest): Promise // discriminated union +createAndWait(companyId, data: NFSeRtcRequest, options?: PollingOptions): Promise +retrieve(companyId, invoiceId): Promise +downloadCancellationXml(companyId, invoiceId): Promise // XML do evento de cancelamento (Ambiente Nacional) +``` +`NFSeRtcRequest` carries the `ibsCbs` group. `create()` returns +`{ status: 'immediate', invoice } | { status: 'async', response }` (same shape as +`serviceInvoices.create`). + +### `nfe.productInvoicesRtc` (NF-e/NFC-e RTC · api.nfse.io · Company) +```typescript +create(companyId, data: ProductInvoiceRtcRequest): Promise // webhook-driven, no polling +``` +`ProductInvoiceRtcRequest` carries the IBS (estadual/municipal), CBS and IS groups. + +--- + +## `nfe.consumerInvoices` — NFC-e (api.nfse.io · Company) + +Webhook-driven emission (202 = enqueued; completion via webhook). **`environment` +(`'Production' | 'Test'`) is required on `list`** and accepted (optional) on the reads. + +```typescript +create(companyId, data: ConsumerInvoiceData): Promise // webhook-driven +list(companyId, options: ConsumerInvoiceListOptions): Promise // options.environment REQUIRED +retrieve(companyId, invoiceId, environment?): Promise +cancel(companyId, invoiceId): Promise +getItems(companyId, invoiceId, environment?): Promise +getEvents(companyId, invoiceId, environment?): Promise +downloadPdf(companyId, invoiceId, environment?): Promise // DANFE-NFC-e +downloadXml(companyId, invoiceId, environment?): Promise +downloadRejectionXml(companyId, invoiceId, environment?): Promise +disable(companyId, data: ConsumerInvoiceDisablementData): Promise // inutilização +``` +`ConsumerInvoiceListOptions = { environment: 'Production' | 'Test'; startingAfter?; endingBefore?; limit?; q? }`. +Distinct from `nfe.consumerInvoiceQuery` (read-only CFe-SAT coupon lookup). + +--- + +## `nfe.municipalTaxes` — Inscrições Municipais (api.nfse.io · Company) + +CRUD of municipal enrollments — **prerequisite for NFS-e issuance** (mirrors `stateTaxes`). + +```typescript +list(companyId): Promise // { municipalTaxes, hasMore } +create(companyId, data: CreateMunicipalTaxData): Promise +retrieve(companyId, municipalTaxId): Promise +update(companyId, municipalTaxId, data: UpdateMunicipalTaxData): Promise +delete(companyId, municipalTaxId): Promise +updatePrefecture(companyId, municipalTaxId, data: UpdateMunicipalTaxData): Promise // HTTP PATCH +getSeries(companyId, municipalTaxId, serie): Promise> +``` + +--- + +## `nfe.certificates` — Certificados por thumbprint (api.nfse.io · Company) + +```typescript +list(companyId): Promise // { certificates } +getByThumbprint(companyId, thumbprint): Promise +deleteByThumbprint(companyId, thumbprint): Promise +getByThumbprintV1(companyId, thumbprint): Promise<...> // variantes v1 +deleteByThumbprintV1(companyId, thumbprint): Promise +``` +Complements the legacy `companies.uploadCertificate` (upload lives on api.nfe.io). + +--- + +## `nfe.notifications` — Notificações (api.nfe.io · Company) + +```typescript +list(companyId): Promise // { notifications } +retrieve(companyId, notificationId): Promise +delete(companyId, notificationId): Promise +sendEmail(companyId, data?): Promise +``` + +--- + +## Account-scoped webhooks (`nfe.webhooks.*Account*`) + +Account-level (host-root `/v2/webhooks`, **no companyId**) — distinct from the +company-scoped `nfe.webhooks.list/create(...)` under `/companies/{id}/webhooks`. + +```typescript +listAccountWebhooks(): Promise> // unwraps the {webHooks} envelope +createAccountWebhook(data: Partial): Promise +retrieveAccountWebhook(webhookId): Promise +updateAccountWebhook(webhookId, data): Promise +deleteAccountWebhook(webhookId): Promise +deleteAllAccountWebhooks(): Promise // ⚠️ removes ALL account webhooks +pingAccountWebhook(webhookId): Promise +fetchEventTypes(): Promise> // LIVE event-type list (extracts ids) +``` +Prefer `fetchEventTypes()` over the deprecated hardcoded `getAvailableEvents()`. + +--- + +## Other v5 additions + +- `serviceInvoices.cancelAndWait(companyId, invoiceId, options?)` — polls until the async + cancellation settles (`cancel()` returns a discriminated `CancelInvoiceResponse`). +- `serviceInvoices.retrieveByExternalId(companyId, externalId)` — idempotency/reconciliation. +- `companies.exists(companyId)` — `HEAD` check (404 → `false`). +- `stateTaxes.switchAuthorizer(companyId, stateTaxId, data?)` — NF-e authorizer switch. diff --git a/src/core/client.ts b/src/core/client.ts index 5338bb1..74dc6bd 100644 --- a/src/core/client.ts +++ b/src/core/client.ts @@ -37,6 +37,12 @@ import { TaxCodesResource, ProductInvoicesResource, StateTaxesResource, + ServiceInvoicesRtcResource, + ProductInvoicesRtcResource, + MunicipalTaxesResource, + ConsumerInvoicesResource, + CertificatesResource, + NotificationsResource, ADDRESS_API_BASE_URL, NFE_QUERY_API_BASE_URL, LEGAL_ENTITY_API_BASE_URL, @@ -141,6 +147,8 @@ export class NfeClient { /** @internal HTTP client for CT-e API requests (created lazily) */ private _cteHttp: HttpClient | undefined; + private _nfseMainHttp: HttpClient | undefined; + private _webhooksAccountHttp: HttpClient | undefined; /** @internal HTTP client for NF-e query API requests (created lazily) */ private _nfeQueryHttp: HttpClient | undefined; @@ -171,6 +179,12 @@ export class NfeClient { private _taxCodes: TaxCodesResource | undefined; private _productInvoices: ProductInvoicesResource | undefined; private _stateTaxes: StateTaxesResource | undefined; + private _serviceInvoicesRtc: ServiceInvoicesRtcResource | undefined; + private _productInvoicesRtc: ProductInvoicesRtcResource | undefined; + private _municipalTaxes: MunicipalTaxesResource | undefined; + private _consumerInvoices: ConsumerInvoicesResource | undefined; + private _certificates: CertificatesResource | undefined; + private _notifications: NotificationsResource | undefined; /** * Service Invoices API resource @@ -224,7 +238,7 @@ export class NfeClient { */ get companies(): CompaniesResource { if (!this._companies) { - this._companies = new CompaniesResource(this.getMainHttpClient()); + this._companies = new CompaniesResource(this.getMainHttpClient(), this.getCteHttpClient()); } return this._companies; } @@ -306,7 +320,10 @@ export class NfeClient { */ get webhooks(): WebhooksResource { if (!this._webhooks) { - this._webhooks = new WebhooksResource(this.getMainHttpClient()); + this._webhooks = new WebhooksResource( + this.getMainHttpClient(), + this.getWebhooksAccountHttpClient() + ); } return this._webhooks; } @@ -315,10 +332,8 @@ export class NfeClient { * Addresses API resource * * @description - * Provides operations for looking up Brazilian addresses: - * - Lookup by postal code (CEP) - * - Search by filter - * - Search by generic term + * Provides postal code (CEP) lookup for Brazilian addresses. The live API host + * supports postal-code lookup only and returns a single {@link Address}. * * **Note:** This resource uses a different API host (address.api.nfe.io). * Configure `dataApiKey` for a separate key, or it will fallback to `apiKey`. @@ -328,8 +343,8 @@ export class NfeClient { * * @example * ```typescript - * const result = await nfe.addresses.lookupByPostalCode('01310-100'); - * console.log(result.addresses[0].street); // 'Paulista' + * const address = await nfe.addresses.lookupByPostalCode('01310-100'); + * console.log(address.street); // 'Paulista' * ``` */ get addresses(): AddressesResource { @@ -626,7 +641,7 @@ export class NfeClient { */ get taxCodes(): TaxCodesResource { if (!this._taxCodes) { - this._taxCodes = new TaxCodesResource(this.getCteHttpClient()); + this._taxCodes = new TaxCodesResource(this.getNfseMainHttpClient()); } return this._taxCodes; } @@ -684,6 +699,76 @@ export class NfeClient { return this._stateTaxes; } + /** + * Service Invoices RTC resource — emit NFS-e under the Reforma Tributária layout + * (IBS/CBS groups). Uses the main host (api.nfe.io); supports polling. Emission + * is opt-in via this resource; retrieve/cancel/PDF/XML are shared with + * {@link serviceInvoices}. + */ + get serviceInvoicesRtc(): ServiceInvoicesRtcResource { + if (!this._serviceInvoicesRtc) { + this._serviceInvoicesRtc = new ServiceInvoicesRtcResource(this.getMainHttpClient()); + } + return this._serviceInvoicesRtc; + } + + /** + * Product Invoices RTC resource — emit NF-e/NFC-e under the Reforma Tributária + * layout (IBS state+municipal, CBS, IS). Uses api.nfse.io; webhook-driven (not + * polled), mirroring {@link productInvoices}. + */ + get productInvoicesRtc(): ProductInvoicesRtcResource { + if (!this._productInvoicesRtc) { + this._productInvoicesRtc = new ProductInvoicesRtcResource(this.getCteHttpClient()); + } + return this._productInvoicesRtc; + } + + /** + * Municipal Taxes resource — CRUD for company municipal tax registrations + * (Inscrições Municipais), prerequisite for NFS-e issuance. Uses api.nfse.io. + */ + get municipalTaxes(): MunicipalTaxesResource { + if (!this._municipalTaxes) { + this._municipalTaxes = new MunicipalTaxesResource(this.getCteHttpClient()); + } + return this._municipalTaxes; + } + + /** + * Consumer Invoices resource — emit & manage NFC-e (company-scoped) on + * api.nfse.io. Webhook-driven emission. Distinct from {@link consumerInvoiceQuery} + * (read-only coupon lookup). + */ + get consumerInvoices(): ConsumerInvoicesResource { + if (!this._consumerInvoices) { + this._consumerInvoices = new ConsumerInvoicesResource(this.getNfseMainHttpClient()); + } + return this._consumerInvoices; + } + + /** + * Certificates resource — manage company digital certificates (retrieve/delete + * by thumbprint, list) via the contribuintes-v2 API on api.nfse.io. Complements + * the legacy `companies.uploadCertificate` (which targets the api.nfe.io host). + */ + get certificates(): CertificatesResource { + if (!this._certificates) { + this._certificates = new CertificatesResource(this.getCteHttpClient()); + } + return this._certificates; + } + + /** + * Notifications resource — company notification operations (api.nfe.io). + */ + get notifications(): NotificationsResource { + if (!this._notifications) { + this._notifications = new NotificationsResource(this.getMainHttpClient()); + } + return this._notifications; + } + /** * Create a new NFE.io API client * @@ -836,6 +921,58 @@ export class NfeClient { return this._cteHttp; } + /** + * Get or create the HTTP client for api.nfse.io resources that require the + * MAIN api key (not the data key) — e.g. tax-codes and consumer invoices + * (NFC-e emission). Host is CTE_API_BASE_URL (api.nfse.io); key is the main key. + * @throws {ConfigurationError} If no main API key is configured + */ + private getNfseMainHttpClient(): HttpClient { + if (!this._nfseMainHttp) { + const apiKey = this.resolveMainApiKey(); + if (!apiKey) { + throw new ConfigurationError( + 'API key required for this resource. Set "apiKey" in config or NFE_API_KEY environment variable.' + ); + } + const httpConfig = buildHttpConfig( + apiKey, + CTE_API_BASE_URL, + this.config.timeout, + this.config.retryConfig + ); + this._nfseMainHttp = new HttpClient(httpConfig); + } + return this._nfseMainHttp; + } + + /** + * Get or create the HTTP client for ACCOUNT-scoped webhook endpoints. These + * live at the host root under `/v2` (e.g. https://api.nfe.io/v2/webhooks) and + * use the MAIN key — NOT under the `/v1` base of the main client. + * @throws {ConfigurationError} If no main API key is configured + */ + private getWebhooksAccountHttpClient(): HttpClient { + if (!this._webhooksAccountHttp) { + const apiKey = this.resolveMainApiKey(); + if (!apiKey) { + throw new ConfigurationError( + 'API key required for this resource. Set "apiKey" in config or NFE_API_KEY environment variable.' + ); + } + // Account webhooks are at host-root /v2, not under the /v1 main base. + const v2BaseUrl = this.config.baseUrl.replace(/\/v1(\/)?$/, '/v2'); + const httpConfig = buildHttpConfig( + apiKey, + v2BaseUrl, + this.config.timeout, + this.config.retryConfig + ); + this._webhooksAccountHttp = new HttpClient(httpConfig); + } + return this._webhooksAccountHttp; + } + /** * Get or create the NF-e Query API HTTP client (nfe.api.nfe.io) * @throws {ConfigurationError} If no API key is configured @@ -1035,10 +1172,28 @@ export class NfeClient { // Update internal config Object.assign(this.config, normalizedConfig); - // Clear cached HTTP clients and resources so they're recreated with new config + // Clear ALL cached HTTP clients and resources so they're recreated with new config. + this.resetCaches(); + } + + /** + * Invalidate every lazily-cached HTTP client and resource. + * + * Must list every `_*Http` and resource field so that, after `updateConfig`, + * no cached instance retains a stale baseUrl/apiKey/timeout. When adding a new + * resource or HTTP client, add it here too (single source of cache truth). + */ + private resetCaches(): void { + // HTTP clients this._http = undefined; this._addressHttp = undefined; this._cteHttp = undefined; + this._nfseMainHttp = undefined; + this._webhooksAccountHttp = undefined; + this._nfeQueryHttp = undefined; + this._legalEntityHttp = undefined; + this._naturalPersonHttp = undefined; + // Resources this._serviceInvoices = undefined; this._companies = undefined; this._legalPeople = undefined; @@ -1046,6 +1201,21 @@ export class NfeClient { this._webhooks = undefined; this._addresses = undefined; this._transportationInvoices = undefined; + this._inboundProductInvoices = undefined; + this._productInvoiceQuery = undefined; + this._consumerInvoiceQuery = undefined; + this._legalEntityLookup = undefined; + this._naturalPersonLookup = undefined; + this._taxCalculation = undefined; + this._taxCodes = undefined; + this._productInvoices = undefined; + this._stateTaxes = undefined; + this._serviceInvoicesRtc = undefined; + this._productInvoicesRtc = undefined; + this._municipalTaxes = undefined; + this._consumerInvoices = undefined; + this._certificates = undefined; + this._notifications = undefined; } /** @@ -1324,7 +1494,7 @@ export class NfeClient { hasApiKey: boolean; } { return { - version: '3.0.0-beta.1', // TODO: Read from package.json + version: VERSION, nodeVersion: this.getNodeVersion(), environment: this.config.environment, baseUrl: this.config.baseUrl, @@ -1407,7 +1577,7 @@ export default function nfe(apiKey: string | NfeConfig): NfeClient { * Current SDK version * @constant */ -export const VERSION = '3.0.0-beta.1'; +export const VERSION = '5.0.0'; /** * Supported Node.js version range (semver format) diff --git a/src/core/http/client.ts b/src/core/http/client.ts index 65a091c..fe4fa53 100644 --- a/src/core/http/client.ts +++ b/src/core/http/client.ts @@ -64,6 +64,21 @@ export class HttpClient { return this.request('DELETE', url); } + async patch(path: string, data?: unknown): Promise> { + const url = this.buildUrl(path); + return this.request('PATCH', url, data); + } + + /** + * HEAD request — returns status/headers, no body. Non-2xx still throws via the + * error factory (e.g. 404 → NotFoundError), so callers checking existence + * should catch NotFoundError and treat it as "not found". + */ + async head(path: string): Promise> { + const url = this.buildUrl(path); + return this.request('HEAD', url); + } + /** * GET request expecting a binary buffer response (e.g., PDF, XML downloads). * diff --git a/src/core/resources/addresses.ts b/src/core/resources/addresses.ts index 39d2c19..7e07653 100644 --- a/src/core/resources/addresses.ts +++ b/src/core/resources/addresses.ts @@ -6,7 +6,7 @@ */ import type { HttpClient } from '../http/client.js'; -import type { AddressLookupResponse, AddressSearchOptions } from '../types.js'; +import type { Address, AddressLookupResponse } from '../types.js'; import { ValidationError } from '../errors/index.js'; // ============================================================================ @@ -40,15 +40,6 @@ function validatePostalCode(postalCode: string): void { } } -/** - * Validates search term is not empty - */ -function validateTerm(term: string): void { - if (!term || term.trim() === '') { - throw new ValidationError('Search term is required'); - } -} - /** * Normalizes postal code by removing hyphen and trimming whitespace */ @@ -70,21 +61,15 @@ function normalizePostalCode(postalCode: string): string { * **Note:** This resource uses a different API host (address.api.nfe.io) and may require * a separate API key configured via `dataApiKey` in the client configuration. * - * @example Basic postal code lookup - * ```typescript - * const result = await nfe.addresses.lookupByPostalCode('01310-100'); - * console.log(result.addresses[0].street); // 'Paulista' - * ``` - * - * @example Search by term - * ```typescript - * const result = await nfe.addresses.lookupByTerm('Avenida Paulista'); - * console.log(result.addresses.length); // Number of matching addresses - * ``` + * The live `address.api.nfe.io/v2` API supports **postal code lookup only**. A single + * address is returned for a given CEP; there is no working address search/free-text + * endpoint on this host (see the `fix-address-lookup-api-mismatch` change). * - * @example Search with filter + * @example Basic postal code lookup * ```typescript - * const result = await nfe.addresses.search({ filter: "city eq 'São Paulo'" }); + * const address = await nfe.addresses.lookupByPostalCode('01310-100'); + * console.log(address.street); // 'Paulista' + * console.log(`${address.streetSuffix} ${address.street}, ${address.city.name}/${address.state}`); * ``` */ export class AddressesResource { @@ -101,25 +86,24 @@ export class AddressesResource { /** * Lookup address by postal code (CEP) * + * Calls `GET /v2/addresses/{cep}` and returns the single {@link Address} carried in + * the API's `{ address }` envelope. + * * @param postalCode - Brazilian postal code (CEP), with or without hyphen - * @returns Promise with address lookup response + * @returns Promise resolving to the matching {@link Address} * @throws {ValidationError} If postal code format is invalid * @throws {NotFoundError} If no address found for the postal code * * @example * ```typescript - * // With hyphen - * const result = await nfe.addresses.lookupByPostalCode('01310-100'); - * - * // Without hyphen - * const result = await nfe.addresses.lookupByPostalCode('01310100'); + * // With or without hyphen — both normalize to the 8-digit form + * const address = await nfe.addresses.lookupByPostalCode('01310-100'); * - * // Access address data - * const address = result.addresses[0]; * console.log(`${address.streetSuffix} ${address.street}, ${address.city.name} - ${address.state}`); + * console.log(address.postalCode); // '01310-100' (API returns it formatted) * ``` */ - async lookupByPostalCode(postalCode: string): Promise { + async lookupByPostalCode(postalCode: string): Promise
{ validatePostalCode(postalCode); const normalizedCode = normalizePostalCode(postalCode); @@ -127,64 +111,7 @@ export class AddressesResource { `/addresses/${normalizedCode}` ); - return response.data; - } - - /** - * Search addresses by OData filter - * - * @param options - Search options with filter expression - * @returns Promise with address lookup response - * - * @example - * ```typescript - * // Search by city - * const result = await nfe.addresses.search({ filter: "city eq 'São Paulo'" }); - * - * // Search by street - * const result = await nfe.addresses.search({ filter: "street eq 'Paulista'" }); - * ``` - */ - async search(options: AddressSearchOptions = {}): Promise { - const params: Record = {}; - - if (options.filter) { - params['$filter'] = options.filter; - } - - const response = await this.http.get( - '/addresses', - params - ); - - return response.data; - } - - /** - * Lookup addresses by generic search term - * - * @param term - Search term (street name, neighborhood, etc.) - * @returns Promise with address lookup response - * @throws {ValidationError} If term is empty - * @throws {NotFoundError} If no addresses found matching the term - * - * @example - * ```typescript - * const result = await nfe.addresses.lookupByTerm('Avenida Paulista'); - * - * for (const address of result.addresses) { - * console.log(`${address.postalCode} - ${address.city.name}/${address.state}`); - * } - * ``` - */ - async lookupByTerm(term: string): Promise { - validateTerm(term); - - const response = await this.http.get( - `/addresses/${encodeURIComponent(term.trim())}` - ); - - return response.data; + return response.data.address; } } diff --git a/src/core/resources/certificates.ts b/src/core/resources/certificates.ts new file mode 100644 index 0000000..9b1de3a --- /dev/null +++ b/src/core/resources/certificates.ts @@ -0,0 +1,97 @@ +/** + * NFE.io SDK v4 - Certificates Resource (digital certificate management) + * + * Manages company digital certificates via the contribuintes-v2 (Empresas) API + * on **api.nfse.io**. Covers the gap left by the legacy singular upload on + * `companies` (which targets the api.nfe.io host): retrieve/delete by thumbprint + * and the plural collection. + * + * Host note: this targets `api.nfse.io` (the contribuintes-v2 server). It is a + * dedicated resource — NOT folded into `companies` — because `companies` is wired + * to the api.nfe.io main client, a different host. Types come from contribuintes-v2. + * + * Validation note: `CertificateValidator` only pre-flights file format; the + * certificate validity/password is verified server-side (see fix-repo-bugs). + */ + +import type { HttpClient } from '../http/client.js'; +import type { + CertificateMetadataResource, + CertificatesMetadataResource, +} from '../types.js'; +import { ValidationError } from '../errors/index.js'; + +function validateCompanyId(companyId: string): void { + if (!companyId || companyId.trim() === '') { + throw new ValidationError('Company ID is required'); + } +} + +function validateThumbprint(thumbprint: string): void { + if (!thumbprint || thumbprint.trim() === '') { + throw new ValidationError('Certificate thumbprint is required'); + } +} + +export class CertificatesResource { + constructor(private readonly http: HttpClient) {} + + private v2Base(companyId: string): string { + return `/v2/companies/${companyId}/certificates`; + } + + private v1Base(companyId: string): string { + return `/v1/companies/${companyId}/certificate`; + } + + /** List a company's certificates (`GET /v2/companies/{id}/certificates`). */ + async list(companyId: string): Promise { + validateCompanyId(companyId); + const response = await this.http.get(this.v2Base(companyId)); + return response.data; + } + + /** Retrieve a certificate by thumbprint (`GET /v2/companies/{id}/certificates/{thumbprint}`). */ + async getByThumbprint( + companyId: string, + thumbprint: string + ): Promise { + validateCompanyId(companyId); + validateThumbprint(thumbprint); + const response = await this.http.get( + `${this.v2Base(companyId)}/${thumbprint}` + ); + return response.data; + } + + /** Delete a certificate by thumbprint (`DELETE /v2/companies/{id}/certificates/{thumbprint}`). */ + async deleteByThumbprint(companyId: string, thumbprint: string): Promise { + validateCompanyId(companyId); + validateThumbprint(thumbprint); + await this.http.delete(`${this.v2Base(companyId)}/${thumbprint}`); + } + + /** Retrieve a certificate by thumbprint via the v1 path (`/v1/companies/{id}/certificate/{thumbprint}`). */ + async getByThumbprintV1( + companyId: string, + thumbprint: string + ): Promise { + validateCompanyId(companyId); + validateThumbprint(thumbprint); + const response = await this.http.get( + `${this.v1Base(companyId)}/${thumbprint}` + ); + return response.data; + } + + /** Delete a certificate by thumbprint via the v1 path. */ + async deleteByThumbprintV1(companyId: string, thumbprint: string): Promise { + validateCompanyId(companyId); + validateThumbprint(thumbprint); + await this.http.delete(`${this.v1Base(companyId)}/${thumbprint}`); + } +} + +export function createCertificatesResource(http: HttpClient): CertificatesResource { + return new CertificatesResource(http); +} diff --git a/src/core/resources/companies.ts b/src/core/resources/companies.ts index f517524..c2a5a7f 100644 --- a/src/core/resources/companies.ts +++ b/src/core/resources/companies.ts @@ -10,7 +10,7 @@ import type { PaginationOptions } from '../types.js'; import type { HttpClient } from '../http/client.js'; -import { ValidationError } from '../errors/index.js'; +import { ValidationError, NotFoundError } from '../errors/index.js'; import { CertificateValidator } from '../utils/certificate-validator.js'; // ============================================================================ @@ -114,12 +114,38 @@ function validateCompanyData(data: Partial): void { // ============================================================================ export class CompaniesResource { - constructor(private readonly http: HttpClient) {} + /** + * @param http - Main client (api.nfe.io) for the legacy v1 company CRUD. + * @param v2Http - Optional client for the contribuintes-v2 endpoints on + * api.nfse.io (e.g. the HEAD existence check). Falls back to `http`. + */ + constructor( + private readonly http: HttpClient, + private readonly v2Http: HttpClient = http + ) {} // -------------------------------------------------------------------------- // Core CRUD Operations // -------------------------------------------------------------------------- + /** + * Check whether a company exists, via `HEAD /v2/companies/{id}` (api.nfse.io). + * + * @returns `true` if the company exists (2xx), `false` on 404. Other errors propagate. + */ + async exists(companyId: string): Promise { + if (!companyId || companyId.trim() === '') { + throw new ValidationError('Company ID is required'); + } + try { + await this.v2Http.head(`/v2/companies/${companyId}`); + return true; + } catch (error) { + if (error instanceof NotFoundError) return false; + throw error; + } + } + /** * Create a new company * diff --git a/src/core/resources/consumer-invoices.ts b/src/core/resources/consumer-invoices.ts new file mode 100644 index 0000000..26cf49c --- /dev/null +++ b/src/core/resources/consumer-invoices.ts @@ -0,0 +1,212 @@ +/** + * NFE.io SDK v4 - Consumer Invoices Resource (NFC-e issuance) + * + * Company-scoped NFC-e lifecycle via the api.nfse.io v2 API. Distinct from the + * read-only `ConsumerInvoiceQueryResource` (CFe-SAT coupon lookup) and from RTC + * NFC-e (different payload). Emission is **webhook-driven** (202 = enqueued; + * completion notified via webhooks), mirroring `product-invoices` (no polling). + */ + +import type { HttpClient } from '../http/client.js'; +import type { + ConsumerInvoiceData, + ConsumerInvoice, + ConsumerInvoiceListResponse, + ConsumerInvoiceDisablementData, + NfeInvoiceItemsResponse, + NfeProductInvoiceEventsResponse, + NfeDisablementResource, +} from '../types.js'; +import { ValidationError } from '../errors/index.js'; + +/** The NFC-e API requires which environment's invoices to operate on. */ +export type ConsumerInvoiceEnvironment = 'Production' | 'Test'; + +/** Options for {@link ConsumerInvoicesResource.list}. `environment` is required by the API. */ +export interface ConsumerInvoiceListOptions { + /** Required by the API (`Production` or `Test`). Omitting it yields HTTP 400. */ + environment: ConsumerInvoiceEnvironment; + startingAfter?: string; + endingBefore?: string; + limit?: number; + /** Free-text query filter, if supported by the endpoint. */ + q?: string; +} + +function validateCompanyId(companyId: string): void { + if (!companyId || companyId.trim() === '') { + throw new ValidationError('Company ID is required'); + } +} + +function validateInvoiceId(invoiceId: string): void { + if (!invoiceId || invoiceId.trim() === '') { + throw new ValidationError('Invoice ID is required'); + } +} + +export class ConsumerInvoicesResource { + constructor(private readonly http: HttpClient) {} + + private basePath(companyId: string): string { + return `/v2/companies/${companyId}/consumerinvoices`; + } + + /** + * Emit an NFC-e (consumer invoice). + * + * Webhook-driven: a 202 indicates the invoice was enqueued; completion is + * notified via webhooks. Returns the enqueued invoice (does NOT poll). + */ + async create(companyId: string, data: ConsumerInvoiceData): Promise { + validateCompanyId(companyId); + const response = await this.http.post(this.basePath(companyId), data); + return response.data; + } + + /** + * List NFC-e for a company. The API **requires** `environment` (`Production` or + * `Test`); omitting it returns HTTP 400. + */ + async list( + companyId: string, + options: ConsumerInvoiceListOptions + ): Promise { + validateCompanyId(companyId); + if (!options?.environment) { + throw new ValidationError('Environment is required (Production or Test)'); + } + const params: Record = { environment: options.environment }; + if (options.startingAfter) params.startingAfter = options.startingAfter; + if (options.endingBefore) params.endingBefore = options.endingBefore; + if (options.limit !== undefined) params.limit = options.limit; + if (options.q) params.q = options.q; + const response = await this.http.get( + this.basePath(companyId), + params + ); + return response.data; + } + + /** + * Retrieve an NFC-e by id. Pass `environment` if the API requires it for reads. + */ + async retrieve( + companyId: string, + invoiceId: string, + environment?: ConsumerInvoiceEnvironment + ): Promise { + validateCompanyId(companyId); + validateInvoiceId(invoiceId); + const response = await this.http.get( + `${this.basePath(companyId)}/${invoiceId}`, + environment ? { environment } : undefined + ); + return response.data; + } + + /** Cancel an NFC-e. */ + async cancel(companyId: string, invoiceId: string): Promise { + validateCompanyId(companyId); + validateInvoiceId(invoiceId); + const response = await this.http.delete( + `${this.basePath(companyId)}/${invoiceId}` + ); + return response.data; + } + + /** List the items of an NFC-e. Pass `environment` if the API requires it. */ + async getItems( + companyId: string, + invoiceId: string, + environment?: ConsumerInvoiceEnvironment + ): Promise { + validateCompanyId(companyId); + validateInvoiceId(invoiceId); + const response = await this.http.get( + `${this.basePath(companyId)}/${invoiceId}/items`, + environment ? { environment } : undefined + ); + return response.data; + } + + /** List the events of an NFC-e. Pass `environment` if the API requires it. */ + async getEvents( + companyId: string, + invoiceId: string, + environment?: ConsumerInvoiceEnvironment + ): Promise { + validateCompanyId(companyId); + validateInvoiceId(invoiceId); + const response = await this.http.get( + `${this.basePath(companyId)}/${invoiceId}/events`, + environment ? { environment } : undefined + ); + return response.data; + } + + /** Download the DANFE-NFC-e PDF. Pass `environment` if the API requires it. */ + async downloadPdf( + companyId: string, + invoiceId: string, + environment?: ConsumerInvoiceEnvironment + ): Promise { + validateCompanyId(companyId); + validateInvoiceId(invoiceId); + const response = await this.http.get( + `${this.basePath(companyId)}/${invoiceId}/pdf`, + environment ? { environment } : undefined, + { Accept: 'application/pdf' } + ); + return response.data; + } + + /** Download the NFC-e XML. Pass `environment` if the API requires it. */ + async downloadXml( + companyId: string, + invoiceId: string, + environment?: ConsumerInvoiceEnvironment + ): Promise { + validateCompanyId(companyId); + validateInvoiceId(invoiceId); + const response = await this.http.get( + `${this.basePath(companyId)}/${invoiceId}/xml`, + environment ? { environment } : undefined, + { Accept: 'application/xml' } + ); + return response.data; + } + + /** Download the rejection XML for a rejected NFC-e. Pass `environment` if required. */ + async downloadRejectionXml( + companyId: string, + invoiceId: string, + environment?: ConsumerInvoiceEnvironment + ): Promise { + validateCompanyId(companyId); + validateInvoiceId(invoiceId); + const response = await this.http.get( + `${this.basePath(companyId)}/${invoiceId}/xml/rejection`, + environment ? { environment } : undefined, + { Accept: 'application/xml' } + ); + return response.data; + } + + /** Disable (inutilizar) a range of NFC-e numbers. */ + async disable( + companyId: string, + data: ConsumerInvoiceDisablementData + ): Promise { + validateCompanyId(companyId); + const response = await this.http.post( + `${this.basePath(companyId)}/disablement`, + data + ); + return response.data; + } +} + +export function createConsumerInvoicesResource(http: HttpClient): ConsumerInvoicesResource { + return new ConsumerInvoicesResource(http); +} diff --git a/src/core/resources/index.ts b/src/core/resources/index.ts index 794d90c..15025ab 100644 --- a/src/core/resources/index.ts +++ b/src/core/resources/index.ts @@ -21,3 +21,9 @@ export { TaxCalculationResource, createTaxCalculationResource } from './tax-calc export { TaxCodesResource, createTaxCodesResource } from './tax-codes.js'; export { ProductInvoicesResource } from './product-invoices.js'; export { StateTaxesResource } from './state-taxes.js'; +export { ServiceInvoicesRtcResource, createServiceInvoicesRtcResource } from './service-invoices-rtc.js'; +export { ProductInvoicesRtcResource, createProductInvoicesRtcResource } from './product-invoices-rtc.js'; +export { MunicipalTaxesResource, createMunicipalTaxesResource } from './municipal-taxes.js'; +export { ConsumerInvoicesResource, createConsumerInvoicesResource } from './consumer-invoices.js'; +export { CertificatesResource, createCertificatesResource } from './certificates.js'; +export { NotificationsResource, createNotificationsResource } from './notifications.js'; diff --git a/src/core/resources/municipal-taxes.ts b/src/core/resources/municipal-taxes.ts new file mode 100644 index 0000000..bd705cc --- /dev/null +++ b/src/core/resources/municipal-taxes.ts @@ -0,0 +1,124 @@ +/** + * NFE.io SDK v4 - Municipal Taxes Resource (Inscrições Municipais) + * + * CRUD for company municipal tax registrations via the api.nfse.io v2 API, + * mirroring StateTaxesResource. Municipal registration is a prerequisite for + * NFS-e issuance in most municipalities. Adds `updateprefecture` (PATCH) and the + * RPS `series` lookup. Types come from `contribuintes-v2` (sync change). + */ + +import type { HttpClient } from '../http/client.js'; +import type { + MunicipalTax, + CreateMunicipalTaxData, + UpdateMunicipalTaxData, + MunicipalTaxListResponse, +} from '../types.js'; +import { ValidationError } from '../errors/index.js'; + +function validateCompanyId(companyId: string): void { + if (!companyId || companyId.trim() === '') { + throw new ValidationError('Company ID is required'); + } +} + +function validateMunicipalTaxId(municipalTaxId: string): void { + if (!municipalTaxId || municipalTaxId.trim() === '') { + throw new ValidationError('Municipal tax ID is required'); + } +} + +export class MunicipalTaxesResource { + constructor(private readonly http: HttpClient) {} + + private basePath(companyId: string): string { + return `/v2/companies/${companyId}/municipaltaxes`; + } + + /** List municipal tax registrations for a company. */ + async list(companyId: string): Promise { + validateCompanyId(companyId); + const response = await this.http.get(this.basePath(companyId)); + return response.data; + } + + /** Create a municipal tax registration (wrapped as `{ municipalTax }`). */ + async create(companyId: string, data: CreateMunicipalTaxData): Promise { + validateCompanyId(companyId); + const response = await this.http.post(this.basePath(companyId), { + municipalTax: data, + }); + return response.data; + } + + /** Retrieve a municipal tax registration by id. */ + async retrieve(companyId: string, municipalTaxId: string): Promise { + validateCompanyId(companyId); + validateMunicipalTaxId(municipalTaxId); + const response = await this.http.get( + `${this.basePath(companyId)}/${municipalTaxId}` + ); + return response.data; + } + + /** Update a municipal tax registration. */ + async update( + companyId: string, + municipalTaxId: string, + data: UpdateMunicipalTaxData + ): Promise { + validateCompanyId(companyId); + validateMunicipalTaxId(municipalTaxId); + const response = await this.http.put( + `${this.basePath(companyId)}/${municipalTaxId}`, + { municipalTax: data } + ); + return response.data; + } + + /** Delete a municipal tax registration. */ + async delete(companyId: string, municipalTaxId: string): Promise { + validateCompanyId(companyId); + validateMunicipalTaxId(municipalTaxId); + await this.http.delete(`${this.basePath(companyId)}/${municipalTaxId}`); + } + + /** + * Update the prefecture (city hall) credentials/integration for a municipal tax + * registration. Uses HTTP PATCH (`.../updateprefecture`). + */ + async updatePrefecture( + companyId: string, + municipalTaxId: string, + data: UpdateMunicipalTaxData + ): Promise { + validateCompanyId(companyId); + validateMunicipalTaxId(municipalTaxId); + const response = await this.http.patch( + `${this.basePath(companyId)}/${municipalTaxId}/updateprefecture`, + { municipalTax: data } + ); + return response.data; + } + + /** Look up an RPS series for a municipal tax registration. */ + async getSeries( + companyId: string, + municipalTaxId: string, + serie: string + ): Promise> { + validateCompanyId(companyId); + validateMunicipalTaxId(municipalTaxId); + if (!serie || serie.trim() === '') { + throw new ValidationError('Serie is required'); + } + const response = await this.http.get>( + `${this.basePath(companyId)}/${municipalTaxId}/series/${serie}` + ); + return response.data; + } +} + +export function createMunicipalTaxesResource(http: HttpClient): MunicipalTaxesResource { + return new MunicipalTaxesResource(http); +} diff --git a/src/core/resources/notifications.ts b/src/core/resources/notifications.ts new file mode 100644 index 0000000..6891083 --- /dev/null +++ b/src/core/resources/notifications.ts @@ -0,0 +1,76 @@ +/** + * NFE.io SDK v4 - Notifications Resource (company notifications) + * + * Company-scoped notification operations on the main API (api.nfe.io). The + * nf-servico-v1 spec has no component schemas for these, so types are minimal + * hand types with a permissive index (consistent with the rest of that surface). + */ + +import type { HttpClient } from '../http/client.js'; +import { ValidationError } from '../errors/index.js'; + +/** A company notification (minimal, spec has no named schema). */ +export interface Notification { + id?: string; + [key: string]: unknown; +} + +/** List response for notifications (best-effort shape). */ +export interface NotificationListResponse { + notifications?: Notification[]; + [key: string]: unknown; +} + +function validateCompanyId(companyId: string): void { + if (!companyId || companyId.trim() === '') { + throw new ValidationError('Company ID is required'); + } +} + +function validateNotificationId(notificationId: string): void { + if (!notificationId || notificationId.trim() === '') { + throw new ValidationError('Notification ID is required'); + } +} + +export class NotificationsResource { + constructor(private readonly http: HttpClient) {} + + private basePath(companyId: string): string { + return `/companies/${companyId}/notifications`; + } + + /** List company notifications. */ + async list(companyId: string): Promise { + validateCompanyId(companyId); + const response = await this.http.get(this.basePath(companyId)); + return response.data; + } + + /** Retrieve a notification by id. */ + async retrieve(companyId: string, notificationId: string): Promise { + validateCompanyId(companyId); + validateNotificationId(notificationId); + const response = await this.http.get( + `${this.basePath(companyId)}/${notificationId}` + ); + return response.data; + } + + /** Delete a notification. */ + async delete(companyId: string, notificationId: string): Promise { + validateCompanyId(companyId); + validateNotificationId(notificationId); + await this.http.delete(`${this.basePath(companyId)}/${notificationId}`); + } + + /** Configure / send notification email settings. */ + async sendEmail(companyId: string, data?: Record): Promise { + validateCompanyId(companyId); + await this.http.post(`${this.basePath(companyId)}/email`, data ?? {}); + } +} + +export function createNotificationsResource(http: HttpClient): NotificationsResource { + return new NotificationsResource(http); +} diff --git a/src/core/resources/product-invoices-rtc.ts b/src/core/resources/product-invoices-rtc.ts new file mode 100644 index 0000000..4f11cbe --- /dev/null +++ b/src/core/resources/product-invoices-rtc.ts @@ -0,0 +1,55 @@ +/** + * NFE.io SDK v4 - Product Invoices RTC Resource (NF-e/NFC-e, Reforma Tributária) + * + * Emits NF-e / NFC-e under the RTC layout (IBS state+municipal, CBS, IS groups) + * via the same endpoint as the legacy product-invoices resource. RTC is selected + * by the payload shape (`tax.IBSCBS`), not by a header or a different URL. + * + * Host: api.nfse.io (CT-e/data client). The base URL has no version segment, so + * `/v2` belongs in the path. + * + * Async model: like the legacy product-invoices resource, emission is + * **webhook-driven** — a 202 means the invoice was enqueued; completion is + * notified via webhooks (NOT polled). Retrieve/cancel/PDF/XML are shared with + * `nfe.productInvoices` (same invoice id space). + */ + +import type { HttpClient } from '../http/client.js'; +import type { NfeProductInvoiceIssueData, ProductInvoiceRtcRequest } from '../types.js'; +import { ValidationError } from '../errors/index.js'; + +function validateCompanyId(companyId: string): void { + if (!companyId || companyId.trim() === '') { + throw new ValidationError('Company ID is required'); + } +} + +export class ProductInvoicesRtcResource { + constructor(private readonly http: HttpClient) {} + + private basePath(companyId: string): string { + return `/v2/companies/${companyId}/productinvoices`; + } + + /** + * Emit an NF-e / NFC-e with the RTC layout (item-level `tax.IBSCBS`). + * + * Webhook-driven: a 202 indicates the invoice was enqueued; monitor completion + * via webhooks. Returns the enqueued invoice data (does NOT poll). + */ + async create( + companyId: string, + data: ProductInvoiceRtcRequest + ): Promise { + validateCompanyId(companyId); + const response = await this.http.post( + this.basePath(companyId), + data + ); + return response.data; + } +} + +export function createProductInvoicesRtcResource(http: HttpClient): ProductInvoicesRtcResource { + return new ProductInvoicesRtcResource(http); +} diff --git a/src/core/resources/service-invoices-rtc.ts b/src/core/resources/service-invoices-rtc.ts new file mode 100644 index 0000000..5e49468 --- /dev/null +++ b/src/core/resources/service-invoices-rtc.ts @@ -0,0 +1,161 @@ +/** + * NFE.io SDK v4 - Service Invoices RTC Resource (NFS-e, Reforma Tributária) + * + * Emits NFS-e under the RTC layout (IBS/CBS groups) via the same endpoint as the + * legacy service-invoices resource. RTC is selected by the payload shape + * (`ibsCbs` group), not by a header or a different URL. + * + * Host: api.nfe.io (main client). The base URL already carries `/v1`, so paths + * here MUST NOT prepend `/v1`. + * + * Async model: NFS-e supports polling (202 + Location -> poll until terminal), + * mirroring the legacy service-invoices resource. Retrieve/cancel/PDF/XML of an + * emitted invoice are shared with `nfe.serviceInvoices` (same invoice id space); + * this resource adds RTC emission + the cancellation-event XML download. + */ + +import type { HttpClient } from '../http/client.js'; +import type { + ServiceInvoiceData, + NFSeRtcRequest, + PollingOptions, + FlowStatus, +} from '../types.js'; +import type { CreateInvoiceResponse } from './service-invoices.js'; +import { InvoiceProcessingError, NotFoundError, ValidationError } from '../errors/index.js'; +import { poll } from '../utils/polling.js'; +import { isTerminalFlowStatus } from '../types.js'; + +function validateCompanyId(companyId: string): void { + if (!companyId || companyId.trim() === '') { + throw new ValidationError('Company ID is required'); + } +} + +function validateInvoiceId(invoiceId: string): void { + if (!invoiceId || invoiceId.trim() === '') { + throw new ValidationError('Invoice ID is required'); + } +} + +export class ServiceInvoicesRtcResource { + constructor(private readonly http: HttpClient) {} + + /** No `/v1` prefix — the main client base URL (`https://api.nfe.io/v1`) carries it. */ + private basePath(companyId: string): string { + return `/companies/${companyId}/serviceinvoices`; + } + + /** + * Emit an NFS-e with the RTC layout (`ibsCbs` group). + * + * Returns a discriminated union: immediate (201) or async (202 + Location). + * Use {@link createAndWait} to poll until the invoice reaches a terminal state. + */ + async create( + companyId: string, + data: NFSeRtcRequest + ): Promise { + validateCompanyId(companyId); + const response = await this.http.post(this.basePath(companyId), data); + + if (response.status === 202) { + const location = response.headers['location'] || response.headers['Location']; + if (!location) { + throw new InvoiceProcessingError( + 'Async response (202) received but no Location header found', + { status: 202, headers: response.headers } + ); + } + const fullPath = location.startsWith('http') ? new URL(location).pathname : location; + return { + status: 'async', + response: { + code: 202, + status: 'pending', + location: fullPath, + invoiceId: this.extractInvoiceIdFromLocation(location), + }, + }; + } + + return { status: 'immediate', invoice: response.data }; + } + + /** + * Emit an RTC NFS-e and poll until it reaches a terminal flow status. + */ + async createAndWait( + companyId: string, + data: NFSeRtcRequest, + options: PollingOptions = {} + ): Promise { + const createResult = await this.create(companyId, data); + if (createResult.status === 'immediate') { + return createResult.invoice; + } + + const { invoiceId } = createResult.response; + const pollingConfig: import('../utils/polling.js').PollingOptions = { + fn: async () => this.retrieve(companyId, invoiceId), + isComplete: (invoice) => isTerminalFlowStatus(invoice.flowStatus as FlowStatus), + timeout: options.timeout ?? 120000, + initialDelay: options.initialDelay ?? 1000, + maxDelay: options.maxDelay ?? 10000, + backoffFactor: options.backoffFactor ?? 1.5, + }; + if (options.onPoll) { + pollingConfig.onPoll = (attempt, result) => + options.onPoll!(attempt, result.flowStatus as FlowStatus); + } + + const invoice = await poll(pollingConfig); + const flowStatus = invoice.flowStatus as FlowStatus; + if (flowStatus === 'IssueFailed' || flowStatus === 'CancelFailed') { + throw new InvoiceProcessingError( + `Invoice processing failed with status: ${flowStatus}`, + { flowStatus, flowMessage: invoice.flowMessage, invoice } + ); + } + return invoice; + } + + /** Retrieve an emitted invoice (used for polling; shares the endpoint with serviceInvoices). */ + async retrieve(companyId: string, invoiceId: string): Promise { + validateCompanyId(companyId); + validateInvoiceId(invoiceId); + const response = await this.http.get( + `${this.basePath(companyId)}/${invoiceId}` + ); + if (!response.data) { + throw new NotFoundError(`Invoice ${invoiceId} not found`, { companyId, invoiceId }); + } + return response.data; + } + + /** + * Download the XML of the cancellation event (Ambiente Nacional) for an NFS-e. + * + * @returns The cancellation-event XML as a Buffer. + * @throws {NotFoundError} If the invoice/cancellation XML is not found/not ready. + */ + async downloadCancellationXml(companyId: string, invoiceId: string): Promise { + validateCompanyId(companyId); + validateInvoiceId(invoiceId); + const response = await this.http.get( + `${this.basePath(companyId)}/${invoiceId}/cancellation-xml`, + undefined, + { Accept: 'application/xml' } + ); + return response.data; + } + + private extractInvoiceIdFromLocation(location: string): string { + const path = location.split('?')[0]!.replace(/\/+$/, ''); + return path.split('/').pop() ?? ''; + } +} + +export function createServiceInvoicesRtcResource(http: HttpClient): ServiceInvoicesRtcResource { + return new ServiceInvoicesRtcResource(http); +} diff --git a/src/core/resources/service-invoices.ts b/src/core/resources/service-invoices.ts index 8954ac9..692d486 100644 --- a/src/core/resources/service-invoices.ts +++ b/src/core/resources/service-invoices.ts @@ -29,6 +29,17 @@ export type CreateInvoiceResponse = | { status: 'immediate'; invoice: ServiceInvoiceData } | { status: 'async'; response: ServiceInvoiceAsyncResponse }; +/** + * Discriminated union for cancel() response. + * + * Cancellation is normally asynchronous: the API replies `202 + Location` and the + * invoice moves through `WaitingSendCancel` → `Cancelled`. Use `cancelAndWait()` to poll + * until it settles. + */ +export type CancelInvoiceResponse = + | { status: 'immediate'; invoice: ServiceInvoiceData } + | { status: 'async'; response: ServiceInvoiceAsyncResponse }; + // ============================================================================ // Service Invoices Resource // ============================================================================ @@ -180,30 +191,153 @@ export class ServiceInvoicesResource { return response.data; } + /** + * Retrieve a service invoice by the caller's external id (idempotency key). + * + * @param companyId - Company ID (GUID) + * @param externalId - The `externalId` supplied at creation + * @returns The matching invoice + * @throws {NotFoundError} If no invoice with that external id exists + */ + async retrieveByExternalId( + companyId: string, + externalId: string + ): Promise { + const path = `/companies/${companyId}/serviceinvoices/external/${externalId}`; + const response = await this.http.get(path); + if (!response.data) { + throw new NotFoundError(`Invoice with externalId ${externalId} not found`, { + companyId, + externalId, + }); + } + return response.data; + } + /** * Cancel a service invoice * - * Note: Cancellation may also be async (returns 202). Check response status. + * Cancellation is normally **asynchronous**: the API replies `202 Accepted` with a + * `Location` header and the invoice transitions `WaitingSendCancel` → `Cancelled`. + * This method returns a discriminated union (mirroring {@link create}) so the static + * type always matches the runtime shape. Use {@link cancelAndWait} to poll until the + * cancellation settles. * * @param companyId - Company ID (GUID) * @param invoiceId - Invoice ID (GUID) - * @returns Cancelled invoice data - * @throws {InvoiceProcessingError} If invoice cannot be cancelled + * @returns `{ status: 'async', response }` (202 + Location) or `{ status: 'immediate', invoice }` + * @throws {InvoiceProcessingError} If a 202 is returned without a Location header * * @example * ```typescript - * const cancelled = await nfe.serviceInvoices.cancel(companyId, invoiceId); - * console.log('Cancellation status:', cancelled.flowStatus); + * const result = await nfe.serviceInvoices.cancel(companyId, invoiceId); + * if (result.status === 'async') { + * console.log('Cancellation in progress:', result.response.invoiceId); + * // or use cancelAndWait() to block until it settles + * } else { + * console.log('Cancelled immediately:', result.invoice.flowStatus); + * } * ``` */ async cancel( companyId: string, invoiceId: string - ): Promise { + ): Promise { const path = `/companies/${companyId}/serviceinvoices/${invoiceId}`; const response = await this.http.delete(path); - return response.data; + // Async cancellation (202 + Location) — the normal path + if (response.status === 202) { + const location = response.headers['location'] || response.headers['Location']; + + if (!location) { + throw new InvoiceProcessingError( + 'Async cancel response (202) received but no Location header found', + { status: 202, headers: response.headers } + ); + } + + const extractedId = this.extractInvoiceIdFromLocation(location); + // Keep full path for polling (with or without /v1 prefix) + const fullPath = location.startsWith('http') ? new URL(location).pathname : location; + + return { + status: 'async', + response: { + code: 202, + status: 'pending', + location: fullPath, + invoiceId: extractedId, + }, + }; + } + + // Immediate cancellation (200/201 with the invoice body) + return { + status: 'immediate', + invoice: response.data, + }; + } + + /** + * Cancel an invoice and wait for the cancellation to settle. + * + * Combines {@link cancel} + polling, mirroring {@link createAndWait}. Polls the invoice + * until it reaches a terminal flow status and returns it. + * + * @param companyId - Company ID (GUID) + * @param invoiceId - Invoice ID (GUID) + * @param options - Polling configuration (timeout, delays, callback) + * @returns The settled invoice (expected `flowStatus: 'Cancelled'`) + * @throws {TimeoutError} If polling timeout exceeded + * @throws {InvoiceProcessingError} If cancellation failed (`flowStatus: 'CancelFailed'`) + * + * @example + * ```typescript + * const invoice = await nfe.serviceInvoices.cancelAndWait(companyId, invoiceId); + * console.log(invoice.flowStatus); // 'Cancelled' + * ``` + */ + async cancelAndWait( + companyId: string, + invoiceId: string, + options: PollingOptions = {} + ): Promise { + const cancelResult = await this.cancel(companyId, invoiceId); + + // Immediate cancellation — nothing to poll + if (cancelResult.status === 'immediate') { + return cancelResult.invoice; + } + + const { invoiceId: targetId } = cancelResult.response; + + const pollingConfig: import('../utils/polling.js').PollingOptions = { + fn: async () => this.retrieve(companyId, targetId), + isComplete: (invoice) => isTerminalFlowStatus(invoice.flowStatus as FlowStatus), + timeout: options.timeout ?? 120000, + initialDelay: options.initialDelay ?? 1000, + maxDelay: options.maxDelay ?? 10000, + backoffFactor: options.backoffFactor ?? 1.5, + }; + + if (options.onPoll) { + pollingConfig.onPoll = (attempt, result) => { + options.onPoll!(attempt, result.flowStatus as FlowStatus); + }; + } + + const invoice = await poll(pollingConfig); + + const flowStatus = invoice.flowStatus as FlowStatus; + if (flowStatus === 'CancelFailed') { + throw new InvoiceProcessingError( + `Invoice cancellation failed with status: ${flowStatus}`, + { flowStatus, flowMessage: invoice.flowMessage, invoice } + ); + } + + return invoice; } // -------------------------------------------------------------------------- diff --git a/src/core/resources/state-taxes.ts b/src/core/resources/state-taxes.ts index 1d46935..90a4979 100644 --- a/src/core/resources/state-taxes.ts +++ b/src/core/resources/state-taxes.ts @@ -265,4 +265,26 @@ export class StateTaxesResource { `${this.basePath(companyId)}/${stateTaxId}`, ); } + + /** + * Switch the NF-e authorizer (SEFAZ environment/authorizer) for a state tax. + * + * @param companyId - The company ID + * @param stateTaxId - The state tax ID + * @param data - Optional switch payload (authorizer selection) + * @returns The updated state tax record + */ + async switchAuthorizer( + companyId: string, + stateTaxId: string, + data?: Record, + ): Promise { + validateCompanyId(companyId); + validateStateTaxId(stateTaxId); + const response = await this.http.post( + `${this.basePath(companyId)}/${stateTaxId}/switch-authorizer`, + data ?? {}, + ); + return response.data; + } } diff --git a/src/core/resources/webhooks.ts b/src/core/resources/webhooks.ts index 146f9fd..14768f9 100644 --- a/src/core/resources/webhooks.ts +++ b/src/core/resources/webhooks.ts @@ -13,7 +13,18 @@ import type { Webhook, WebhookEvent, ListResponse, ResourceId } from '../types.j * All operations are scoped by company_id */ export class WebhooksResource { - constructor(private readonly http: HttpClient) {} + /** + * HTTP client for ACCOUNT-scoped endpoints (host-root `/v2/webhooks`). When not + * provided, falls back to the company-scoped client (back-compat for tests). + */ + private readonly account: HttpClient; + + constructor( + private readonly http: HttpClient, + accountHttp?: HttpClient + ) { + this.account = accountHttp ?? http; + } /** * List all webhooks for a company @@ -236,18 +247,82 @@ export class WebhooksResource { return response.data; } + // -------------------------------------------------------------------------- + // Account-scoped operations (/v2/webhooks) — NOT company-scoped. + // These take no companyId; they manage webhooks at the account level. + // -------------------------------------------------------------------------- + /** - * Get available webhook events - * - * Returns a list of all available webhook event types - * + * List account-level webhooks (`GET /v2/webhooks`). + * + * The API wraps the result as `{ webHooks: [...] }`; this normalizes it to the + * SDK's `ListResponse` (`{ data: [...] }`). + */ + async listAccountWebhooks(): Promise> { + const response = await this.account.get<{ webHooks?: Webhook[] }>('/webhooks'); + return { data: response.data?.webHooks ?? [] }; + } + + /** Create an account-level webhook (`POST /v2/webhooks`). */ + async createAccountWebhook(data: Partial): Promise { + const response = await this.account.post('/webhooks', data); + return response.data; + } + + /** Retrieve an account-level webhook by id (`GET /v2/webhooks/{id}`). */ + async retrieveAccountWebhook(webhookId: ResourceId): Promise { + const response = await this.account.get(`/webhooks/${webhookId}`); + return response.data; + } + + /** Update an account-level webhook by id (`PUT /v2/webhooks/{id}`). */ + async updateAccountWebhook(webhookId: ResourceId, data: Partial): Promise { + const response = await this.account.put(`/webhooks/${webhookId}`, data); + return response.data; + } + + /** Delete a single account-level webhook by id (`DELETE /v2/webhooks/{id}`). */ + async deleteAccountWebhook(webhookId: ResourceId): Promise { + await this.account.delete(`/webhooks/${webhookId}`); + } + + /** + * ⚠️ DANGEROUS: delete **ALL** account-level webhooks (`DELETE /v2/webhooks`). + * + * Named distinctly from {@link deleteAccountWebhook} so it can never be reached + * by a mistyped single delete. This removes every webhook on the account. + */ + async deleteAllAccountWebhooks(): Promise { + await this.account.delete('/webhooks'); + } + + /** Trigger a test ping for an account-level webhook (`PUT /v2/webhooks/{id}/pings`). */ + async pingAccountWebhook(webhookId: ResourceId): Promise { + await this.account.put(`/webhooks/${webhookId}/pings`, {}); + } + + /** + * Fetch the live list of available webhook event types (`GET /v2/webhooks/eventTypes`). + * + * Prefer this over {@link getAvailableEvents}: the server is the source of truth, + * so new event types are picked up automatically. The return is an **open** union + * (`WebhookEvent | (string & {})`) so new server-side events don't break typing. + * The API wraps the result as `{ eventTypes: [{ id, ... }] }`; this extracts the ids. + */ + async fetchEventTypes(): Promise> { + const response = await this.account.get<{ eventTypes?: Array<{ id: string }> }>( + '/webhooks/eventTypes' + ); + return (response.data?.eventTypes ?? []).map((e) => e.id); + } + + /** + * Get available webhook events. + * + * @deprecated This returns a hardcoded list that can drift from the platform's + * real event set. Use {@link fetchEventTypes} to get the live list from the API. + * * @returns List of available events - * - * @example - * ```typescript - * const events = nfe.webhooks.getAvailableEvents(); - * console.log('Available events:', events); - * ``` */ getAvailableEvents(): WebhookEvent[] { return [ diff --git a/src/core/types.ts b/src/core/types.ts index 3400897..eb38789 100644 --- a/src/core/types.ts +++ b/src/core/types.ts @@ -215,32 +215,27 @@ export interface Address { streetSuffix: string; /** Street name */ street: string; - /** Address number */ + /** Address number (may be a textual range, e.g. "de 612 a 1510 - lado par") */ number: string; - /** Minimum number in range */ - numberMin: string; - /** Maximum number in range */ - numberMax: string; - /** Postal code (CEP) */ + /** Minimum number in range (omitted by the API for some entries) */ + numberMin?: string; + /** Maximum number in range (omitted by the API for some entries) */ + numberMax?: string; + /** Postal code (CEP), returned formatted with hyphen (e.g. "01310-100") */ postalCode: string; - /** Country code */ + /** Country code (ISO 3166-1 alpha-3, e.g. "BRA") */ country: string; } /** - * Response from address lookup endpoints + * Raw response envelope from the postal-code lookup endpoint + * (`GET /v2/addresses/{cep}`). The API wraps the single result in an `address` key; + * {@link AddressesResource.lookupByPostalCode} unwraps it and returns the inner + * {@link Address}. */ export interface AddressLookupResponse { - /** Array of matching addresses */ - addresses: Address[]; -} - -/** - * Options for address search - */ -export interface AddressSearchOptions { - /** OData filter expression (e.g., "city eq 'São Paulo'") */ - filter?: string; + /** The single matching address */ + address: Address; } // ============================================================================ @@ -370,16 +365,31 @@ export type ServiceInvoiceSingleResponse = export type { ServiceInvoiceData as ServiceInvoice }; // TODO: Add proper type exports when implementing other resources -/** Placeholder: Company type - to be properly defined when implementing Companies resource */ +/** + * Company entity. + * + * Additive enrichment (no breaking change, ships in a minor): the original + * required fields and the permissive `[key: string]: unknown` index are kept, + * and the documented `contribuintes-v2` fields (address, taxRegime, tradeName, + * stateTaxes, …) are added as **optional** — so autocomplete improves without + * tightening the type or the `create()` input. Use {@link CompanyResourceItem} / + * {@link CompanyResourceV1} for the strict spec shapes, and + * {@link CreateCompanyResourceItem} for a strict create input. + */ export type Company = { id?: string; name: string; federalTaxNumber: number; email: string; - [key: string]: unknown; -}; +} & Partial> & { + [key: string]: unknown; + }; -/** Placeholder: Legal Person type - to be properly defined when implementing LegalPeople resource */ +/** + * Legal Person type. + * NOTE: no dedicated 3.x spec backs the company-scoped legalpeople sub-resource yet, + * so this remains hand-typed (review F14). Keep the permissive index for compat. + */ export type LegalPerson = { id?: string; federalTaxNumber: string; @@ -387,7 +397,10 @@ export type LegalPerson = { [key: string]: unknown; }; -/** Placeholder: Natural Person type - to be properly defined when implementing NaturalPeople resource */ +/** + * Natural Person type. + * NOTE: hand-typed for the same reason as {@link LegalPerson}. + */ export type NaturalPerson = { id?: string; federalTaxNumber: string; @@ -402,6 +415,91 @@ export type NaturalPerson = { // Import the components type from generated spec import type { components as CteComponents } from '../generated/consulta-cte-v2.js'; +// ---------------------------------------------------------------------------- +// RTC (Reforma Tributária do Consumo) request types — from the dedicated specs +// ---------------------------------------------------------------------------- +import type { components as ServiceInvoiceRtcComponents } from '../generated/service-invoice-rtc-v1.js'; +import type { components as ProductInvoiceRtcComponents } from '../generated/product-invoice-rtc-v1.js'; + +/** RTC NFS-e (service) emission request body — named schema `NFSeRequest`. */ +export type NFSeRtcRequest = ServiceInvoiceRtcComponents['schemas']['NFSeRequest']; + +/** RTC NF-e/NFC-e (product) emission request body — named schema `ProductInvoiceRequest`. */ +export type ProductInvoiceRtcRequest = + ProductInvoiceRtcComponents['schemas']['ProductInvoiceRequest']; + +// ---------------------------------------------------------------------------- +// Empresas (contribuintes-v2) — spec-backed company/certificate/address types. +// Clean public aliases over the .NET-qualified generated keys (same pattern as +// CteComponents above). Full key-normalization remains a pipeline task (1.2). +// ---------------------------------------------------------------------------- +import type { components as ContribuintesComponents } from '../generated/contribuintes-v2.js'; + +/** Company entity (list/item shape) — `contribuintes-v2` field-bearing schema. */ +export type CompanyResourceItem = + ContribuintesComponents['schemas']['DFeTech.TaxPayers.Resources.CompanyResourceItem']; + +/** Company entity (rich v1 shape, 28 fields). */ +export type CompanyResourceV1 = + ContribuintesComponents['schemas']['DFeTech.TaxPayers.Resources.CompanyResourceV1']; + +/** Company creation request body (opt-in, decoupled from the response type). */ +export type CreateCompanyResourceItem = + ContribuintesComponents['schemas']['DFeTech.TaxPayers.Resources.CreateCompanyResourceItem']; + +/** Company update request body. */ +export type UpdateCompanyResourceItem = + ContribuintesComponents['schemas']['DFeTech.TaxPayers.Resources.UpdateCompanyResourceItem']; + +/** Digital certificate metadata (real, spec-backed). */ +export type CertificateMetadataResource = + ContribuintesComponents['schemas']['DFeTech.TaxPayers.Resources.CertificateMetadataResource']; + +/** Digital certificate metadata collection (plural `/certificates`). */ +export type CertificatesMetadataResource = + ContribuintesComponents['schemas']['DFeTech.TaxPayers.Resources.CertificatesMetadataResource']; + +/** Company address (spec-backed). */ +export type CompanyAddress = + ContribuintesComponents['schemas']['DFeTech.TaxPayers.Resources.AddressResource']; + +/** Municipal tax registration (Inscrição Municipal) entity. */ +export type MunicipalTax = + ContribuintesComponents['schemas']['DFeTech.TaxPayers.Resources.MunicipalTaxResourceItem']; + +/** Municipal tax creation input (item; wrapped as `{ municipalTax }` on the wire). */ +export type CreateMunicipalTaxData = + ContribuintesComponents['schemas']['DFeTech.TaxPayers.Resources.CreateMunicipalTaxResourceItem']; + +/** Municipal tax update input (item). */ +export type UpdateMunicipalTaxData = + ContribuintesComponents['schemas']['DFeTech.TaxPayers.Resources.UpdateMunicipalTaxResourceItem']; + +/** List response for municipal taxes (best-effort shape). */ +export interface MunicipalTaxListResponse { + municipalTaxes?: MunicipalTax[]; + [key: string]: unknown; +} + +// ---------------------------------------------------------------------------- +// Consumer invoices (NFC-e) emission — from nf-consumidor-v2 named schemas. +// ---------------------------------------------------------------------------- +import type { components as NfConsumidorComponents } from '../generated/nf-consumidor-v2.js'; + +/** NFC-e emission request body (`ConsumerInvoiceRequest`). */ +export type ConsumerInvoiceData = NfConsumidorComponents['schemas']['ConsumerInvoiceRequest']; + +/** NFC-e invoice entity (`InvoiceResource`). */ +export type ConsumerInvoice = NfConsumidorComponents['schemas']['InvoiceResource']; + +/** NFC-e list response envelope (`ConsumerInvoicesResource`). */ +export type ConsumerInvoiceListResponse = + NfConsumidorComponents['schemas']['ConsumerInvoicesResource']; + +/** NFC-e disablement (inutilização) request body (`DisablementResource`). */ +export type ConsumerInvoiceDisablementData = + NfConsumidorComponents['schemas']['DisablementResource']; + /** * Transportation Invoice inbound settings * Configuration for automatic CT-e search via SEFAZ Distribuição DFe diff --git a/src/core/utils/certificate-validator.ts b/src/core/utils/certificate-validator.ts index c2fe9f9..561ab76 100644 --- a/src/core/utils/certificate-validator.ts +++ b/src/core/utils/certificate-validator.ts @@ -29,11 +29,21 @@ export interface CertificateValidationResult { export class CertificateValidator { /** - * Validate certificate file and extract metadata + * Pre-flight a certificate file before upload. + * + * IMPORTANT: this performs **format-only** local checks (non-empty buffer, + * password provided, PKCS#12 `3082` magic bytes). It does NOT parse the + * certificate and CANNOT verify the password, expiry, subject or issuer — + * doing so requires a PKCS#12 reader, which would violate the SDK's + * no-runtime-dependency rule. Subject/issuer/validity and the password are + * verified **server-side** by the NFE.io API when the certificate is uploaded. + * + * Therefore a `valid: true` result means "looks like a PKCS#12 file", not + * "this is a valid, in-date certificate with the right password". * * @param file - Certificate file buffer - * @param password - Certificate password - * @returns Validation result with metadata if valid + * @param password - Certificate password (presence checked only) + * @returns Pre-flight result (no fabricated metadata) */ static async validate( file: Buffer, @@ -57,19 +67,11 @@ export class CertificateValidator { return { valid: false, error: 'Invalid certificate format. Expected PKCS#12 (.pfx/.p12)' }; } - // Note: Full PKCS#12 parsing requires native crypto libraries or specialized packages - // For production, consider using packages like 'node-forge' or rely on API validation - // This implementation provides basic pre-flight checks - - return { - valid: true, - metadata: { - subject: 'Certificate Subject', - issuer: 'Certificate Issuer', - validFrom: new Date(), - validTo: new Date(Date.now() + 365 * 24 * 60 * 60 * 1000), // 1 year from now - } - }; + // Format pre-flight passed. We deliberately DO NOT return metadata: + // full PKCS#12 parsing (subject/issuer/validity) and password verification + // require a runtime dependency we don't take. The API validates these on + // upload. Returning fabricated metadata here would lie about validation. + return { valid: true }; } catch (error) { if (error instanceof Error) { diff --git a/src/generated/calculo-impostos-v1.ts b/src/generated/calculo-impostos-v1.ts index 7c26a65..59a9d07 100644 --- a/src/generated/calculo-impostos-v1.ts +++ b/src/generated/calculo-impostos-v1.ts @@ -4,7 +4,7 @@ * Do not edit this file directly. * * To regenerate: npm run generate - * Last generated: 2026-04-25T23:27:48.962Z + * Last generated: 2026-06-27T15:46:53.795Z * Generator: openapi-typescript */ diff --git a/src/generated/consulta-cte-v2.ts b/src/generated/consulta-cte-v2.ts index 3d58d9c..2e1bd94 100644 --- a/src/generated/consulta-cte-v2.ts +++ b/src/generated/consulta-cte-v2.ts @@ -4,7 +4,7 @@ * Do not edit this file directly. * * To regenerate: npm run generate - * Last generated: 2026-04-25T23:27:48.983Z + * Last generated: 2026-06-27T15:46:53.815Z * Generator: openapi-typescript */ diff --git a/src/generated/consulta-nfe-distribuicao-v1.ts b/src/generated/consulta-nfe-distribuicao-v1.ts index f65a436..cb0d1cf 100644 --- a/src/generated/consulta-nfe-distribuicao-v1.ts +++ b/src/generated/consulta-nfe-distribuicao-v1.ts @@ -4,7 +4,7 @@ * Do not edit this file directly. * * To regenerate: npm run generate - * Last generated: 2026-04-25T23:27:49.018Z + * Last generated: 2026-06-27T15:46:53.844Z * Generator: openapi-typescript */ diff --git a/src/generated/contribuintes-v2.ts b/src/generated/contribuintes-v2.ts new file mode 100644 index 0000000..a54ded2 --- /dev/null +++ b/src/generated/contribuintes-v2.ts @@ -0,0 +1,2757 @@ +/** + * ⚠️ AUTO-GENERATED from contribuintes-v2.json + * + * Do not edit this file directly. + * + * To regenerate: npm run generate + * Last generated: 2026-06-27T15:46:53.895Z + * Generator: openapi-typescript + */ + +export interface paths { + readonly "/v2/companies": { + readonly parameters: { + readonly query?: never; + readonly header?: never; + readonly path?: never; + readonly cookie?: never; + }; + /** + * Consultar todas as Empresas da Conta + * @description ### Informações adicionais + * Utilize esta requisição para consultar os dados das empresas vinculadas a conta. + */ + readonly get: { + readonly parameters: { + readonly query?: { + /** @description Id de início do contador (Default: Empty) */ + readonly startingAfter?: string; + /** @description Id final do contador (Default: Empty) */ + readonly endingBefore?: string; + /** @description Limite de resultados na página (Default: 10) */ + readonly limit?: number; + }; + readonly header?: never; + readonly path?: never; + readonly cookie?: never; + }; + readonly requestBody?: never; + readonly responses: { + /** @description Sucesso na consulta da Empresa */ + readonly 200: { + headers: { + readonly [name: string]: unknown; + }; + content: { + readonly "application/json": components["schemas"]["DFeTech.TaxPayers.Resources.CompaniesResource"]; + }; + }; + /** @description Algum parametro informado não é válido, verificar resposta */ + readonly 400: { + headers: { + readonly [name: string]: unknown; + }; + content: { + readonly "application/json": components["schemas"]["DFeTech.TaxPayers.Resources.ErrorsResource"]; + }; + }; + /** @description Empresa não encontrada */ + readonly 404: { + headers: { + readonly [name: string]: unknown; + }; + content: { + readonly "application/json": components["schemas"]["DFeTech.TaxPayers.Resources.ErrorsResource"]; + }; + }; + }; + }; + readonly put?: never; + /** + * Criar uma Empresa + * @description ### Informações adicionais + * Utilize esta requisição para criar novas empresas plataforma para processar Documentos Fiscais. + * **Empresa** representa uma pessoa jurídica que precisa processar Documentos Fiscais. + */ + readonly post: { + readonly parameters: { + readonly query?: never; + readonly header?: never; + readonly path?: never; + readonly cookie?: never; + }; + /** @description Dados da Empresa a ser criada */ + readonly requestBody?: { + readonly content: { + readonly "application/json;odata.metadata=minimal;odata.streaming=true": components["schemas"]["DFeTech.TaxPayers.Resources.CreateCompanyResource"]; + readonly "application/json;odata.metadata=minimal;odata.streaming=false": components["schemas"]["DFeTech.TaxPayers.Resources.CreateCompanyResource"]; + readonly "application/json;odata.metadata=minimal": components["schemas"]["DFeTech.TaxPayers.Resources.CreateCompanyResource"]; + readonly "application/json;odata.metadata=full;odata.streaming=true": components["schemas"]["DFeTech.TaxPayers.Resources.CreateCompanyResource"]; + readonly "application/json;odata.metadata=full;odata.streaming=false": components["schemas"]["DFeTech.TaxPayers.Resources.CreateCompanyResource"]; + readonly "application/json;odata.metadata=full": components["schemas"]["DFeTech.TaxPayers.Resources.CreateCompanyResource"]; + readonly "application/json;odata.metadata=none;odata.streaming=true": components["schemas"]["DFeTech.TaxPayers.Resources.CreateCompanyResource"]; + readonly "application/json;odata.metadata=none;odata.streaming=false": components["schemas"]["DFeTech.TaxPayers.Resources.CreateCompanyResource"]; + readonly "application/json;odata.metadata=none": components["schemas"]["DFeTech.TaxPayers.Resources.CreateCompanyResource"]; + readonly "application/json;odata.streaming=true": components["schemas"]["DFeTech.TaxPayers.Resources.CreateCompanyResource"]; + readonly "application/json;odata.streaming=false": components["schemas"]["DFeTech.TaxPayers.Resources.CreateCompanyResource"]; + readonly "application/json": components["schemas"]["DFeTech.TaxPayers.Resources.CreateCompanyResource"]; + readonly "application/json;odata.metadata=minimal;odata.streaming=true;IEEE754Compatible=false": components["schemas"]["DFeTech.TaxPayers.Resources.CreateCompanyResource"]; + readonly "application/json;odata.metadata=minimal;odata.streaming=true;IEEE754Compatible=true": components["schemas"]["DFeTech.TaxPayers.Resources.CreateCompanyResource"]; + readonly "application/json;odata.metadata=minimal;odata.streaming=false;IEEE754Compatible=false": components["schemas"]["DFeTech.TaxPayers.Resources.CreateCompanyResource"]; + readonly "application/json;odata.metadata=minimal;odata.streaming=false;IEEE754Compatible=true": components["schemas"]["DFeTech.TaxPayers.Resources.CreateCompanyResource"]; + readonly "application/json;odata.metadata=minimal;IEEE754Compatible=false": components["schemas"]["DFeTech.TaxPayers.Resources.CreateCompanyResource"]; + readonly "application/json;odata.metadata=minimal;IEEE754Compatible=true": components["schemas"]["DFeTech.TaxPayers.Resources.CreateCompanyResource"]; + readonly "application/json;odata.metadata=full;odata.streaming=true;IEEE754Compatible=false": components["schemas"]["DFeTech.TaxPayers.Resources.CreateCompanyResource"]; + readonly "application/json;odata.metadata=full;odata.streaming=true;IEEE754Compatible=true": components["schemas"]["DFeTech.TaxPayers.Resources.CreateCompanyResource"]; + readonly "application/json;odata.metadata=full;odata.streaming=false;IEEE754Compatible=false": components["schemas"]["DFeTech.TaxPayers.Resources.CreateCompanyResource"]; + readonly "application/json;odata.metadata=full;odata.streaming=false;IEEE754Compatible=true": components["schemas"]["DFeTech.TaxPayers.Resources.CreateCompanyResource"]; + readonly "application/json;odata.metadata=full;IEEE754Compatible=false": components["schemas"]["DFeTech.TaxPayers.Resources.CreateCompanyResource"]; + readonly "application/json;odata.metadata=full;IEEE754Compatible=true": components["schemas"]["DFeTech.TaxPayers.Resources.CreateCompanyResource"]; + readonly "application/json;odata.metadata=none;odata.streaming=true;IEEE754Compatible=false": components["schemas"]["DFeTech.TaxPayers.Resources.CreateCompanyResource"]; + readonly "application/json;odata.metadata=none;odata.streaming=true;IEEE754Compatible=true": components["schemas"]["DFeTech.TaxPayers.Resources.CreateCompanyResource"]; + readonly "application/json;odata.metadata=none;odata.streaming=false;IEEE754Compatible=true": components["schemas"]["DFeTech.TaxPayers.Resources.CreateCompanyResource"]; + readonly "application/json;odata.metadata=none;odata.streaming=false;IEEE754Compatible=false": components["schemas"]["DFeTech.TaxPayers.Resources.CreateCompanyResource"]; + readonly "application/json;odata.metadata=none;IEEE754Compatible=false": components["schemas"]["DFeTech.TaxPayers.Resources.CreateCompanyResource"]; + readonly "application/json;odata.metadata=none;IEEE754Compatible=true": components["schemas"]["DFeTech.TaxPayers.Resources.CreateCompanyResource"]; + readonly "application/json;odata.streaming=true;IEEE754Compatible=false": components["schemas"]["DFeTech.TaxPayers.Resources.CreateCompanyResource"]; + readonly "application/json;odata.streaming=true;IEEE754Compatible=true": components["schemas"]["DFeTech.TaxPayers.Resources.CreateCompanyResource"]; + readonly "application/json;odata.streaming=false;IEEE754Compatible=false": components["schemas"]["DFeTech.TaxPayers.Resources.CreateCompanyResource"]; + readonly "application/json;odata.streaming=false;IEEE754Compatible=true": components["schemas"]["DFeTech.TaxPayers.Resources.CreateCompanyResource"]; + readonly "application/json;IEEE754Compatible=false": components["schemas"]["DFeTech.TaxPayers.Resources.CreateCompanyResource"]; + readonly "application/json;IEEE754Compatible=true": components["schemas"]["DFeTech.TaxPayers.Resources.CreateCompanyResource"]; + readonly "application/xml": components["schemas"]["DFeTech.TaxPayers.Resources.CreateCompanyResource"]; + readonly "text/plain": components["schemas"]["DFeTech.TaxPayers.Resources.CreateCompanyResource"]; + readonly "text/json": components["schemas"]["DFeTech.TaxPayers.Resources.CreateCompanyResource"]; + readonly "application/*+json": components["schemas"]["DFeTech.TaxPayers.Resources.CreateCompanyResource"]; + }; + }; + readonly responses: { + /** @description Sucesso na criação da Empresa */ + readonly 200: { + headers: { + readonly [name: string]: unknown; + }; + content: { + readonly "application/json": components["schemas"]["DFeTech.TaxPayers.Resources.CompanyResource"]; + }; + }; + /** @description Algum parametro informado não é válido, verificar resposta */ + readonly 400: { + headers: { + readonly [name: string]: unknown; + }; + content: { + readonly "application/json": components["schemas"]["DFeTech.TaxPayers.Resources.ErrorsResource"]; + }; + }; + }; + }; + readonly delete?: never; + readonly options?: never; + readonly head?: never; + readonly patch?: never; + readonly trace?: never; + }; + readonly "/v2/companies/{company_id}": { + readonly parameters: { + readonly query?: never; + readonly header?: never; + readonly path?: never; + readonly cookie?: never; + }; + /** + * Consultar uma Empresa pelo ID + * @description ### Informações adicionais + * Utilize esta requisição para consultar os dados de uma empresas pelo ID. + */ + readonly get: { + readonly parameters: { + readonly query?: never; + readonly header?: never; + readonly path: { + /** @description ID da Empresa que deverá ser retornado */ + readonly company_id: string; + }; + readonly cookie?: never; + }; + readonly requestBody?: never; + readonly responses: { + /** @description Sucesso na consulta da Empresa */ + readonly 200: { + headers: { + readonly [name: string]: unknown; + }; + content: { + readonly "application/json": components["schemas"]["DFeTech.TaxPayers.Resources.CompanyResource"]; + }; + }; + /** @description Algum parametro informado não é válido, verificar resposta */ + readonly 400: { + headers: { + readonly [name: string]: unknown; + }; + content: { + readonly "application/json": components["schemas"]["DFeTech.TaxPayers.Resources.ErrorsResource"]; + }; + }; + /** @description Empresa não encontrada */ + readonly 404: { + headers: { + readonly [name: string]: unknown; + }; + content: { + readonly "application/json": components["schemas"]["DFeTech.TaxPayers.Resources.ErrorsResource"]; + }; + }; + }; + }; + /** + * Alterar uma Empresa pelo ID + * @description ### Informações adicionais + * Utilize esta requisição para alterar os dados de uma empresas pelo ID. + */ + readonly put: { + readonly parameters: { + readonly query?: never; + readonly header?: never; + readonly path: { + /** @description ID da Empresa que deverá ser retornado */ + readonly company_id: string; + }; + readonly cookie?: never; + }; + /** @description Dados da Empresa a ser alterada */ + readonly requestBody?: { + readonly content: { + readonly "application/json;odata.metadata=minimal;odata.streaming=true": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateCompanyResource"]; + readonly "application/json;odata.metadata=minimal;odata.streaming=false": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateCompanyResource"]; + readonly "application/json;odata.metadata=minimal": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateCompanyResource"]; + readonly "application/json;odata.metadata=full;odata.streaming=true": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateCompanyResource"]; + readonly "application/json;odata.metadata=full;odata.streaming=false": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateCompanyResource"]; + readonly "application/json;odata.metadata=full": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateCompanyResource"]; + readonly "application/json;odata.metadata=none;odata.streaming=true": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateCompanyResource"]; + readonly "application/json;odata.metadata=none;odata.streaming=false": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateCompanyResource"]; + readonly "application/json;odata.metadata=none": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateCompanyResource"]; + readonly "application/json;odata.streaming=true": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateCompanyResource"]; + readonly "application/json;odata.streaming=false": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateCompanyResource"]; + readonly "application/json": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateCompanyResource"]; + readonly "application/json;odata.metadata=minimal;odata.streaming=true;IEEE754Compatible=false": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateCompanyResource"]; + readonly "application/json;odata.metadata=minimal;odata.streaming=true;IEEE754Compatible=true": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateCompanyResource"]; + readonly "application/json;odata.metadata=minimal;odata.streaming=false;IEEE754Compatible=false": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateCompanyResource"]; + readonly "application/json;odata.metadata=minimal;odata.streaming=false;IEEE754Compatible=true": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateCompanyResource"]; + readonly "application/json;odata.metadata=minimal;IEEE754Compatible=false": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateCompanyResource"]; + readonly "application/json;odata.metadata=minimal;IEEE754Compatible=true": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateCompanyResource"]; + readonly "application/json;odata.metadata=full;odata.streaming=true;IEEE754Compatible=false": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateCompanyResource"]; + readonly "application/json;odata.metadata=full;odata.streaming=true;IEEE754Compatible=true": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateCompanyResource"]; + readonly "application/json;odata.metadata=full;odata.streaming=false;IEEE754Compatible=false": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateCompanyResource"]; + readonly "application/json;odata.metadata=full;odata.streaming=false;IEEE754Compatible=true": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateCompanyResource"]; + readonly "application/json;odata.metadata=full;IEEE754Compatible=false": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateCompanyResource"]; + readonly "application/json;odata.metadata=full;IEEE754Compatible=true": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateCompanyResource"]; + readonly "application/json;odata.metadata=none;odata.streaming=true;IEEE754Compatible=false": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateCompanyResource"]; + readonly "application/json;odata.metadata=none;odata.streaming=true;IEEE754Compatible=true": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateCompanyResource"]; + readonly "application/json;odata.metadata=none;odata.streaming=false;IEEE754Compatible=true": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateCompanyResource"]; + readonly "application/json;odata.metadata=none;odata.streaming=false;IEEE754Compatible=false": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateCompanyResource"]; + readonly "application/json;odata.metadata=none;IEEE754Compatible=false": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateCompanyResource"]; + readonly "application/json;odata.metadata=none;IEEE754Compatible=true": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateCompanyResource"]; + readonly "application/json;odata.streaming=true;IEEE754Compatible=false": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateCompanyResource"]; + readonly "application/json;odata.streaming=true;IEEE754Compatible=true": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateCompanyResource"]; + readonly "application/json;odata.streaming=false;IEEE754Compatible=false": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateCompanyResource"]; + readonly "application/json;odata.streaming=false;IEEE754Compatible=true": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateCompanyResource"]; + readonly "application/json;IEEE754Compatible=false": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateCompanyResource"]; + readonly "application/json;IEEE754Compatible=true": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateCompanyResource"]; + readonly "application/xml": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateCompanyResource"]; + readonly "text/plain": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateCompanyResource"]; + readonly "text/json": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateCompanyResource"]; + readonly "application/*+json": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateCompanyResource"]; + }; + }; + readonly responses: { + /** @description Sucesso na alteração da Empresa */ + readonly 200: { + headers: { + readonly [name: string]: unknown; + }; + content: { + readonly "application/json": components["schemas"]["DFeTech.TaxPayers.Resources.CompanyResource"]; + }; + }; + /** @description Algum parametro informado não é válido, verificar resposta */ + readonly 400: { + headers: { + readonly [name: string]: unknown; + }; + content: { + readonly "application/json": components["schemas"]["DFeTech.TaxPayers.Resources.ErrorsResource"]; + }; + }; + /** @description Empresa não encontrada */ + readonly 404: { + headers: { + readonly [name: string]: unknown; + }; + content: { + readonly "application/json": components["schemas"]["DFeTech.TaxPayers.Resources.ErrorsResource"]; + }; + }; + }; + }; + readonly post?: never; + /** + * Excluir uma Empresa por ID + * @description ### Informações adicionais + * Utilize esta requisição para excluir uma empresas pelo ID, cuidado pois esse processo é irreversível. + */ + readonly delete: { + readonly parameters: { + readonly query?: never; + readonly header?: never; + readonly path: { + /** @description ID da Empresa que deverá ser retornado */ + readonly company_id: string; + }; + readonly cookie?: never; + }; + readonly requestBody?: never; + readonly responses: { + /** @description Sucesso na exclusão da Empresa */ + readonly 204: { + headers: { + readonly [name: string]: unknown; + }; + content?: never; + }; + /** @description Algum parametro informado não é válido, verificar resposta */ + readonly 400: { + headers: { + readonly [name: string]: unknown; + }; + content: { + readonly "application/json": components["schemas"]["DFeTech.TaxPayers.Resources.ErrorsResource"]; + }; + }; + /** @description Empresa não encontrada */ + readonly 404: { + headers: { + readonly [name: string]: unknown; + }; + content: { + readonly "application/json": components["schemas"]["DFeTech.TaxPayers.Resources.ErrorsResource"]; + }; + }; + }; + }; + readonly options?: never; + /** + * Consultar se Empresa Existe pelo ID + * @description ### Informações adicionais + * Utilize esta requisição para consultar os dados de uma empresas pelo ID. + */ + readonly head: { + readonly parameters: { + readonly query?: never; + readonly header?: never; + readonly path: { + /** @description ID da Empresa que deverá ser retornado */ + readonly company_id: string; + }; + readonly cookie?: never; + }; + readonly requestBody?: never; + readonly responses: { + /** @description Sucesso na consulta da Empresa */ + readonly 200: { + headers: { + readonly [name: string]: unknown; + }; + content: { + readonly "application/json": components["schemas"]["DFeTech.TaxPayers.Resources.CompanyResource"]; + }; + }; + /** @description Algum parametro informado não é válido, verificar resposta */ + readonly 400: { + headers: { + readonly [name: string]: unknown; + }; + content: { + readonly "application/json": components["schemas"]["DFeTech.TaxPayers.Resources.ErrorsResource"]; + }; + }; + /** @description Empresa não encontrada */ + readonly 404: { + headers: { + readonly [name: string]: unknown; + }; + content: { + readonly "application/json": components["schemas"]["DFeTech.TaxPayers.Resources.ErrorsResource"]; + }; + }; + }; + }; + readonly patch?: never; + readonly trace?: never; + }; + readonly "/v2/companies/{company_id}/certificates": { + readonly parameters: { + readonly query?: never; + readonly header?: never; + readonly path?: never; + readonly cookie?: never; + }; + /** + * Consultar Certificado habilitado para empresa + * @description ### Informações adicionais + * Utilize esta requisição para consultar os dados do **Certificado da ICP-Brasil** habilitado para a empresa. + */ + readonly get: { + readonly parameters: { + readonly query?: never; + readonly header?: never; + readonly path: { + /** @description ID da Empresa relacionada ao certificado */ + readonly company_id: string; + }; + readonly cookie?: never; + }; + readonly requestBody?: never; + readonly responses: { + /** @description Sucesso na consulta */ + readonly 200: { + headers: { + readonly [name: string]: unknown; + }; + content: { + readonly "application/json": components["schemas"]["DFeTech.TaxPayers.Resources.CertificatesMetadataResource"]; + }; + }; + /** @description Algum parametro informado não é válido, verificar resposta */ + readonly 400: { + headers: { + readonly [name: string]: unknown; + }; + content: { + readonly "application/json": components["schemas"]["DFeTech.TaxPayers.Resources.ErrorsResource"]; + }; + }; + }; + }; + readonly put?: never; + /** + * Upload de um Certificado + * @description ### Informações adicionais + * Utilize esta requisição para fazer upload de um **Certificado da ICP-Brasil** do tipo __e-CNPJ A1__ ou __NFE A1__ em uma **Empresa** e vincula-lo para processamentos. + * O **Certificado da ICP-Brasil** funciona como uma identidade virtual, para empresas e pessoas, que permite a identificação segura e inequívoca do autor de uma mensagem ou transação feita em meios eletrônicos, como a web. + */ + readonly post: { + readonly parameters: { + readonly query?: never; + readonly header?: never; + readonly path: { + /** @description ID da empresa */ + readonly company_id: string; + }; + readonly cookie?: never; + }; + readonly requestBody?: { + readonly content: { + readonly "multipart/form-data": { + /** + * Format: binary + * @description Arquivo com certificado ICP-Brasil com extensão .pfx ou .p12 + */ + readonly File: string; + /** @description Senha do certificado ICP-Brasil */ + readonly Password: string; + }; + readonly "application/form-data": { + /** + * Format: binary + * @description Arquivo com certificado ICP-Brasil com extensão .pfx ou .p12 + */ + readonly File: string; + /** @description Senha do certificado ICP-Brasil */ + readonly Password: string; + }; + }; + }; + readonly responses: { + /** @description Sucesso no upload e vinculo com a Empresa */ + readonly 200: { + headers: { + readonly [name: string]: unknown; + }; + content: { + readonly "application/json": components["schemas"]["DFeTech.TaxPayers.Resources.CertificateMetadataResource"]; + }; + }; + /** @description Algum parametro informado não é válido, verificar resposta */ + readonly 400: { + headers: { + readonly [name: string]: unknown; + }; + content: { + readonly "application/json": components["schemas"]["DFeTech.TaxPayers.Resources.ErrorsResource"]; + }; + }; + }; + }; + readonly delete?: never; + readonly options?: never; + readonly head?: never; + readonly patch?: never; + readonly trace?: never; + }; + readonly "/v1/companies/{company_id}/certificate": { + readonly parameters: { + readonly query?: never; + readonly header?: never; + readonly path?: never; + readonly cookie?: never; + }; + /** + * Consultar Certificado habilitado para empresa + * @description ### Informações adicionais + * Utilize esta requisição para consultar os dados do **Certificado da ICP-Brasil** habilitado para a empresa. + */ + readonly get: { + readonly parameters: { + readonly query?: never; + readonly header?: never; + readonly path: { + /** @description ID da Empresa relacionada ao certificado */ + readonly company_id: string; + }; + readonly cookie?: never; + }; + readonly requestBody?: never; + readonly responses: { + /** @description Sucesso na consulta */ + readonly 200: { + headers: { + readonly [name: string]: unknown; + }; + content: { + readonly "application/json": components["schemas"]["DFeTech.TaxPayers.Resources.CertificatesMetadataResource"]; + }; + }; + /** @description Algum parametro informado não é válido, verificar resposta */ + readonly 400: { + headers: { + readonly [name: string]: unknown; + }; + content: { + readonly "application/json": components["schemas"]["DFeTech.TaxPayers.Resources.ErrorsResource"]; + }; + }; + }; + }; + readonly put?: never; + /** + * Upload de um Certificado + * @description ### Informações adicionais + * Utilize esta requisição para fazer upload de um **Certificado da ICP-Brasil** do tipo __e-CNPJ A1__ ou __NFE A1__ em uma **Empresa** e vincula-lo para processamentos. + * O **Certificado da ICP-Brasil** funciona como uma identidade virtual, para empresas e pessoas, que permite a identificação segura e inequívoca do autor de uma mensagem ou transação feita em meios eletrônicos, como a web. + */ + readonly post: { + readonly parameters: { + readonly query?: never; + readonly header?: never; + readonly path: { + /** @description ID da empresa */ + readonly company_id: string; + }; + readonly cookie?: never; + }; + readonly requestBody?: { + readonly content: { + readonly "multipart/form-data": { + /** + * Format: binary + * @description Arquivo com certificado ICP-Brasil com extensão .pfx ou .p12 + */ + readonly File: string; + /** @description Senha do certificado ICP-Brasil */ + readonly Password: string; + }; + readonly "application/form-data": { + /** + * Format: binary + * @description Arquivo com certificado ICP-Brasil com extensão .pfx ou .p12 + */ + readonly File: string; + /** @description Senha do certificado ICP-Brasil */ + readonly Password: string; + }; + }; + }; + readonly responses: { + /** @description Sucesso no upload e vinculo com a Empresa */ + readonly 200: { + headers: { + readonly [name: string]: unknown; + }; + content: { + readonly "application/json": components["schemas"]["DFeTech.TaxPayers.Resources.CertificateMetadataResource"]; + }; + }; + /** @description Algum parametro informado não é válido, verificar resposta */ + readonly 400: { + headers: { + readonly [name: string]: unknown; + }; + content: { + readonly "application/json": components["schemas"]["DFeTech.TaxPayers.Resources.ErrorsResource"]; + }; + }; + }; + }; + readonly delete?: never; + readonly options?: never; + readonly head?: never; + readonly patch?: never; + readonly trace?: never; + }; + readonly "/v2/companies/{company_id}/certificates/{certificate_thumbprint}": { + readonly parameters: { + readonly query?: never; + readonly header?: never; + readonly path?: never; + readonly cookie?: never; + }; + /** + * Consultar um Certificado por sua impressão digital + * @description ### Informações adicionais + * Utilize esta requisição para consultar os dados de um **Certificado da ICP-Brasil** através da **impressão digital do certificado** (__thumbprint__). + */ + readonly get: { + readonly parameters: { + readonly query?: never; + readonly header?: never; + readonly path: { + /** @description ID da Empresa relacionada ao certificado */ + readonly company_id: string; + /** @description Impressão digital do certificado */ + readonly certificate_thumbprint: string; + }; + readonly cookie?: never; + }; + readonly requestBody?: never; + readonly responses: { + /** @description Sucesso na consulta */ + readonly 200: { + headers: { + readonly [name: string]: unknown; + }; + content: { + readonly "application/json": components["schemas"]["DFeTech.TaxPayers.Resources.CertificateMetadataResource"]; + }; + }; + /** @description Algum parametro informado não é válido, verificar resposta */ + readonly 400: { + headers: { + readonly [name: string]: unknown; + }; + content: { + readonly "application/json": components["schemas"]["DFeTech.TaxPayers.Resources.ErrorsResource"]; + }; + }; + /** @description Certificado não encontrado */ + readonly 404: { + headers: { + readonly [name: string]: unknown; + }; + content: { + readonly "application/json": components["schemas"]["DFeTech.TaxPayers.Resources.ErrorsResource"]; + }; + }; + }; + }; + readonly put?: never; + readonly post?: never; + /** + * Excluir um Certificado por sua impressão digital + * @description ### Informações adicionais + * Utilize esta requisição para excluir o **Certificado da ICP-Brasil** através da **impressão digital do certificado** (__thumbprint__) e desvincula-lo da **Empresa**. + * **ATENÇÃO pois esta requisição é irreversível** + */ + readonly delete: { + readonly parameters: { + readonly query?: never; + readonly header?: never; + readonly path: { + /** @description ID da Empresa relacionada ao certificado */ + readonly company_id: string; + /** @description Impressão digital do certificado */ + readonly certificate_thumbprint: string; + }; + readonly cookie?: never; + }; + readonly requestBody?: never; + readonly responses: { + /** @description Sucesso na exclusão e desvinculo com a Empresa */ + readonly 204: { + headers: { + readonly [name: string]: unknown; + }; + content?: never; + }; + /** @description Algum parametro informado não é válido, verificar resposta */ + readonly 400: { + headers: { + readonly [name: string]: unknown; + }; + content: { + readonly "application/json": components["schemas"]["DFeTech.TaxPayers.Resources.ErrorsResource"]; + }; + }; + /** @description Certificado não encontrado */ + readonly 404: { + headers: { + readonly [name: string]: unknown; + }; + content: { + readonly "application/json": components["schemas"]["DFeTech.TaxPayers.Resources.ErrorsResource"]; + }; + }; + }; + }; + readonly options?: never; + readonly head?: never; + readonly patch?: never; + readonly trace?: never; + }; + readonly "/v1/companies/{company_id}/certificate/{certificate_thumbprint}": { + readonly parameters: { + readonly query?: never; + readonly header?: never; + readonly path?: never; + readonly cookie?: never; + }; + /** + * Consultar um Certificado por sua impressão digital + * @description ### Informações adicionais + * Utilize esta requisição para consultar os dados de um **Certificado da ICP-Brasil** através da **impressão digital do certificado** (__thumbprint__). + */ + readonly get: { + readonly parameters: { + readonly query?: never; + readonly header?: never; + readonly path: { + /** @description ID da Empresa relacionada ao certificado */ + readonly company_id: string; + /** @description Impressão digital do certificado */ + readonly certificate_thumbprint: string; + }; + readonly cookie?: never; + }; + readonly requestBody?: never; + readonly responses: { + /** @description Sucesso na consulta */ + readonly 200: { + headers: { + readonly [name: string]: unknown; + }; + content: { + readonly "application/json": components["schemas"]["DFeTech.TaxPayers.Resources.CertificateMetadataResource"]; + }; + }; + /** @description Algum parametro informado não é válido, verificar resposta */ + readonly 400: { + headers: { + readonly [name: string]: unknown; + }; + content: { + readonly "application/json": components["schemas"]["DFeTech.TaxPayers.Resources.ErrorsResource"]; + }; + }; + /** @description Certificado não encontrado */ + readonly 404: { + headers: { + readonly [name: string]: unknown; + }; + content: { + readonly "application/json": components["schemas"]["DFeTech.TaxPayers.Resources.ErrorsResource"]; + }; + }; + }; + }; + readonly put?: never; + readonly post?: never; + /** + * Excluir um Certificado por sua impressão digital + * @description ### Informações adicionais + * Utilize esta requisição para excluir o **Certificado da ICP-Brasil** através da **impressão digital do certificado** (__thumbprint__) e desvincula-lo da **Empresa**. + * **ATENÇÃO pois esta requisição é irreversível** + */ + readonly delete: { + readonly parameters: { + readonly query?: never; + readonly header?: never; + readonly path: { + /** @description ID da Empresa relacionada ao certificado */ + readonly company_id: string; + /** @description Impressão digital do certificado */ + readonly certificate_thumbprint: string; + }; + readonly cookie?: never; + }; + readonly requestBody?: never; + readonly responses: { + /** @description Sucesso na exclusão e desvinculo com a Empresa */ + readonly 204: { + headers: { + readonly [name: string]: unknown; + }; + content?: never; + }; + /** @description Algum parametro informado não é válido, verificar resposta */ + readonly 400: { + headers: { + readonly [name: string]: unknown; + }; + content: { + readonly "application/json": components["schemas"]["DFeTech.TaxPayers.Resources.ErrorsResource"]; + }; + }; + /** @description Certificado não encontrado */ + readonly 404: { + headers: { + readonly [name: string]: unknown; + }; + content: { + readonly "application/json": components["schemas"]["DFeTech.TaxPayers.Resources.ErrorsResource"]; + }; + }; + }; + }; + readonly options?: never; + readonly head?: never; + readonly patch?: never; + readonly trace?: never; + }; + readonly "/v2/companies/{company_id}/municipaltaxes": { + readonly parameters: { + readonly query?: never; + readonly header?: never; + readonly path?: never; + readonly cookie?: never; + }; + /** + * Listar as Inscrições Municipais + * @description ### Informações adicionais + * Utilize esta requisição para listar as inscrições municipais na empresa para processar __Documentos Fiscais__. + * **Empresa** representa uma pessoa jurídica que precisa processar Documentos Fiscais. + * **Inscrição Municipal** representa os dados necessários sobre o cadastro municipal da empresa junto à prefeitura, exigidos para a emissão de notas fiscais de serviços (NFS-e) e para o cumprimento das obrigações tributárias no âmbito municipal. + */ + readonly get: { + readonly parameters: { + readonly query?: { + /** @description Id de início do contador (Default: Empty) */ + readonly startingAfter?: string; + /** @description Id final do contador (Default: Empty) */ + readonly endingBefore?: string; + /** @description Limite de resultados na página (Default: 10) */ + readonly limit?: number; + }; + readonly header?: never; + readonly path: { + /** @description ID da Empresa */ + readonly company_id: string; + }; + readonly cookie?: never; + }; + readonly requestBody?: never; + readonly responses: { + /** @description Sucesso na criação da Inscrição Municipal */ + readonly 200: { + headers: { + readonly [name: string]: unknown; + }; + content: { + readonly "application/json": components["schemas"]["DFeTech.TaxPayers.Resources.MunicipalTaxResource"]; + }; + }; + /** @description Algum parametro informado não é válido, verificar resposta */ + readonly 400: { + headers: { + readonly [name: string]: unknown; + }; + content: { + readonly "application/json": components["schemas"]["DFeTech.TaxPayers.Resources.ErrorsResource"]; + }; + }; + }; + }; + readonly put?: never; + /** + * Criar uma Inscrição Municipal + * @description ### Informações adicionais + * Utilize esta requisição para criar novas inscrição municipais na empresa para processar __Documentos Fiscais__. + * **Empresa** representa uma pessoa jurídica que precisa processar Documentos Fiscais. + * **Inscrição Municipal** representa os dados necessários sobre o cadastro municipal da empresa junto à prefeitura, exigidos para a emissão de notas fiscais de serviços (NFS-e) e para o cumprimento das obrigações tributárias no âmbito municipal. + */ + readonly post: { + readonly parameters: { + readonly query?: never; + readonly header?: never; + readonly path: { + /** @description ID da Empresa */ + readonly company_id: string; + }; + readonly cookie?: never; + }; + /** @description Dados da Inscrição Municipal a ser criada */ + readonly requestBody?: { + readonly content: { + readonly "application/json;odata.metadata=minimal;odata.streaming=true": components["schemas"]["DFeTech.TaxPayers.Resources.CreateMunicipalTaxResource"]; + readonly "application/json;odata.metadata=minimal;odata.streaming=false": components["schemas"]["DFeTech.TaxPayers.Resources.CreateMunicipalTaxResource"]; + readonly "application/json;odata.metadata=minimal": components["schemas"]["DFeTech.TaxPayers.Resources.CreateMunicipalTaxResource"]; + readonly "application/json;odata.metadata=full;odata.streaming=true": components["schemas"]["DFeTech.TaxPayers.Resources.CreateMunicipalTaxResource"]; + readonly "application/json;odata.metadata=full;odata.streaming=false": components["schemas"]["DFeTech.TaxPayers.Resources.CreateMunicipalTaxResource"]; + readonly "application/json;odata.metadata=full": components["schemas"]["DFeTech.TaxPayers.Resources.CreateMunicipalTaxResource"]; + readonly "application/json;odata.metadata=none;odata.streaming=true": components["schemas"]["DFeTech.TaxPayers.Resources.CreateMunicipalTaxResource"]; + readonly "application/json;odata.metadata=none;odata.streaming=false": components["schemas"]["DFeTech.TaxPayers.Resources.CreateMunicipalTaxResource"]; + readonly "application/json;odata.metadata=none": components["schemas"]["DFeTech.TaxPayers.Resources.CreateMunicipalTaxResource"]; + readonly "application/json;odata.streaming=true": components["schemas"]["DFeTech.TaxPayers.Resources.CreateMunicipalTaxResource"]; + readonly "application/json;odata.streaming=false": components["schemas"]["DFeTech.TaxPayers.Resources.CreateMunicipalTaxResource"]; + readonly "application/json": components["schemas"]["DFeTech.TaxPayers.Resources.CreateMunicipalTaxResource"]; + readonly "application/json;odata.metadata=minimal;odata.streaming=true;IEEE754Compatible=false": components["schemas"]["DFeTech.TaxPayers.Resources.CreateMunicipalTaxResource"]; + readonly "application/json;odata.metadata=minimal;odata.streaming=true;IEEE754Compatible=true": components["schemas"]["DFeTech.TaxPayers.Resources.CreateMunicipalTaxResource"]; + readonly "application/json;odata.metadata=minimal;odata.streaming=false;IEEE754Compatible=false": components["schemas"]["DFeTech.TaxPayers.Resources.CreateMunicipalTaxResource"]; + readonly "application/json;odata.metadata=minimal;odata.streaming=false;IEEE754Compatible=true": components["schemas"]["DFeTech.TaxPayers.Resources.CreateMunicipalTaxResource"]; + readonly "application/json;odata.metadata=minimal;IEEE754Compatible=false": components["schemas"]["DFeTech.TaxPayers.Resources.CreateMunicipalTaxResource"]; + readonly "application/json;odata.metadata=minimal;IEEE754Compatible=true": components["schemas"]["DFeTech.TaxPayers.Resources.CreateMunicipalTaxResource"]; + readonly "application/json;odata.metadata=full;odata.streaming=true;IEEE754Compatible=false": components["schemas"]["DFeTech.TaxPayers.Resources.CreateMunicipalTaxResource"]; + readonly "application/json;odata.metadata=full;odata.streaming=true;IEEE754Compatible=true": components["schemas"]["DFeTech.TaxPayers.Resources.CreateMunicipalTaxResource"]; + readonly "application/json;odata.metadata=full;odata.streaming=false;IEEE754Compatible=false": components["schemas"]["DFeTech.TaxPayers.Resources.CreateMunicipalTaxResource"]; + readonly "application/json;odata.metadata=full;odata.streaming=false;IEEE754Compatible=true": components["schemas"]["DFeTech.TaxPayers.Resources.CreateMunicipalTaxResource"]; + readonly "application/json;odata.metadata=full;IEEE754Compatible=false": components["schemas"]["DFeTech.TaxPayers.Resources.CreateMunicipalTaxResource"]; + readonly "application/json;odata.metadata=full;IEEE754Compatible=true": components["schemas"]["DFeTech.TaxPayers.Resources.CreateMunicipalTaxResource"]; + readonly "application/json;odata.metadata=none;odata.streaming=true;IEEE754Compatible=false": components["schemas"]["DFeTech.TaxPayers.Resources.CreateMunicipalTaxResource"]; + readonly "application/json;odata.metadata=none;odata.streaming=true;IEEE754Compatible=true": components["schemas"]["DFeTech.TaxPayers.Resources.CreateMunicipalTaxResource"]; + readonly "application/json;odata.metadata=none;odata.streaming=false;IEEE754Compatible=true": components["schemas"]["DFeTech.TaxPayers.Resources.CreateMunicipalTaxResource"]; + readonly "application/json;odata.metadata=none;odata.streaming=false;IEEE754Compatible=false": components["schemas"]["DFeTech.TaxPayers.Resources.CreateMunicipalTaxResource"]; + readonly "application/json;odata.metadata=none;IEEE754Compatible=false": components["schemas"]["DFeTech.TaxPayers.Resources.CreateMunicipalTaxResource"]; + readonly "application/json;odata.metadata=none;IEEE754Compatible=true": components["schemas"]["DFeTech.TaxPayers.Resources.CreateMunicipalTaxResource"]; + readonly "application/json;odata.streaming=true;IEEE754Compatible=false": components["schemas"]["DFeTech.TaxPayers.Resources.CreateMunicipalTaxResource"]; + readonly "application/json;odata.streaming=true;IEEE754Compatible=true": components["schemas"]["DFeTech.TaxPayers.Resources.CreateMunicipalTaxResource"]; + readonly "application/json;odata.streaming=false;IEEE754Compatible=false": components["schemas"]["DFeTech.TaxPayers.Resources.CreateMunicipalTaxResource"]; + readonly "application/json;odata.streaming=false;IEEE754Compatible=true": components["schemas"]["DFeTech.TaxPayers.Resources.CreateMunicipalTaxResource"]; + readonly "application/json;IEEE754Compatible=false": components["schemas"]["DFeTech.TaxPayers.Resources.CreateMunicipalTaxResource"]; + readonly "application/json;IEEE754Compatible=true": components["schemas"]["DFeTech.TaxPayers.Resources.CreateMunicipalTaxResource"]; + readonly "application/xml": components["schemas"]["DFeTech.TaxPayers.Resources.CreateMunicipalTaxResource"]; + readonly "text/plain": components["schemas"]["DFeTech.TaxPayers.Resources.CreateMunicipalTaxResource"]; + readonly "text/json": components["schemas"]["DFeTech.TaxPayers.Resources.CreateMunicipalTaxResource"]; + readonly "application/*+json": components["schemas"]["DFeTech.TaxPayers.Resources.CreateMunicipalTaxResource"]; + }; + }; + readonly responses: { + /** @description Sucesso na criação da Inscrição Municipal */ + readonly 200: { + headers: { + readonly [name: string]: unknown; + }; + content: { + readonly "application/json": components["schemas"]["DFeTech.TaxPayers.Resources.MunicipalTaxResource"]; + }; + }; + /** @description Algum parametro informado não é válido, verificar resposta */ + readonly 400: { + headers: { + readonly [name: string]: unknown; + }; + content: { + readonly "application/json": components["schemas"]["DFeTech.TaxPayers.Resources.ErrorsResource"]; + }; + }; + }; + }; + readonly delete?: never; + readonly options?: never; + readonly head?: never; + readonly patch?: never; + readonly trace?: never; + }; + readonly "/v2/companies/{company_id}/municipaltaxes/{municipal_tax_id}": { + readonly parameters: { + readonly query?: never; + readonly header?: never; + readonly path?: never; + readonly cookie?: never; + }; + /** + * Consultar uma Inscrição Municipal pelo ID + * @description ### Informações adicionais + * Utilize esta requisição para consultar os dados de uma empresas pelo ID. + * **Empresa** representa uma pessoa jurídica que precisa processar Documentos Fiscais. + * **Inscrição Municipal** representa os dados necessários sobre o cadastro municipal da empresa junto à prefeitura, exigidos para a emissão de notas fiscais de serviços (NFS-e) e para o cumprimento das obrigações tributárias no âmbito municipal. + */ + readonly get: { + readonly parameters: { + readonly query?: never; + readonly header?: never; + readonly path: { + /** @description ID da Empresa */ + readonly company_id: string; + /** @description ID da Inscrição Mnicipal que deverá ser retornado */ + readonly municipal_tax_id: string; + }; + readonly cookie?: never; + }; + readonly requestBody?: never; + readonly responses: { + /** @description Sucesso na consulta da Inscrição Municipal */ + readonly 200: { + headers: { + readonly [name: string]: unknown; + }; + content: { + readonly "application/json": components["schemas"]["DFeTech.TaxPayers.Resources.MunicipalTaxResource"]; + }; + }; + /** @description Algum parametro informado não é válido, verificar resposta */ + readonly 400: { + headers: { + readonly [name: string]: unknown; + }; + content: { + readonly "application/json": components["schemas"]["DFeTech.TaxPayers.Resources.ErrorsResource"]; + }; + }; + /** @description Inscrição Municipal não encontrada */ + readonly 404: { + headers: { + readonly [name: string]: unknown; + }; + content: { + readonly "application/json": components["schemas"]["DFeTech.TaxPayers.Resources.ErrorsResource"]; + }; + }; + }; + }; + /** + * Alterar uma Inscrição Municipal pelo ID + * @description ### Informações adicionais + * Utilize esta requisição para alterar os dados de uma Inscrição Municipal pelo ID. + * **Empresa** representa uma pessoa jurídica que precisa processar Documentos Fiscais. + * **Inscrição Municipal** representa os dados necessários sobre o cadastro municipal da empresa junto à prefeitura, exigidos para a emissão de notas fiscais de serviços (NFS-e) e para o cumprimento das obrigações tributárias no âmbito municipal. + */ + readonly put: { + readonly parameters: { + readonly query?: never; + readonly header?: never; + readonly path: { + /** @description ID da Empresa */ + readonly company_id: string; + /** @description ID da Inscrição Municipal que deverá ser retornado */ + readonly municipal_tax_id: string; + }; + readonly cookie?: never; + }; + /** @description Dados da Inscrição Municipal a ser alterada */ + readonly requestBody?: { + readonly content: { + readonly "application/json;odata.metadata=minimal;odata.streaming=true": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateMunicipalTaxResource"]; + readonly "application/json;odata.metadata=minimal;odata.streaming=false": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateMunicipalTaxResource"]; + readonly "application/json;odata.metadata=minimal": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateMunicipalTaxResource"]; + readonly "application/json;odata.metadata=full;odata.streaming=true": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateMunicipalTaxResource"]; + readonly "application/json;odata.metadata=full;odata.streaming=false": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateMunicipalTaxResource"]; + readonly "application/json;odata.metadata=full": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateMunicipalTaxResource"]; + readonly "application/json;odata.metadata=none;odata.streaming=true": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateMunicipalTaxResource"]; + readonly "application/json;odata.metadata=none;odata.streaming=false": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateMunicipalTaxResource"]; + readonly "application/json;odata.metadata=none": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateMunicipalTaxResource"]; + readonly "application/json;odata.streaming=true": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateMunicipalTaxResource"]; + readonly "application/json;odata.streaming=false": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateMunicipalTaxResource"]; + readonly "application/json": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateMunicipalTaxResource"]; + readonly "application/json;odata.metadata=minimal;odata.streaming=true;IEEE754Compatible=false": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateMunicipalTaxResource"]; + readonly "application/json;odata.metadata=minimal;odata.streaming=true;IEEE754Compatible=true": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateMunicipalTaxResource"]; + readonly "application/json;odata.metadata=minimal;odata.streaming=false;IEEE754Compatible=false": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateMunicipalTaxResource"]; + readonly "application/json;odata.metadata=minimal;odata.streaming=false;IEEE754Compatible=true": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateMunicipalTaxResource"]; + readonly "application/json;odata.metadata=minimal;IEEE754Compatible=false": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateMunicipalTaxResource"]; + readonly "application/json;odata.metadata=minimal;IEEE754Compatible=true": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateMunicipalTaxResource"]; + readonly "application/json;odata.metadata=full;odata.streaming=true;IEEE754Compatible=false": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateMunicipalTaxResource"]; + readonly "application/json;odata.metadata=full;odata.streaming=true;IEEE754Compatible=true": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateMunicipalTaxResource"]; + readonly "application/json;odata.metadata=full;odata.streaming=false;IEEE754Compatible=false": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateMunicipalTaxResource"]; + readonly "application/json;odata.metadata=full;odata.streaming=false;IEEE754Compatible=true": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateMunicipalTaxResource"]; + readonly "application/json;odata.metadata=full;IEEE754Compatible=false": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateMunicipalTaxResource"]; + readonly "application/json;odata.metadata=full;IEEE754Compatible=true": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateMunicipalTaxResource"]; + readonly "application/json;odata.metadata=none;odata.streaming=true;IEEE754Compatible=false": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateMunicipalTaxResource"]; + readonly "application/json;odata.metadata=none;odata.streaming=true;IEEE754Compatible=true": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateMunicipalTaxResource"]; + readonly "application/json;odata.metadata=none;odata.streaming=false;IEEE754Compatible=true": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateMunicipalTaxResource"]; + readonly "application/json;odata.metadata=none;odata.streaming=false;IEEE754Compatible=false": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateMunicipalTaxResource"]; + readonly "application/json;odata.metadata=none;IEEE754Compatible=false": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateMunicipalTaxResource"]; + readonly "application/json;odata.metadata=none;IEEE754Compatible=true": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateMunicipalTaxResource"]; + readonly "application/json;odata.streaming=true;IEEE754Compatible=false": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateMunicipalTaxResource"]; + readonly "application/json;odata.streaming=true;IEEE754Compatible=true": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateMunicipalTaxResource"]; + readonly "application/json;odata.streaming=false;IEEE754Compatible=false": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateMunicipalTaxResource"]; + readonly "application/json;odata.streaming=false;IEEE754Compatible=true": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateMunicipalTaxResource"]; + readonly "application/json;IEEE754Compatible=false": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateMunicipalTaxResource"]; + readonly "application/json;IEEE754Compatible=true": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateMunicipalTaxResource"]; + readonly "application/xml": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateMunicipalTaxResource"]; + readonly "text/plain": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateMunicipalTaxResource"]; + readonly "text/json": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateMunicipalTaxResource"]; + readonly "application/*+json": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateMunicipalTaxResource"]; + }; + }; + readonly responses: { + /** @description Sucesso na alteração da Inscrição Municipal */ + readonly 200: { + headers: { + readonly [name: string]: unknown; + }; + content: { + readonly "application/json": components["schemas"]["DFeTech.TaxPayers.Resources.MunicipalTaxResource"]; + }; + }; + /** @description Algum parametro informado não é válido, verificar resposta */ + readonly 400: { + headers: { + readonly [name: string]: unknown; + }; + content: { + readonly "application/json": components["schemas"]["DFeTech.TaxPayers.Resources.ErrorsResource"]; + }; + }; + /** @description Inscrição Municipal não encontrada */ + readonly 404: { + headers: { + readonly [name: string]: unknown; + }; + content: { + readonly "application/json": components["schemas"]["DFeTech.TaxPayers.Resources.ErrorsResource"]; + }; + }; + }; + }; + readonly post?: never; + /** + * Excluir uma Inscrição Municipal pelo ID + * @description ### Informações adicionais + * Utilize esta requisição para excluir uma Inscrição Municipal pelo ID, cuidado pois esse processo é irreversível. + * **Empresa** representa uma pessoa jurídica que precisa processar Documentos Fiscais. + * **Inscrição Municipal** representa os dados necessários sobre o cadastro municipal da empresa junto à prefeitura, exigidos para a emissão de notas fiscais de serviços (NFS-e) e para o cumprimento das obrigações tributárias no âmbito municipal. + */ + readonly delete: { + readonly parameters: { + readonly query?: never; + readonly header?: never; + readonly path: { + /** @description ID da Empresa */ + readonly company_id: string; + /** @description ID da Inscrição Municipal que deverá ser retornado */ + readonly municipal_tax_id: string; + }; + readonly cookie?: never; + }; + readonly requestBody?: never; + readonly responses: { + /** @description Sucesso na exclusão da Inscrição Municipal */ + readonly 204: { + headers: { + readonly [name: string]: unknown; + }; + content?: never; + }; + /** @description Algum parametro informado não é válido, verificar resposta */ + readonly 400: { + headers: { + readonly [name: string]: unknown; + }; + content: { + readonly "application/json": components["schemas"]["DFeTech.TaxPayers.Resources.ErrorsResource"]; + }; + }; + /** @description Inscrição Municipal não encontrada */ + readonly 404: { + headers: { + readonly [name: string]: unknown; + }; + content: { + readonly "application/json": components["schemas"]["DFeTech.TaxPayers.Resources.ErrorsResource"]; + }; + }; + }; + }; + readonly options?: never; + readonly head?: never; + readonly patch?: never; + readonly trace?: never; + }; + readonly "/v2/companies/{company_id}/municipaltaxes/{municipal_tax_id}/updateprefecture": { + readonly parameters: { + readonly query?: never; + readonly header?: never; + readonly path?: never; + readonly cookie?: never; + }; + readonly get?: never; + readonly put?: never; + readonly post?: never; + readonly delete?: never; + readonly options?: never; + readonly head?: never; + /** + * Atualizar status da prefeitura para uma Inscrição Municipal pelo ID + * @description ### Informações adicionais + * Utilize esta requisição para atualizar o status de uma prefeitura relacionada a uma Inscrição Municipal pelo ID. + * **Empresa** representa uma pessoa jurídica que precisa processar Documentos Fiscais. + * **Inscrição Municipal** representa os dados necessários sobre o cadastro municipal da empresa junto à prefeitura, exigidos para a emissão de notas fiscais de serviços (NFS-e) e para o cumprimento das obrigações tributárias no âmbito municipal. + */ + readonly patch: { + readonly parameters: { + readonly query?: never; + readonly header?: never; + readonly path: { + /** @description ID da Empresa */ + readonly company_id: string; + /** @description ID da Inscrição Municipal que deverá ser retornado */ + readonly municipal_tax_id: string; + }; + readonly cookie?: never; + }; + readonly requestBody?: never; + readonly responses: { + /** @description Sucesso na atualização do status da prefeitura da Inscrição Municipal */ + readonly 204: { + headers: { + readonly [name: string]: unknown; + }; + content?: never; + }; + /** @description Algum parametro informado não é válido, verificar resposta */ + readonly 400: { + headers: { + readonly [name: string]: unknown; + }; + content: { + readonly "application/json": components["schemas"]["DFeTech.TaxPayers.Resources.ErrorsResource"]; + }; + }; + /** @description Inscrição Municipal não encontrada */ + readonly 404: { + headers: { + readonly [name: string]: unknown; + }; + content: { + readonly "application/json": components["schemas"]["DFeTech.TaxPayers.Resources.ErrorsResource"]; + }; + }; + }; + }; + readonly trace?: never; + }; + readonly "/v2/companies/{company_id}/municipaltaxes/{municipal_tax_id}/series/{serie}": { + readonly parameters: { + readonly query?: never; + readonly header?: never; + readonly path?: never; + readonly cookie?: never; + }; + /** + * Consultar uma Serie pelo ID + * @description ### Informações adicionais + * Utilize esta requisição para consultar os dados de uma Serie de RPS. + * **Empresa** representa uma pessoa jurídica que precisa processar Documentos Fiscais. + * **Inscrição Municipal** representa os dados necessários sobre o cadastro municipal da empresa junto à prefeitura, exigidos para a emissão de notas fiscais de serviços (NFS-e) e para o cumprimento das obrigações tributárias no âmbito municipal. + * **Serie** representa a série do RPS (Recibo Provisório de Serviços) utilizada para a emissão de notas fiscais de serviços eletrônicas (NFS-e). + */ + readonly get: { + readonly parameters: { + readonly query?: never; + readonly header?: never; + readonly path: { + /** @description ID da Empresa */ + readonly company_id: string; + /** @description ID da Inscrição Mnicipal que deverá ser retornado */ + readonly municipal_tax_id: string; + /** @description Série do RPS que deverá ser retornado */ + readonly serie: string; + }; + readonly cookie?: never; + }; + readonly requestBody?: never; + readonly responses: { + /** @description Sucesso na consulta da Série */ + readonly 200: { + headers: { + readonly [name: string]: unknown; + }; + content: { + readonly "application/json": components["schemas"]["DFeTech.TaxPayers.Resources.SerieResource"]; + }; + }; + /** @description Algum parametro informado não é válido, verificar resposta */ + readonly 400: { + headers: { + readonly [name: string]: unknown; + }; + content: { + readonly "application/json": components["schemas"]["DFeTech.TaxPayers.Resources.ErrorsResource"]; + }; + }; + /** @description Inscrição Municipal não encontrada */ + readonly 404: { + headers: { + readonly [name: string]: unknown; + }; + content: { + readonly "application/json": components["schemas"]["DFeTech.TaxPayers.Resources.ErrorsResource"]; + }; + }; + }; + }; + readonly put?: never; + readonly post?: never; + readonly delete?: never; + readonly options?: never; + readonly head?: never; + readonly patch?: never; + readonly trace?: never; + }; + readonly "/v2/companies/{company_id}/statetaxes": { + readonly parameters: { + readonly query?: never; + readonly header?: never; + readonly path?: never; + readonly cookie?: never; + }; + /** + * Listar as Inscrições Estaduais + * @description ### Informações adicionais + * Utilize esta requisição para listar as inscrições estaduais na empresa para processar __Documentos Fiscais__. + * **Empresa** representa uma pessoa jurídica que precisa processar Documentos Fiscais. + * **Inscrição Estadual** representa os dados necessários sobre o cadastro Estadual (ICMS) que é preciso para processar Documentos Fiscais na SEFAZ. + */ + readonly get: { + readonly parameters: { + readonly query?: { + /** @description Id de início do contador (Default: Empty) */ + readonly startingAfter?: string; + /** @description Id final do contador (Default: Empty) */ + readonly endingBefore?: string; + /** @description Limite de resultados na página (Default: 10) */ + readonly limit?: number; + }; + readonly header?: never; + readonly path: { + /** @description ID da Empresa */ + readonly company_id: string; + }; + readonly cookie?: never; + }; + readonly requestBody?: never; + readonly responses: { + /** @description Sucesso na criação da Inscrição Estadual */ + readonly 200: { + headers: { + readonly [name: string]: unknown; + }; + content: { + readonly "application/json": components["schemas"]["DFeTech.TaxPayers.Resources.StateTaxesResource"]; + }; + }; + /** @description Algum parametro informado não é válido, verificar resposta */ + readonly 400: { + headers: { + readonly [name: string]: unknown; + }; + content: { + readonly "application/json": components["schemas"]["DFeTech.TaxPayers.Resources.ErrorsResource"]; + }; + }; + }; + }; + readonly put?: never; + /** + * Criar uma Inscrição Estadual + * @description ### Informações adicionais + * Utilize esta requisição para criar novas inscrição estadual na empresa para processar __Documentos Fiscais__. + * **Empresa** representa uma pessoa jurídica que precisa processar Documentos Fiscais. + * **Inscrição Estadual** representa os dados necessários sobre o cadastro Estadual (ICMS) que é preciso para processar Documentos Fiscais na SEFAZ. + */ + readonly post: { + readonly parameters: { + readonly query?: never; + readonly header?: never; + readonly path: { + /** @description ID da Empresa */ + readonly company_id: string; + }; + readonly cookie?: never; + }; + /** @description Dados da Inscrição Estadual a ser criada */ + readonly requestBody?: { + readonly content: { + readonly "application/json;odata.metadata=minimal;odata.streaming=true": components["schemas"]["DFeTech.TaxPayers.Resources.CreateStateTaxResource"]; + readonly "application/json;odata.metadata=minimal;odata.streaming=false": components["schemas"]["DFeTech.TaxPayers.Resources.CreateStateTaxResource"]; + readonly "application/json;odata.metadata=minimal": components["schemas"]["DFeTech.TaxPayers.Resources.CreateStateTaxResource"]; + readonly "application/json;odata.metadata=full;odata.streaming=true": components["schemas"]["DFeTech.TaxPayers.Resources.CreateStateTaxResource"]; + readonly "application/json;odata.metadata=full;odata.streaming=false": components["schemas"]["DFeTech.TaxPayers.Resources.CreateStateTaxResource"]; + readonly "application/json;odata.metadata=full": components["schemas"]["DFeTech.TaxPayers.Resources.CreateStateTaxResource"]; + readonly "application/json;odata.metadata=none;odata.streaming=true": components["schemas"]["DFeTech.TaxPayers.Resources.CreateStateTaxResource"]; + readonly "application/json;odata.metadata=none;odata.streaming=false": components["schemas"]["DFeTech.TaxPayers.Resources.CreateStateTaxResource"]; + readonly "application/json;odata.metadata=none": components["schemas"]["DFeTech.TaxPayers.Resources.CreateStateTaxResource"]; + readonly "application/json;odata.streaming=true": components["schemas"]["DFeTech.TaxPayers.Resources.CreateStateTaxResource"]; + readonly "application/json;odata.streaming=false": components["schemas"]["DFeTech.TaxPayers.Resources.CreateStateTaxResource"]; + readonly "application/json": components["schemas"]["DFeTech.TaxPayers.Resources.CreateStateTaxResource"]; + readonly "application/json;odata.metadata=minimal;odata.streaming=true;IEEE754Compatible=false": components["schemas"]["DFeTech.TaxPayers.Resources.CreateStateTaxResource"]; + readonly "application/json;odata.metadata=minimal;odata.streaming=true;IEEE754Compatible=true": components["schemas"]["DFeTech.TaxPayers.Resources.CreateStateTaxResource"]; + readonly "application/json;odata.metadata=minimal;odata.streaming=false;IEEE754Compatible=false": components["schemas"]["DFeTech.TaxPayers.Resources.CreateStateTaxResource"]; + readonly "application/json;odata.metadata=minimal;odata.streaming=false;IEEE754Compatible=true": components["schemas"]["DFeTech.TaxPayers.Resources.CreateStateTaxResource"]; + readonly "application/json;odata.metadata=minimal;IEEE754Compatible=false": components["schemas"]["DFeTech.TaxPayers.Resources.CreateStateTaxResource"]; + readonly "application/json;odata.metadata=minimal;IEEE754Compatible=true": components["schemas"]["DFeTech.TaxPayers.Resources.CreateStateTaxResource"]; + readonly "application/json;odata.metadata=full;odata.streaming=true;IEEE754Compatible=false": components["schemas"]["DFeTech.TaxPayers.Resources.CreateStateTaxResource"]; + readonly "application/json;odata.metadata=full;odata.streaming=true;IEEE754Compatible=true": components["schemas"]["DFeTech.TaxPayers.Resources.CreateStateTaxResource"]; + readonly "application/json;odata.metadata=full;odata.streaming=false;IEEE754Compatible=false": components["schemas"]["DFeTech.TaxPayers.Resources.CreateStateTaxResource"]; + readonly "application/json;odata.metadata=full;odata.streaming=false;IEEE754Compatible=true": components["schemas"]["DFeTech.TaxPayers.Resources.CreateStateTaxResource"]; + readonly "application/json;odata.metadata=full;IEEE754Compatible=false": components["schemas"]["DFeTech.TaxPayers.Resources.CreateStateTaxResource"]; + readonly "application/json;odata.metadata=full;IEEE754Compatible=true": components["schemas"]["DFeTech.TaxPayers.Resources.CreateStateTaxResource"]; + readonly "application/json;odata.metadata=none;odata.streaming=true;IEEE754Compatible=false": components["schemas"]["DFeTech.TaxPayers.Resources.CreateStateTaxResource"]; + readonly "application/json;odata.metadata=none;odata.streaming=true;IEEE754Compatible=true": components["schemas"]["DFeTech.TaxPayers.Resources.CreateStateTaxResource"]; + readonly "application/json;odata.metadata=none;odata.streaming=false;IEEE754Compatible=true": components["schemas"]["DFeTech.TaxPayers.Resources.CreateStateTaxResource"]; + readonly "application/json;odata.metadata=none;odata.streaming=false;IEEE754Compatible=false": components["schemas"]["DFeTech.TaxPayers.Resources.CreateStateTaxResource"]; + readonly "application/json;odata.metadata=none;IEEE754Compatible=false": components["schemas"]["DFeTech.TaxPayers.Resources.CreateStateTaxResource"]; + readonly "application/json;odata.metadata=none;IEEE754Compatible=true": components["schemas"]["DFeTech.TaxPayers.Resources.CreateStateTaxResource"]; + readonly "application/json;odata.streaming=true;IEEE754Compatible=false": components["schemas"]["DFeTech.TaxPayers.Resources.CreateStateTaxResource"]; + readonly "application/json;odata.streaming=true;IEEE754Compatible=true": components["schemas"]["DFeTech.TaxPayers.Resources.CreateStateTaxResource"]; + readonly "application/json;odata.streaming=false;IEEE754Compatible=false": components["schemas"]["DFeTech.TaxPayers.Resources.CreateStateTaxResource"]; + readonly "application/json;odata.streaming=false;IEEE754Compatible=true": components["schemas"]["DFeTech.TaxPayers.Resources.CreateStateTaxResource"]; + readonly "application/json;IEEE754Compatible=false": components["schemas"]["DFeTech.TaxPayers.Resources.CreateStateTaxResource"]; + readonly "application/json;IEEE754Compatible=true": components["schemas"]["DFeTech.TaxPayers.Resources.CreateStateTaxResource"]; + readonly "application/xml": components["schemas"]["DFeTech.TaxPayers.Resources.CreateStateTaxResource"]; + readonly "text/plain": components["schemas"]["DFeTech.TaxPayers.Resources.CreateStateTaxResource"]; + readonly "text/json": components["schemas"]["DFeTech.TaxPayers.Resources.CreateStateTaxResource"]; + readonly "application/*+json": components["schemas"]["DFeTech.TaxPayers.Resources.CreateStateTaxResource"]; + }; + }; + readonly responses: { + /** @description Sucesso na criação da Inscrição Estadual */ + readonly 200: { + headers: { + readonly [name: string]: unknown; + }; + content: { + readonly "application/json": components["schemas"]["DFeTech.TaxPayers.Resources.StateTaxResource"]; + }; + }; + /** @description Algum parametro informado não é válido, verificar resposta */ + readonly 400: { + headers: { + readonly [name: string]: unknown; + }; + content: { + readonly "application/json": components["schemas"]["DFeTech.TaxPayers.Resources.ErrorsResource"]; + }; + }; + }; + }; + readonly delete?: never; + readonly options?: never; + readonly head?: never; + readonly patch?: never; + readonly trace?: never; + }; + readonly "/v2/companies/{company_id}/statetaxes/{state_tax_id}": { + readonly parameters: { + readonly query?: never; + readonly header?: never; + readonly path?: never; + readonly cookie?: never; + }; + /** + * Consultar uma Inscrição Estadual pelo ID + * @description ### Informações adicionais + * Utilize esta requisição para consultar os dados de uma empresas pelo ID. + * **Empresa** representa uma pessoa jurídica que precisa processar Documentos Fiscais. + * **Inscrição Estadual** representa os dados necessários sobre o cadastro Estadual (ICMS) que é preciso para processar Documentos Fiscais na SEFAZ. + */ + readonly get: { + readonly parameters: { + readonly query?: never; + readonly header?: never; + readonly path: { + /** @description ID da Empresa */ + readonly company_id: string; + /** @description ID da Inscrição Estadual que deverá ser retornado */ + readonly state_tax_id: string; + }; + readonly cookie?: never; + }; + readonly requestBody?: never; + readonly responses: { + /** @description Sucesso na consulta da Inscrição Estadual */ + readonly 200: { + headers: { + readonly [name: string]: unknown; + }; + content: { + readonly "application/json": components["schemas"]["DFeTech.TaxPayers.Resources.StateTaxResource"]; + }; + }; + /** @description Algum parametro informado não é válido, verificar resposta */ + readonly 400: { + headers: { + readonly [name: string]: unknown; + }; + content: { + readonly "application/json": components["schemas"]["DFeTech.TaxPayers.Resources.ErrorsResource"]; + }; + }; + /** @description Inscrição Estadual não encontrada */ + readonly 404: { + headers: { + readonly [name: string]: unknown; + }; + content: { + readonly "application/json": components["schemas"]["DFeTech.TaxPayers.Resources.ErrorsResource"]; + }; + }; + }; + }; + /** + * Alterar uma Inscrição Estadual pelo ID + * @description ### Informações adicionais + * Utilize esta requisição para alterar os dados de uma Inscrição Estadual pelo ID. + * **Empresa** representa uma pessoa jurídica que precisa processar Documentos Fiscais. + * **Inscrição Estadual** representa os dados necessários sobre o cadastro Estadual (ICMS) que é preciso para processar Documentos Fiscais na SEFAZ. + */ + readonly put: { + readonly parameters: { + readonly query?: never; + readonly header?: never; + readonly path: { + /** @description ID da Empresa */ + readonly company_id: string; + /** @description ID da Inscrição Estadual que deverá ser retornado */ + readonly state_tax_id: string; + }; + readonly cookie?: never; + }; + /** @description Dados da Inscrição Estadual a ser alterada */ + readonly requestBody?: { + readonly content: { + readonly "application/json;odata.metadata=minimal;odata.streaming=true": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateStateTaxResource"]; + readonly "application/json;odata.metadata=minimal;odata.streaming=false": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateStateTaxResource"]; + readonly "application/json;odata.metadata=minimal": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateStateTaxResource"]; + readonly "application/json;odata.metadata=full;odata.streaming=true": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateStateTaxResource"]; + readonly "application/json;odata.metadata=full;odata.streaming=false": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateStateTaxResource"]; + readonly "application/json;odata.metadata=full": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateStateTaxResource"]; + readonly "application/json;odata.metadata=none;odata.streaming=true": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateStateTaxResource"]; + readonly "application/json;odata.metadata=none;odata.streaming=false": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateStateTaxResource"]; + readonly "application/json;odata.metadata=none": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateStateTaxResource"]; + readonly "application/json;odata.streaming=true": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateStateTaxResource"]; + readonly "application/json;odata.streaming=false": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateStateTaxResource"]; + readonly "application/json": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateStateTaxResource"]; + readonly "application/json;odata.metadata=minimal;odata.streaming=true;IEEE754Compatible=false": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateStateTaxResource"]; + readonly "application/json;odata.metadata=minimal;odata.streaming=true;IEEE754Compatible=true": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateStateTaxResource"]; + readonly "application/json;odata.metadata=minimal;odata.streaming=false;IEEE754Compatible=false": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateStateTaxResource"]; + readonly "application/json;odata.metadata=minimal;odata.streaming=false;IEEE754Compatible=true": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateStateTaxResource"]; + readonly "application/json;odata.metadata=minimal;IEEE754Compatible=false": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateStateTaxResource"]; + readonly "application/json;odata.metadata=minimal;IEEE754Compatible=true": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateStateTaxResource"]; + readonly "application/json;odata.metadata=full;odata.streaming=true;IEEE754Compatible=false": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateStateTaxResource"]; + readonly "application/json;odata.metadata=full;odata.streaming=true;IEEE754Compatible=true": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateStateTaxResource"]; + readonly "application/json;odata.metadata=full;odata.streaming=false;IEEE754Compatible=false": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateStateTaxResource"]; + readonly "application/json;odata.metadata=full;odata.streaming=false;IEEE754Compatible=true": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateStateTaxResource"]; + readonly "application/json;odata.metadata=full;IEEE754Compatible=false": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateStateTaxResource"]; + readonly "application/json;odata.metadata=full;IEEE754Compatible=true": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateStateTaxResource"]; + readonly "application/json;odata.metadata=none;odata.streaming=true;IEEE754Compatible=false": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateStateTaxResource"]; + readonly "application/json;odata.metadata=none;odata.streaming=true;IEEE754Compatible=true": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateStateTaxResource"]; + readonly "application/json;odata.metadata=none;odata.streaming=false;IEEE754Compatible=true": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateStateTaxResource"]; + readonly "application/json;odata.metadata=none;odata.streaming=false;IEEE754Compatible=false": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateStateTaxResource"]; + readonly "application/json;odata.metadata=none;IEEE754Compatible=false": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateStateTaxResource"]; + readonly "application/json;odata.metadata=none;IEEE754Compatible=true": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateStateTaxResource"]; + readonly "application/json;odata.streaming=true;IEEE754Compatible=false": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateStateTaxResource"]; + readonly "application/json;odata.streaming=true;IEEE754Compatible=true": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateStateTaxResource"]; + readonly "application/json;odata.streaming=false;IEEE754Compatible=false": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateStateTaxResource"]; + readonly "application/json;odata.streaming=false;IEEE754Compatible=true": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateStateTaxResource"]; + readonly "application/json;IEEE754Compatible=false": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateStateTaxResource"]; + readonly "application/json;IEEE754Compatible=true": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateStateTaxResource"]; + readonly "application/xml": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateStateTaxResource"]; + readonly "text/plain": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateStateTaxResource"]; + readonly "text/json": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateStateTaxResource"]; + readonly "application/*+json": components["schemas"]["DFeTech.TaxPayers.Resources.UpdateStateTaxResource"]; + }; + }; + readonly responses: { + /** @description Sucesso na alteração da Inscrição Estadual */ + readonly 200: { + headers: { + readonly [name: string]: unknown; + }; + content: { + readonly "application/json": components["schemas"]["DFeTech.TaxPayers.Resources.StateTaxResource"]; + }; + }; + /** @description Algum parametro informado não é válido, verificar resposta */ + readonly 400: { + headers: { + readonly [name: string]: unknown; + }; + content: { + readonly "application/json": components["schemas"]["DFeTech.TaxPayers.Resources.ErrorsResource"]; + }; + }; + /** @description Inscrição Estadual não encontrada */ + readonly 404: { + headers: { + readonly [name: string]: unknown; + }; + content: { + readonly "application/json": components["schemas"]["DFeTech.TaxPayers.Resources.ErrorsResource"]; + }; + }; + }; + }; + readonly post?: never; + /** + * Excluir uma Inscrição Estadual pelo ID + * @description ### Informações adicionais + * Utilize esta requisição para excluir uma Inscrição Estadual pelo ID, cuidado pois esse processo é irreversível. + * **Empresa** representa uma pessoa jurídica que precisa processar Documentos Fiscais. + * **Inscrição Estadual** representa os dados necessários sobre o cadastro Estadual (ICMS) que é preciso para processar Documentos Fiscais na SEFAZ. + */ + readonly delete: { + readonly parameters: { + readonly query?: never; + readonly header?: never; + readonly path: { + /** @description ID da Empresa */ + readonly company_id: string; + /** @description ID da Inscrição Estadual que deverá ser retornado */ + readonly state_tax_id: string; + }; + readonly cookie?: never; + }; + readonly requestBody?: never; + readonly responses: { + /** @description Sucesso na exclusão da Inscrição Estadual */ + readonly 204: { + headers: { + readonly [name: string]: unknown; + }; + content?: never; + }; + /** @description Algum parametro informado não é válido, verificar resposta */ + readonly 400: { + headers: { + readonly [name: string]: unknown; + }; + content: { + readonly "application/json": components["schemas"]["DFeTech.TaxPayers.Resources.ErrorsResource"]; + }; + }; + /** @description Inscrição Estadual não encontrada */ + readonly 404: { + headers: { + readonly [name: string]: unknown; + }; + content: { + readonly "application/json": components["schemas"]["DFeTech.TaxPayers.Resources.ErrorsResource"]; + }; + }; + }; + }; + readonly options?: never; + readonly head?: never; + readonly patch?: never; + readonly trace?: never; + }; + readonly "/v2/companies/{company_id}/statetaxes/{state_tax_id}/switch-authorizer": { + readonly parameters: { + readonly query?: never; + readonly header?: never; + readonly path?: never; + readonly cookie?: never; + }; + readonly get?: never; + readonly put?: never; + /** + * Alterar o ambiente de uma Inscrição Estadual pelo ID + * @description ### Informações adicionais + * Utilize esta requisição para definir o ambiente de autorização das Notas Fiscais de uma Inscrição Estadual pelo ID. + */ + readonly post: { + readonly parameters: { + readonly query?: never; + readonly header?: never; + readonly path: { + /** @description ID da Empresa */ + readonly company_id: string; + /** @description ID da Inscrição Estadual que deverá ser retornado */ + readonly state_tax_id: string; + }; + readonly cookie?: never; + }; + readonly requestBody?: { + readonly content: { + readonly "application/json;odata.metadata=minimal;odata.streaming=true": components["schemas"]["DFeTech.TaxPayers.Resources.StateTaxSwitchAuthorizerRequest"]; + readonly "application/json;odata.metadata=minimal;odata.streaming=false": components["schemas"]["DFeTech.TaxPayers.Resources.StateTaxSwitchAuthorizerRequest"]; + readonly "application/json;odata.metadata=minimal": components["schemas"]["DFeTech.TaxPayers.Resources.StateTaxSwitchAuthorizerRequest"]; + readonly "application/json;odata.metadata=full;odata.streaming=true": components["schemas"]["DFeTech.TaxPayers.Resources.StateTaxSwitchAuthorizerRequest"]; + readonly "application/json;odata.metadata=full;odata.streaming=false": components["schemas"]["DFeTech.TaxPayers.Resources.StateTaxSwitchAuthorizerRequest"]; + readonly "application/json;odata.metadata=full": components["schemas"]["DFeTech.TaxPayers.Resources.StateTaxSwitchAuthorizerRequest"]; + readonly "application/json;odata.metadata=none;odata.streaming=true": components["schemas"]["DFeTech.TaxPayers.Resources.StateTaxSwitchAuthorizerRequest"]; + readonly "application/json;odata.metadata=none;odata.streaming=false": components["schemas"]["DFeTech.TaxPayers.Resources.StateTaxSwitchAuthorizerRequest"]; + readonly "application/json;odata.metadata=none": components["schemas"]["DFeTech.TaxPayers.Resources.StateTaxSwitchAuthorizerRequest"]; + readonly "application/json;odata.streaming=true": components["schemas"]["DFeTech.TaxPayers.Resources.StateTaxSwitchAuthorizerRequest"]; + readonly "application/json;odata.streaming=false": components["schemas"]["DFeTech.TaxPayers.Resources.StateTaxSwitchAuthorizerRequest"]; + readonly "application/json": components["schemas"]["DFeTech.TaxPayers.Resources.StateTaxSwitchAuthorizerRequest"]; + readonly "application/json;odata.metadata=minimal;odata.streaming=true;IEEE754Compatible=false": components["schemas"]["DFeTech.TaxPayers.Resources.StateTaxSwitchAuthorizerRequest"]; + readonly "application/json;odata.metadata=minimal;odata.streaming=true;IEEE754Compatible=true": components["schemas"]["DFeTech.TaxPayers.Resources.StateTaxSwitchAuthorizerRequest"]; + readonly "application/json;odata.metadata=minimal;odata.streaming=false;IEEE754Compatible=false": components["schemas"]["DFeTech.TaxPayers.Resources.StateTaxSwitchAuthorizerRequest"]; + readonly "application/json;odata.metadata=minimal;odata.streaming=false;IEEE754Compatible=true": components["schemas"]["DFeTech.TaxPayers.Resources.StateTaxSwitchAuthorizerRequest"]; + readonly "application/json;odata.metadata=minimal;IEEE754Compatible=false": components["schemas"]["DFeTech.TaxPayers.Resources.StateTaxSwitchAuthorizerRequest"]; + readonly "application/json;odata.metadata=minimal;IEEE754Compatible=true": components["schemas"]["DFeTech.TaxPayers.Resources.StateTaxSwitchAuthorizerRequest"]; + readonly "application/json;odata.metadata=full;odata.streaming=true;IEEE754Compatible=false": components["schemas"]["DFeTech.TaxPayers.Resources.StateTaxSwitchAuthorizerRequest"]; + readonly "application/json;odata.metadata=full;odata.streaming=true;IEEE754Compatible=true": components["schemas"]["DFeTech.TaxPayers.Resources.StateTaxSwitchAuthorizerRequest"]; + readonly "application/json;odata.metadata=full;odata.streaming=false;IEEE754Compatible=false": components["schemas"]["DFeTech.TaxPayers.Resources.StateTaxSwitchAuthorizerRequest"]; + readonly "application/json;odata.metadata=full;odata.streaming=false;IEEE754Compatible=true": components["schemas"]["DFeTech.TaxPayers.Resources.StateTaxSwitchAuthorizerRequest"]; + readonly "application/json;odata.metadata=full;IEEE754Compatible=false": components["schemas"]["DFeTech.TaxPayers.Resources.StateTaxSwitchAuthorizerRequest"]; + readonly "application/json;odata.metadata=full;IEEE754Compatible=true": components["schemas"]["DFeTech.TaxPayers.Resources.StateTaxSwitchAuthorizerRequest"]; + readonly "application/json;odata.metadata=none;odata.streaming=true;IEEE754Compatible=false": components["schemas"]["DFeTech.TaxPayers.Resources.StateTaxSwitchAuthorizerRequest"]; + readonly "application/json;odata.metadata=none;odata.streaming=true;IEEE754Compatible=true": components["schemas"]["DFeTech.TaxPayers.Resources.StateTaxSwitchAuthorizerRequest"]; + readonly "application/json;odata.metadata=none;odata.streaming=false;IEEE754Compatible=true": components["schemas"]["DFeTech.TaxPayers.Resources.StateTaxSwitchAuthorizerRequest"]; + readonly "application/json;odata.metadata=none;odata.streaming=false;IEEE754Compatible=false": components["schemas"]["DFeTech.TaxPayers.Resources.StateTaxSwitchAuthorizerRequest"]; + readonly "application/json;odata.metadata=none;IEEE754Compatible=false": components["schemas"]["DFeTech.TaxPayers.Resources.StateTaxSwitchAuthorizerRequest"]; + readonly "application/json;odata.metadata=none;IEEE754Compatible=true": components["schemas"]["DFeTech.TaxPayers.Resources.StateTaxSwitchAuthorizerRequest"]; + readonly "application/json;odata.streaming=true;IEEE754Compatible=false": components["schemas"]["DFeTech.TaxPayers.Resources.StateTaxSwitchAuthorizerRequest"]; + readonly "application/json;odata.streaming=true;IEEE754Compatible=true": components["schemas"]["DFeTech.TaxPayers.Resources.StateTaxSwitchAuthorizerRequest"]; + readonly "application/json;odata.streaming=false;IEEE754Compatible=false": components["schemas"]["DFeTech.TaxPayers.Resources.StateTaxSwitchAuthorizerRequest"]; + readonly "application/json;odata.streaming=false;IEEE754Compatible=true": components["schemas"]["DFeTech.TaxPayers.Resources.StateTaxSwitchAuthorizerRequest"]; + readonly "application/json;IEEE754Compatible=false": components["schemas"]["DFeTech.TaxPayers.Resources.StateTaxSwitchAuthorizerRequest"]; + readonly "application/json;IEEE754Compatible=true": components["schemas"]["DFeTech.TaxPayers.Resources.StateTaxSwitchAuthorizerRequest"]; + readonly "application/xml": components["schemas"]["DFeTech.TaxPayers.Resources.StateTaxSwitchAuthorizerRequest"]; + readonly "text/plain": components["schemas"]["DFeTech.TaxPayers.Resources.StateTaxSwitchAuthorizerRequest"]; + readonly "text/json": components["schemas"]["DFeTech.TaxPayers.Resources.StateTaxSwitchAuthorizerRequest"]; + readonly "application/*+json": components["schemas"]["DFeTech.TaxPayers.Resources.StateTaxSwitchAuthorizerRequest"]; + }; + }; + readonly responses: { + /** @description OK */ + readonly 200: { + headers: { + readonly [name: string]: unknown; + }; + content: { + readonly "application/json": components["schemas"]["DFeTech.TaxPayers.Resources.StateTaxSwitchAuthorizerResponse"]; + }; + }; + /** @description Sucesso na exclusão da Inscrição Estadual */ + readonly 204: { + headers: { + readonly [name: string]: unknown; + }; + content?: never; + }; + /** @description Algum parâmetro informado não é válido, verificar resposta */ + readonly 400: { + headers: { + readonly [name: string]: unknown; + }; + content: { + readonly "application/json": components["schemas"]["DFeTech.TaxPayers.Resources.ErrorsResource"]; + }; + }; + /** @description Inscrição Estadual não encontrada */ + readonly 404: { + headers: { + readonly [name: string]: unknown; + }; + content: { + readonly "application/json": components["schemas"]["DFeTech.TaxPayers.Resources.ErrorsResource"]; + }; + }; + }; + }; + readonly delete?: never; + readonly options?: never; + readonly head?: never; + readonly patch?: never; + readonly trace?: never; + }; + readonly "/v1/companies/{companyIdOrTaxNumber}": { + readonly parameters: { + readonly query?: never; + readonly header?: never; + readonly path?: never; + readonly cookie?: never; + }; + /** Obter os detalhes de uma empresa */ + readonly get: { + readonly parameters: { + readonly query?: never; + readonly header?: never; + readonly path: { + /** @description ID da empresa ou Inscrição Federal (CNPJ) */ + readonly companyIdOrTaxNumber: string; + }; + readonly cookie?: never; + }; + readonly requestBody?: never; + readonly responses: { + /** @description Sucesso na requisição */ + readonly 200: { + headers: { + readonly [name: string]: unknown; + }; + content: { + readonly "application/json": components["schemas"]["DFeTech.TaxPayers.Resources.CompanySingleResourceV1"]; + }; + }; + /** @description Algum parametro informado não é válido */ + readonly 400: { + headers: { + readonly [name: string]: unknown; + }; + content?: never; + }; + /** @description API Key da conta não é valida */ + readonly 401: { + headers: { + readonly [name: string]: unknown; + }; + content?: never; + }; + /** @description Erro no processamento */ + readonly 500: { + headers: { + readonly [name: string]: unknown; + }; + content?: never; + }; + }; + }; + readonly put?: never; + readonly post?: never; + readonly delete?: never; + readonly options?: never; + readonly head?: never; + readonly patch?: never; + readonly trace?: never; + }; + readonly "/v1/companies": { + readonly parameters: { + readonly query?: never; + readonly header?: never; + readonly path?: never; + readonly cookie?: never; + }; + /** Obter os detalhes de todas as empresas da conta */ + readonly get: { + readonly parameters: { + readonly query?: { + readonly pageCount?: number; + readonly pageIndex?: number; + readonly search?: string; + }; + readonly header?: never; + readonly path?: never; + readonly cookie?: never; + }; + readonly requestBody?: never; + readonly responses: { + /** @description Sucesso na requisição */ + readonly 200: { + headers: { + readonly [name: string]: unknown; + }; + content: { + readonly "application/json": components["schemas"]["DFeTech.TaxPayers.Resources.CompanyCollectionResourceV1"]; + }; + }; + /** @description Algum parametro informado não é válido */ + readonly 400: { + headers: { + readonly [name: string]: unknown; + }; + content?: never; + }; + /** @description API Key da conta não é valida */ + readonly 401: { + headers: { + readonly [name: string]: unknown; + }; + content?: never; + }; + /** @description Erro no processamento */ + readonly 500: { + headers: { + readonly [name: string]: unknown; + }; + content?: never; + }; + }; + }; + readonly put?: never; + /** Criar uma empresa */ + readonly post: { + readonly parameters: { + readonly query?: never; + readonly header?: never; + readonly path?: never; + readonly cookie?: never; + }; + /** @description Dados da empresa */ + readonly requestBody?: { + readonly content: { + readonly "application/json": components["schemas"]["DFeTech.TaxPayers.Resources.CompanyResourceV1"]; + }; + }; + readonly responses: { + /** @description Sucesso na criação da empresa */ + readonly 201: { + headers: { + readonly [name: string]: unknown; + }; + content: { + readonly "application/json": components["schemas"]["DFeTech.TaxPayers.Resources.CompanySingleResourceV1"]; + }; + }; + /** @description Algum parametro informado não é válido */ + readonly 400: { + headers: { + readonly [name: string]: unknown; + }; + content?: never; + }; + /** @description API Key da conta não é valida */ + readonly 401: { + headers: { + readonly [name: string]: unknown; + }; + content?: never; + }; + /** @description Já existe uma empresa com o CNPJ informado */ + readonly 409: { + headers: { + readonly [name: string]: unknown; + }; + content?: never; + }; + /** @description Erro no processamento */ + readonly 500: { + headers: { + readonly [name: string]: unknown; + }; + content?: never; + }; + }; + }; + readonly delete?: never; + readonly options?: never; + readonly head?: never; + readonly patch?: never; + readonly trace?: never; + }; + readonly "/v1/companies/{companyId}": { + readonly parameters: { + readonly query?: never; + readonly header?: never; + readonly path?: never; + readonly cookie?: never; + }; + readonly get?: never; + /** Atualizar uma empresa */ + readonly put: { + readonly parameters: { + readonly query?: never; + readonly header?: never; + readonly path: { + /** @description ID da empresa */ + readonly companyId: string; + }; + readonly cookie?: never; + }; + /** @description Dados da empresa */ + readonly requestBody?: { + readonly content: { + readonly "application/json": components["schemas"]["DFeTech.TaxPayers.Resources.CompanyResourceV1"]; + }; + }; + readonly responses: { + /** @description Sucesso na atualização da empresa */ + readonly 200: { + headers: { + readonly [name: string]: unknown; + }; + content: { + readonly "application/json": components["schemas"]["DFeTech.TaxPayers.Resources.CompanySingleResourceV1"]; + }; + }; + /** @description Algum parametro informado não é válido */ + readonly 400: { + headers: { + readonly [name: string]: unknown; + }; + content?: never; + }; + /** @description API Key da conta não é valida */ + readonly 401: { + headers: { + readonly [name: string]: unknown; + }; + content?: never; + }; + /** @description Erro no processamento */ + readonly 500: { + headers: { + readonly [name: string]: unknown; + }; + content?: never; + }; + }; + }; + readonly post?: never; + /** Deletar uma empresa */ + readonly delete: { + readonly parameters: { + readonly query?: never; + readonly header?: never; + readonly path: { + /** @description ID da empresa */ + readonly companyId: string; + }; + readonly cookie?: never; + }; + readonly requestBody?: never; + readonly responses: { + /** @description Sucesso na deleção da empresa */ + readonly 204: { + headers: { + readonly [name: string]: unknown; + }; + content?: never; + }; + /** @description Algum parametro informado não é válido */ + readonly 400: { + headers: { + readonly [name: string]: unknown; + }; + content?: never; + }; + /** @description API Key da conta não é valida */ + readonly 401: { + headers: { + readonly [name: string]: unknown; + }; + content?: never; + }; + /** @description Erro no processamento */ + readonly 500: { + headers: { + readonly [name: string]: unknown; + }; + content?: never; + }; + }; + }; + readonly options?: never; + readonly head?: never; + readonly patch?: never; + readonly trace?: never; + }; +} +export type webhooks = Record; +export interface components { + schemas: { + readonly "DFeTech.TaxPayers.Domain.Entities.Address": { + readonly state?: string | null; + readonly city?: components["schemas"]["DFeTech.TaxPayers.Domain.Entities.CityBase"]; + readonly district?: string | null; + readonly additionalInformation?: string | null; + readonly streetPrefix?: string | null; + readonly street?: string | null; + readonly number?: string | null; + readonly postalCode?: string | null; + readonly country?: string | null; + }; + /** @enum {string} */ + readonly "DFeTech.TaxPayers.Domain.Entities.ApiEnvironment": "Development" | "Production" | "Staging"; + /** @enum {string} */ + readonly "DFeTech.TaxPayers.Domain.Entities.CertificateStatus": "None" | "Active" | "Inactive" | "Overdue" | "Pending"; + readonly "DFeTech.TaxPayers.Domain.Entities.CityBase": { + readonly code?: string | null; + readonly name?: string | null; + }; + readonly "DFeTech.TaxPayers.Domain.Entities.CityExtended": { + readonly code?: string | null; + readonly name?: string | null; + readonly country?: string | null; + readonly state?: string | null; + }; + /** @enum {string} */ + readonly "DFeTech.TaxPayers.Domain.Entities.CompanyFiscalStatus": "None" | "Active" | "CityNotSupported" | "Pending" | "Inactive"; + /** @enum {string} */ + readonly "DFeTech.TaxPayers.Domain.Entities.EnvironmentType": "None" | "Production" | "Test"; + /** @enum {string} */ + readonly "DFeTech.TaxPayers.Domain.Entities.FederalTaxDeterminationBy": "NotInformed" | "Default" | "SimplesNacional"; + /** @enum {string} */ + readonly "DFeTech.TaxPayers.Domain.Entities.LegalNature": "None" | "EmpresaPublica" | "SociedadeEconomiaMista" | "SociedadeAnonimaAberta" | "SociedadeAnonimaFechada" | "SociedadeEmpresariaLimitada" | "SociedadeEmpresariaEmNomeColetivo" | "SociedadeEmpresariaEmComanditaSimples" | "SociedadeEmpresariaEmComanditaporAcoes" | "SociedadeemContaParticipacao" | "Empresario" | "Cooperativa" | "ConsorcioSociedades" | "GrupoSociedades" | "SociedadeEstrangeiraNoBrasil" | "EmpresaBinacionalArgentinoBrasileira" | "EmpresaDomiciliadaExterior" | "ClubeFundoInvestimento" | "SociedadeSimplesPura" | "SociedadeSimplesLimitada" | "SociedadeSimplesEmNomeColetivo" | "SociedadeSimplesEmComanditaSimples" | "EmpresaBinacional" | "ConsorcioEmpregadores" | "ConsorcioSimples" | "EireliNaturezaEmpresaria" | "EireliNaturezaSimples" | "SociedadeUnipessoaldeAdvogados" | "CooperativaDeConsumo" | "EmpresaSimplesDeInovacao" | "InvestidorNaoResidente" | "ServicoNotarial" | "FundacaoPrivada" | "ServicoSocialAutonomo" | "CondominioEdilicio" | "ComissaoConciliacaoPrevia" | "EntidadeMediacaoArbitragem" | "PartidoPolitico" | "EntidadeSindical" | "EstabelecimentoBrasilFundacaoAssociacaoEstrangeiras" | "FundacaoAssociacaoDomiciliadaExterior" | "OrganizacaoReligiosa" | "ComunidadeIndigena" | "FundoPrivado" | "OrgaoDirecaoNacionalPartidoPolitico" | "OrgaoDirecaoRegionalPartidoPolitico" | "OrgaoDirecaoLocalPartidoPolitico" | "ComiteFinanceiroDePartidoPolitico" | "FrentePlebiscitariaOuReferendaria" | "OrganizacaoSocial" | "DemaisCondominios" | "PlanoBeneficiosPrevidenciaComplementarFechada" | "AssociacaoPrivada" | "EmpresaIndividualImobiliaria" | "SeguradoEspecial" | "ContribuinteIndividual" | "CandidatoCargoPoliticoEletivo" | "Leiloeiro" | "ProdutorRural" | "OrganizacaoInternacional" | "RepresentacaoDiplomaticaEstrangeira" | "OutrasInstituicoesExtraterritoriais"; + /** @enum {string} */ + readonly "DFeTech.TaxPayers.Domain.Entities.MunicipalTaxDeterminationBy": "NotInformed" | "Default" | "SimplesNacional"; + /** @enum {string} */ + readonly "DFeTech.TaxPayers.Domain.Entities.MunicipalTaxFiscalStatus": "None" | "Active" | "CityNotSupported" | "Pending" | "Inactive"; + readonly "DFeTech.TaxPayers.Domain.Entities.SecurityCredential": { + /** Format: int32 */ + readonly id?: number; + readonly code?: string | null; + }; + /** @enum {string} */ + readonly "DFeTech.TaxPayers.Domain.Entities.SpecialTaxRegime": "Nenhum" | "MicroempresaMunicipal" | "Estimativa" | "SociedadeDeProfissionais" | "Cooperativa" | "MicroempreendedorIndividual" | "MicroempresarioEmpresaPequenoPorte" | "Automatico"; + /** @enum {string} */ + readonly "DFeTech.TaxPayers.Domain.Entities.StateCode": "NA" | "RO" | "AC" | "AM" | "RR" | "PA" | "AP" | "TO" | "MA" | "PI" | "CE" | "RN" | "PB" | "PE" | "AL" | "SE" | "BA" | "MG" | "ES" | "RJ" | "SP" | "PR" | "SC" | "RS" | "MS" | "MT" | "GO" | "DF" | "EX"; + /** @enum {string} */ + readonly "DFeTech.TaxPayers.Domain.Entities.StateTaxProcessingAuthorizer": "Normal" | "EPEC"; + /** @enum {string} */ + readonly "DFeTech.TaxPayers.Domain.Entities.StateTaxType": "Default" | "NFe" | "NFCe"; + /** @enum {string} */ + readonly "DFeTech.TaxPayers.Domain.Entities.Status": "None" | "Active" | "Inactive"; + /** @enum {string} */ + readonly "DFeTech.TaxPayers.Domain.Entities.TaxRegime": "None" | "LucroReal" | "LucroPresumido" | "SimplesNacional" | "SimplesNacionalExcessoSublimite" | "MicroempreendedorIndividual" | "Isento"; + readonly "DFeTech.TaxPayers.Resources.AddressResource": { + /** @description Estado, ex.: SP, RJ, AC, padrão ISO 3166-2 ALFA 2. */ + readonly state: string; + readonly city: components["schemas"]["DFeTech.TaxPayers.Resources.CityBaseResource"]; + /** @description Bairro do Endereço */ + readonly district: string; + /** @description Complemento do Endereço, ex.: AP 2, BL A. */ + readonly additionalInformation?: string | null; + /** @description Logradouro do Endereço */ + readonly street: string; + /** @description Número do Endereço. Usar S/N para "sem número". */ + readonly number: string; + /** @description Cód. Endereço Postal (CEP) */ + readonly postalCode: string; + /** @description País, ex.: BRA, ARG, USA, padrão ISO 3166-1 ALFA-3. */ + readonly country: string; + }; + /** @description Certificado */ + readonly "DFeTech.TaxPayers.Resources.CertificateMetadataResource": { + readonly certificate?: components["schemas"]["DFeTech.TaxPayers.Resources.CertificateMetadataResourceItem"]; + }; + /** @description Certificado */ + readonly "DFeTech.TaxPayers.Resources.CertificateMetadataResourceItem": { + /** @description Identificador do contribuinte */ + readonly taxPayerId?: string | null; + /** @description A impressão digital do certificado */ + readonly thumbprint?: string | null; + /** @description Documento do contribuinte */ + readonly taxId?: string | null; + /** @description Nome do certificado (subject distinguished name) */ + readonly subject?: string | null; + /** + * Format: date-time + * @description Data no horário local após o qual um certificado não é mais válido + */ + readonly validUntil?: string; + /** + * Format: date-time + * @description Data de modificação + */ + readonly modifiedOn?: string | null; + readonly status?: components["schemas"]["DFeTech.TaxPayers.Domain.Entities.CertificateStatus"]; + }; + /** @description Certificado */ + readonly "DFeTech.TaxPayers.Resources.CertificatesMetadataResource": { + /** @description Certificado */ + readonly certificates?: readonly components["schemas"]["DFeTech.TaxPayers.Resources.CertificateMetadataResourceItem"][] | null; + }; + readonly "DFeTech.TaxPayers.Resources.CityBaseResource": { + /** @description Cód. do Município, segundo o Tabela de Municípios do IBGE */ + readonly code: string; + /** @description Nome do Município */ + readonly name: string; + }; + /** @description Empresas */ + readonly "DFeTech.TaxPayers.Resources.CompaniesResource": { + /** @description Lista de Empresa */ + readonly companies?: readonly components["schemas"]["DFeTech.TaxPayers.Resources.CompanyResourceItem"][] | null; + }; + readonly "DFeTech.TaxPayers.Resources.CompanyCertificateV1": { + /** @description Thumbprint certificado */ + readonly thumbprint?: string | null; + /** + * Format: date-time + * @description Certificado alterado em + */ + readonly modifiedOn?: string | null; + /** + * Format: date-time + * @description Certificado expira em + */ + readonly expiresOn?: string | null; + readonly status?: components["schemas"]["DFeTech.TaxPayers.Domain.Entities.CertificateStatus"]; + }; + readonly "DFeTech.TaxPayers.Resources.CompanyCollectionResourceV1": { + /** Format: int64 */ + readonly totalResults?: number | null; + /** Format: int32 */ + readonly totalPages?: number | null; + /** Format: int32 */ + readonly page?: number | null; + readonly companies?: readonly components["schemas"]["DFeTech.TaxPayers.Resources.CompanyResourceV1"][] | null; + }; + /** @description Empresa */ + readonly "DFeTech.TaxPayers.Resources.CompanyResource": { + readonly company?: components["schemas"]["DFeTech.TaxPayers.Resources.CompanyResourceItem"]; + }; + /** @description Dados da Empresa */ + readonly "DFeTech.TaxPayers.Resources.CompanyResourceItem": { + /** @description Razão Social */ + readonly name: string; + /** @description Identificador da conta */ + readonly accountId?: string | null; + /** @description Nome Fantasia */ + readonly tradeName?: string | null; + /** + * Format: int64 + * @description Número de Inscrição Federal (CNPJ) + */ + readonly federalTaxNumber: number; + /** @description Número da Inscrição Municipal */ + readonly municipalTaxNumber?: string | null; + readonly taxRegime: components["schemas"]["DFeTech.TaxPayers.Domain.Entities.TaxRegime"]; + readonly address: components["schemas"]["DFeTech.TaxPayers.Resources.AddressResource"]; + /** @description Identificador (gerado automaticamente) */ + readonly id?: string | null; + /** @description Lista de Inscrição Estadual */ + readonly stateTaxes?: readonly string[] | null; + /** @description Lista de Inscrição Municipal */ + readonly municipalTaxes?: readonly string[] | null; + readonly status?: components["schemas"]["DFeTech.TaxPayers.Domain.Entities.Status"]; + /** + * Format: date-time + * @description Data de criação + */ + readonly createdOn?: string; + /** + * Format: date-time + * @description Data de modificação + */ + readonly modifiedOn?: string | null; + }; + readonly "DFeTech.TaxPayers.Resources.CompanyResourceV1": { + /** @description Identificação */ + readonly id?: string | null; + /** @description Nome ou Razão Social */ + readonly name?: string | null; + /** @description Nome fantasia */ + readonly tradeName?: string | null; + /** + * Format: int64 + * @description CNPJ ou CPF + */ + readonly federalTaxNumber?: number | null; + /** @description Email */ + readonly email?: string | null; + readonly address?: components["schemas"]["DFeTech.TaxPayers.Domain.Entities.Address"]; + /** + * Format: date-time + * @description Data abertura da empresa + */ + readonly openningDate?: string | null; + readonly taxRegime?: components["schemas"]["DFeTech.TaxPayers.Domain.Entities.TaxRegime"]; + readonly specialTaxRegime?: components["schemas"]["DFeTech.TaxPayers.Domain.Entities.SpecialTaxRegime"]; + readonly legalNature?: components["schemas"]["DFeTech.TaxPayers.Domain.Entities.LegalNature"]; + /** + * Format: int64 + * @description Número de Inscricação na Junta Comercial + */ + readonly companyRegistryNumber?: number | null; + /** + * Format: int64 + * @description Número de Inscricação na SEFAZ (IE) + */ + readonly regionalTaxNumber?: number | null; + /** @description Número de Inscricação na Prefeitura (CCM) */ + readonly municipalTaxNumber?: string | null; + /** @description RPS número serie */ + readonly rpsSerialNumber?: string | null; + /** + * Format: int64 + * @description RPS número + */ + readonly rpsNumber?: number | null; + /** + * Format: int64 + * @description RPS número + */ + readonly lastRpsSent?: number | null; + /** + * Format: double + * @description Alíquota do ISS para Simples Nacional + */ + readonly issRate?: number | null; + readonly environment?: components["schemas"]["DFeTech.TaxPayers.Domain.Entities.ApiEnvironment"]; + readonly fiscalStatus?: components["schemas"]["DFeTech.TaxPayers.Domain.Entities.MunicipalTaxFiscalStatus"]; + readonly federalTaxDetermination?: components["schemas"]["DFeTech.TaxPayers.Domain.Entities.FederalTaxDeterminationBy"]; + readonly municipalTaxDetermination?: components["schemas"]["DFeTech.TaxPayers.Domain.Entities.MunicipalTaxDeterminationBy"]; + /** @description Login para acesso ao sistema */ + readonly loginName?: string | null; + /** @description Senha para acesso ao sistema */ + readonly loginPassword?: string | null; + /** @description Código de autenticação gerado pela prefeitura */ + readonly authIssueValue?: string | null; + readonly certificate?: components["schemas"]["DFeTech.TaxPayers.Resources.CompanyCertificateV1"]; + /** + * Format: date-time + * @description Data de criação + */ + readonly createdOn?: string | null; + /** + * Format: date-time + * @description Data da última modificação + */ + readonly modifiedOn?: string | null; + readonly status?: components["schemas"]["DFeTech.TaxPayers.Domain.Entities.Status"]; + }; + readonly "DFeTech.TaxPayers.Resources.CompanySingleResourceV1": { + readonly companies?: components["schemas"]["DFeTech.TaxPayers.Resources.CompanyResourceV1"]; + }; + /** @description Dados para Criar Empresa */ + readonly "DFeTech.TaxPayers.Resources.CreateCompanyResource": { + readonly company?: components["schemas"]["DFeTech.TaxPayers.Resources.CreateCompanyResourceItem"]; + }; + /** @description Criar Empresa */ + readonly "DFeTech.TaxPayers.Resources.CreateCompanyResourceItem": { + /** @description Razão Social */ + readonly name: string; + /** @description Identificador da conta */ + readonly accountId?: string | null; + /** @description Nome Fantasia */ + readonly tradeName?: string | null; + /** + * Format: int64 + * @description Número de Inscrição Federal (CNPJ) + */ + readonly federalTaxNumber: number; + /** @description Número da Inscrição Municipal */ + readonly municipalTaxNumber?: string | null; + readonly taxRegime: components["schemas"]["DFeTech.TaxPayers.Domain.Entities.TaxRegime"]; + readonly address: components["schemas"]["DFeTech.TaxPayers.Resources.AddressResource"]; + }; + readonly "DFeTech.TaxPayers.Resources.CreateMunicipalTaxResource": { + readonly municipalTax?: components["schemas"]["DFeTech.TaxPayers.Resources.CreateMunicipalTaxResourceItem"]; + }; + readonly "DFeTech.TaxPayers.Resources.CreateMunicipalTaxResourceItem": { + readonly city?: components["schemas"]["DFeTech.TaxPayers.Domain.Entities.CityExtended"]; + /** @description Inscrição Municipal */ + readonly taxNumber?: string | null; + readonly environment?: components["schemas"]["DFeTech.TaxPayers.Domain.Entities.ApiEnvironment"]; + readonly specialTaxRegime?: components["schemas"]["DFeTech.TaxPayers.Domain.Entities.SpecialTaxRegime"]; + /** @description Email */ + readonly email?: string | null; + readonly legalNature?: components["schemas"]["DFeTech.TaxPayers.Domain.Entities.LegalNature"]; + /** + * Format: int64 + * @description Registro da Empresa na Junta Comercial + */ + readonly companyRegistryNumber?: number | null; + /** + * Format: int64 + * @description Número Região + */ + readonly regionalTaxNumber?: number | null; + /** + * Format: double + * @description ALíquota do ISS (%) + */ + readonly issRate?: number | null; + readonly federalTaxDetermination?: components["schemas"]["DFeTech.TaxPayers.Domain.Entities.FederalTaxDeterminationBy"]; + readonly municipalTaxDetermination?: components["schemas"]["DFeTech.TaxPayers.Domain.Entities.MunicipalTaxDeterminationBy"]; + /** @description Login para acesso ao sistema */ + readonly loginName?: string | null; + /** @description Senha para acesso ao sistema */ + readonly loginPassword?: string | null; + /** @description Código de autenticação gerado pela prefeitura */ + readonly authIssueValue?: string | null; + /** + * Format: int64 + * @description Número do RPS + */ + readonly rpsNumber?: number; + /** + * Format: int64 + * @description Último RPS enviado + */ + readonly lastRpsSent?: number; + /** @description Série do RPS */ + readonly rpsSerialNumber?: string | null; + }; + readonly "DFeTech.TaxPayers.Resources.CreateStateTaxProcessingDetailsResource": { + readonly switchAuthorizerStrategy?: components["schemas"]["DFeTech.TaxPayers.Resources.StateTaxProcessingSwitchAuthorizerStrategyResource"]; + }; + readonly "DFeTech.TaxPayers.Resources.CreateStateTaxResource": { + readonly stateTax?: components["schemas"]["DFeTech.TaxPayers.Resources.CreateStateTaxResourceItem"]; + }; + readonly "DFeTech.TaxPayers.Resources.CreateStateTaxResourceItem": { + readonly code: components["schemas"]["DFeTech.TaxPayers.Domain.Entities.StateCode"]; + readonly environmentType?: components["schemas"]["DFeTech.TaxPayers.Domain.Entities.EnvironmentType"]; + /** @description Inscrição Estadual */ + readonly taxNumber: string; + readonly specialTaxRegime?: components["schemas"]["DFeTech.TaxPayers.Domain.Entities.SpecialTaxRegime"]; + /** + * Format: int32 + * @description Serie para a emissão NFe + */ + readonly serie?: number | null; + /** + * Format: int64 + * @description Número para a emissão NFe + */ + readonly number?: number | null; + readonly securityCredential?: components["schemas"]["DFeTech.TaxPayers.Domain.Entities.SecurityCredential"]; + readonly type: components["schemas"]["DFeTech.TaxPayers.Domain.Entities.StateTaxType"]; + readonly processingDetails?: components["schemas"]["DFeTech.TaxPayers.Resources.CreateStateTaxProcessingDetailsResource"]; + }; + /** @description Erro */ + readonly "DFeTech.TaxPayers.Resources.ErrorResource": { + /** + * Format: int32 + * @description Código do erro + */ + readonly code?: number | null; + /** @description Mensagem contendo os detalhes do erro */ + readonly message?: string | null; + }; + /** @description Lista de Erros */ + readonly "DFeTech.TaxPayers.Resources.ErrorsResource": { + /** @description Lista de Erros */ + readonly errors?: readonly components["schemas"]["DFeTech.TaxPayers.Resources.ErrorResource"][] | null; + }; + /** @description Inscrição Municipal */ + readonly "DFeTech.TaxPayers.Resources.MunicipalTaxResource": { + readonly municipalTax?: components["schemas"]["DFeTech.TaxPayers.Resources.MunicipalTaxResourceItem"]; + }; + /** @description Dados da Inscrição Municipal */ + readonly "DFeTech.TaxPayers.Resources.MunicipalTaxResourceItem": { + /** @description Identificador (gerado automaticamente) */ + readonly id?: string | null; + /** @description Código da Empresa */ + readonly companyId?: string | null; + /** @description Código da Conta */ + readonly accountId?: string | null; + readonly status?: components["schemas"]["DFeTech.TaxPayers.Domain.Entities.Status"]; + readonly city?: components["schemas"]["DFeTech.TaxPayers.Domain.Entities.CityExtended"]; + /** @description Inscrição Municipal */ + readonly taxNumber?: string | null; + readonly specialTaxRegime?: components["schemas"]["DFeTech.TaxPayers.Domain.Entities.SpecialTaxRegime"]; + /** @description Email */ + readonly email?: string | null; + readonly legalNature?: components["schemas"]["DFeTech.TaxPayers.Domain.Entities.LegalNature"]; + /** + * Format: int64 + * @description Registro da Empresa na Junta Comercial + */ + readonly companyRegistryNumber?: number | null; + /** + * Format: int64 + * @description Número Região + */ + readonly regionalTaxNumber?: number | null; + /** @description Série do RPS */ + readonly rpsSerialNumber?: string | null; + /** + * Format: int64 + * @description Número do RPS + */ + readonly rpsNumber?: number; + /** + * Format: int64 + * @description Número do último RPS + */ + readonly lastRpsSent?: number; + /** + * Format: double + * @description ALíquota do ISS (%) + */ + readonly issRate?: number | null; + readonly environment?: components["schemas"]["DFeTech.TaxPayers.Domain.Entities.ApiEnvironment"]; + readonly fiscalStatus?: components["schemas"]["DFeTech.TaxPayers.Domain.Entities.CompanyFiscalStatus"]; + readonly federalTaxDetermination?: components["schemas"]["DFeTech.TaxPayers.Domain.Entities.FederalTaxDeterminationBy"]; + readonly municipalTaxDetermination?: components["schemas"]["DFeTech.TaxPayers.Domain.Entities.MunicipalTaxDeterminationBy"]; + /** @description Login para acesso ao sistema */ + readonly loginName?: string | null; + /** @description Senha para acesso ao sistema */ + readonly loginPassword?: string | null; + /** @description Código de autenticação gerado pela prefeitura */ + readonly authIssueValue?: string | null; + /** @description Todas as séries para esta Inscrição Municipal */ + readonly rpsSerialNumbers?: readonly string[] | null; + }; + readonly "DFeTech.TaxPayers.Resources.SerieResource": { + readonly serie?: components["schemas"]["DFeTech.TaxPayers.Resources.SerieResourceItem"]; + }; + /** @description Dados da Serie */ + readonly "DFeTech.TaxPayers.Resources.SerieResourceItem": { + /** + * Format: int64 + * @description Próximo número de RPS + */ + readonly rpsNumber?: number; + /** + * Format: int64 + * @description Último RPS enviado + */ + readonly lastRpsSent?: number; + }; + /** @enum {string} */ + readonly "DFeTech.TaxPayers.Resources.StateTaxProcessingAuthorizerResource": "Normal" | "EPEC"; + /** @enum {string} */ + readonly "DFeTech.TaxPayers.Resources.StateTaxProcessingSwitchAuthorizerStrategyResource": "Manual" | "StateTaxAuthorityStatusUnavailable"; + /** @description Inscrição Estadual */ + readonly "DFeTech.TaxPayers.Resources.StateTaxResource": { + readonly stateTax?: components["schemas"]["DFeTech.TaxPayers.Resources.StateTaxResourceItem"]; + }; + /** @description Dados da Inscrição Estadual */ + readonly "DFeTech.TaxPayers.Resources.StateTaxResourceItem": { + readonly code: components["schemas"]["DFeTech.TaxPayers.Domain.Entities.StateCode"]; + readonly environmentType?: components["schemas"]["DFeTech.TaxPayers.Domain.Entities.EnvironmentType"]; + /** @description Inscrição Estadual */ + readonly taxNumber: string; + readonly specialTaxRegime?: components["schemas"]["DFeTech.TaxPayers.Domain.Entities.SpecialTaxRegime"]; + /** + * Format: int32 + * @description Serie para a emissão NFe + */ + readonly serie?: number | null; + /** + * Format: int64 + * @description Número para a emissão NFe + */ + readonly number?: number | null; + readonly securityCredential?: components["schemas"]["DFeTech.TaxPayers.Domain.Entities.SecurityCredential"]; + readonly type: components["schemas"]["DFeTech.TaxPayers.Domain.Entities.StateTaxType"]; + readonly processingDetails?: components["schemas"]["DFeTech.TaxPayers.Resources.CreateStateTaxProcessingDetailsResource"]; + /** @description Identificador (gerado automaticamente) */ + readonly id?: string | null; + /** @description Código da Empresa */ + readonly companyId?: string | null; + /** @description Código da Conta */ + readonly accountId?: string | null; + readonly status?: components["schemas"]["DFeTech.TaxPayers.Domain.Entities.Status"]; + /** @description Todas as séries para esta Inscrição Estadual */ + readonly series?: readonly number[] | null; + /** + * Format: date-time + * @description Data de criação + */ + readonly createdOn?: string; + /** + * Format: date-time + * @description Data de modificação + */ + readonly modifiedOn?: string | null; + }; + readonly "DFeTech.TaxPayers.Resources.StateTaxSwitchAuthorizerRequest": { + readonly authorizer?: components["schemas"]["DFeTech.TaxPayers.Resources.StateTaxProcessingAuthorizerResource"]; + /** @description Motivo da contingência */ + readonly reason?: string | null; + }; + readonly "DFeTech.TaxPayers.Resources.StateTaxSwitchAuthorizerResponse": { + readonly fromAuthorizer?: components["schemas"]["DFeTech.TaxPayers.Domain.Entities.StateTaxProcessingAuthorizer"]; + readonly toAuthorizer?: components["schemas"]["DFeTech.TaxPayers.Domain.Entities.StateTaxProcessingAuthorizer"]; + /** @description Motivo da contingência */ + readonly reason?: string | null; + /** + * Format: date-time + * @description Data e hora da modificação + */ + readonly modifiedOn?: string; + }; + /** @description Inscrições Estaduais */ + readonly "DFeTech.TaxPayers.Resources.StateTaxesResource": { + /** @description Lista de Inscrições Estaduais */ + readonly stateTaxes?: readonly components["schemas"]["DFeTech.TaxPayers.Resources.StateTaxResourceItem"][] | null; + }; + /** @description Dados para Alterar Empresa */ + readonly "DFeTech.TaxPayers.Resources.UpdateCompanyResource": { + readonly company?: components["schemas"]["DFeTech.TaxPayers.Resources.UpdateCompanyResourceItem"]; + }; + /** @description Alterar Empresa */ + readonly "DFeTech.TaxPayers.Resources.UpdateCompanyResourceItem": { + /** @description Razão Social */ + readonly name: string; + /** @description Identificador da conta */ + readonly accountId?: string | null; + /** @description Nome Fantasia */ + readonly tradeName?: string | null; + /** + * Format: int64 + * @description Número de Inscrição Federal (CNPJ) + */ + readonly federalTaxNumber: number; + /** @description Número da Inscrição Municipal */ + readonly municipalTaxNumber?: string | null; + readonly taxRegime: components["schemas"]["DFeTech.TaxPayers.Domain.Entities.TaxRegime"]; + readonly address: components["schemas"]["DFeTech.TaxPayers.Resources.AddressResource"]; + /** @description Identificador (gerado automaticamente) */ + readonly id?: string | null; + }; + readonly "DFeTech.TaxPayers.Resources.UpdateMunicipalTaxResource": { + readonly municipalTax?: components["schemas"]["DFeTech.TaxPayers.Resources.UpdateMunicipalTaxResourceItem"]; + }; + readonly "DFeTech.TaxPayers.Resources.UpdateMunicipalTaxResourceItem": { + readonly city?: components["schemas"]["DFeTech.TaxPayers.Domain.Entities.CityExtended"]; + /** @description Inscrição Municipal */ + readonly taxNumber?: string | null; + readonly specialTaxRegime?: components["schemas"]["DFeTech.TaxPayers.Domain.Entities.SpecialTaxRegime"]; + /** @description Email */ + readonly email?: string | null; + readonly legalNature?: components["schemas"]["DFeTech.TaxPayers.Domain.Entities.LegalNature"]; + /** + * Format: int64 + * @description Registro da Empresa na Junta Comercial + */ + readonly companyRegistryNumber?: number | null; + /** + * Format: int64 + * @description Número Região + */ + readonly regionalTaxNumber?: number | null; + /** + * Format: int64 + * @description Número do RPS + */ + readonly rpsNumber?: number; + /** + * Format: int64 + * @description Número do último RPS + */ + readonly lastRpsSent?: number; + /** + * Format: double + * @description ALíquota do ISS (%) + */ + readonly issRate?: number | null; + readonly federalTaxDetermination?: components["schemas"]["DFeTech.TaxPayers.Domain.Entities.FederalTaxDeterminationBy"]; + readonly municipalTaxDetermination?: components["schemas"]["DFeTech.TaxPayers.Domain.Entities.MunicipalTaxDeterminationBy"]; + /** @description Login para acesso ao sistema */ + readonly loginName?: string | null; + /** @description Senha para acesso ao sistema */ + readonly loginPassword?: string | null; + /** @description Código de autenticação gerado pela prefeitura */ + readonly authIssueValue?: string | null; + readonly environment?: components["schemas"]["DFeTech.TaxPayers.Domain.Entities.ApiEnvironment"]; + readonly rpsSerialNumber?: string | null; + }; + readonly "DFeTech.TaxPayers.Resources.UpdateStateTaxResource": { + readonly stateTax?: components["schemas"]["DFeTech.TaxPayers.Resources.UpdateStateTaxResourceItem"]; + }; + readonly "DFeTech.TaxPayers.Resources.UpdateStateTaxResourceItem": { + readonly code: components["schemas"]["DFeTech.TaxPayers.Domain.Entities.StateCode"]; + readonly environmentType?: components["schemas"]["DFeTech.TaxPayers.Domain.Entities.EnvironmentType"]; + /** @description Inscrição Estadual */ + readonly taxNumber: string; + readonly specialTaxRegime?: components["schemas"]["DFeTech.TaxPayers.Domain.Entities.SpecialTaxRegime"]; + /** + * Format: int32 + * @description Serie para a emissão NFe + */ + readonly serie?: number | null; + /** + * Format: int64 + * @description Número para a emissão NFe + */ + readonly number?: number | null; + readonly securityCredential?: components["schemas"]["DFeTech.TaxPayers.Domain.Entities.SecurityCredential"]; + readonly type: components["schemas"]["DFeTech.TaxPayers.Domain.Entities.StateTaxType"]; + readonly processingDetails?: components["schemas"]["DFeTech.TaxPayers.Resources.CreateStateTaxProcessingDetailsResource"]; + }; + /** @enum {string} */ + readonly "Microsoft.OData.Edm.EdmContainerElementKind": "None" | "EntitySet" | "ActionImport" | "FunctionImport" | "Singleton"; + /** @enum {string} */ + readonly "Microsoft.OData.Edm.EdmExpressionKind": "None" | "BinaryConstant" | "BooleanConstant" | "DateTimeOffsetConstant" | "DecimalConstant" | "FloatingConstant" | "GuidConstant" | "IntegerConstant" | "StringConstant" | "DurationConstant" | "Null" | "Record" | "Collection" | "Path" | "If" | "Cast" | "IsOf" | "FunctionApplication" | "LabeledExpressionReference" | "Labeled" | "PropertyPath" | "NavigationPropertyPath" | "DateConstant" | "TimeOfDayConstant" | "EnumMember" | "AnnotationPath"; + /** @enum {string} */ + readonly "Microsoft.OData.Edm.EdmSchemaElementKind": "None" | "TypeDefinition" | "Term" | "Action" | "EntityContainer" | "Function"; + /** @enum {string} */ + readonly "Microsoft.OData.Edm.EdmTypeKind": "None" | "Primitive" | "Entity" | "Complex" | "Collection" | "EntityReference" | "Enum" | "TypeDefinition" | "Untyped" | "Path"; + readonly "Microsoft.OData.Edm.IEdmEntityContainer": { + readonly name?: string | null; + readonly schemaElementKind?: components["schemas"]["Microsoft.OData.Edm.EdmSchemaElementKind"]; + readonly namespace?: string | null; + readonly elements?: readonly components["schemas"]["Microsoft.OData.Edm.IEdmEntityContainerElement"][] | null; + }; + readonly "Microsoft.OData.Edm.IEdmEntityContainerElement": { + readonly name?: string | null; + readonly containerElementKind?: components["schemas"]["Microsoft.OData.Edm.EdmContainerElementKind"]; + readonly container?: components["schemas"]["Microsoft.OData.Edm.IEdmEntityContainer"]; + }; + readonly "Microsoft.OData.Edm.IEdmExpression": { + readonly expressionKind?: components["schemas"]["Microsoft.OData.Edm.EdmExpressionKind"]; + }; + readonly "Microsoft.OData.Edm.IEdmModel": { + readonly schemaElements?: readonly components["schemas"]["Microsoft.OData.Edm.IEdmSchemaElement"][] | null; + readonly vocabularyAnnotations?: readonly components["schemas"]["Microsoft.OData.Edm.Vocabularies.IEdmVocabularyAnnotation"][] | null; + readonly referencedModels?: readonly components["schemas"]["Microsoft.OData.Edm.IEdmModel"][] | null; + readonly declaredNamespaces?: readonly string[] | null; + readonly directValueAnnotationsManager?: components["schemas"]["Microsoft.OData.Edm.Vocabularies.IEdmDirectValueAnnotationsManager"]; + readonly entityContainer?: components["schemas"]["Microsoft.OData.Edm.IEdmEntityContainer"]; + }; + readonly "Microsoft.OData.Edm.IEdmSchemaElement": { + readonly name?: string | null; + readonly schemaElementKind?: components["schemas"]["Microsoft.OData.Edm.EdmSchemaElementKind"]; + readonly namespace?: string | null; + }; + readonly "Microsoft.OData.Edm.IEdmType": { + readonly typeKind?: components["schemas"]["Microsoft.OData.Edm.EdmTypeKind"]; + }; + readonly "Microsoft.OData.Edm.IEdmTypeReference": { + readonly isNullable?: boolean; + readonly definition?: components["schemas"]["Microsoft.OData.Edm.IEdmType"]; + }; + readonly "Microsoft.OData.Edm.Vocabularies.IEdmDirectValueAnnotationsManager": Record; + readonly "Microsoft.OData.Edm.Vocabularies.IEdmTerm": { + readonly name?: string | null; + readonly schemaElementKind?: components["schemas"]["Microsoft.OData.Edm.EdmSchemaElementKind"]; + readonly namespace?: string | null; + readonly type?: components["schemas"]["Microsoft.OData.Edm.IEdmTypeReference"]; + readonly appliesTo?: string | null; + readonly defaultValue?: string | null; + }; + readonly "Microsoft.OData.Edm.Vocabularies.IEdmVocabularyAnnotatable": Record; + readonly "Microsoft.OData.Edm.Vocabularies.IEdmVocabularyAnnotation": { + readonly qualifier?: string | null; + readonly term?: components["schemas"]["Microsoft.OData.Edm.Vocabularies.IEdmTerm"]; + readonly target?: components["schemas"]["Microsoft.OData.Edm.Vocabularies.IEdmVocabularyAnnotatable"]; + readonly value?: components["schemas"]["Microsoft.OData.Edm.IEdmExpression"]; + readonly usesDefault?: boolean; + }; + readonly "Microsoft.OData.ODataEntitySetInfo": { + readonly typeAnnotation?: components["schemas"]["Microsoft.OData.ODataTypeAnnotation"]; + /** Format: uri */ + readonly url?: string | null; + readonly name?: string | null; + readonly title?: string | null; + }; + readonly "Microsoft.OData.ODataFunctionImportInfo": { + readonly typeAnnotation?: components["schemas"]["Microsoft.OData.ODataTypeAnnotation"]; + /** Format: uri */ + readonly url?: string | null; + readonly name?: string | null; + readonly title?: string | null; + }; + readonly "Microsoft.OData.ODataServiceDocument": { + readonly typeAnnotation?: components["schemas"]["Microsoft.OData.ODataTypeAnnotation"]; + readonly entitySets?: readonly components["schemas"]["Microsoft.OData.ODataEntitySetInfo"][] | null; + readonly singletons?: readonly components["schemas"]["Microsoft.OData.ODataSingletonInfo"][] | null; + readonly functionImports?: readonly components["schemas"]["Microsoft.OData.ODataFunctionImportInfo"][] | null; + }; + readonly "Microsoft.OData.ODataSingletonInfo": { + readonly typeAnnotation?: components["schemas"]["Microsoft.OData.ODataTypeAnnotation"]; + /** Format: uri */ + readonly url?: string | null; + readonly name?: string | null; + readonly title?: string | null; + }; + readonly "Microsoft.OData.ODataTypeAnnotation": { + readonly typeName?: string | null; + }; + }; + responses: never; + parameters: never; + requestBodies: never; + headers: never; + pathItems: never; +} +export type $defs = Record; +export type operations = Record; diff --git a/src/generated/index.ts b/src/generated/index.ts index 9e1c647..55da5fe 100644 --- a/src/generated/index.ts +++ b/src/generated/index.ts @@ -5,7 +5,7 @@ * Types are namespaced by spec to avoid conflicts. * * @generated - * Last updated: 2026-04-25T23:27:49.313Z + * Last updated: 2026-06-27T15:46:54.263Z */ // ============================================================================ @@ -15,49 +15,20 @@ export * as CalculoImpostos from './calculo-impostos-v1.js'; export * as ConsultaCte from './consulta-cte-v2.js'; export * as ConsultaNfeDistribuicao from './consulta-nfe-distribuicao-v1.js'; +export * as Contribuintes from './contribuintes-v2.js'; export * as NfConsumidor from './nf-consumidor-v2.js'; export * as NfProduto from './nf-produto-v2.js'; export * as NfServico from './nf-servico-v1.js'; export * as Nfeio from './nfeio.js'; +export * as ProductInvoiceRtc from './product-invoice-rtc-v1.js'; +export * as ServiceInvoiceRtc from './service-invoice-rtc-v1.js'; // ============================================================================ // Convenience Type Aliases // ============================================================================ -// Common types from main spec (nf-servico-v1) -// Use these for convenience, or use namespaced versions for specificity - -// Since OpenAPI specs don't have separate schemas (schemas: never), -// we define minimal types here for backward compatibility -// These are placeholders - real API responses may have more fields - -export interface ServiceInvoice { - id?: string; - flowStatus?: string; - status?: string; - [key: string]: unknown; -} - -export interface Company { - id?: string; - federalTaxNumber?: number; - name?: string; - [key: string]: unknown; -} - -export interface LegalPerson { - id?: string; - federalTaxNumber?: string | number; - name?: string; - [key: string]: unknown; -} - -export interface NaturalPerson { - id?: string; - federalTaxNumber?: string | number; - name?: string; - [key: string]: unknown; -} +// Domain types are defined in src/core/types.ts (the public surface). +// Main spec namespace: NfServico. Use namespaced generated types for raw operations/paths. // ============================================================================ // Backward Compatibility diff --git a/src/generated/nf-consumidor-v2.ts b/src/generated/nf-consumidor-v2.ts index 4cabdce..575a7a7 100644 --- a/src/generated/nf-consumidor-v2.ts +++ b/src/generated/nf-consumidor-v2.ts @@ -4,7 +4,7 @@ * Do not edit this file directly. * * To regenerate: npm run generate - * Last generated: 2026-04-25T23:27:49.118Z + * Last generated: 2026-06-27T15:46:54.008Z * Generator: openapi-typescript */ diff --git a/src/generated/nf-produto-v2.ts b/src/generated/nf-produto-v2.ts index 407ab8c..24df3b5 100644 --- a/src/generated/nf-produto-v2.ts +++ b/src/generated/nf-produto-v2.ts @@ -4,7 +4,7 @@ * Do not edit this file directly. * * To regenerate: npm run generate - * Last generated: 2026-04-25T23:27:49.229Z + * Last generated: 2026-06-27T15:46:54.094Z * Generator: openapi-typescript */ diff --git a/src/generated/nf-servico-v1.ts b/src/generated/nf-servico-v1.ts index ce321bc..a99a507 100644 --- a/src/generated/nf-servico-v1.ts +++ b/src/generated/nf-servico-v1.ts @@ -4,7 +4,7 @@ * Do not edit this file directly. * * To regenerate: npm run generate - * Last generated: 2026-04-25T23:27:49.297Z + * Last generated: 2026-06-27T15:46:54.166Z * Generator: openapi-typescript */ diff --git a/src/generated/nfeio.ts b/src/generated/nfeio.ts index f84588c..fc042d8 100644 --- a/src/generated/nfeio.ts +++ b/src/generated/nfeio.ts @@ -4,7 +4,7 @@ * Do not edit this file directly. * * To regenerate: npm run generate - * Last generated: 2026-04-25T23:27:49.313Z + * Last generated: 2026-06-27T15:46:54.177Z * Generator: openapi-typescript */ diff --git a/src/generated/product-invoice-rtc-v1.ts b/src/generated/product-invoice-rtc-v1.ts new file mode 100644 index 0000000..7b88e2f --- /dev/null +++ b/src/generated/product-invoice-rtc-v1.ts @@ -0,0 +1,3275 @@ +/** + * ⚠️ AUTO-GENERATED from product-invoice-rtc-v1.yaml + * + * Do not edit this file directly. + * + * To regenerate: npm run generate + * Last generated: 2026-06-27T15:46:54.234Z + * Generator: openapi-typescript + */ + +export interface paths { + readonly "/v2/companies/:companyId/productinvoices": { + readonly parameters: { + readonly query?: never; + readonly header?: never; + readonly path?: never; + readonly cookie?: never; + }; + readonly get?: never; + readonly put?: never; + /** + * Envio de dados para emissão de NF-e/NFC-e + * @description Envia os dados para a criação e autorização de uma nova Nota Fiscal de Produto. + */ + readonly post: operations["createProductInvoice"]; + readonly delete?: never; + readonly options?: never; + readonly head?: never; + readonly patch?: never; + readonly trace?: never; + }; +} +export type webhooks = Record; +export interface components { + schemas: { + /** + * Nota Fiscal de Produto (NFe) Schema + * @description Schema para validar o objeto ProductInvoiceRequest da API + */ + readonly ProductInvoiceRequest: { + /** @description Identificador único */ + readonly id?: string | null; + /** @description Grupo de Formas de Pagamento (pag) */ + readonly payment: readonly components["schemas"]["PaymentResource"][]; + /** + * Format: int32 + * @description Série do Documento Fiscal (serie) + */ + readonly serie?: number | null; + /** + * Format: int64 + * @description Número do Documento Fiscal (nNF) + */ + readonly number?: number | null; + /** + * Format: date-time + * @description Data e Hora de Saída ou da Entrada da Mercadoria/Produto (dhSaiEnt) + * + * Data e hora no formato UTC (Universal Coordinated Time): AAAA-MM-DDThh:mm:ssTZD. + * + */ + readonly operationOn?: string | null; + /** + * Format: date + * @description Data da previsão de entrega ou disponibilização do bem (dPrevEntrega). Formato: “AAAA-MM-DD”. Observação: Não informar este campo para a NFC-e. + */ + readonly expectedDeliveryOn?: string | null; + /** @description Descrição da Natureza da Operação (natOp) */ + readonly operationNature?: string | null; + /** @description Tipo do Documento Fiscal (tpNF) */ + readonly operationType?: components["schemas"]["OperationType"]; + /** @description Identificador de Local de destino da operação (idDest) */ + readonly destination?: components["schemas"]["Destination"]; + /** @description Código do Município de Ocorrência do Fato Gerador (cMunFG) */ + readonly consumptionCityCode?: components["schemas"]["ConsumptionCityCode"]; + /** @description Município de ocorrência do fato gerador do IBS/CBS (cMunFGIBS) */ + readonly ibsConsumptionCityCode?: components["schemas"]["IbsConsumptionCityCode"]; + /** @description Formato de impressão do DANFE (tpImp) */ + readonly printType?: components["schemas"]["PrintType"]; + /** @description Finalidade da emissão da NF-e (finNFe) */ + readonly purposeType?: components["schemas"]["PurposeType"]; + /** @description Tipo de Nota de Débito (tpNFDebito) */ + readonly debitType?: components["schemas"]["DebitType"]; + readonly creditType?: components["schemas"]["CreditType"]; + /** @description Indica operação com Consumidor final (indFinal) */ + readonly consumerType?: components["schemas"]["ConsumerType"]; + /** @description Indicador de presença do comprador no estabelecimento (indPres) */ + readonly presenceType?: components["schemas"]["ConsumerPresenceType"]; + /** + * Format: date-time + * @description Data e Hora da entrada em contingência (dhCont) + * + * Data e hora no formato UTC (Universal Coordinated Time): AAAA-MM-DDThh:mm:ssTZD + * + */ + readonly contingencyOn?: string | null; + /** @description Justificativa da entrada em contingência (xJust) */ + readonly contingencyJustification?: string | null; + /** @description Grupo de Compras Governamentais (gCompraGov) */ + readonly governmentPurchase?: components["schemas"]["GovernmentPurchaseResource"]; + /** @description Identificação do Destinatário (dest) */ + readonly buyer?: components["schemas"]["BuyerResource"]; + /** @description Grupo de Informações do Transporte da NF-e (transp) */ + readonly transport?: components["schemas"]["TransportInformationResource"]; + /** @description Informações adicionais da NF-e (infAdic) */ + readonly additionalInformation?: components["schemas"]["AdditionalInformationResource"]; + /** @description Informações de exportação (exporta) */ + readonly export?: components["schemas"]["ExportResource"]; + /** @description Detalhamento de Produtos e Serviços (det). Pelo menos um item é obrigatório. */ + readonly items: readonly components["schemas"]["InvoiceItemResource"][]; + /** @description Grupo de Valores Totais da NF-e (total) */ + readonly totals?: components["schemas"]["Total"]; + readonly billing?: components["schemas"]["BillingResource"]; + /** @description IE do Substituto Tributário da UF de destino da mercadoria, quando houver a retenção do ICMS ST para a UF de destino. (IEST) */ + readonly issuerTaxSubstitute?: components["schemas"]["IssuerFromRequestResource"]; + /** @description Grupo de Informações do Intermediador da Transação (infIntermed) */ + readonly transactionIntermediate?: components["schemas"]["IntermediateResource"]; + /** @description Identificação do Local de entrega (entrega) */ + readonly delivery?: components["schemas"]["DeliveryInformationResource"]; + /** @description Identificação do Local de retirada (retirada) */ + readonly withdrawal?: components["schemas"]["WithdrawalInformationResource"]; + readonly purchaseInformation?: components["schemas"]["PurchaseInformationResource"]; + }; + readonly ActivityResource: { + /** @description Detalhes do Evento */ + readonly data?: unknown; + /** @description Nome do Evento gerado */ + readonly type?: string | null; + /** + * Format: int32 + * @description Número sequencial do Evento + */ + readonly sequence?: number | null; + }; + /** @description Adições (adi) */ + readonly AdditionResource: { + /** + * Format: int64 + * @description Numero da adição (nAdicao) + */ + readonly code?: number | null; + /** @description Código do fabricante estrangeiro (cFabricante) */ + readonly manufacturer?: string | null; + /** + * Format: double + * @description Valor do desconto do item da DI – Adição (vDescDI) + */ + readonly amount?: number | null; + /** + * Format: int64 + * @description Número do ato concessório de Drawback (nDraw) + */ + readonly drawback?: number | null; + }; + readonly AdditionalInformationResource: { + /** @description Informações Adicionais de Interesse do Fisco (infAdFisco) */ + readonly fisco?: string | null; + /** @description Informações Complementares de interesse do Contribuinte (infCpl) */ + readonly taxpayer?: string | null; + /** @description Pessoas autorizadas para o download do XML da NF-e (autXML). CNPJ ou CPF das pessoas autorizadas. Máximo de 10 autorizações. */ + readonly xmlAuthorized?: readonly number[] | null; + /** @description Esforço (xCampo) */ + readonly effort?: string | null; + /** @description Pedido (xCampo) */ + readonly order?: string | null; + /** @description Contrato (xCampo) */ + readonly contract?: string | null; + /** @description Documentos Fiscais Referenciados (refECF) */ + readonly taxDocumentsReference?: readonly components["schemas"]["TaxDocumentsReferenceResource"][] | null; + /** @description Grupo de notas de antecipação de pagamento - Informado para abater as parcelas de antecipação de pagamento, conforme Art. 10. § 4º (gPagAntecipado). */ + readonly advancePayment?: readonly components["schemas"]["AdvancePaymentItemResource"][] | null; + /** @description Observações fiscais (obsCont) */ + readonly taxpayerComments?: readonly components["schemas"]["TaxpayerCommentsResource"][] | null; + /** @description Processos referenciados (procRef) */ + readonly referencedProcess?: readonly components["schemas"]["ReferencedProcessResource"][] | null; + }; + /** @description Dados do Endereço */ + readonly AddressResource: { + /** @description Estado, ex.: SP, RJ, AC, padrão ISO 3166-2 ALFA 2. */ + readonly state: string; + /** @description Cidade do Endereço (cMun) */ + readonly city: components["schemas"]["CityResource"]; + /** @description Bairro do Endereço (xBairro) */ + readonly district: string; + /** @description Complemento do Endereço, ex.: AP 2, BL A. (xCpl) */ + readonly additionalInformation?: string | null; + /** @description Logradouro do Endereço (xLgr) */ + readonly street: string; + /** @description Número do Endereço. Usar S/N para "sem número". (nro) */ + readonly number: string; + /** @description Cód. Endereço Postal (CEP) */ + readonly postalCode?: string | null; + /** + * @description País, ex.: BRA, ARG, USA, padrão ISO 3166-1 ALFA-3. (xPais). Opcional — se omitido, assume BRA. + * @default BRA + */ + readonly country: string | null; + /** @description Telefone (fone) */ + readonly phone?: string | null; + }; + /** @description Grupo de notas de antecipação de pagamento - Informado para abater as parcelas de antecipação de pagamento, conforme Art. 10. § 4º (gPagAntecipado). */ + readonly AdvancePaymentItemResource: { + readonly accessKey?: string | null; + }; + readonly AuthorizationResource: { + /** + * Format: date-time + * @description Data e hora do recebimento (dhRecbto) + */ + readonly receiptOn?: string | null; + /** @description Chave de Acesso da NF-e (chNFe) */ + readonly accessKey?: string | null; + readonly message?: string | null; + /** @description Mensagem da SEFAZ (xMotivo) */ + readonly description?: string; + }; + readonly BillResource: { + /** @description Número da Fatura (nFat) */ + readonly number?: string | null; + /** + * Format: double + * @description Valor Original da Fatura (vOrig) + */ + readonly originalAmount?: number | null; + /** + * Format: double + * @description Valor do desconto (vDesc) + */ + readonly discountAmount?: number | null; + /** + * Format: double + * @description Valor Líquido da Fatura (vLiq) + */ + readonly netAmount?: number | null; + }; + readonly BillingResource: { + /** @description Grupo Fatura (fat) */ + readonly bill?: components["schemas"]["BillResource"]; + /** @description Grupo Duplicata (dup) */ + readonly duplicates?: readonly components["schemas"]["DuplicateResource"][] | null; + }; + /** @description Identificação do Destinatário da Nota Fiscal eletrônica - Grupo Obrigatório para a NF-e (modelo 55) e opcional para NFC-e (modelo 65). */ + readonly BuyerResource: { + /** @description Identificador da Conta */ + readonly accountId?: string | null; + /** @description Identificação */ + readonly id?: string | null; + /** @description Nome ou Razão Social (xNome) */ + readonly name: string; + /** + * Format: int64 + * @description CNPJ ou CPF (CNPJ/CPF) + */ + readonly federalTaxNumber: number; + /** @description Email (email) */ + readonly email?: string | null; + /** @description Dados do Endereço (enderDest) */ + readonly address: components["schemas"]["AddressResource"]; + /** @description Tipo da pessoa (indIEDest) */ + readonly type?: components["schemas"]["PersonType"]; + /** @description Indicador Inscrição Estadual (indIEDest) */ + readonly stateTaxNumberIndicator?: components["schemas"]["ReceiverStateTaxIndicator"]; + /** @description Nome fantasia (xFant) */ + readonly tradeName?: string | null; + /** @description Regime de tributação (CRT) */ + readonly taxRegime?: components["schemas"]["TaxRegime"]; + /** @description Inscrição Estadual (IE) */ + readonly stateTaxNumber?: string | null; + }; + /** @description Grupo de Informações da CBS */ + readonly CBSTaxResource: { + /** + * Format: double + * @description Alíquota da CBS (em percentual) (pCBS) + */ + readonly rate?: number | null; + /** @description Grupo de Informações do Diferimento (gDif) */ + readonly deferment?: components["schemas"]["DefermentTaxResource"]; + /** @description Grupo de Informações da Devolução de Tributos (gDevTrib) */ + readonly returnedAmount?: components["schemas"]["ReturnedTaxResource"]; + /** @description Grupo de informações da redução da alíquota (gRed) */ + readonly reduction?: components["schemas"]["ReductionTaxResource"]; + /** + * Format: double + * @description Valor total da CBS (vCBS). + */ + readonly amount?: number | null; + }; + readonly CIDEResource: { + /** + * Format: double + * @description BC da CIDE (qBCProd) + */ + readonly bc?: number | null; + /** + * Format: double + * @description Valor da alíquota da CIDE (vAliqProd) + */ + readonly rate?: number | null; + /** + * Format: double + * @description Valor da CIDE (vCIDE) + */ + readonly cideAmount?: number | null; + }; + readonly CardResource: { + /** @description CNPJ da Credenciadora de cartão de crédito e/ou débito (CNPJ) */ + readonly federalTaxNumber?: string | null; + /** @description Bandeira da operadora de cartão de crédito e/ou débito (tBand) */ + readonly flag?: components["schemas"]["FlagCard"]; + /** @description Número de autorização da operação cartão de crédito e/ou débito (cAut) */ + readonly authorization?: string | null; + /** @description Tipo de Integração para pagamento (tpIntegra) */ + readonly integrationPaymentType?: components["schemas"]["IntegrationPaymentType"]; + readonly federalTaxNumberRecipient?: string | null; + readonly idPaymentTerminal?: string | null; + }; + readonly CityResource: { + /** @description Cód. do Município, segundo o Tabela de Municípios do IBGE (cMun) */ + readonly code: string; + /** @description Nome do Município (xMun) */ + readonly name: string; + }; + /** + * @description Grupo do COFINS + * + * ID: S01 + * Pai: M01 + * + * Obs: Informar apenas um dos grupos S02, S03, S04 ou S04 + * com base valor atribuído ao campo S06 – CST do COFINS + */ + readonly CofinsTaxResource: { + /** @description Código de Situação Tributária da COFINS (CST) */ + readonly cst: string; + /** + * Format: double + * @description Valor da Base de Cálculo da COFINS (vBC) + */ + readonly baseTax?: number | null; + /** + * Format: double + * @description Alíquota da COFINS (em percentual) (pCOFINS) + */ + readonly rate?: number | null; + /** + * Format: double + * @description Valor da COFINS (vCOFINS) + */ + readonly amount?: number | null; + /** + * Format: double + * @description Quantidade Vendida (qBCProd) + */ + readonly baseTaxProductQuantity?: number | null; + /** + * Format: double + * @description Alíquota da COFINS (em reais) (vAliqProd) + */ + readonly productRate?: number | null; + }; + /** @description Ajuste de Competência (gAjusteCompet) */ + readonly CompetenceAdjustmentResource: { + /** @description Ano e mês referência do período de apuração (AAAA-MM) (competApur) */ + readonly assessmentPeriod?: string | null; + /** + * Format: double + * @description Valor de IBS referente ao ajuste de competência (vIBS) + */ + readonly ibsAmount?: number | null; + /** + * Format: double + * @description Valor de CBS referente ao ajuste de competência (vCBS) + */ + readonly cbsAmount?: number | null; + }; + /** + * @description Indicador de Presença (indPres). + * Valores possíveis: + * - `None`: Não se aplica (ex: nota complementar ou de ajuste) + * - `Presence`: Operação presencial + * - `Internet`: Operação não presencial, pela Internet + * - `Telephone`: Operação não presencial, Teleatendimento + * - `Delivery`: NFC-e em operação com entrega a domicílio + * - `OthersNonPresenceOperation`: Operação não presencial, outros + * @default Internet + * @enum {string} + */ + readonly ConsumerPresenceType: "None" | "Presence" | "Internet" | "Telephone" | "Delivery" | "OthersNonPresenceOperation"; + /** + * @description Indica operação com Consumidor final (indFinal). O valor padrão é determinado pelo `federalTaxNumber` do `buyer`: `Normal` para CNPJ, `FinalConsumer` para CPF. + * Valores possíveis: + * - `FinalConsumer`: Consumidor final + * - `Normal`: Operação normal + * @default Normal + * @enum {string} + */ + readonly ConsumerType: "FinalConsumer" | "Normal"; + /** + * Format: int64 + * @description Código do Município de ocorrência do fato gerador do IBS/CBS (cMunFGIBS). Informar o município de ocorrência do fato gerador do IBS/CBS. Este campo só é preenchido quando "indPres = 5 (Operação presencial, fora do estabelecimento)" e não incluir o endereço do destinatário (Grupo: E05) ou local de entrega (Grupo: G01). + */ + readonly ConsumptionCityCode: number | null; + readonly ContingencyDetails: { + readonly authorizer?: components["schemas"]["StateTaxProcessingAuthorizer"]; + /** + * Format: date-time + * @description Data e hora do início da contingência + */ + readonly startedOn?: string; + /** @description Justificativa da entrada em contingência */ + readonly reason?: string | null; + }; + /** @description Estorno de Crédito (gEstornoCred) */ + readonly CreditReversalResource: { + /** + * Format: double + * @description Valor do IBS a ser estornado (vIBSEstCred) + */ + readonly ibsReversalAmount?: number | null; + /** + * Format: double + * @description Valor da CBS a ser estornada (vCBSEstCred) + */ + readonly cbsReversalAmount?: number | null; + }; + /** @description Estorno de Crédito (gEstornoCred) */ + readonly CreditReversalTotalsResource: { + /** + * Format: double + * @description Valor do IBS a ser estornado (vIBSEstCred) + */ + readonly ibsReversalAmount?: number | null; + /** + * Format: double + * @description Valor da CBS a ser estornada (vCBSEstCred) + */ + readonly cbsReversalAmount?: number | null; + }; + /** @description Informações de Transferências de Crédito (gTransfCred) */ + readonly CreditTransferTaxResource: { + /** + * Format: double + * @description Valor de IBS a transferir (vIBS). + */ + readonly ibsAmount?: number | null; + /** + * Format: double + * @description Valor de CBS a transferir (vCBS). + */ + readonly cbsAmount?: number | null; + }; + /** + * @description Tipo de Nota de Crédito (tpNFCredito). + * Valores possíveis: + * - `FinesAndInterest`: Multa e juros + * - `IbsPresumedCreditAppropriationZfm`: Apropriação de crédito presumido de IBS sobre o saldo devedor na ZFM + * - `ReturnDeliveryRefusedOrNotFound`: Retorno por recusa na entrega ou não localização do destinatário + * - `ValueReduction`: Redução de valores + * - `TransferCreditSuccession`: Transferência de crédito na sucessão + * @enum {string|null} + */ + readonly CreditType: "FinesAndInterest" | "IbsPresumedCreditAppropriationZfm" | "ReturnDeliveryRefusedOrNotFound" | "ValueReduction" | "TransferCreditSuccession" | null; + /** + * @description Tipo de Nota de Débito (tpNFDebito). + * Valores possíveis: + * - `TransferCreditsToCooperatives`: Transferência de créditos para cooperativas + * - `CancelCreditsExemptImmuneSales`: Cancelamento de créditos por vendas isentas/imunes + * - `UnprocessedInvoicesDebits`: Débitos de faturas não processadas no cálculo + * - `FinesAndInterest`: Multas e juros + * - `TransferInheritanceCredit`: Transferência de crédito de herança + * - `AdvancePayment`: Pagamento antecipado + * - `InventoryLoss`: Perda de estoque + * - `SnDisqualification`: Desenquadramento do Simples Nacional + * @enum {string|null} + */ + readonly DebitType: "TransferCreditsToCooperatives" | "CancelCreditsExemptImmuneSales" | "UnprocessedInvoicesDebits" | "FinesAndInterest" | "TransferInheritanceCredit" | "AdvancePayment" | "InventoryLoss" | "SnDisqualification" | null; + /** @description Grupo de Informações do Diferimento */ + readonly DefermentTaxResource: { + /** + * Format: double + * @description Percentual do diferimento + */ + readonly rate?: number | null; + /** + * Format: double + * @description Valor do Diferimento + */ + readonly amount?: number | null; + }; + /** @description Identificação do Local de entrega (entrega) */ + readonly DeliveryInformationResource: { + /** @description Identificador da Conta */ + readonly accountId?: string | null; + /** @description Identificação */ + readonly id?: string | null; + /** @description Nome ou Razão Social (xNome) */ + readonly name?: string | null; + /** + * Format: int64 + * @description CNPJ ou CPF + */ + readonly federalTaxNumber?: number | null; + /** @description Email */ + readonly email?: string | null; + readonly address?: components["schemas"]["AddressResource"]; + readonly type?: components["schemas"]["PersonType"]; + /** @description Inscrição Estadual (IE) */ + readonly stateTaxNumber?: string | null; + }; + /** + * @description Identificador de local de destino da operação (idDest). + * Valores possíveis: + * - `None`: Não definido + * - `InternalOperation`: Operação interna + * - `InterstateOperation`: Operação interestadual + * - `InternationalOperation`: Operação com o exterior + * @default InternalOperation + * @enum {string} + */ + readonly Destination: "None" | "InternalOperation" | "InterstateOperation" | "InternationalOperation"; + /** @description Dados para inutilizar números de nota fiscal */ + readonly DisablementResource: { + readonly environment?: components["schemas"]["EnvironmentType"]; + /** + * Format: int32 + * @description Série + */ + readonly serie?: number; + readonly state?: components["schemas"]["StateCode"]; + /** + * Format: int32 + * @description Número inicial + */ + readonly beginNumber?: number; + /** + * Format: int32 + * @description Número final (usar o mesmo número inicial se for apenas um número) + */ + readonly lastNumber?: number; + /** @description Motivo da inutilização */ + readonly reason?: string | null; + }; + readonly DocumentElectronicInvoiceResource: { + /** @description Chave de Acesso (refNFe) */ + readonly accessKey?: string | null; + }; + readonly DocumentInvoiceReferenceResource: { + /** + * Format: double + * @description Código da UF (cUF) + */ + readonly state?: number | null; + /** @description Ano / Mês (AAMM) */ + readonly yearMonth?: string | null; + /** @description CNPJ (CNPJ) */ + readonly federalTaxNumber?: string | null; + /** @description Modelo (mod) */ + readonly model?: string | null; + /** @description Série (serie) */ + readonly series?: string | null; + /** @description Número (nNF) */ + readonly number?: string | null; + }; + /** + * @description Indicador de dedução do ICMS desonerado do valor total da NF-e (indDeducao). + * Valores possíveis: + * - `NotDeduct`: O valor do ICMS desonerado (vICMSDeson) não deduz do valor total da NF-e + * - `Deduce`: O valor do ICMS desonerado (vICMSDeson) deduz do valor total da NF-e + * @enum {string} + */ + readonly DuductionIndicator: "NotDeduct" | "Deduce"; + readonly DuplicateResource: { + /** @description Número da Duplicata (nDup) */ + readonly number?: string | null; + /** + * Format: date-time + * @description Data de vencimento (dVenc) + */ + readonly expirationOn?: string | null; + /** + * Format: double + * @description Valor da duplicata (vDup) + */ + readonly amount?: number | null; + }; + readonly EconomicActivityResource: { + readonly type?: components["schemas"]["EconomicActivityType"]; + /** + * Format: int32 + * @description Código da Atividade da Empresa + */ + readonly code?: number | null; + }; + /** + * @description Tipo de Atividade Econômica. + * Valores possíveis: + * - `Main`: Principal + * - `Secondary`: Secundária + * @enum {string} + */ + readonly EconomicActivityType: "Main" | "Secondary"; + /** + * @description Tipo de Ambiente (tpAmb). + * Valores possíveis: + * - `None`: Não definido + * - `Production`: Produção + * - `Test`: Homologação (Teste) + * @enum {string} + */ + readonly EnvironmentType: "None" | "Production" | "Test"; + readonly ErrorResource: { + /** Format: int32 */ + readonly code?: number | null; + readonly message?: string | null; + }; + readonly ErrorsResource: { + readonly errors?: readonly components["schemas"]["ErrorResource"][] | null; + }; + /** + * @description Motivo da desoneração. Campo será preenchido quando o campo anterior estiver preenchido. + * Valores possíveis: + * - `Agriculture`: Produtos agropecuários + * - `Others`: Outros + * - `DevelopmentEntities`: Órgãos de fomento e desenvolvimento + * @enum {string} + */ + readonly ExemptReason: "Agriculture" | "Others" | "DevelopmentEntities"; + readonly ExportDetailResource: { + /** @description Número do ato concessório de Drawback (nDraw) */ + readonly drawback?: string | null; + readonly hintInformation?: components["schemas"]["ExportHintResource"]; + }; + readonly ExportHintResource: { + /** @description Número do Registro de Exportação (nRE) */ + readonly registryId?: string | null; + /** @description Chave de Acesso da NF-e recebida para exportação (chNFe) */ + readonly accessKey?: string | null; + /** + * Format: double + * @description Quantidade do item realmente exportado (qExport) + */ + readonly quantity?: number | null; + }; + readonly ExportResource: { + readonly state?: components["schemas"]["StateCode"]; + /** @description Descrição do Local de Embarque ou de transposição de fronteira (xLocExporta) */ + readonly office?: string | null; + /** @description Informações Complementares de interesse do Contribuinte (xLocDespacho) */ + readonly local?: string | null; + }; + /** @description Arquivo */ + readonly FileResource: { + /** @description Endereço Absoluto URI para o arquivo */ + readonly uri?: string | null; + }; + /** + * @description Bandeira da operadora de cartão de crédito e/ou débito (tBand). + * Valores possíveis: + * - `None`: Não definido + * - `Visa`: Visa + * - `Mastercard`: Mastercard + * - `AmericanExpress`: American Express + * - `Sorocred`: Sorocred + * - `DinersClub`: Diners Club + * - `Elo`: Elo + * - `Hipercard`: Hipercard + * - `Aura`: Aura + * - `Cabal`: Cabal + * - `Alelo`: Alelo + * - `BanesCard`: Banescard + * - `CalCard`: Calcard + * - `Credz`: Credz + * - `Discover`: Discover + * - `GoodCard`: Good Card + * - `GreenCard`: Green Card + * - `Hiper`: Hiper + * - `JCB`: JCB + * - `Mais`: Mais + * - `MaxVan`: Maxvan + * - `Policard`: Policard + * - `RedeCompras`: Rede Compras + * - `Sodexo`: Sodexo + * - `ValeCard`: ValeCard + * - `Verocheque`: Verocheque + * - `VR`: VR + * - `Ticket`: Ticket + * - `Other`: Outros + * @enum {string} + */ + readonly FlagCard: "None" | "Visa" | "Mastercard" | "AmericanExpress" | "Sorocred" | "DinersClub" | "Elo" | "Hipercard" | "Aura" | "Cabal" | "Alelo" | "BanesCard" | "CalCard" | "Credz" | "Discover" | "GoodCard" | "GreenCard" | "Hiper" | "JCB" | "Mais" | "MaxVan" | "Policard" | "RedeCompras" | "Sodexo" | "ValeCard" | "Verocheque" | "VR" | "Ticket" | "Other"; + readonly FuelOriginResource: { + /** + * Format: int32 + * @description Indicador de importação (indImport) + */ + readonly indImport?: number | null; + /** + * Format: int32 + * @description Código da UF (cUFOrig) + */ + readonly cUFOrig?: number | null; + /** + * Format: double + * @description Percentual originário para a UF (pOrig) + */ + readonly pOrig?: number | null; + }; + /** @description Detalhamento de Veículos novos (veicProd) - grupo J01 */ + readonly VehicleDetailResource: { + /** + * Format: int32 + * @description Tipo da Operação (tpOp). + * Valores possíveis: + * - `0`: Outros + * - `1`: Venda concessionária + * - `2`: Faturamento direto para consumidor final + * - `3`: Venda direta para grandes consumidores + * @enum {integer|null} + */ + readonly operationType?: 0 | 1 | 2 | 3 | null; + /** @description Chassi do veículo - VIN (chassi). 17 caracteres alfanuméricos */ + readonly chassis?: string | null; + /** @description Cor do veículo - código de cada montadora (cCor) */ + readonly colorCode?: string | null; + /** @description Descrição da Cor (xCor) */ + readonly colorDescription?: string | null; + /** @description Potência máxima do motor em cavalo vapor - CV (pot) */ + readonly enginePower?: string | null; + /** @description Capacidade volumétrica do motor em centímetros cúbicos - CC (cilin) */ + readonly engineDisplacement?: string | null; + /** @description Peso Líquido (pesoL) */ + readonly netWeight?: string | null; + /** @description Peso Bruto (pesoB) */ + readonly grossWeight?: string | null; + /** @description Serial - série (nSerie) */ + readonly serialNumber?: string | null; + /** @description Tipo de combustível - Tabela RENAVAM (tpComb). Ex: 01-Álcool; 02-Gasolina; 03-Diesel; 16-Álcool/Gas.; 17-Gas./Álcool/GNV; 18-Gasolina/Elétrico */ + readonly fuelType?: string | null; + /** @description Número do motor (nMotor) */ + readonly engineNumber?: string | null; + /** @description Capacidade Máxima de Tração em toneladas - 4 casas decimais (CMT) */ + readonly maximumTractionCapacity?: string | null; + /** @description Distância entre eixos (dist) */ + readonly wheelBase?: string | null; + /** + * Format: int32 + * @description Ano Modelo de Fabricação (anoMod). Formato: 4 dígitos + */ + readonly modelYear?: number | null; + /** + * Format: int32 + * @description Ano de Fabricação (anoFab). Formato: 4 dígitos + */ + readonly manufactureYear?: number | null; + /** @description Tipo de pintura (tpPint) */ + readonly paintType?: string | null; + /** @description Tipo de veículo - Tabela RENAVAM (tpVeic) */ + readonly vehicleType?: string | null; + /** + * Format: int32 + * @description Espécie de veículo - Tabela RENAVAM (espVeic) + */ + readonly vehicleSpecies?: number | null; + /** + * @description Condição do VIN — chassi (VIN). + * Valores possíveis: + * - `R`: Remarcado (chassi regravado pelo DETRAN) + * - `N`: Normal (chassi original de fábrica) + * @enum {string|null} + */ + readonly vinCondition?: "R" | "N" | null; + /** + * Format: int32 + * @description Condição do veículo (condVeic). + * Valores possíveis: + * - `1`: Acabado (veículo pronto para uso) + * - `2`: Inacabado (veículo sem algum componente essencial) + * - `3`: Semi-acabado (veículo parcialmente montado, ex: chassis-cabina) + * @enum {integer|null} + */ + readonly vehicleCondition?: 1 | 2 | 3 | null; + /** @description Código Marca Modelo - Tabela RENAVAM (cMod) */ + readonly brandModelCode?: string | null; + /** @description Código da Cor DENATRAN (cCorDENATRAN). 01-AMARELO; 02-AZUL; 03-BEGE; 04-BRANCA; 05-CINZA; 06-DOURADA; 07-GRENA; 08-LARANJA; 09-MARROM; 10-PRATA; 11-PRETA; 12-ROSA; 13-ROXA; 14-VERDE; 15-VERMELHA; 16-FANTASIA */ + readonly denatranColorCode?: string | null; + /** + * Format: int32 + * @description Quantidade máxima de passageiros sentados, inclusive motorista (lota) + */ + readonly seatingCapacity?: number | null; + /** + * Format: int32 + * @description Tipo de restrição sobre o veículo (tpRest). + * Valores possíveis: + * - `0`: Não há restrição (veículo livre para venda e transferência) + * - `1`: Alienação Fiduciária (veículo dado como garantia em financiamento) + * - `2`: Arrendamento Mercantil (veículo em contrato de leasing) + * - `3`: Reserva de Domínio (vendedor mantém a propriedade até quitação total) + * - `4`: Penhor de Veículos (veículo dado como garantia em empréstimo) + * - `9`: Outras restrições + * @enum {integer|null} + */ + readonly restrictionType?: 0 | 1 | 2 | 3 | 4 | 9 | null; + }; + readonly FuelResource: { + /** @description Código de produto da ANP (cProdANP) */ + readonly codeANP?: string | null; + /** + * Format: double + * @description Percentual de Gás Natural para o produto GLP (cProdANP=210203001) (pMixGN) + */ + readonly percentageNG?: number | null; + /** @description Descrição do produto conforme ANP (descANP) */ + readonly descriptionANP?: string | null; + /** + * Format: double + * @description Percentual do GLP derivado do petróleo no produto GLP (cProdANP=210203001) (pGLP) + */ + readonly percentageGLP?: number | null; + /** + * Format: double + * @description Percentual de Gás Natural Nacional – GLGNn para o produto GLP (cProdANP= 210203001) (pGNn) + */ + readonly percentageNGn?: number | null; + /** + * Format: double + * @description Percentual + * de Gás Natural Importado – GLGNi para o produto GLP (cProdANP= 210203001) (pGNi) + */ + readonly percentageGNi?: number | null; + /** + * Format: double + * @description Valor de partida (cProdANP=210203001) (vPart) + */ + readonly startingAmount?: number | null; + /** @description Código de autorização / registro do CODIF (CODIF) */ + readonly codif?: string | null; + /** + * Format: double + * @description Quantidade de combustível faturada à temperatura ambiente (qTemp) + */ + readonly amountTemp?: number | null; + /** @description Sigla da UF de consumo (UFCons) */ + readonly stateBuyer?: string | null; + readonly cide?: components["schemas"]["CIDEResource"]; + readonly pump?: components["schemas"]["PumpResource"]; + readonly fuelOrigin?: components["schemas"]["FuelOriginResource"]; + }; + /** + * @description Tipo de ente governamental (tpEnteGov). Para administração pública direta e suas autarquias e fundações. + * Valores possíveis: + * - `Union`: União + * - `State`: Estado + * - `FederalDistrict`: Distrito Federal + * - `Municipality`: Município + * @enum {string|null} + */ + readonly GovernmentPurchaseEntityType: "Union" | "State" | "FederalDistrict" | "Municipality" | null; + /** + * @description Tipo de operação com o ente governamental (tpOperGov). + * Valores possíveis: + * - `Supply`: Fornecimento + * - `Payment`: Recebimento do pagamento + * - `SupplyThenPay`: Fornecimento e pagamento simultâneo + * - `PayForPastSupply`: Pagamento por fornecimento passado + * - `SupplyAfterPay`: Fornecimento após pagamento + * - `PayNowSupplyLater`: Pagamento agora, fornecimento depois + * - `SupplyAndPayNow`: Fornecimento e pagamento agora + * @enum {string|null} + */ + readonly GovernmentPurchaseOperationType: "Supply" | "Payment" | "SupplyThenPay" | "PayForPastSupply" | "SupplyAfterPay" | "PayNowSupplyLater" | "SupplyAndPayNow" | null; + /** @description Grupo de Compras Governamentais (gCompraGov). Grupo opcional. Informar apenas para compras governamentais. */ + readonly GovernmentPurchaseResource: { + readonly entityType?: components["schemas"]["GovernmentPurchaseEntityType"]; + /** + * Format: double + * @description Percentual de redução de alíquota em compra governamental (pRedutor). Conforme o art. 472/370 da LC 214/2025. + */ + readonly rateReduction?: number | null; + readonly operationType?: components["schemas"]["GovernmentPurchaseOperationType"]; + }; + /** @description Grupo de informações sobre a composição do valor do IBS e CBS nas compras governamentais. Informar apenas para compras governamentais. */ + readonly GovernmentPurchaseTaxResource: { + /** + * Format: double + * @description Alíquota IBS UF em compras governamentais (pAliqIBSUF). + */ + readonly stateRate?: number | null; + /** + * Format: double + * @description Valor IBS UF em compras governamentais (vTribIBSUF). + */ + readonly stateAmount?: number | null; + /** + * Format: double + * @description Alíquota IBS Município em compras governamentais (pAliqIBSMun). + */ + readonly municipalRate?: number | null; + /** + * Format: double + * @description Valor IBS Município em compras governamentais (vTribIBSMun). + */ + readonly municipalAmount?: number | null; + /** + * Format: double + * @description Alíquota CBS em compras governamentais (pAliqCBS). + */ + readonly cbsRate?: number | null; + /** + * Format: double + * @description Valor CBS em compras governamentais (vTribCBS) + */ + readonly cbsAmount?: number | null; + }; + /** @description Grupo de Informações do IBS e CBS */ + readonly IBSCBSTaxResource: { + /** @description Código de Situação Tributária do IBS/CBS (CST). Campo opcional. Se preenchido, será considerado; caso contrário, será definido com base no valor informado no campo `classCode`. Consulte a tabela de referência de `situationCode` (CST) disponível na nossa documentação funcional. */ + readonly situationCode?: string | null; + /** @description Código de Classificação Tributária do IBS/CBS (cClassTrib). Consulte a tabela de referência [cClassTrib](https://nfe.io/docs/documentacao/reforma-tributaria/conceitos-funcionais/tabelas-de-referencia/tabela-referencia-cst-classificacao-tributaria-ibs-cbs/) disponível na nossa documentação funcional. */ + readonly classCode?: string | null; + /** + * @description Modo de cálculo do IBS/CBS. + * Valores possíveis: + * - `Manual`: Preenchimento manual dos campos de tributo. + * - `OfficialService`: Utiliza o serviço oficial para calcular IBS/CBS. Quando ativo, apenas `situationCode` e `classCode` são necessários; os demais campos de tributo são ignorados na entrada e preenchidos pelo serviço. + * @default Manual + * @enum {string|null} + */ + readonly calculationMode: "Manual" | "OfficialService" | null; + /** @description Indica a natureza da operação de doação, orientando a apuração e a geração de débitos ou estornos conforme o cenário (indDoacao). Informar quando for doação. */ + readonly donationIndicator?: string | null; + /** + * Format: double + * @description Base de cálculo antes de reduções para cálculo do tributo bruto. Este campo é opcional. Se enviado, será considerado; caso contrário, será calculado. + */ + readonly basis?: number | null; + readonly state?: components["schemas"]["IBSStateTaxResource"]; + readonly municipal?: components["schemas"]["IBSMunicipalTaxResource"]; + /** + * Format: double + * @description Total do IBS (vIBSTot = vIBSUF + vIBSMun). Quando houver crédito presumido com o indicador "IndDeduzCredPres=1", o valor de vCredPres deve ser deduzido deste total. + */ + readonly ibsTotalAmount?: number | null; + readonly cbs?: components["schemas"]["CBSTaxResource"]; + /** @description Tributação regular hipotética caso condição resolutória/suspensiva não se aplique. */ + readonly regularTaxation?: components["schemas"]["RegularTaxationResource"]; + /** @description Grupo de informações sobre a composição do valor do IBS e CBS nas compras governamentais. */ + readonly governmentPurchase?: components["schemas"]["GovernmentPurchaseTaxResource"]; + /** @description Grupo de Informações do IBS e CBS sobre transações monofásicas */ + readonly monophase?: components["schemas"]["MonophaseIBSCBSTaxResource"]; + /** @description Grupo de Informações sobre Transferências de Crédito */ + readonly creditTransfer?: components["schemas"]["CreditTransferTaxResource"]; + /** @description Informações sobre o crédito presumido operacional (gCredPresOper). */ + readonly operationalPresumedCredit?: components["schemas"]["OperationalPresumedCreditResource"]; + /** @description Estorno de Crédito (gEstornoCred). Observação: a obrigatoriedade ou vedação do preenchimento deste grupo está condicionada ao indicador “ind_gEstornoCred” da tabela de cClassTrib do IBS e da CBS. */ + readonly creditReversal?: components["schemas"]["CreditReversalResource"]; + /** @description Informações sobre o crédito presumido de IBS para fornecimentos da ZFM (gCredPresIBSZFM). */ + readonly zfmPresumedCredit?: components["schemas"]["ZfmPresumedCreditResource"]; + }; + /** @description Grupo de Valores Totais referentes ao ICMS */ + readonly ICMSTotal: { + /** + * Format: double + * @description Base de Cálculo do ICMS (vBC) + */ + readonly baseTax?: number | null; + /** + * Format: double + * @description Valor Total do ICMS (vICMS) + */ + readonly icmsAmount?: number | null; + /** + * Format: double + * @description Valor ICMS Total desonerado (vICMSDeson) + */ + readonly icmsExemptAmount?: number | null; + /** + * Format: double + * @description Base de Cálculo do ICMS Substituição Tributária (vBCST) + */ + readonly stCalculationBasisAmount?: number | null; + /** + * Format: double + * @description Valor Total do ICMS ST (vST) + */ + readonly stAmount?: number | null; + /** + * Format: double + * @description Valor Total dos produtos e serviços (vProd) + */ + readonly productAmount?: number; + /** + * Format: double + * @description Valor Total do Frete (vFrete) + */ + readonly freightAmount?: number | null; + /** + * Format: double + * @description Valor Total do Seguro (vSeg) + */ + readonly insuranceAmount?: number | null; + /** + * Format: double + * @description Valor Total do Desconto (vDesc) + */ + readonly discountAmount?: number | null; + /** + * Format: double + * @description Valor Total do Imposto de Importação (vII) + */ + readonly iiAmount?: number | null; + /** + * Format: double + * @description Valor Total do IPI (vIPI) + */ + readonly ipiAmount?: number | null; + /** + * Format: double + * @description Valor do PIS (vPIS) + */ + readonly pisAmount?: number | null; + /** + * Format: double + * @description Valor do COFINS (vCOFINS) + */ + readonly cofinsAmount?: number | null; + /** + * Format: double + * @description Outras Despesas acessórias (vOutro) + */ + readonly othersAmount?: number | null; + /** + * Format: double + * @description Valor Total da NF-e (vNF) + */ + readonly invoiceAmount?: number; + /** + * Format: double + * @description Valor Total ICMS FCP UF Destino + */ + readonly fcpufDestinationAmount?: number | null; + /** + * Format: double + * @description Valor Total ICMS Interestadual UF Destino + */ + readonly icmsufDestinationAmount?: number | null; + /** + * Format: double + * @description Valor Total ICMS Interestadual UF Rem. + */ + readonly icmsufSenderAmount?: number | null; + /** + * Format: double + * @description Valor aproximado total de tributos federais, estaduais e municipais. (vTotTrib) + */ + readonly federalTaxesAmount?: number; + /** + * Format: double + * @description Valor Total do FCP - Valor do ICMS relativo ao Fundo de Combate à Pobreza (vFCP) + */ + readonly fcpAmount?: number | null; + /** + * Format: double + * @description Valor Total do FCP retido por ST - Valor do ICMS relativo ao Fundo de Combate à Pobreza (vFCPST) retido por substituição tributária. + */ + readonly fcpstAmount?: number | null; + /** + * Format: double + * @description Valor Total do FCP retido por anteriormente por ST - Valor do ICMS relativo ao Fundo de Combate à Pobreza (vFCP) retido anteriormente por substituição tributária. + */ + readonly fcpstRetAmount?: number | null; + /** + * Format: double + * @description Valor total do IPI devolvido (vIPIDevol) + */ + readonly ipiDevolAmount?: number | null; + /** Format: double */ + readonly qBCMono?: number | null; + /** + * Format: double + * @description Valor total do ICMS monofásico próprio (vICMSMono). + */ + readonly vICMSMono?: number | null; + /** + * Format: double + * @description Valor total da quantidade tributada do ICMS monofásico sujeito a retenção (qBCMonoReten). + */ + readonly qBCMonoReten?: number | null; + /** + * Format: double + * @description Valor total da quantidade tributada do ICMS monofásico retido anteriormente(vICMSMonoReten) + */ + readonly vICMSMonoReten?: number | null; + /** + * Format: double + * @description Valor total do ICMS monofásico retido anteriormente (vICMSMonoRet) + */ + readonly qBCMonoRet?: number | null; + /** + * Format: double + * @description Valor total do ICMS monofásico retido anteriormente (vICMSMonoRet) + */ + readonly vICMSMonoRet?: number | null; + }; + /** @description Grupo de Valores Totais referentes ao ICMS */ + readonly ICMSTotalResource: { + /** + * Format: double + * @description Base de Cálculo do ICMS (vBC) + */ + readonly baseTax?: number | null; + /** + * Format: double + * @description Valor Total do ICMS (vICMS) + */ + readonly icmsAmount?: number | null; + /** + * Format: double + * @description Valor ICMS Total desonerado (vICMSDeson) + */ + readonly icmsExemptAmount?: number | null; + /** + * Format: double + * @description Base de Cálculo do ICMS Substituição Tributária (vBCST) + */ + readonly stCalculationBasisAmount?: number | null; + /** + * Format: double + * @description Valor Total do ICMS ST (vST) + */ + readonly stAmount?: number | null; + /** + * Format: double + * @description Valor Total dos produtos e serviços (vProd) + */ + readonly productAmount?: number | null; + /** + * Format: double + * @description Valor Total do Frete (vFrete) + */ + readonly freightAmount?: number | null; + /** + * Format: double + * @description Valor Total do Seguro (vSeg) + */ + readonly insuranceAmount?: number | null; + /** + * Format: double + * @description Valor Total do Desconto (vDesc) + */ + readonly discountAmount?: number | null; + /** + * Format: double + * @description Valor Total do Imposto de Importação (vII) + */ + readonly iiAmount?: number | null; + /** + * Format: double + * @description Valor Total do IPI (vIPI) + */ + readonly ipiAmount?: number | null; + /** + * Format: double + * @description Valor do PIS (vPIS) + */ + readonly pisAmount?: number | null; + /** + * Format: double + * @description Valor do COFINS (vCOFINS) + */ + readonly cofinsAmount?: number | null; + /** + * Format: double + * @description Outras Despesas acessórias (vOutro) + */ + readonly othersAmount?: number | null; + /** + * Format: double + * @description Valor Total da NF-e (vNF) + */ + readonly invoiceAmount?: number | null; + /** + * Format: double + * @description Valor Total ICMS FCP UF Destino (vFCPUFDest) + */ + readonly fcpufDestinationAmount?: number | null; + /** + * Format: double + * @description Valor Total ICMS Interestadual UF Destino (vICMSUFDest) + */ + readonly icmsufDestinationAmount?: number | null; + /** + * Format: double + * @description Valor Total ICMS Interestadual UF Remetente (vICMSUFRemet) + */ + readonly icmsufSenderAmount?: number | null; + /** + * Format: double + * @description Valor aproximado total de tributos federais, estaduais e municipais. + * (vTotTrib) + */ + readonly federalTaxesAmount?: number | null; + /** + * Format: double + * @description Valor Total do FCP - Valor do ICMS relativo ao Fundo de Combate à Pobreza (vFCP) + */ + readonly fcpAmount?: number | null; + /** + * Format: double + * @description Valor Total do FCP retido por ST - Valor do ICMS relativo ao Fundo de Combate à Pobreza (vFCPST) retido por substituição tributária. + */ + readonly fcpstAmount?: number | null; + /** + * Format: double + * @description Valor Total do FCP retido por anteriormente por ST - Valor do ICMS relativo ao Fundo de Combate à Pobreza retido anteriormente por substituição tributária (vFCPSTRet) + */ + readonly fcpstRetAmount?: number | null; + /** + * Format: double + * @description Valor total do IPI devolvido (vIPIDevol) + */ + readonly ipiDevolAmount?: number | null; + /** + * Format: double + * @description Valor total da quantidade tributada do ICMS monofásico próprio (qBCMono) + */ + readonly qBCMono?: number | null; + /** + * Format: double + * @description Valor total do ICMS monofásico próprio (vICMSMono). + */ + readonly vICMSMono?: number | null; + /** + * Format: double + * @description Valor total da quantidade tributada do ICMS monofásico sujeito a retenção (qBCMonoReten). + */ + readonly qBCMonoReten?: number | null; + /** + * Format: double + * @description Valor total da quantidade tributada do ICMS monofásico retido anteriormente(vICMSMonoReten) + */ + readonly vICMSMonoReten?: number | null; + /** + * Format: double + * @description Valor total da quantidade tributada do ICMS monofásico retido anteriormente(qBCMonoRet) + */ + readonly qBCMonoRet?: number | null; + /** + * Format: double + * @description Valor total do ICMS monofásico retido anteriormente (vICMSMonoRet) + */ + readonly vICMSMonoRet?: number | null; + }; + /** @description Grupo de Tributação do ICMS de Destino da UF */ + readonly ICMSUFDestinationTaxResource: { + /** + * Format: double + * @description Valor da Base de Cálculo do ICMS na UF de destino (vBCUFDest) + */ + readonly vBCUFDest?: number | null; + /** + * Format: double + * @description Percentual adicional inserido na alíquota interna da UF de destino, relativo ao Fundo de Combate à Pobreza (FCP) naquela UF (pFCPUFDest) + */ + readonly pFCPUFDest?: number | null; + /** + * Format: double + * @description Alíquota adotada nas operações internas na UF de destino para o produto / mercadoria (pICMSUFDest) + */ + readonly pICMSUFDest?: number | null; + /** + * Format: double + * @description Alíquota interestadual das UF envolvidas (pICMSInter) + */ + readonly pICMSInter?: number | null; + /** + * Format: double + * @description Percentual de ICMS Interestadual para a UF de destino (pICMSInterPart) + */ + readonly pICMSInterPart?: number | null; + /** + * Format: double + * @description Valor do ICMS relativo ao Fundo de Combate à Pobreza (FCP) da UF de destino (vFCPUFDest + */ + readonly vFCPUFDest?: number | null; + /** + * Format: double + * @description Valor do ICMS + * Interestadual para a UF de destino (vICMSUFDest) + */ + readonly vICMSUFDest?: number | null; + /** + * Format: double + * @description Valor do ICMS Interestadual para a UF do remetente (vICMSUFRemet) + */ + readonly vICMSUFRemet?: number | null; + /** + * Format: double + * @description Valor da BC FCP na UF de destino (vBCFCPUFDest) + */ + readonly vBCFCPUFDest?: number | null; + }; + /** @description Grupo de Informações do IBS para o município */ + readonly IBSMunicipalTaxResource: { + /** + * Format: double + * @description Alíquota IBS (Município) (%). + */ + readonly rate?: number | null; + readonly deferment?: components["schemas"]["DefermentTaxResource"]; + readonly returnedAmount?: components["schemas"]["ReturnedTaxResource"]; + readonly reduction?: components["schemas"]["ReductionTaxResource"]; + /** + * Format: double + * @description Valor do IBS do Município (vIBSMun). + */ + readonly amount?: number | null; + }; + /** @description Grupo de Informações do IBS para a UF */ + readonly IBSStateTaxResource: { + /** + * Format: double + * @description Alíquota do IBS de competência das UF (em percentual). Alíquota vigente do IBS da UF.Preencher de acordo com a tabela de alíquotas do IBS e CBS. + */ + readonly rate?: number | null; + readonly deferment?: components["schemas"]["DefermentTaxResource"]; + readonly returnedAmount?: components["schemas"]["ReturnedTaxResource"]; + readonly reduction?: components["schemas"]["ReductionTaxResource"]; + /** + * Format: double + * @description Valor do IBS da UF (vIBSUF). + */ + readonly amount?: number | null; + }; + /** + * @description Grupo do Imposto de Importação + * + * Id: P01 + * Pai: O01 + */ + readonly IITaxResource: { + /** @description Valor BC do Imposto de Importação (vBC) */ + readonly baseTax?: string | null; + /** @description Valor despesas aduaneiras (vDespAdu) */ + readonly customsExpenditureAmount?: string | null; + /** + * Format: double + * @description Valor Imposto de Importação (vII) + */ + readonly amount?: number | null; + /** + * Format: double + * @description Valor Imposto sobre Operações Financeiras (vIOF) + */ + readonly iofAmount?: number | null; + /** + * Format: double + * @description Valor dos encargos cambiais + */ + readonly vEnqCamb?: number | null; + }; + /** + * @description Grupo do IPI + * + * Informar apenas quando + * o item for sujeito ao IPI + * + * ID: O01 + * + * Pai: M01 + */ + readonly IPITaxResource: { + /** @description Código da situação tributária do IPI (CST) */ + readonly cst?: string | null; + /** @description Código de Enquadramento Legal do IPI (cEnq) */ + readonly classificationCode?: string | null; + /** @description Classe de enquadramento do IPI para Cigarros e Bebidas (clEnq) */ + readonly classification?: string | null; + /** @description CNPJ do produtor da mercadoria, quando diferente do emitente. Somente para os casos de exportação direta ou indireta (CNPJProd) */ + readonly producerCNPJ?: string | null; + /** @description Código do selo de controle IPI (cSelo) */ + readonly stampCode?: string | null; + /** + * Format: double + * @description Quantidade de selo de controle (qSelo) + */ + readonly stampQuantity?: number | null; + /** + * Format: double + * @description Valor da BC do IPI (vBC) + */ + readonly base?: number | null; + /** + * Format: double + * @description Alíquota do IPI (pIPI) + */ + readonly rate?: number | null; + /** + * Format: double + * @description Quantidade total na unidade padrão para tributação (somente para os produtos tributados por unidade) (qUnid) + */ + readonly unitQuantity?: number | null; + /** + * Format: double + * @description Valor por Unidade Tributável (vUnid) + */ + readonly unitAmount?: number | null; + /** + * Format: double + * @description Valor IPI (vIPI) + */ + readonly amount?: number | null; + }; + readonly ISSQNTotal: { + /** + * Format: double + * @description Valor Total Serv.Não Tributados p/ ICMS (vServ) + */ + readonly totalServiceNotTaxedICMS?: number | null; + /** + * Format: double + * @description Base de Cálculo do ISS (vBC) + */ + readonly baseRateISS?: number | null; + /** + * Format: double + * @description Valor Total do ISS (vISS) + */ + readonly totalISS?: number | null; + /** + * Format: double + * @description Valor do PIS sobre Serviços (vPIS) + */ + readonly valueServicePIS?: number | null; + /** + * Format: double + * @description Valor da COFINS sobre Serviços (vCOFINS) + */ + readonly valueServiceCOFINS?: number | null; + /** + * Format: date-time + * @description Data Prestação Serviço (dCompet) + */ + readonly provisionService?: string | null; + /** + * Format: double + * @description Valor Dedução para Redução da BC (vDeducao) + */ + readonly deductionReductionBC?: number | null; + /** + * Format: double + * @description Valor Outras Retenções (vOutro) + */ + readonly valueOtherRetention?: number | null; + /** + * Format: double + * @description Valor Desconto Incondicionado (vDescIncond) + */ + readonly discountUnconditional?: number | null; + /** + * Format: double + * @description Valor Desconto Condicionado (vDescCond) + */ + readonly discountConditioning?: number | null; + /** + * Format: double + * @description Valor Total Retenção ISS (vISSRet) + */ + readonly totalRetentionISS?: number | null; + /** + * Format: double + * @description Código Regime Tributação (cRegTrib) + */ + readonly codeTaxRegime?: number | null; + }; + readonly ISSQNTotalResource: { + /** + * Format: double + * @description Valor Total Serv.Não Tributados p/ ICMS (vServ) + */ + readonly totalServiceNotTaxedICMS?: number | null; + /** + * Format: double + * @description Base de Cálculo do ISS (vBC) + */ + readonly baseRateISS?: number | null; + /** + * Format: double + * @description Valor Total do ISS (vISS) + */ + readonly totalISS?: number | null; + /** + * Format: double + * @description Valor do PIS sobre Serviços (vPIS) + */ + readonly valueServicePIS?: number | null; + /** + * Format: double + * @description Valor da COFINS sobre Serviços (vCOFINS) + */ + readonly valueServiceCOFINS?: number | null; + /** + * Format: date-time + * @description Data Prestação Serviço (dCompet) + */ + readonly provisionService?: string | null; + /** + * Format: double + * @description Valor Dedução para Redução da BC (vDeducao) + */ + readonly deductionReductionBC?: number | null; + /** + * Format: double + * @description Valor Outras Retenções (vOutro) + */ + readonly valueOtherRetention?: number | null; + /** + * Format: double + * @description Valor Desconto Incondicionado (vDescIncond) + */ + readonly discountUnconditional?: number | null; + /** + * Format: double + * @description Valor Desconto Condicionado (vDescCond) + */ + readonly discountConditioning?: number | null; + /** + * Format: double + * @description Valor Total Retenção ISS (vISSRet) + */ + readonly totalRetentionISS?: number | null; + /** + * Format: double + * @description Código Regime Tributação (cRegTrib) + */ + readonly codeTaxRegime?: number | null; + }; + /** @description Imposto Seletivo (IS). Informe quando o item estiver sujeito ao IS. */ + readonly ISTaxResource: { + /** @description CST do Imposto Seletivo (IS). Código de Situação Tributária de 3 dígitos (CST). Consulte a tabela de referência. */ + readonly situationCode?: string | null; + /** @description Código de Classificação Tributária do IS (cClassTribIS), 6 dígitos. Consulte a tabela de referência. */ + readonly classificationCode?: string | null; + /** + * Format: double + * @description Base de cálculo do IS (vBCIS). 15 posições, 13 inteiros e 2 decimais. + */ + readonly basis?: number | null; + /** + * Format: double + * @description Alíquota do IS em percentual (pIS). Até 4 casas decimais. + */ + readonly rate?: number | null; + /** + * Format: double + * @description Alíquota específica por unidade (pISEspec), em R$ por unidade tributável. + */ + readonly unitRate?: number | null; + /** @description Unidade tributável usada quando houver alíquota específica por unidade (uTrib). */ + readonly unit?: string | null; + /** + * Format: double + * @description Quantidade tributável usada quando houver alíquota específica por unidade (qTrib). Até 4 casas decimais. + */ + readonly quantity?: number | null; + /** + * Format: double + * @description Valor do Imposto Seletivo (vIS). 15 posições, 13 inteiros e 2 decimais. + */ + readonly amount?: number | null; + }; + /** + * Format: int64 + * @description Código do Município de ocorrência do fato gerador do IBS/CBS (cMunFGIBS). Informar o município de ocorrência do fato gerador do IBS/CBS. Este campo só é preenchido quando "indPres = 5 (Operação presencial, fora do estabelecimento)" e não incluir o endereço do destinatário (Grupo: E05) ou local de entrega (Grupo: G01). + */ + readonly IbsConsumptionCityCode: number | null; + /** + * @description Classificação para cálculo do crédito presumido de IBS para fornecimentos da ZFM (tpCredPresIBSZFM). + * Valores possíveis: + * - `NoPresumedCredit`: Sem Crédito Presumido + * - `FinalConsumptionGoods`: Bens de consumo final (55%) + * - `CapitalGoods`: Bens de capital (75%) + * - `IntermediateGoods`: Bens intermediários (90,25%) + * - `ItAndOtherGoods`: Bens de informática e outros definidos em legislação (100%) + * @enum {string|null} + */ + readonly IbsZfmPresumedCreditClassification: "NoPresumedCredit" | "FinalConsumptionGoods" | "CapitalGoods" | "IntermediateGoods" | "ItAndOtherGoods" | null; + /** + * @description Grupo do ICMS da Operação própria e ST (ICMS). + * **Regra de obrigatoriedade**: deve-se informar **exatamente um** entre `cst` (Regime Normal) ou `csosn` (Simples Nacional). A SEFAZ não aceita ambos preenchidos ou nenhum dos dois. + */ + readonly IcmsTaxResource: { + /** + * @description Origem da mercadoria (orig). + * 0 - Nacional, exceto as indicadas nos códigos 3, 4, 5 e 8; + * 1 - Estrangeira - Importação direta, exceto a indicada no código 6; + * 2 - Estrangeira - Adquirida no mercado interno, exceto a indicada no código 7; + * 3 - Nacional, mercadoria ou bem com Conteúdo de Importação superior a 40% e inferior ou igual a 70%; + * 4 - Nacional, cuja produção tenha sido feita em conformidade com os processos produtivos básicos de que tratam as legislações citadas nos Ajustes; + * 5 - Nacional, mercadoria ou bem com Conteúdo de Importação inferior ou igual a 40%; + * 6 - Estrangeira - Importação direta, sem similar nacional, constante em lista da CAMEX e gás natural; + * 7 - Estrangeira - Adquirida no mercado interno, sem similar nacional, constante lista CAMEX e gás natural; + * 8 - Nacional, mercadoria ou bem com Conteúdo de Importação superior a 70%; + */ + readonly origin: string; + /** @description Tributação do ICMS (CST) — obrigatório quando o emitente é do Regime Normal. Mutuamente exclusivo com `csosn`. */ + readonly cst?: string | null; + /** @description 101- Tributada pelo Simples Nacional com permissão de crédito (v.2.0) (CSOSN). Código de Situação da Operação – Simples Nacional */ + readonly csosn?: string | null; + /** + * @description Modalidade de determinação da BC do ICMS (modBC). + * Margem Valor Agregado (%) = 0 + * Pauta (valor) = 1 + * Preço Tabelado Máximo (valor) = 2 + * Valor da Operação = 3 + */ + readonly baseTaxModality?: string | null; + /** + * Format: double + * @description Valor da BC do ICMS (vBC) + */ + readonly baseTax?: number | null; + /** @description Modalidade de determinação da BC do ICMS ST (modBCST) */ + readonly baseTaxSTModality?: string | null; + /** @description Percentual da Redução de BC do ICMS ST (pRedBCST) */ + readonly baseTaxSTReduction?: string | null; + /** + * Format: double + * @description Valor da BC do ICMS ST (vBCST) + */ + readonly baseTaxST?: number | null; + /** + * Format: double + * @description Percentual da Redução de BC (pRedBC) + */ + readonly baseTaxReduction?: number | null; + /** @description Código de Benefício na UF para Redução da Base de Cálculo (cBenefRBC). Tamanho 8 ou 10, quando exigido pela UF (NT 2019.001). Código de Benefício Fiscal utilizado pelo Estado, aplicado ao item quando houver RBC. Observação: Deve ser utilizado o mesmo código utilizado na EFD e demais declarações nos Estados que o exigem. (Incluído na NT 2019.001) */ + readonly basisBenefitCode?: string | null; + /** + * Format: double + * @description Alíquota do imposto do ICMS ST (pICMSST) + */ + readonly stRate?: number | null; + /** + * Format: double + * @description Valor do ICMS ST (vICMSST) + */ + readonly stAmount?: number | null; + /** + * Format: double + * @description Percentual da margem de valor Adicionado do ICMS ST (pMVAST) + */ + readonly stMarginAmount?: number | null; + /** + * Format: double + * @description Alíquota do imposto (pICMS) + */ + readonly rate?: number | null; + /** + * Format: double + * @description Valor do ICMS (vICMS). O valor do ICMS desonerado será informado apenas nas operações: + * a) com produtos beneficiados com a desoneração condicional do ICMS. + * b) destinadas à SUFRAMA, informando-se o valor que seria devido se não houvesse isenção. + * c) de venda a órgãos da administração pública direta e suas fundações e autarquias com isenção do ICMS. (NT 2011/004) + */ + readonly amount?: number | null; + /** + * Format: double + * @description Percentual da Redução de BC (pICMS) + */ + readonly percentual?: number | null; + /** + * Format: double + * @description Alíquota aplicável de cálculo do crédito (Simples Nacional) (pCredSN). + */ + readonly snCreditRate?: number | null; + /** + * Format: double + * @description Valor crédito do ICMS que pode ser aproveitado nos termos do art. 23 da LC 123 Simples Nacional (vCredICMSSN) + */ + readonly snCreditAmount?: number | null; + /** @description Percentual da margem de valor Adicionado do ICMS ST (pMVAST) */ + readonly stMarginAddedAmount?: string | null; + /** @description Valor do ICMS ST retido (vICMSSTRet) */ + readonly stRetentionAmount?: string | null; + /** @description Valor da BC do ICMS ST retido (vBCSTRet) */ + readonly baseSTRetentionAmount?: string | null; + /** @description Percentual da BC operação própria (pBCOp). Percentual para determinação do valor da Base de Cálculo da operação própria (v2.0) */ + readonly baseTaxOperationPercentual?: string | null; + /** @description UF para qual é devido o ICMS ST (UFST). Sigla da UF para qual é devido o ICMS ST da operação (v2.0) */ + readonly ufst?: string | null; + /** @description Motivo Desoneração ICMS (motDesICMS) */ + readonly amountSTReason?: string | null; + /** @description Valor da BC do ICMS ST retido (vBCSTRet) */ + readonly baseSNRetentionAmount?: string | null; + /** @description Valor do ICMS ST retido (vICMSSTRet) */ + readonly snRetentionAmount?: string | null; + /** @description Valor do ICMS da Operação (vICMSOp) */ + readonly amountOperation?: string | null; + /** @description Percentual do Diferimento (pDif) */ + readonly percentualDeferment?: string | null; + /** @description Valor do ICMS Diferido (vICMSDif) */ + readonly baseDeferred?: string | null; + /** + * Format: double + * @description Valor ICMS Desonerado (vICMSDeson) + */ + readonly exemptAmount?: number | null; + /** @description Motivo da desoneração do ICMS (motDesICMS) */ + readonly exemptReason?: components["schemas"]["ExemptReason"]; + /** + * Format: double + * @description Valor ICMS Desonerado (vICMSSTDeson) + */ + readonly exemptAmountST?: number | null; + /** @description Motivo da desoneração do ICMS ST (motDesICMSST) */ + readonly exemptReasonST?: components["schemas"]["ExemptReason"]; + /** + * Format: double + * @description Percentual do FCP - Valor do ICMS relativo ao Fundo de Combate à Pobreza (pFCP) + */ + readonly fcpRate?: number | null; + /** + * Format: double + * @description Valor Total do FCP - Valor do ICMS relativo ao Fundo de Combate à Pobreza (vFCP) + */ + readonly fcpAmount?: number | null; + /** + * Format: double + * @description Percentual do FCP retido por ST - Valor do ICMS relativo ao Fundo de Combate à Pobreza retido por substituição tributária (pFCPST) + */ + readonly fcpstRate?: number | null; + /** + * Format: double + * @description Valor Total do FCP retido por ST - Valor do ICMS relativo ao Fundo de Combate à Pobreza retido por substituição tributária (vFCPST) + */ + readonly fcpstAmount?: number | null; + /** + * Format: double + * @description Percentual do FCP retido por anteriormente por ST - Valor do ICMS relativo ao Fundo de Combate à Pobreza retido anteriormente por substituição tributária (pFCPSTRet) + */ + readonly fcpstRetRate?: number | null; + /** + * Format: double + * @description Valor Total do FCP retido por anteriormente por ST - Valor do ICMS relativo ao Fundo de Combate à Pobreza retido anteriormente por substituição tributária (vFCPSTRet) + */ + readonly fcpstRetAmount?: number | null; + /** + * Format: double + * @description Informar o valor da Base de Cálculo do FCP (vBCFCPST) + */ + readonly baseTaxFCPSTAmount?: number | null; + /** + * Format: double + * @description Valor do ICMS próprio do Substituto (tag: vICMSSubstituto) + */ + readonly substituteAmount?: number | null; + /** + * Format: double + * @description N26a - Alíquota suportada pelo Consumidor Final (pST). Deve ser informada a alíquota do cálculo do ICMS-ST, já incluso o FCP caso incida sobre a mercadoria + */ + readonly stFinalConsumerRate?: number | null; + /** + * Format: double + * @description N34 - Percentual de redução da base de cálculo efetiva, caso estivesse submetida ao regime comum de tributação (pRedBCEfet) + */ + readonly effectiveBaseTaxReductionRate?: number | null; + /** + * Format: double + * @description N35 - Valor da base de cálculo efetiva, caso estivesse submetida ao regime comum de tributação (vBCEfet) + */ + readonly effectiveBaseTaxAmount?: number | null; + /** + * Format: double + * @description N36 - Alíquota do ICMS efetiva, caso estivesse submetida ao regime comum de tributação (pICMSEFET) + */ + readonly effectiveRate?: number | null; + /** + * Format: double + * @description N37 - Valor do ICMS efetivo, caso estivesse submetida ao regime comum de tributação (vICMSEFET) + */ + readonly effectiveAmount?: number | null; + /** @description Indicador de dedução do ICMS desonerado (indDeducao) */ + readonly deductionIndicator?: components["schemas"]["DuductionIndicator"]; + }; + /** @description Declaração Importação (DI) */ + readonly ImportDeclarationResource: { + /** @description Número do Documento de Importação da DI/DSI/DA (nDI) */ + readonly code?: string | null; + /** + * Format: date-time + * @description Data de Registro da DI/DSI/DA (dDI) + */ + readonly registeredOn?: string | null; + /** @description Local de desembaraço (xLocDesemb) */ + readonly customsClearanceName?: string | null; + /** @description Sigla da UF onde ocorreu o Desembaraço Aduaneiro (UFDesemb) */ + readonly customsClearanceState?: components["schemas"]["StateCode"]; + /** + * Format: date-time + * @description Data do Desembaraço Aduaneiro (dDesemb) + */ + readonly customsClearancedOn?: string | null; + /** @description Adições (adi) */ + readonly additions?: readonly components["schemas"]["AdditionResource"][] | null; + /** @description Código do exportador (cExportador) */ + readonly exporter?: string | null; + /** @description Via de transporte internacional informada na Declaração de Importação (DI) (tpViaTransp) */ + readonly internationalTransport?: components["schemas"]["InternationalTransportType"]; + /** + * Format: double + * @description Valor Adicional ao frete para renovação de marinha mercante (vAFRMM) + */ + readonly afrmmAmount?: number | null; + /** @description Forma de importação quanto a intermediação (tpIntermedio) */ + readonly intermediation?: components["schemas"]["IntermediationType"]; + /** @description CNPJ/CPF do adquirente ou do encomendante (CNPJ ou CPF) */ + readonly acquirerFederalTaxNumber?: string | null; + /** @description Sigla da UF do adquirente ou do encomendante (UFTerceiro) */ + readonly stateThird?: string | null; + }; + /** + * @description Tipo de Integração para pagamento (tpIntegra). + * Valores possíveis: + * - `Integrated`: 1 - Pagamento integrado com o sistema de automação da empresa (ex: equipamento TEF, Comércio Eletrônico) + * - `NotIntegrated`: 2 - Pagamento não integrado com o sistema de automação da empresa (ex: equipamento POS) + * @enum {string} + */ + readonly IntegrationPaymentType: "Integrated" | "NotIntegrated"; + /** @description Grupo de Informações do Intermediador da Transação (infIntermed) */ + readonly IntermediateResource: { + /** + * Format: int64 + * @description CNPJ do Intermediador da Transação (agenciador, plataforma de delivery, marketplace e similar) de serviços e de negócios (CNPJ). + */ + readonly federalTaxNumber?: number | null; + /** @description Identificador cadastrado no intermediador (idCadIntTran) */ + readonly identifier?: string | null; + }; + /** + * @description Forma de importação quanto a intermediação (tpIntermedio). + * Valores possíveis: + * - `None`: Não definido + * - `ByOwn`: Importação por conta própria + * - `ImportOnBehalf`: Importação por conta e ordem de terceiro + * - `ByOrder`: Importação por encomenda + * @enum {string} + */ + readonly IntermediationType: "None" | "ByOwn" | "ImportOnBehalf" | "ByOrder"; + /** + * @description Via de transporte internacional (tpViaTransp). + * Valores possíveis: + * - `None`: Não definido + * - `Maritime`: Marítima + * - `River`: Fluvial + * - `Lake`: Lacustre + * - `Airline`: Aérea + * - `Postal`: Postal + * - `Railway`: Ferroviária + * - `Highway`: Rodoviária + * - `Network`: Conduto / Rede Transmissão + * - `Own`: Meios Próprios + * - `Ficta`: Entrada / Saída ficta + * - `Courier`: Serviço de Courrier + * - `Handcarry`: Em mãos + * @enum {string} + */ + readonly InternationalTransportType: "None" | "Maritime" | "River" | "Lake" | "Airline" | "Postal" | "Railway" | "Highway" | "Network" | "Own" | "Ficta" | "Courier" | "Handcarry"; + readonly InvoiceEventsResource: { + /** @description Lista de Eventos ocorridos na Nota Fiscal */ + readonly events?: readonly components["schemas"]["ActivityResource"][] | null; + /** @description Identificador de possibilidade de mais itens. */ + readonly hasMore?: boolean | null; + /** @description Identificação */ + readonly id?: string | null; + /** @description Identificador da Conta */ + readonly accountId?: string | null; + /** @description Identificador da Empresa */ + readonly companyId?: string | null; + }; + readonly InvoiceEventsResourceBase: { + /** @description Lista de Eventos ocorridos na Nota Fiscal */ + readonly events?: readonly components["schemas"]["ActivityResource"][] | null; + /** @description Identificador de possibilidade de mais itens. */ + readonly hasMore?: boolean | null; + }; + /** @description Grupo do detalhamento de Produtos e Serviços da NF-e */ + readonly InvoiceItemResource: { + /** @description Código do produto ou serviço (cProd) */ + readonly code: string; + /** @description GTIN (Global Trade Item Number) do produto, antigo código EAN ou código de barras (cEAN) */ + readonly codeGTIN?: string | null; + /** @description Descrição do produto ou serviço (xProd) */ + readonly description: string; + /** @description Código NCM com 8 dígitos ou 2 dígitos (gênero) (NCM) */ + readonly ncm: string; + /** @description Nomenclatura de Valor Aduaneiro e Estatístico (NVE) */ + readonly nve?: readonly string[] | null; + /** @description Código Exceção da Tabela de IPI (EXTIPI) */ + readonly extipi?: string | null; + /** + * Format: int64 + * @description Código Fiscal de Operações e Prestações (CFOP) + */ + readonly cfop?: number | null; + /** @description Unidade Comercial (uCom) */ + readonly unit: string; + /** + * Format: double + * @description Quantidade Comercial (qCom) + */ + readonly quantity: number; + /** + * Format: double + * @description Valor Unitário de Comercialização (vUnCom) + */ + readonly unitAmount: number; + /** + * Format: double + * @description Valor Total Bruto dos Produtos ou Serviços (vProd) + */ + readonly totalAmount: number; + /** @description GTIN (Global Trade Item Number) da unidade tributável, antigo código EAN ou código de barras (cEANTrib) */ + readonly codeTaxGTIN?: string | null; + /** @description Unidade Tributável (uTrib) */ + readonly unitTax: string; + /** + * Format: double + * @description Quantidade Tributável (qTrib) + */ + readonly quantityTax?: number | null; + /** + * Format: double + * @description Valor Unitário de tributação (vUnTrib) + */ + readonly taxUnitAmount?: number | null; + /** + * Format: double + * @description Valor Total do Frete (vFrete) + */ + readonly freightAmount?: number | null; + /** + * Format: double + * @description Valor Total do Seguro (vSeg) + */ + readonly insuranceAmount?: number | null; + /** + * Format: double + * @description Valor do Desconto (vDesc) + */ + readonly discountAmount?: number | null; + /** + * Format: double + * @description Outras despesas acessórias (vOutro) + */ + readonly othersAmount?: number | null; + /** + * @description Indica se valor do Item (vProd) entra no valor total da NF-e (vProd) (indTot) + * @default true + */ + readonly totalIndicator: boolean | null; + /** @description Indica fornecimento de bem móvel usado (indBemMovelUsado) */ + readonly usedMovableAssetIndicator?: components["schemas"]["UsedMovableAssetIndicator"]; + /** @description CEST - Código especificador da substituição tributária (CEST) */ + readonly cest?: string | null; + /** @description Tributos incidentes no Produto ou Serviço (imposto) */ + readonly tax: components["schemas"]["InvoiceItemTaxResource"]; + /** @description Informações Adicionais do Produto (infAdProd) */ + readonly additionalInformation?: string | null; + /** + * Format: double + * @description Valor total do item (vItem). + */ + readonly itemAmount?: number | null; + /** @description Número do pedido de compra (xPed) */ + readonly numberOrderBuy?: string | null; + /** + * Format: int32 + * @description Item do Pedido de Compra (nItemPed) + */ + readonly itemNumberOrderBuy?: number | null; + /** @description Número de controle da FCI - Ficha de Conteúdo de Importação (nFCI) */ + readonly importControlSheetNumber?: string | null; + /** + * @description Detalhamento de Veículos novos (veicProd). Preencher exclusivamente quando o item da nota fiscal é um veículo 0 km. + * **Mutuamente exclusivo** com `fuelDetail` e `medicineDetail` — cada item da nota pode conter no máximo um grupo de produto específico. + */ + readonly vehicleDetail?: components["schemas"]["VehicleDetailResource"]; + /** @description Detalhamento de Combustível (comb) */ + readonly fuelDetail?: components["schemas"]["FuelResource"]; + /** @description Código de Benefício Fiscal na UF aplicado ao item (cBenef) */ + readonly benefit?: string | null; + /** @description Informações de Crédito Presumido por item (gCred) */ + readonly presumedCredit?: components["schemas"]["PresumedCreditResource"]; + /** @description Classificação conforme percentuais definidos no art. 450, § 1º, da LC 214/25 para o cálculo do crédito presumido (tpCredPresIBSZFM) */ + readonly ibsZfmPresumedCreditClassification?: components["schemas"]["IbsZfmPresumedCreditClassification"]; + /** @description Declaração Importação (DI) */ + readonly importDeclarations?: readonly components["schemas"]["ImportDeclarationResource"][] | null; + /** @description Grupo de informações de exportação para o item (detExport) */ + readonly exportDetails?: readonly components["schemas"]["ExportDetailResource"][] | null; + readonly referencedDFe?: components["schemas"]["ReferencedDFeResource"]; + /** @description Grupo de informações fiscais para o cálculo automático de impostos (infFiscal). Se este grupo for preenchido, o grupo `tax.IBSCBS` será calculado automaticamente. Se não for preenchido, o cálculo automático do grupo `tax.IBSCBS` torna-se opcional. */ + readonly taxDetermination?: components["schemas"]["TaxDeterminationResource"]; + }; + readonly InvoiceItemTaxResource: { + /** + * Format: double + * @description Valor aproximado total de tributos federais, estaduais e municipais (vTotTrib) + */ + readonly totalTax?: number | null; + /** @description Grupo do ICMS da Operação própria e ST (ICMS) */ + readonly icms: components["schemas"]["IcmsTaxResource"]; + /** @description Grupo do IPI (IPI) */ + readonly ipi?: components["schemas"]["IPITaxResource"]; + /** @description Grupo do Imposto de Importação (II) */ + readonly ii?: components["schemas"]["IITaxResource"]; + /** @description Grupo do PIS (PIS) */ + readonly pis?: components["schemas"]["PISTaxResource"]; + /** @description Grupo do COFINS (COFINS) */ + readonly cofins?: components["schemas"]["CofinsTaxResource"]; + /** @description Grupo de Tributação do ICMS de Destino da UF (ICMSUFDest) */ + readonly icmsDestination?: components["schemas"]["ICMSUFDestinationTaxResource"]; + /** @description Imposto Seletivo (IS) */ + readonly IS?: components["schemas"]["ISTaxResource"]; + /** @description Grupo de Informações do IBS e CBS */ + readonly IBSCBS?: components["schemas"]["IBSCBSTaxResource"]; + /** @description Ajuste de Competência (gAjusteCompet) */ + readonly competenceAdjustment?: components["schemas"]["CompetenceAdjustmentResource"]; + }; + readonly InvoiceItemsResource: { + /** @description Identificador da Conta */ + readonly accountId?: string | null; + /** @description Identificador da Empresa */ + readonly companyId?: string | null; + /** @description Identificador da Nota Fiscal */ + readonly id?: string | null; + /** @description Detalhamento de Produtos e Serviços (det) - Lista de Items da Nota Fiscal */ + readonly items?: readonly components["schemas"]["InvoiceItemResource"][] | null; + /** @description Identifica se existem mais items a serem consultados */ + readonly hasMore?: boolean | null; + }; + readonly InvoiceResource: { + /** @description Identificador único */ + readonly id?: string | null; + /** + * Format: int32 + * @description Série do Documento Fiscal (serie) + */ + readonly serie?: number | null; + /** + * Format: int64 + * @description Número do Documento Fiscal (nNF) + */ + readonly number?: number | null; + /** @description Status da NF-e (cStat) */ + readonly status?: components["schemas"]["InvoiceStatus"]; + /** @description Informações do Protocolo de Autorização (protNFe) */ + readonly authorization?: components["schemas"]["AuthorizationResource"]; + /** @description Detalhes da contingência (dhCont) */ + readonly contingencyDetails?: components["schemas"]["ContingencyDetails"]; + /** @description Descrição da Natureza da Operação (natOp) */ + readonly operationNature?: string | null; + /** + * Format: date-time + * @description Data de criação + */ + readonly createdOn?: string | null; + /** + * Format: date-time + * @description Data de modificação + */ + readonly modifiedOn?: string | null; + /** + * Format: date-time + * @description Data e Hora de Saída ou da Entrada da Mercadoria/Produto (dhSaiEnt). Data e hora no formato UTC (Universal Coordinated Time): AAAA-MM-DDThh:mm:ssTZD. + */ + readonly operationOn?: string | null; + /** @description Tipo do Documento Fiscal (tpNF) */ + readonly operationType?: components["schemas"]["OperationType"]; + /** @description Tipo de Ambiente (tpAmb) */ + readonly environmentType?: components["schemas"]["EnvironmentType"]; + /** @description Finalidade da emissão da NF-e (finNFe) */ + readonly purposeType?: components["schemas"]["PurposeType"]; + /** @description Identificação do Emitente (emit) */ + readonly issuer?: components["schemas"]["IssuerResource"]; + /** @description Identificação do Destinatário (dest) */ + readonly buyer?: components["schemas"]["BuyerResource"]; + /** @description Grupo de Valores Totais da NF-e (total) */ + readonly totals?: components["schemas"]["TotalResource"]; + /** @description Grupo de Informações do Transporte da NF-e (transp) */ + readonly transport?: components["schemas"]["TransportInformationResource"]; + /** @description Informações adicionais da NF-e (infAdic) */ + readonly additionalInformation?: components["schemas"]["AdditionalInformationResource"]; + /** @description Informações de exportação (exporta) */ + readonly export?: components["schemas"]["ExportResource"]; + /** @description Grupo Cobrança (cobr) */ + readonly billing?: components["schemas"]["BillingResource"]; + /** @description Grupo de Formas de Pagamento (pag) */ + readonly payment?: readonly components["schemas"]["PaymentResource"][]; + /** @description Grupo de Informações do Intermediador da Transação (infIntermed) */ + readonly transactionIntermediate?: components["schemas"]["IntermediateResource"]; + /** @description Identificação do Local de entrega (entrega) */ + readonly delivery?: components["schemas"]["DeliveryInformationResource"]; + /** @description Identificação do Local de retirada (retirada) */ + readonly withdrawal?: components["schemas"]["WithdrawalInformationResource"]; + /** @description Últimos eventos da NF-e (eventos) */ + readonly lastEvents?: components["schemas"]["InvoiceEventsResourceBase"]; + }; + /** + * @description Status da NF-e. + * Valores possíveis: + * - `None`: Não definido + * - `Created`: Criada + * - `Processing`: Processando + * - `Issued`: Emitida + * - `IssuedContingency`: Emitida em Contingência + * - `Cancelled`: Cancelada + * - `Disabled`: Inutilizada + * - `IssueDenied`: Emissão Denegada + * - `Error`: Erro + * @enum {string} + */ + readonly InvoiceStatus: "None" | "Created" | "Processing" | "Issued" | "IssuedContingency" | "Cancelled" | "Disabled" | "IssueDenied" | "Error"; + readonly InvoiceWithoutEventsResource: { + /** @description Identificador único */ + readonly id?: string | null; + /** + * Format: int32 + * @description Série do Documento Fiscal (serie) + */ + readonly serie?: number | null; + /** + * Format: int64 + * @description Número do Documento Fiscal (nNF) + */ + readonly number?: number | null; + /** @description Status da NF-e (cStat) */ + readonly status?: components["schemas"]["InvoiceStatus"]; + /** @description Informações do Protocolo de Autorização (protNFe) */ + readonly authorization?: components["schemas"]["AuthorizationResource"]; + /** @description Detalhes da contingência (dhCont) */ + readonly contingencyDetails?: components["schemas"]["ContingencyDetails"]; + /** @description Descrição da Natureza da Operação (natOp) */ + readonly operationNature?: string | null; + /** + * Format: date-time + * @description Data de criação + */ + readonly createdOn?: string | null; + /** + * Format: date-time + * @description Data de modificação + */ + readonly modifiedOn?: string | null; + /** + * Format: date-time + * @description Data e Hora de Saída ou da Entrada da Mercadoria/Produto (dhSaiEnt). Data e hora no formato UTC (Universal Coordinated Time): AAAA-MM-DDThh:mm:ssTZD. + */ + readonly operationOn?: string | null; + /** @description Tipo do Documento Fiscal (tpNF) */ + readonly operationType?: components["schemas"]["OperationType"]; + /** @description Tipo de Ambiente (tpAmb) */ + readonly environmentType?: components["schemas"]["EnvironmentType"]; + /** @description Finalidade da emissão da NF-e (finNFe) */ + readonly purposeType?: components["schemas"]["PurposeType"]; + /** @description Identificação do Emitente (emit) */ + readonly issuer?: components["schemas"]["IssuerResource"]; + /** @description Identificação do Destinatário (dest) */ + readonly buyer?: components["schemas"]["BuyerResource"]; + /** @description Grupo de Valores Totais da NF-e (total) */ + readonly totals?: components["schemas"]["TotalResource"]; + /** @description Grupo de Informações do Transporte da NF-e (transp) */ + readonly transport?: components["schemas"]["TransportInformationResource"]; + /** @description Informações adicionais da NF-e (infAdic) */ + readonly additionalInformation?: components["schemas"]["AdditionalInformationResource"]; + /** @description Informações de exportação (exporta) */ + readonly export?: components["schemas"]["ExportResource"]; + /** @description Grupo Cobrança (cobr) */ + readonly billing?: components["schemas"]["BillingResource"]; + /** @description Grupo de Formas de Pagamento (pag) */ + readonly payment?: readonly components["schemas"]["PaymentResource"][]; + /** @description Grupo de Informações do Intermediador da Transação (infIntermed) */ + readonly transactionIntermediate?: components["schemas"]["IntermediateResource"]; + /** @description Identificação do Local de entrega (entrega) */ + readonly delivery?: components["schemas"]["DeliveryInformationResource"]; + /** @description Identificação do Local de retirada (retirada) */ + readonly withdrawal?: components["schemas"]["WithdrawalInformationResource"]; + }; + readonly IssuerFromRequestResource: { + /** @description IE do Substituto Tributário da UF de destino da mercadoria, quando houver a retenção do ICMS ST para a UF de destino. (IEST) */ + readonly stStateTaxNumber?: string | null; + }; + /** @description Grupo de identificação do emitente da NF-e */ + readonly IssuerResource: { + /** @description Identificador da Conta */ + readonly accountId?: string | null; + /** @description Identificação */ + readonly id?: string | null; + /** @description Nome ou Razão Social (xNome) */ + readonly name?: string | null; + /** + * Format: int64 + * @description CNPJ ou CPF (CNPJ/CPF) + */ + readonly federalTaxNumber?: number | null; + /** @description Email (email) */ + readonly email?: string | null; + /** @description Dados do Endereço (enderEmit) */ + readonly address?: components["schemas"]["AddressResource"]; + /** @description Tipo da pessoa (indIEDest) */ + readonly type?: components["schemas"]["PersonType"]; + /** @description Nome Fantasia (xFant) */ + readonly tradeName?: string | null; + /** + * Format: date-time + * @description Data abertura da empresa (dIniAtiv) + */ + readonly openningDate?: string | null; + /** @description Regime de tributação (CRT) */ + readonly taxRegime?: components["schemas"]["TaxRegime"]; + /** @description Regime especial de tributação (indRegTrib) */ + readonly specialTaxRegime?: components["schemas"]["SpecialTaxRegime"]; + /** @description Natureza jurídica (natJuridica) */ + readonly legalNature?: components["schemas"]["LegalNature"]; + /** @description Atividades da Empresa (CNAE) */ + readonly economicActivities?: readonly components["schemas"]["EconomicActivityResource"][] | null; + /** + * Format: int64 + * @description Número de Inscrição na Junta Comercial (nIRE) + */ + readonly companyRegistryNumber?: number | null; + /** + * Format: int64 + * @description Número de Inscrição na SEFAZ (IE) + */ + readonly regionalTaxNumber?: number | null; + /** + * Format: int64 + * @description Inscrição Estadual do Substituto Tributário (IEST) + */ + readonly regionalSTTaxNumber?: number | null; + /** @description Número de Inscrição na Prefeitura (IM/CCM) */ + readonly municipalTaxNumber?: string | null; + /** @description IE do Substituto Tributário (IEST) */ + readonly stStateTaxNumber?: string | null; + }; + /** + * @description Código da Natureza Jurídica da empresa (natJuridica), conforme tabela do IBGE. + * Valores possíveis: + * - `EmpresaPublica`: Empresa Pública + * - `SociedadeEconomiaMista`: Sociedade de Economia Mista + * - `SociedadeAnonimaAberta`: Sociedade Anônima Aberta + * - `SociedadeAnonimaFechada`: Sociedade Anônima Fechada + * - `SociedadeEmpresariaLimitada`: Sociedade Empresária Limitada (LTDA) + * - `SociedadeEmpresariaEmNomeColetivo`: Sociedade Empresária em Nome Coletivo + * - `SociedadeEmpresariaEmComanditaSimples`: Sociedade Empresária em Comandita Simples + * - `SociedadeEmpresariaEmComanditaporAcoes`: Sociedade Empresária em Comandita por Ações + * - `SociedadeemContaParticipacao`: Sociedade em Conta de Participação + * - `Empresario`: Empresário Individual + * - `Cooperativa`: Cooperativa + * - `ConsorcioSociedades`: Consórcio de Sociedades + * - `GrupoSociedades`: Grupo de Sociedades + * - `EmpresaDomiciliadaExterior`: Empresa Domiciliada no Exterior + * - `ClubeFundoInvestimento`: Clube/Fundo de Investimento + * - `SociedadeSimplesPura`: Sociedade Simples Pura + * - `SociedadeSimplesLimitada`: Sociedade Simples Limitada + * - `SociedadeSimplesEmNomeColetivo`: Sociedade Simples em Nome Coletivo + * - `SociedadeSimplesEmComanditaSimples`: Sociedade Simples em Comandita Simples + * - `EmpresaBinacional`: Empresa Binacional + * - `ConsorcioEmpregadores`: Consórcio de Empregadores + * - `ConsorcioSimples`: Consórcio Simples + * - `EireliNaturezaEmpresaria`: EIRELI de Natureza Empresária + * - `EireliNaturezaSimples`: EIRELI de Natureza Simples + * - `ServicoNotarial`: Serviço Notarial e Registral (Cartórios) + * - `FundacaoPrivada`: Fundação Privada + * - `ServicoSocialAutonomo`: Serviço Social Autônomo + * - `CondominioEdilicio`: Condomínio Edilício + * - `ComissaoConciliacaoPrevia`: Comissão de Conciliação Prévia + * - `EntidadeMediacaoArbitragem`: Entidade de Mediação e Arbitragem + * - `PartidoPolitico`: Partido Político + * - `EntidadeSindical`: Entidade Sindical + * - `EstabelecimentoBrasilFundacaoAssociacaoEstrangeiras`: Estabelecimento no Brasil de Fundação ou Associação Estrangeiras + * - `FundacaoAssociacaoDomiciliadaExterior`: Fundação ou Associação Domiciliada no Exterior + * - `OrganizacaoReligiosa`: Organização Religiosa + * - `ComunidadeIndigena`: Comunidade Indígena + * - `FundoPrivado`: Fundo Privado + * - `AssociacaoPrivada`: Associação Privada + * @enum {string} + */ + readonly LegalNature: "EmpresaPublica" | "SociedadeEconomiaMista" | "SociedadeAnonimaAberta" | "SociedadeAnonimaFechada" | "SociedadeEmpresariaLimitada" | "SociedadeEmpresariaEmNomeColetivo" | "SociedadeEmpresariaEmComanditaSimples" | "SociedadeEmpresariaEmComanditaporAcoes" | "SociedadeemContaParticipacao" | "Empresario" | "Cooperativa" | "ConsorcioSociedades" | "GrupoSociedades" | "EmpresaDomiciliadaExterior" | "ClubeFundoInvestimento" | "SociedadeSimplesPura" | "SociedadeSimplesLimitada" | "SociedadeSimplesEmNomeColetivo" | "SociedadeSimplesEmComanditaSimples" | "EmpresaBinacional" | "ConsorcioEmpregadores" | "ConsorcioSimples" | "EireliNaturezaEmpresaria" | "EireliNaturezaSimples" | "ServicoNotarial" | "FundacaoPrivada" | "ServicoSocialAutonomo" | "CondominioEdilicio" | "ComissaoConciliacaoPrevia" | "EntidadeMediacaoArbitragem" | "PartidoPolitico" | "EntidadeSindical" | "EstabelecimentoBrasilFundacaoAssociacaoEstrangeiras" | "FundacaoAssociacaoDomiciliadaExterior" | "OrganizacaoReligiosa" | "ComunidadeIndigena" | "FundoPrivado" | "AssociacaoPrivada"; + /** @description Deferimento na tributação monofásica (para biocombustíveis). */ + readonly MonophaseDefermentTaxResource: { + /** + * Format: double + * @description Percentual de diferimento do IBS monofásico (pDifIBS). + */ + readonly ibsRate?: number | null; + /** + * Format: double + * @description Valor do diferimento IBS monofásico (vIBSMonoDif). + */ + readonly ibsAmount?: number | null; + /** + * Format: double + * @description Percentual de diferimento do CBS monofásico (pDifCBS). + */ + readonly cbsRate?: number | null; + /** + * Format: double + * @description Valor do diferimento CBS monofásico (vCBSMonoDif). + */ + readonly cbsAmount?: number | null; + }; + /** @description Grupo de Informações do IBS e CBS sobre transações monofásicas */ + readonly MonophaseIBSCBSTaxResource: { + /** @description Informações de tributação monofásica padrão para IBS/CBS (gMonofasicoPadrao). */ + readonly standart?: components["schemas"]["MonophaseStandardTaxResource"]; + /** @description Tributação monofásica sujeita à retenção (gMonofasicoRetido). */ + readonly withholding?: components["schemas"]["MonophaseWithholdingTaxResource"]; + /** @description Tributação monofásica previamente retida (gMonofasicoRetidoAnt). */ + readonly previouslyWithheld?: components["schemas"]["MonophasePreviouslyWithheldTaxResource"]; + /** @description Deferimento na tributação monofásica (para biocombustíveis) (gMonofasicoDif). */ + readonly deferment?: components["schemas"]["MonophaseDefermentTaxResource"]; + /** + * Format: double + * @description Total de IBS monofásico do item (vTotIBSMonoItem). + */ + readonly ibsAmount?: number | null; + /** + * Format: double + * @description Total de CBS monofásico do item (vTotCBSMonoItem). + */ + readonly cbsAmount?: number | null; + }; + /** @description Tributação monofásica previamente retida. */ + readonly MonophasePreviouslyWithheldTaxResource: { + /** + * Format: double + * @description Quantidade base previamente retida (qBCMonoRet). + */ + readonly quantityBasis?: number | null; + /** + * Format: double + * @description Alíquota ad rem IBS previamente retida (adRemIBSRet). + */ + readonly ibsAdRemRate?: number | null; + /** + * Format: double + * @description Valor IBS previamente retido (vIBSMonoRet). + */ + readonly ibsAmount?: number | null; + /** + * Format: double + * @description Alíquota ad rem CBS previamente retida (adRemCBSRet). + */ + readonly cbsAdRemRate?: number | null; + /** + * Format: double + * @description Valor CBS previamente retido (vCBSMonoRet). + */ + readonly cbsAmount?: number | null; + }; + /** @description Informações de tributação monofásica padrão para IBS/CBS. */ + readonly MonophaseStandardTaxResource: { + /** + * Format: double + * @description Quantidade base tributada na monofásica (qBCMono). + */ + readonly quantityBasis?: number | null; + /** + * Format: double + * @description Alíquota ad rem IBS (adRemIBS). + */ + readonly ibsAdRemRate?: number | null; + /** + * Format: double + * @description Alíquota ad rem CBS (adRemCBS). + */ + readonly cbsAdRemRate?: number | null; + /** + * Format: double + * @description Valor IBS monofásico (vIBSMono). + */ + readonly ibsAmount?: number | null; + /** + * Format: double + * @description Valor CBS monofásico (vCBSMono). + */ + readonly cbsAmount?: number | null; + }; + /** @description Tributação monofásica sujeita à retenção. */ + readonly MonophaseWithholdingTaxResource: { + /** + * Format: double + * @description Quantidade base tributada na retenção monofásica (qBCMonoReten). + */ + readonly quantityBasis?: number | null; + /** + * Format: double + * @description Alíquota ad rem IBS retida (adRemIBSReten). + */ + readonly ibsAdRemRate?: number | null; + /** + * Format: double + * @description Valor IBS monofásico retido (vIBSMonoReten). + */ + readonly ibsAmount?: number | null; + /** + * Format: double + * @description Alíquota ad rem CBS retida (adRemCBSReten). + */ + readonly cbsAdRemRate?: number | null; + /** + * Format: double + * @description Valor CBS monofásico retido (vCBSMonoReten). + */ + readonly cbsAmount?: number | null; + }; + /** @description Informações sobre o crédito presumido de IBS para fornecimentos (gCredPresOper). */ + readonly OperationalPresumedCreditResource: { + /** + * Format: double + * @description Valor da Base de Cálculo do Crédito Presumido da Operação (vBC) + */ + readonly basis?: number | null; + /** @description Código de Classificação do Crédito Presumido (cCredPres). */ + readonly classificationCode?: components["schemas"]["PresumedCreditClassificationCode"]; + /** @description Grupo de Informações do Crédito Presumido referente ao IBS (gIBSCredPres) */ + readonly ibs?: components["schemas"]["PresumedCreditDetailsResource"]; + /** @description Grupo de Informações do Crédito Presumido referente a CBS (gCBSCredPres) */ + readonly cbs?: components["schemas"]["PresumedCreditDetailsResource"]; + }; + /** + * @description Tipo de Operação (tpNF). + * Valores possíveis: + * - `Outgoing`: Saída + * - `Incoming`: Entrada + * @default Outgoing + * @enum {string} + */ + readonly OperationType: "Outgoing" | "Incoming"; + /** @description Grupo do PIS */ + readonly PISTaxResource: { + /** @description Código de Situação Tributária do PIS (CST) */ + readonly cst?: string | null; + /** + * Format: double + * @description Valor da Base de Cálculo do PIS (vBC) + */ + readonly baseTax?: number | null; + /** + * Format: double + * @description Alíquota do PIS (em percentual) (pPIS) + */ + readonly rate?: number | null; + /** + * Format: double + * @description Valor do PIS (vPIS) + */ + readonly amount?: number | null; + /** + * Format: double + * @description Quantidade Vendida (qBCProd) + */ + readonly baseTaxProductQuantity?: number | null; + /** + * Format: double + * @description Alíquota do PIS (em reais) (vAliqProd) + */ + readonly productRate?: number | null; + }; + readonly PaymentDetailResource: { + /** @description Forma de pagamento (tPag) */ + readonly method: components["schemas"]["PaymentMethod"]; + /** @description Descrição do meio de pagamento (xPag) */ + readonly methodDescription?: string | null; + /** @description Indicador da forma de pagamento (indPag) */ + readonly paymentType?: components["schemas"]["PaymentType"]; + /** + * Format: double + * @description Valor do Pagamento (vPag) + */ + readonly amount: number; + /** @description Grupo de Cartões (card) */ + readonly card?: components["schemas"]["CardResource"]; + /** + * Format: date-time + * @description Data do pagamento (dPag) - Data e hora no formato UTC (Universal Coordinated Time): AAAA-MM-DDThh:mm:ssTZD + */ + readonly paymentDate?: string | null; + /** @description CNPJ transacional do pagamento (CNPJPag) */ + readonly federalTaxNumberPag?: string | null; + /** @description UF do CNPJ do estabelecimento onde o pagamento foi processado/transacionado/recebido (UFPag) */ + readonly statePag?: string | null; + }; + /** + * @description Forma de pagamento (tPag). + * Valores possíveis: + * - `Cash`: 01 - Dinheiro + * - `Cheque`: 02 - Cheque + * - `CreditCard`: 03 - Cartão de Crédito + * - `DebitCard`: 04 - Cartão de Débito + * - `StoreCredict`: 05 - Crédito Loja + * - `FoodVouchers`: 10 - Vale Alimentação + * - `MealVouchers`: 11 - Vale Refeição + * - `GiftVouchers`: 12 - Vale Presente + * - `FuelVouchers`: 13 - Vale Combustível + * - `MercantileDuplicate`: 14 - Duplicata Mercantil + * - `BankBill`: 15 - Boleto Bancário + * - `BankDeposit`: 16 - Depósito Bancário + * - `InstantPayment`: 17 - Pagamento Instantâneo (PIX) + * - `WireTransfer`: 18 - Transferência bancária, Carteira Digital + * - `Cashback`: 19 - Programa de fidelidade, Cashback, Crédito Virtual + * - `StaticInstantPayment`: 20 - Pagamento Instantâneo (PIX) Estático + * - `StoreCredit`: 21 - Crédito em Loja + * - `ElectronicPaymentNotInformed`: 22 - Pagamento Eletrônico Não Informado + * - `WithoutPayment`: 90 - Sem Pagamento + * - `Others`: 99 - Outros + * @enum {string} + */ + readonly PaymentMethod: "Cash" | "Cheque" | "CreditCard" | "DebitCard" | "StoreCredict" | "FoodVouchers" | "MealVouchers" | "GiftVouchers" | "FuelVouchers" | "MercantileDuplicate" | "BankBill" | "BankDeposit" | "InstantPayment" | "WireTransfer" | "Cashback" | "StaticInstantPayment" | "StoreCredit" | "ElectronicPaymentNotInformed" | "WithoutPayment" | "Others"; + readonly PaymentResource: { + /** @description YA01a - Grupo Detalhamento da Forma de Pagamento (detPag) VERSÃO 4.00 */ + readonly paymentDetail: readonly components["schemas"]["PaymentDetailResource"][]; + /** + * Format: double + * @description Valor do troco (vTroco) VERSÃO 4.00 + */ + readonly payBack?: number | null; + }; + /** + * @description Indicador da forma de pagamento (indPag). + * Valores possíveis: + * - `InCash`: Pagamento à vista + * - `Term`: Pagamento a prazo + * @enum {string} + */ + readonly PaymentType: "InCash" | "Term"; + /** + * @description Tipo de Pessoa. + * Valores possíveis: + * - `Undefined`: Não definido + * - `NaturalPerson`: Pessoa Física + * - `LegalEntity`: Pessoa Jurídica + * - `Company`: Empresa + * - `Customer`: Cliente + * @enum {string} + */ + readonly PersonType: "Undefined" | "NaturalPerson" | "LegalEntity" | "Company" | "Customer"; + /** + * @description Código de Classificação do Crédito Presumido (cCredPres). + * Valores possíveis: + * - `RuralProducerNonTaxpayer`: Produtor rural não contribuinte + * - `TacPfTransportServiceNonTaxpayer`: TAC Pessoa Física não contribuinte do serviço de transporte + * - `RecyclingFromIndividual`: Reciclagem de pessoa física + * - `UsedMovableGoodsFromIndividualForResale`: Bens móveis usados de pessoa física para revenda + * - `OptionalRegimeForCooperative`: Regime opcional para cooperativa + * @enum {string|null} + */ + readonly PresumedCreditClassificationCode: "RuralProducerNonTaxpayer" | "TacPfTransportServiceNonTaxpayer" | "RecyclingFromIndividual" | "UsedMovableGoodsFromIndividualForResale" | "OptionalRegimeForCooperative" | null; + /** @description Grupo de Informações do Crédito Presumido */ + readonly PresumedCreditDetailsResource: { + /** + * Format: double + * @description Percentual do Crédito Presumido (pCredPres) + */ + readonly rate?: number | null; + /** + * Format: double + * @description Valor do Crédito Presumido (vCredPres) + */ + readonly amount?: number | null; + /** + * Format: double + * @description Valor do Crédito Presumido em condição suspensiva. (vCredPresCondSus) + */ + readonly suspensiveConditionAmount?: number | null; + }; + /** @description Informações de Crédito Presumido por item (gCred). Preenchimento conforme exigência da UF (NT 2019.001). */ + readonly PresumedCreditResource: { + /** @description Código do Benefício de Crédito Presumido na UF (cCredPresumido). Use o mesmo código utilizado na EFD/declarações da UF. Tamanho 8 ou 10. */ + readonly code?: string | null; + /** + * Format: double + * @description Percentual do Crédito Presumido (pCredPresumido). Use fração: 0.04 = 4%. Até 4 casas decimais. + */ + readonly rate?: number | null; + /** + * Format: double + * @description Valor do Crédito Presumido (vCredPresumido). + */ + readonly amount?: number | null; + }; + /** + * @description Formato de impressão do DANFE (tpImp). + * Valores possíveis: + * - `None`: Não definido + * - `NFeNormalPortrait`: DANFE normal, Retrato + * - `NFeNormalLandscape`: DANFE normal, Paisagem + * - `NFeSimplified`: DANFE Simplificado + * - `DANFE_NFC_E`: DANFE NFC-e + * - `DANFE_NFC_E_MSG_ELETRONICA`: DANFE NFC-e em mensagem eletrônica + * @enum {string} + */ + readonly PrintType: "None" | "NFeNormalPortrait" | "NFeNormalLandscape" | "NFeSimplified" | "DANFE_NFC_E" | "DANFE_NFC_E_MSG_ELETRONICA"; + /** @description Notas Fiscais Eletrônicas (NF-e) */ + readonly ProductInvoicesResource: { + /** @description Lista de Notas Fiscais Eletrônicas (NF-e) */ + readonly productInvoices?: readonly components["schemas"]["InvoiceWithoutEventsResource"][] | null; + /** @description Identificador de possibilidade de mais itens. */ + readonly hasMore?: boolean; + }; + readonly PumpResource: { + /** + * Format: int32 + * @description Número de identificação do bico utilizado no abastecimento (nBico) + */ + readonly spoutNumber?: number | null; + /** + * Format: int32 + * @description Número de identificação da bomba ao qual o bico está interligado (nBomba) + */ + readonly number?: number | null; + /** + * Format: int32 + * @description Número de identificação do tanque ao qual o bico está interligado (nTanque) + */ + readonly tankNumber?: number | null; + /** + * Format: double + * @description Valor do Encerrante no início do abastecimento (vEncIni) + */ + readonly beginningAmount?: number | null; + /** + * Format: double + * @description Valor do Encerrante no final do abastecimento (vEncFin) + */ + readonly endAmount?: number | null; + /** + * Format: double + * @description Percentual do índice de mistura do Biodiesel (B100) no Óleo Diesel B instituído pelo órgão regulamentador + */ + readonly percentageBio?: number | null; + }; + /** @description Grupo Compra. Additional purchase information (compra) */ + readonly PurchaseInformationResource: { + /** @description Nota de Empenho. Identification of the Note of Commitment, when dealing with public purchases (xNEmp) */ + readonly commitmentNote?: string | null; + /** @description Pedido. (xPed) */ + readonly purchaseOrder?: string | null; + /** @description Contrato. (xcont) */ + readonly contractNumber?: string | null; + }; + /** + * @description Finalidade da emissão da NF-e (finNFe). + * Valores possíveis: + * - `None`: Não definido + * - `Normal`: NF-e normal + * - `Complement`: NF-e complementar + * - `Adjustment`: NF-e de ajuste + * - `Devolution`: Devolução de mercadoria + * @default Normal + * @enum {string} + */ + readonly PurposeType: "None" | "Normal" | "Complement" | "Adjustment" | "Devolution"; + readonly QueueEventResource: { + /** + * @description Justificativa da carta de correção + * O Texto deve + * conter no mínimo 15 e no máximo 1.000 caracteres + * (os quais não poderão conter acentos e/ou caracteres especiais) + */ + readonly reason?: string | null; + }; + /** @description Grupo Reboque */ + readonly ReboqueResource: { + /** @description Placa do Veiculo (placa) */ + readonly plate?: string | null; + /** @description UF Veiculo Reboque (UF) */ + readonly uf?: string | null; + /** @description Registro Nacional de Transportador de Carga (ANTT) (RNTC) */ + readonly rntc?: string | null; + /** @description Identificação do Vagão (vagao) */ + readonly wagon?: string | null; + /** @description Identificação da Balsa (balsa) */ + readonly ferry?: string | null; + }; + /** + * @description Indicador da IE do Destinatário (indIEDest). + * Valores possíveis: + * - `None`: Não definido + * - `TaxPayer`: Contribuinte ICMS (informar a IE do destinatário) + * - `Exempt`: Contribuinte isento de Inscrição no cadastro de Contribuintes do ICMS + * - `NonTaxPayer`: Não Contribuinte, que pode ou não possuir Inscrição Estadual no Cadastro de Contribuintes do ICMS + * @enum {string} + */ + readonly ReceiverStateTaxIndicator: "None" | "TaxPayer" | "Exempt" | "NonTaxPayer"; + /** @description Grupo de informações da redução da alíquota */ + readonly ReductionTaxResource: { + /** + * Format: double + * @description Percentual da redução de alíquota + */ + readonly rateReduction?: number | null; + /** + * Format: double + * @description Alíquota Efetiva que será aplicada a Base de Cálculo (em percentual) + */ + readonly effectiveRate?: number | null; + }; + readonly ReferencedProcessResource: { + /** @description Identificador do ato concessório (identificadorAtoConcessorio). */ + readonly identifierConcessory?: string | null; + /** + * Format: int32 + * @description Identificador de origem do processo (identificadorOrigemProcesso). + */ + readonly identifierOrigin?: number | null; + /** + * Format: int32 + * @description Tipo do ato concessório (tipoAtoConcessorio). + */ + readonly concessionActType?: number | null; + }; + /** @description Documento Fiscal Eletrônico Referenciado (DFeReferenciado). Grupo para referenciamento de itens de outro DF-e. */ + readonly ReferencedDFeResource: { + /** @description Chave de acesso do DF-e referenciado (chaveAcesso). */ + readonly accessKey: string; + /** + * Format: int32 + * @description Número do item do documento referenciado (nItem). Corresponde ao atributo “nItem” do elemento “det” do documentooriginal. + */ + readonly itemNumber?: number | null; + } | null; + /** @description Grupo de totais do Imposto Seletivo. (ISTot) */ + readonly ISTotalsResource: { + /** + * Format: double + * @description Total do imposto seletivo. (vIS) + */ + readonly amount?: number | null; + } | null; + /** @description Totais da NF-e com IBS e CBS. (IBSCBSTot) */ + readonly IBSCBSTotalsResource: { + /** + * Format: double + * @description Valor total da Base de Cálculo do IBS e da CBS. (vBCIBSCBS) + */ + readonly basis?: number | null; + /** @description Grupo total do IBS (gIBS) */ + readonly ibs?: components["schemas"]["IBSTotalsResource"]; + /** @description Grupo total da CBS (gCBS) */ + readonly cbs?: components["schemas"]["CBSTotalsResource"]; + /** @description Grupo de totais da tributação monofásica. */ + readonly monophase?: components["schemas"]["MonophaseTotalsResource"]; + /** @description Grupo total do Estorno de Crédito (gEstornoCred) */ + readonly creditReversal?: components["schemas"]["CreditReversalTotalsResource"]; + } | null; + /** @description Grupo total do IBS (gIBS) */ + readonly IBSTotalsResource: { + /** @description Informações do IBS da UF. */ + readonly state?: components["schemas"]["IBSStateTotalsResource"]; + /** @description Informações do IBS do Município. */ + readonly municipal?: components["schemas"]["IBSMunicipalTotalsResource"]; + /** + * Format: double + * @description Valor total do IBS (vIBS) + */ + readonly totalAmount?: number | null; + /** + * Format: double + * @description Valor total do crédito presumido (vCredPres) + */ + readonly presumedCreditAmount?: number | null; + /** + * Format: double + * @description Valor total do crédito presumido em condição suspensiva. (vCredPresCondSus) + */ + readonly presumedCreditConditionalAmount?: number | null; + } | null; + /** @description Informações do IBS da UF (gIBSUF) */ + readonly IBSStateTotalsResource: { + /** + * Format: double + * @description Valor total do diferimento (vDif) + */ + readonly defermentAmount?: number | null; + /** + * Format: double + * @description Valor total de devolução de tributos (vDevTrib) + */ + readonly returnedAmount?: number | null; + /** + * Format: double + * @description Valor total do IBS da UF. (vIBSUF) + */ + readonly amount?: number | null; + } | null; + /** @description Informações do IBS do Município (gIBSMun) */ + readonly IBSMunicipalTotalsResource: { + /** + * Format: double + * @description Valor total do diferimento (vDif) + */ + readonly defermentAmount?: number | null; + /** + * Format: double + * @description Valor total de devolução de tributos (vDevTrib) + */ + readonly returnedAmount?: number | null; + /** + * Format: double + * @description Valor total do IBS do Município. (vIBSMun) + */ + readonly amount?: number | null; + } | null; + /** @description Grupo total da CBS (gCBS) */ + readonly CBSTotalsResource: { + /** + * Format: double + * @description Valor total do diferimento (vDif) + */ + readonly defermentAmount?: number | null; + /** + * Format: double + * @description Valor total de devolução de tributos (vDevTrib) + */ + readonly returnedAmount?: number | null; + /** + * Format: double + * @description Valor total da CBS (vCBS) + */ + readonly amount?: number | null; + /** + * Format: double + * @description Valor total do crédito presumido (vCredPres) + */ + readonly presumedCreditAmount?: number | null; + /** + * Format: double + * @description Valor total do crédito presumido em condição suspensiva. (vCredPresCondSus) + */ + readonly presumedCreditConditionalAmount?: number | null; + } | null; + /** @description Grupo de totais da tributação monofásica. */ + readonly MonophaseTotalsResource: { + /** @description Totais do IBS monofásico */ + readonly ibs?: components["schemas"]["MonophaseIBSTotalsResource"]; + /** @description Totais da CBS monofásica */ + readonly cbs?: components["schemas"]["MonophaseCBSTotalsResource"]; + } | null; + /** @description Tributação regular hipotética caso condição resolutória/suspensiva não se aplique. Informar como a tributação seria aplicada se a condição resolutória/suspensiva não for atendida. */ + readonly RegularTaxationResource: { + /** @description CST regular (CSTReg), 3 dígitos. Use tabela CST do IBS/CBS */ + readonly situationCode?: string | null; + /** @description Classificação tributária regular (cClassTribReg), 6 dígitos. Use tabela cClassTrib */ + readonly classCode?: string | null; + /** + * Format: double + * @description Alíquota efetiva IBS UF (pAliqEfetRegIBSUF). + */ + readonly stateEffectiveRate?: number | null; + /** + * Format: double + * @description IBS UF regular (vTribRegIBSUF). + */ + readonly amount?: number | null; + /** + * Format: double + * @description Alíquota efetiva IBS Município (pAliqEfetRegIBSMun). + */ + readonly municipalEffectiveRate?: number | null; + /** + * Format: double + * @description IBS Município regular (vTribRegIBSMun). + */ + readonly municipalAmount?: number | null; + /** + * Format: double + * @description Alíquota efetiva CBS (pAliqEfetRegCBS). + */ + readonly cbsEffectiveRate?: number | null; + /** + * Format: double + * @description CBS regular (vTribRegCBS). + */ + readonly cbsAmount?: number | null; + }; + /** @description Totais do IBS monofásico */ + readonly MonophaseIBSTotalsResource: { + /** + * Format: double + * @description Total do IBS monofásico. (vIBSMono) + */ + readonly amount?: number | null; + /** + * Format: double + * @description Total do IBS monofásico sujeito a retenção. (vIBSMonoReten) + */ + readonly withheldAmount?: number | null; + /** + * Format: double + * @description Total do IBS monofásico retido anteriormente. (vIBSMonoRet) + */ + readonly previouslyWithheldAmount?: number | null; + } | null; + /** @description Totais da CBS monofásica */ + readonly MonophaseCBSTotalsResource: { + /** + * Format: double + * @description Total da CBS monofásica. (vCBSMono) + */ + readonly amount?: number | null; + /** + * Format: double + * @description Total da CBS monofásica sujeita a retenção. (vCBSMonoReten) + */ + readonly withheldAmount?: number | null; + /** + * Format: double + * @description Total da CBS monofásica retida anteriormente. (vCBSMonoRet) + */ + readonly previouslyWithheldAmount?: number | null; + } | null; + readonly RequestCancellationResource: { + readonly accountId?: string | null; + readonly companyId?: string | null; + readonly productInvoiceId?: string | null; + readonly reason?: string | null; + }; + /** @description Grupo de Informações da Devolução de Tributos */ + readonly ReturnedTaxResource: { + /** + * Format: double + * @description Valor do tributo devolvido (vDevTrib) + */ + readonly amount?: number | null; + }; + /** + * @description Modalidade do frete (modFrete). + * Valores possíveis: + * - `ByIssuer`: Contratação do Frete por conta do Remetente (CIF) + * - `ByReceiver`: Contratação do Frete por conta do Destinatário (FOB) + * - `ByThirdParties`: Contratação do Frete por conta de Terceiros + * - `OwnBySender`: Transporte Próprio por conta do Remetente + * - `OwnByBuyer`: Transporte Próprio por conta do Destinatário + * - `Free`: Sem Ocorrência de Transporte + * @default Free + * @enum {string} + */ + readonly ShippingModality: "ByIssuer" | "ByReceiver" | "ByThirdParties" | "OwnBySender" | "OwnByBuyer" | "Free"; + /** + * @description Regime especial de tributação. + * Valores possíveis: + * - `Nenhum`: Nenhum + * - `MicroempresaMunicipal`: Microempresa Municipal + * - `Estimativa`: Estimativa + * - `SociedadeDeProfissionais`: Sociedade de Profissionais + * - `MicroempreendedorIndividual`: Microempreendedor Individual (MEI) + * - `MicroempresarioEmpresaPequenoPorte`: Microempresário e Empresa de Pequeno Porte (ME/EPP) + * - `Automatico`: Automático + * @enum {string} + */ + readonly SpecialTaxRegime: "Nenhum" | "MicroempresaMunicipal" | "Estimativa" | "SociedadeDeProfissionais" | "MicroempreendedorIndividual" | "MicroempresarioEmpresaPequenoPorte" | "Automatico"; + /** @enum {string} */ + readonly StateCode: "NA" | "RO" | "AC" | "AM" | "RR" | "PA" | "AP" | "TO" | "MA" | "PI" | "CE" | "RN" | "PB" | "PE" | "AL" | "SE" | "BA" | "MG" | "ES" | "RJ" | "SP" | "PR" | "SC" | "RS" | "MS" | "MT" | "GO" | "DF" | "EX"; + /** + * @description Autorizador de Processamento de Imposto Estadual. + * Valores possíveis: + * - `Normal`: Autorizador Normal + * - `EPEC`: Autorizador de Evento Prévio de Emissão em Contingência + * @enum {string} + */ + readonly StateTaxProcessingAuthorizer: "Normal" | "EPEC"; + readonly TaxCouponInformationResource: { + /** @description Modelo de Documento Fiscal (mod) */ + readonly modelDocumentFiscal?: string | null; + /** @description Número de Ordem Sequencial do ECF (nECF) */ + readonly orderECF?: string | null; + /** + * Format: int32 + * @description Número do Contador de Ordem de Operação (nCOO) + */ + readonly orderCountOperation?: number | null; + }; + /** @description Grupo de informações fiscais para o cálculo automático de impostos. Se este grupo for preenchido, o grupo `tax.IBSCBS` será calculado automaticamente. Se não for preenchido, o cálculo automático do grupo `tax.IBSCBS` torna-se opcional. */ + readonly TaxDeterminationResource: { + /** + * Format: int32 + * @description Código interno para determinação de natureza de operação + */ + readonly operationCode?: number | null; + /** @description Perfil fiscal do vendedor (origem) - usado para o cálculo automático de impostos */ + readonly issuerTaxProfile?: string | null; + /** @description Perfil fiscal do comprador (destino) - usado para o cálculo automático de impostos */ + readonly buyerTaxProfile?: string | null; + /** @description Origem da mercadoria */ + readonly origin?: string | null; + /** + * @description Finalidade de aquisição - usado para o + * cálculo automático de impostos + */ + readonly acquisitionPurpose?: string | null; + }; + readonly TaxDocumentsReferenceResource: { + readonly taxCouponInformation?: components["schemas"]["TaxCouponInformationResource"]; + readonly documentInvoiceReference?: components["schemas"]["DocumentInvoiceReferenceResource"]; + readonly documentElectronicInvoice?: components["schemas"]["DocumentElectronicInvoiceResource"]; + }; + /** + * @description Código de Regime Tributário (CRT). + * Valores possíveis: + * - `None`: Não definido + * - `LucroReal`: Lucro Real + * - `LucroPresumido`: Lucro Presumido + * - `SimplesNacional`: Simples Nacional + * - `SimplesNacionalExcessoSublimite`: Simples Nacional – excesso de sublimite de receita bruta + * - `MicroempreendedorIndividual`: Microempreendedor Individual - MEI + * - `Isento`: Isento + * @enum {string} + */ + readonly TaxRegime: "None" | "LucroReal" | "LucroPresumido" | "SimplesNacional" | "SimplesNacionalExcessoSublimite" | "MicroempreendedorIndividual" | "Isento"; + readonly TaxpayerCommentsResource: { + /** @description Campo (xCampo) */ + readonly field?: string | null; + /** @description Texto (xTexto) */ + readonly text?: string | null; + }; + readonly Total: { + readonly icms?: components["schemas"]["ICMSTotal"]; + readonly issqn?: components["schemas"]["ISSQNTotal"]; + /** @description Grupo Retenções de Tributos (retTrib) */ + readonly withheldTaxes?: components["schemas"]["TotalsWithholdings"]; + /** @description Grupo de totais do Imposto Seletivo. (ISTot) */ + readonly isTotals?: components["schemas"]["ISTotalsResource"]; + /** @description Totais da NF-e com IBS e CBS. (IBSCBSTot) */ + readonly ibsCbsTotals?: components["schemas"]["IBSCBSTotalsResource"]; + /** + * Format: double + * @description Valor total da NF-e com IBS / CBS / IS (vNFTot) + */ + readonly totalInvoiceAmount?: number | null; + }; + readonly TotalResource: { + readonly icms?: components["schemas"]["ICMSTotalResource"]; + readonly issqn?: components["schemas"]["ISSQNTotalResource"]; + }; + /** @description Grupo Transportador */ + readonly TransportGroupResource: { + /** @description Identificador da Conta */ + readonly accountId?: string | null; + /** @description Identificação */ + readonly id?: string | null; + /** @description Nome ou Razão Social (xNome) */ + readonly name?: string | null; + /** + * Format: int64 + * @description CNPJ ou CPF + */ + readonly federalTaxNumber?: number | null; + /** @description Email */ + readonly email?: string | null; + readonly address?: components["schemas"]["AddressResource"]; + readonly type?: components["schemas"]["PersonType"]; + /** @description Inscrição Estadual do Transportador (IE) */ + readonly stateTaxNumber?: string | null; + /** @description Grupo de Retenção do ICMS do transporte */ + readonly transportRetention?: string | null; + }; + /** + * @description Grupo de Informações do Transporte da NF-e + * Id: X01 Pai: A1 + */ + readonly TransportInformationResource: { + readonly freightModality?: components["schemas"]["ShippingModality"]; + readonly transportGroup?: components["schemas"]["TransportGroupResource"]; + readonly reboque?: components["schemas"]["ReboqueResource"]; + readonly volume?: components["schemas"]["VolumeResource"]; + readonly transportVehicle?: components["schemas"]["TransportVehicleResource"]; + /** @description Número dos Lacres */ + readonly sealNumber?: string | null; + readonly transpRate?: components["schemas"]["TransportRateResource"]; + }; + readonly TransportRateResource: { + /** + * Format: double + * @description Valor do Serviço (vServ) + */ + readonly serviceAmount?: number | null; + /** + * Format: double + * @description BC da Retenção do ICMS (vBCRet) + */ + readonly bcRetentionAmount?: number | null; + /** + * Format: double + * @description Alíquota da Retenção (pICMSRet) //Change to Rate + */ + readonly icmsRetentionRate?: number | null; + /** + * Format: double + * @description Valor do ICMS Retido (vICMSRet) + */ + readonly icmsRetentionAmount?: number | null; + /** + * Format: int64 + * @description CFOP de Serviço de Transporte (CFOP) + */ + readonly cfop?: number | null; + /** + * Format: int64 + * @description Código do Municipio de ocorrencia do fato gerador do ICMS do Transporte (cMunFG) + */ + readonly cityGeneratorFactCode?: number | null; + }; + /** @description Grupo Veiculo */ + readonly TransportVehicleResource: { + /** @description Placa do Veiculo (placa) */ + readonly plate?: string | null; + /** @description Sigla da UF (UF) */ + readonly state?: string | null; + /** @description Registro Nacional de Transportador de Carga (ANTT) (RNTC) */ + readonly rntc?: string | null; + }; + /** @description Indica fornecimento de bem móvel usado (indBemMovelUsado). true = bem usado; false = bem novo. */ + readonly UsedMovableAssetIndicator: boolean; + /** + * @description Volumes + * Id:X26 + */ + readonly VolumeResource: { + /** + * Format: int32 + * @description Quantidade de volumes transportados (qVol) + */ + readonly volumeQuantity?: number | null; + /** @description Espécie dos volumes transportados (esp) */ + readonly species?: string | null; + /** @description Marca dos Volumes Transportados (marca) */ + readonly brand?: string | null; + /** @description Numeração dos Volumes Transportados (nVol) */ + readonly volumeNumeration?: string | null; + /** + * Format: double + * @description Peso Liquido(em Kg) (pesoL) + */ + readonly netWeight?: number | null; + /** + * Format: double + * @description Peso Bruto(em Kg) (pesoB) + */ + readonly grossWeight?: number | null; + }; + /** @description Identificação do Local de retirada (retirada) */ + readonly WithdrawalInformationResource: { + /** @description Identificador da Conta */ + readonly accountId?: string | null; + /** @description Identificação */ + readonly id?: string | null; + /** @description Nome ou Razão Social (xNome) */ + readonly name?: string | null; + /** + * Format: int64 + * @description CNPJ ou CPF + */ + readonly federalTaxNumber?: number | null; + /** @description Email */ + readonly email?: string | null; + readonly address?: components["schemas"]["AddressResource"]; + readonly type?: components["schemas"]["PersonType"]; + /** @description Inscrição Estadual (IE) */ + readonly stateTaxNumber?: string | null; + }; + /** @description Grupo Retenções de Tributos (retTrib) */ + readonly TotalsWithholdings: { + /** + * Format: double + * @description Valor Retido de PIS (vRetPIS) + */ + readonly pisAmount?: number | null; + /** + * Format: double + * @description Valor Retido de COFINS (vRetCOFINS) + */ + readonly cofinsAmount?: number | null; + /** + * Format: double + * @description Valor Retido de CSLL (vRetCSLL) + */ + readonly csllAmount?: number | null; + /** + * Format: double + * @description Base de Cálculo do IRRF (vBCIRRF) + */ + readonly irrfBasis?: number | null; + /** + * Format: double + * @description Valor Retido do IRRF (vIRRF) + */ + readonly irrfAmount?: number | null; + /** + * Format: double + * @description Base de Cálculo da Retenção da Previdência Social (vBCRetPrev) + */ + readonly socialSecutiryBasis?: number | null; + /** + * Format: double + * @description Valor da Retenção da Previdência Social (vRetPrev) + */ + readonly socialSecutiryAmount?: number | null; + } | null; + /** @description Informações sobre o crédito presumido de IBS para fornecimentos da ZFM (gCredPresIBSZFM). */ + readonly ZfmPresumedCreditResource: { + /** @description Tipo de crédito presumido (tpCredPresIBSZFM). */ + readonly classificationCode?: components["schemas"]["IbsZfmPresumedCreditClassification"]; + /** + * Format: double + * @description Valor do crédito presumido ZFM (vCredPresIBSZFM). Obrigatório para notas de crédito com tpNFCredito = 02. + */ + readonly amount?: number | null; + }; + }; + responses: never; + parameters: never; + requestBodies: never; + headers: never; + pathItems: never; +} +export type $defs = Record; +export interface operations { + readonly createProductInvoice: { + readonly parameters: { + readonly query?: never; + readonly header?: never; + readonly path?: never; + readonly cookie?: never; + }; + /** @description Objeto JSON contendo todos os dados da NF-e, conforme o schema ProductInvoiceRequest. */ + readonly requestBody: { + readonly content: { + readonly "application/json": components["schemas"]["ProductInvoiceRequest"]; + }; + }; + readonly responses: { + /** @description NF-e criada com sucesso (aguardando processamento/autorização). */ + readonly 201: { + headers: { + readonly [name: string]: unknown; + }; + content: { + readonly "application/json": components["schemas"]["InvoiceResource"]; + }; + }; + /** @description Erro de validação (Bad Request). O corpo da requisição está inválido. */ + readonly 400: { + headers: { + readonly [name: string]: unknown; + }; + content: { + readonly "application/json": components["schemas"]["ErrorsResource"]; + }; + }; + /** @description Erro interno do servidor. */ + readonly 500: { + headers: { + readonly [name: string]: unknown; + }; + content: { + readonly "application/json": components["schemas"]["ErrorResource"]; + }; + }; + }; + }; +} diff --git a/src/generated/service-invoice-rtc-v1.ts b/src/generated/service-invoice-rtc-v1.ts new file mode 100644 index 0000000..f917094 --- /dev/null +++ b/src/generated/service-invoice-rtc-v1.ts @@ -0,0 +1,1281 @@ +/** + * ⚠️ AUTO-GENERATED from service-invoice-rtc-v1.yaml + * + * Do not edit this file directly. + * + * To regenerate: npm run generate + * Last generated: 2026-06-27T15:46:54.261Z + * Generator: openapi-typescript + */ + +export interface paths { + readonly "/v1/companies/:company_id/serviceinvoices": { + readonly parameters: { + readonly query?: never; + readonly header?: never; + readonly path?: never; + readonly cookie?: never; + }; + readonly get?: never; + readonly put?: never; + /** Envio de dados para emissão de NFS-e (RPS/DPS) */ + readonly post: { + readonly parameters: { + readonly query?: never; + readonly header?: never; + readonly path?: never; + readonly cookie?: never; + }; + readonly requestBody: { + readonly content: { + readonly "application/json": components["schemas"]["NFSeRequest"]; + }; + }; + readonly responses: { + /** @description Processamento do RPS/DPS iniciado com sucesso */ + readonly 200: { + headers: { + readonly [name: string]: unknown; + }; + content?: never; + }; + /** @description Dados inválidos na requisição */ + readonly 400: { + headers: { + readonly [name: string]: unknown; + }; + content?: never; + }; + }; + }; + readonly delete?: never; + readonly options?: never; + readonly head?: never; + readonly patch?: never; + readonly trace?: never; + }; + readonly "/v1/companies/:company_id/serviceinvoices/:id/cancellation-xml": { + readonly parameters: { + readonly query?: never; + readonly header?: never; + readonly path?: never; + readonly cookie?: never; + }; + /** + * Download do XML do evento de cancelamento da NFS-e (Ambiente Nacional) + * @description Retorna o XML do **evento de cancelamento** (`e110001`) de uma NFS-e emitida no **Ambiente Nacional (ADN)**. + * + * > **Atenção** + * > Disponível **apenas** para notas do Ambiente Nacional. Provedores de layout municipal/ABRASF não possuem evento de cancelamento próprio e retornam `404`. + * + * Quando existem o XML enviado (request do `e110001`) e o XML do evento autorizado (retorno do ADN — `procEventoNFSe`), o **retorno autorizado é priorizado**. O XML só está disponível para notas com status `Cancelled`. + */ + readonly get: { + readonly parameters: { + readonly query?: never; + readonly header?: never; + readonly path: { + /** @description ID da empresa */ + readonly company_id: string; + /** @description ID da Nota Fiscal de Serviço (NFS-e) */ + readonly id: string; + }; + readonly cookie?: never; + }; + readonly requestBody?: never; + readonly responses: { + /** @description XML do evento de cancelamento retornado com sucesso */ + readonly 200: { + headers: { + readonly [name: string]: unknown; + }; + content: { + readonly "application/xml": string; + }; + }; + /** @description Não há XML de evento de cancelamento para esta nota (provedor legado, ambiente não Nacional ou nota ainda não cancelada) */ + readonly 404: { + headers: { + readonly [name: string]: unknown; + }; + content?: never; + }; + }; + }; + readonly put?: never; + readonly post?: never; + readonly delete?: never; + readonly options?: never; + readonly head?: never; + readonly patch?: never; + readonly trace?: never; + }; +} +export type webhooks = Record; +export interface components { + schemas: { + /** Nota Fiscal de Serviço (NFSe) Schema */ + readonly NFSeRequest: { + /** @description Identificação única do cliente */ + readonly externalId?: string; + /** @description Código do serviço no municipio */ + readonly cityServiceCode: string; + /** @description Código federal do servico (Item da lista de serviço LC 116) (CodigoServico, tpCodigoServico) */ + readonly federalServiceCode?: string; + /** @description Descrição dos serviços (Discriminacao, tpDiscriminacao) */ + readonly description: string; + /** @description Valor do serviços (ValorServicos - V1.0 SP) - Valor total dos serviços (tpValor) */ + readonly servicesAmount: number; + /** @description Código do NBS no municipio (somente quando necessario na cidade) (NBS, tpCodigoNBS) */ + readonly nbsCode: string; + /** @description Código CNAE (somente quando necessario na cidade) */ + readonly cnaeCode?: string; + /** @description Código NCM (Nomenclatura Comum do Mercosul) (NCM, tpCodigoNCM) */ + readonly ncmCode?: string; + /** @description Valor dos Serviços pago (Valor Total Recebido, tpValor) */ + readonly paidAmount?: number; + /** + * Format: date-time + * @description Data da emissão no formato YYYY-MM-DDTHH:MM:SS.SSSSSS-03:00 (dhEmi, DataEmissaoNFe). + * **Automático**: Se não for informado, o sistema utilizará a data e hora atuais no momento do processamento. + */ + readonly issuedOn?: string; + /** + * Format: date + * @description Data da competência da prestação do serviço no formato AAAA-MM-DD (Competencia). + * **Automático**: Se não for informado, o sistema utilizará a data do campo `issuedOn`. + */ + readonly accrualOn?: string; + /** + * @description Número de Série da RPS (SerieRPS, serie, tpSerieRPS). + * **Prioridade**: Se informado, o valor enviado será utilizado. + * **Automático**: Se não for informado, o sistema utilizará o valor definido no cadastro da empresa. + */ + readonly rpsSerialNumber?: string; + /** + * @description Número da RPS (NumeroRPS, nDPS, tpNumero). + * **Prioridade**: Se informado, o valor enviado será utilizado. + * **Automático**: Se não for informado, o sistema utilizará o próximo número da sequência automática. + */ + readonly rpsNumber?: number; + /** + * @description **Tipo de Tributação (taxationType)** + * + * O campo `taxationType` define o regime de tributação do **ISSQN (Imposto Sobre Serviços de Qualquer Natureza)**. Ele é um campo legado, mantido para compatibilidade com os layouts municipais existentes, como o da Prefeitura de São Paulo, e continua sendo fundamental para o cálculo do ISS durante o período de transição da Reforma Tributária. + * + * **Finalidade Principal (Contexto do ISSQN):** + * + * Este campo indica ao sistema como o ISSQN deve ser tratado na operação. Com base no valor selecionado (ex: `WithinCity`, `OutsideCity`, `Immune`), o sistema determina se o ISSQN é devido, se a tributação ocorrerá dentro ou fora do município, ou se a operação é isenta ou imune a este imposto específico. + * + * **Relação com a Reforma Tributária (IBS/CBS):** + * + * É crucial entender que o `taxationType` **não se aplica aos novos tributos (IBS e CBS)**. Para a apuração do IBS e da CBS, as regras de tributação e o local de incidência são definidos exclusivamente pelos campos dentro do grupo `IbsCbs`, como o `operationIndicator` e o `classCode`. + * + * **Em resumo:** + * * **`taxationType`**: Governa o comportamento do **ISSQN**. + * * **`ibsCbs`**: Governa o comportamento do **IBS e da CBS**. + * + * Durante o período de transição, ambos os sistemas de tributação coexistirão, tornando o preenchimento correto de todos esses campos essencial para a conformidade fiscal da nota. + * + * --- + * + * Tipo da tributação (TributacaoRPS, tpTributacaoNFe). + * Valores possíveis: + * - `None`: Nenhuma + * - `WithinCity`: Tributação no município + * - `OutsideCity`: Tributação fora do município + * - `Export`: Exportação + * - `Free`: Isenta + * - `Immune`: Imune + * - `SuspendedCourtDecision`: Exigibilidade suspensa por decisão judicial + * - `SuspendedAdministrativeProcedure`: Exigibilidade suspensa por procedimento administrativo + * - `OutsideCityFree`: Isenta e fora do município + * - `OutsideCityImmune`: Imune e fora do município + * - `OutsideCitySuspended`: Suspensa e fora do município + * - `OutsideCitySuspendedAdministrativeProcedure`: Suspensa por processo administrativo e fora do município + * - `ObjectiveImune`: Imunidade Objetiva + * @default WithinCity + * @enum {string} + */ + readonly taxationType: "None" | "WithinCity" | "OutsideCity" | "Export" | "Free" | "Immune" | "SuspendedCourtDecision" | "SuspendedAdministrativeProcedure" | "OutsideCityFree" | "OutsideCityImmune" | "OutsideCitySuspended" | "OutsideCitySuspendedAdministrativeProcedure" | "ObjectiveImune"; + /** + * @description Alíquota do ISS (pAliq, tpAliquota). + * **Automático**: Se não for informado, o sistema utilizará o valor definido no cadastro do código de serviço. + */ + readonly issRate?: number; + /** + * @description Valor do ISS (ValorISS, tpValor). + * **Automático**: Se não for informado, o sistema realizará o cálculo automático. + */ + readonly issTaxAmount?: number; + /** + * @description **Deduções: Simples vs. Estruturado (`deductionsAmount` vs. `deduction`)** + * + * O layout oferece duas formas de registrar deduções da base de cálculo do ISSQN, visando flexibilidade e conformidade com os diferentes padrões (municipal legado vs. nacional). + * + * * **`deductionsAmount` (Valor Simples):** + * Um campo numérico para informar o **valor total consolidado** das deduções. É uma abordagem direta, compatível com layouts mais antigos que não exigem o detalhamento dos documentos que comprovam a dedução. + * + * * **`deduction` (Grupo Estruturado):** + * Um objeto complexo que permite justificar cada dedução com base em documentos fiscais, alinhando-se ao **padrão da NFS-e Nacional**. Ele detalha a origem de cada valor (chave do documento, tipo, valor, fornecedor). + * + * **Relação e Recomendação:** + * A coexistência dos campos oferece compatibilidade. `deductionsAmount` é uma opção simplificada, enquanto o grupo `deduction` é a forma mais completa e recomendada para garantir rastreabilidade e conformidade fiscal com o padrão nacional. O sistema de emissão priorizará os dados detalhados do grupo `deduction`, se fornecidos. + * + * **Recomendação de Uso:** + * * Use `deductionsAmount` para integrações simples ou quando não há detalhamento dos documentos. + * * Use o grupo `deduction` sempre que a informação detalhada dos documentos estiver disponível para garantir a máxima conformidade. + * + * --- + * + * Valor de deduções (vDR, tpValor). + */ + readonly deductionsAmount?: number; + /** @description Valor do desconto incondicionado (vDescIncond, tpValor). */ + readonly discountUnconditionedAmount?: number; + /** @description Valor do desconto condicionado (vDescCond, tpValor). */ + readonly discountConditionedAmount?: number; + /** + * @description Valor retido do Imposto de Renda (IR) (vRetIRRF, tpValor). + * **Automático**: Se não for informado, o sistema realizará o cálculo automático. + */ + readonly irAmountWithheld?: number; + /** + * @description Código de Situação Tributária do PIS/COFINS (CST). + * Valores: + * - `00`: Nenhum + * - `01`: Operação Tributável com Alíquota Básica + * - `02`: Operação Tributável com Alíquota Diferenciada + * - `03`: Operação Tributável com Alíquota por Unidade de Medida de Produto + * - `04`: Operação Tributável monofásica - Revenda a Alíquota Zero + * - `05`: Operação Tributável por Substituição Tributária + * - `06`: Operação Tributável a Alíquota Zero + * - `07`: Operação Tributável da Contribuição + * - `08`: Operação sem Incidência da Contribuição + * - `09`: Operação com Suspensão da Contribuição + * @enum {string} + */ + readonly cstPisCofins?: "00" | "01" | "02" | "03" | "04" | "05" | "06" | "07" | "08" | "09"; + /** @description Base de cálculo para o Pis e Cofins (vBCPisCofins, tpValor). */ + readonly pisCofinsBaseTax?: number; + /** + * @description Alíquota do PIS (pAliqPis). + * **Automático**: Se não for informado, o sistema utilizará o valor definido no cadastro do código de serviço. + */ + readonly pisRate?: number; + /** @description Valor do PIS (vPis, tpValor). Campo utilizado para informar o valor do PIS, porém sem retenção. */ + readonly pisAmount?: number; + /** + * @description Valor retido do PIS (vPis, tpValor). + * **Automático**: Se não for informado, o sistema realizará o cálculo automático. + */ + readonly pisAmountWithheld?: number; + /** + * @description Alíquota do COFINS (pAliqCofins). + * **Automático**: Se não for informado, o sistema utilizará o valor definido no cadastro do código de serviço. + */ + readonly cofinsRate?: number; + /** @description Valor do COFINS (vCofins, tpValor). Campo utilizado para informar o valor do COFINS, porém sem retenção. */ + readonly cofinsAmount?: number; + /** + * @description Valor retido do COFINS (vCofins, tpValor). + * **Automático**: Se não for informado, o sistema realizará o cálculo automático. + */ + readonly cofinsAmountWithheld?: number; + /** @description Valor do CSLL (vCSLL, tpValor). Campo utilizado para informar o valor do CSLL, porém sem retenção. */ + readonly csllAmount?: number; + /** @description Alíquota do CSLL (pAliqCSLL). */ + readonly csllRate?: number; + /** + * @description Valor retido do CSLL (vRetCSLL, tpValor). + * **Automático**: Se não for informado, o sistema realizará o cálculo automático. + */ + readonly csllAmountWithheld?: number; + /** + * @description Valor retido do INSS (vRetCP, tpValor). + * **Automático**: Se não for informado, o sistema realizará o cálculo automático. + */ + readonly inssAmountWithheld?: number; + /** + * @description Valor retido do ISS (vISSQN). + * **Automático**: Se não for informado, o sistema realizará o cálculo automático quando houver retenção de ISS. + */ + readonly issAmountWithheld?: number; + /** + * @description SP - Alíquota IPI (pAliqIPI, tpAliquota). + * **Automático**: Se não for informado, o sistema utilizará o valor definido no cadastro do código de serviço. + */ + readonly ipiRate?: number; + /** + * @description SP - Valor IPI (vIPI, tpValor). + * **Automático**: Se não for informado, o sistema realizará o cálculo automático. + */ + readonly ipiAmount?: number; + /** @description Valor de outras retenções (vOutrasRet, tpValor). */ + readonly othersAmountWithheld?: number; + /** + * @description **Informações Adicionais: Simples vs. Estruturado (`additionalInformation` vs. `additionalInformationGroup`)** + * + * O layout oferece duas formas de enviar informações complementares, visando flexibilidade e completude. + * + * * **`additionalInformation` (Texto Simples):** + * Um campo de `string` para adicionar qualquer observação em formato de texto livre. É ideal para anotações rápidas e genéricas. + * + * * **`additionalInformationGroup` (Objeto Estruturado):** + * Um objeto que organiza dados complementares em campos específicos, como `responsibilityDocumentIdentifier` (ART/RRT), `registrationWork` (Matrícula de Obra), `order` (Pedido), etc. É a forma mais completa e padronizada de enviar esses dados. + * + * **Comportamento e Relação:** + * Para facilitar a integração, o campo `additionalInformation` funciona como um atalho. Se você preencher apenas este campo, seu conteúdo será automaticamente copiado para `additionalInformationGroup.otherInformation`. Isso garante que todas as informações adicionais fiquem consolidadas na estrutura mais completa, mantendo a consistência interna dos dados. + * + * **Recomendação:** + * * Para observações simples, use `additionalInformation`. + * * Para dados específicos como ART, Matrícula de Obra, etc., use os campos dedicados dentro de `additionalInformationGroup`. + */ + readonly additionalInformation?: string; + /** + * @description **Informações Adicionais: Simples vs. Estruturado (`additionalInformation` vs. `additionalInformationGroup`)** + * + * O layout oferece duas formas de enviar informações complementares, visando flexibilidade e completude. + * + * * **`additionalInformation` (Texto Simples):** + * Um campo de `string` para adicionar qualquer observação em formato de texto livre. É ideal para anotações rápidas e genéricas. + * + * * **`additionalInformationGroup` (Objeto Estruturado):** + * Um objeto que organiza dados complementares em campos específicos, como `responsibilityDocumentIdentifier` (ART/RRT), `registrationWork` (Matrícula de Obra), `order` (Pedido), etc. É a forma mais completa e padronizada de enviar esses dados. + * + * **Comportamento e Relação:** + * Para facilitar a integração, o campo `additionalInformation` funciona como um atalho. Se você preencher apenas este campo, seu conteúdo será automaticamente copiado para `additionalInformationGroup.otherInformation`. Isso garante que todas as informações adicionais fiquem consolidadas na estrutura mais completa, mantendo a consistência interna dos dados. + * + * **Recomendação:** + * * Para observações simples, use `additionalInformation`. + * * Para dados específicos como ART, Matrícula de Obra, etc., use os campos dedicados dentro de `additionalInformationGroup`. + */ + readonly additionalInformationGroup?: { + /** @description Identificador do documento de responsabilidade técnica (ART, RRT, etc.) (idDocTec). */ + readonly responsibilityDocumentIdentifier?: string; + /** @description Documento de referência relacionado ao serviço prestado (docRef). */ + readonly referencedDocument?: string; + /** @description Número do pedido/ordem de compra/ordem de serviço (xPed). */ + readonly order?: string; + /** @description Grupo de itens do pedido/ordem de compra/ordem de serviço (gItemPed). */ + readonly items?: readonly { + /** @description Item do pedido/ordem de compra/ordem de serviço (xItemPed). */ + readonly item?: string; + }[]; + /** @description Outras informações complementares (xInfComp). */ + readonly otherInformation?: string; + }; + /** @description Indica se é uma nota fiscal de pagamento parcelado antecipado, realizado antes do fornecimento do serviço. (PagamentoParceladoAntecipado, tpNaoSim) */ + readonly isEarlyInstallmentPayment?: boolean; + /** + * @description **Tipo de Imunidade (immunityType)** + * + * O campo `immunityType` é condicional e deve ser utilizado **exclusivamente quando o campo `taxationType` for definido como `Immune`**. + * + * Sua finalidade é especificar a base legal que fundamenta a imunidade tributária do **ISSQN (Imposto Sobre Serviços de Qualquer Natureza)** para a operação. Cada valor disponível no campo corresponde a uma alínea específica do Artigo 150, Inciso VI, da Constituição Federal, que trata das limitações ao poder de tributar. + * + * **Relação com a Reforma Tributária (IBS/CBS):** + * + * É fundamental destacar que este campo se refere **apenas à imunidade do ISSQN**. As regras de imunidade para os novos tributos (IBS e CBS) são tratadas de forma separada, dentro do grupo `ibsCbs`, através de códigos específicos no campo `classCode` (ex: códigos iniciados com `41xxxx`). + * + * **Em resumo:** + * A correta especificação do `immunityType` é essencial para validar a justificativa legal da não incidência do ISSQN na nota fiscal, garantindo a conformidade perante as autoridades fiscais municipais durante o período de transição. + * + * --- + * + * Tipo de imunidade (tpImunidade) — usar apenas quando taxationMode = 'immunity'. + * Valores possíveis: + * - `unspecified`: Imunidade (tipo não informado na nota de origem) + * - `PublicEntitiesMutual`: Patrimônio, renda ou serviços, uns dos outros (CF88, Art 150, VI, a) + * - `Temples`: Templos de qualquer culto (CF88, Art 150, VI, b) + * - `PartiesUnionsEducationSocialNonprofit`: Patrimônio, renda ou serviços dos partidos políticos, inclusive suas fundações, das entidades sindicais dos trabalhadores, das instituições de educação e de assistência social, sem fins lucrativos (CF88, Art 150, VI, c) + * - `BooksPressPaper`: Livros, jornais, periódicos e o papel destinado a sua impressão (CF88, Art 150, VI, d) + * - `BrazilianMusicPhonograms`: Fonogramas e videofonogramas musicais produzidos no Brasil (CF88, Art 150, VI, e) + * @enum {string} + */ + readonly immunityType?: "Unspecified" | "PublicEntitiesMutual" | "Temples" | "PartiesUnionsEducationSocialNonprofit" | "BooksPressPaper" | "BrazilianMusicPhonograms"; + /** + * @description **Tipo de Retenção do ISSQN (retentionType)** + * + * O campo `retentionType` determina quem é o responsável pelo recolhimento do **ISSQN (Imposto Sobre Serviços de Qualquer Natureza)**. Ele é um dos campos mais importantes para a correta apuração do ISSQN, pois define a transferência da responsabilidade de pagamento do imposto do prestador para outra parte na operação. + * + * **Finalidade e Opções:** + * + * * **`notWithheld` (Não Retido):** Esta é a situação padrão. O **prestador** do serviço é o responsável por calcular e recolher o ISSQN devido. + * * **`withheldByBuyer` (Retido pelo Tomador):** A responsabilidade pelo recolhimento do ISSQN é transferida para o **tomador** (cliente/comprador) do serviço. O valor do ISSQN é descontado do total a ser pago ao prestador, e o tomador fica encarregado de repassá-lo ao município. + * * **`withheldByIntermediary` (Retido pelo Intermediário):** Em operações que contam com um intermediário (como marketplaces ou agências), a responsabilidade pelo recolhimento do ISSQN é transferida para este **intermediário**. + * + * **Comportamento Automático vs. Manual:** + * + * Conforme a descrição do campo, ele possui uma lógica de prioridade: + * 1. **Manual:** Se um valor for explicitamente enviado na requisição, ele será utilizado. + * 2. **Automático:** Se o campo não for enviado (nulo), o sistema aplicará as regras de cálculo automáticas (baseadas na legislação, local da prestação e cadastro dos envolvidos) para definir se a retenção é aplicável e quem é o responsável. + * + * **Relação com a Reforma Tributária (IBS/CBS):** + * + * Assim como outros campos legados, o `retentionType` aplica-se **exclusivamente ao ISSQN**. As regras de retenção e recolhimento para os novos tributos (IBS e CBS) serão definidas por legislação complementar específica e tratadas em outros campos do layout. + * + * --- + * + * Tipo de retenção do ISSQN (tpRetISSQN). + * Valores possíveis: + * - `NotWithheld`: Não Retido + * - `WithheldByBuyer`: Retido pelo Tomador + * - `WithheldByIntermediary`: Retido pelo Intermediário + * + * Define o tipo de retenção. Prioridade: Se um valor for enviado na integração, ele será utilizado. Automático: Se o campo não for enviado (nulo), o sistema aplicará a regra de cálculo automática para definir a retenção. + * @default NotWithheld + * @enum {string} + */ + readonly retentionType: "NotWithheld" | "WithheldByBuyer" | "WithheldByIntermediary"; + /** + * @description Tomador dos serviços. + * + * **Opcional**: o grupo `borrower` pode ser omitido. Quando informado, aplicam-se validações condicionais aos seus campos: + * - `name`: se informado, no máximo 115 caracteres; + * - `federalTaxNumber`: se informado (diferente de zero), deve ser um CNPJ/CPF válido e coerente com o `type` (`NaturalPerson` → CPF; `LegalEntity` → CNPJ). Para tomador no exterior (`address.country` ≠ `BRA`), não é validado; + * - `phoneNumber`: se informado, entre 7 e 20 dígitos; + * - `address`: opcional, mas se um endereço **brasileiro** for parcialmente preenchido, passam a ser obrigatórios `postalCode`, `street`, `city.code` (código IBGE de 7 dígitos), `city.name` e `state` (UF de 2 letras), que devem ser consistentes entre si. + */ + readonly borrower?: components["schemas"]["partyDefinition"]; + /** + * @description Grupo de informações relativas ao intermediário do serviço (interm). + * + * **Opcional**: pode ser omitido; envie apenas quando houver intermediário na operação (por exemplo, quando a retenção dos tributos é feita pelo intermediário, `issWithheldIndicator = WithheldByIntermediary`). Quando informado, segue a mesma estrutura do `borrower` (ex.: `type`, `federalTaxNumber`, `name`). + */ + readonly intermediary?: components["schemas"]["partyDefinition"]; + /** + * @description Representa o **Destinatário Final do Serviço**, quando ele é diferente do tomador. Utiliza a estrutura `partyDefinition`. + * + * **IMPORTANTE**: Quando este objeto for preenchido, é obrigatório informar o valor `DifferentFromBuyer` na propriedade `destinationIndicator` dentro do grupo `ibsCbs`. + */ + readonly recipient?: components["schemas"]["partyDefinition"]; + /** @description Local da Prestação do Serviço (cLocPrestacao, cPaisPrestacao) */ + readonly location?: components["schemas"]["addressDefinition"]; + readonly activityEvent?: components["schemas"]["activityEvent"]; + readonly ReferenceSubstitution?: components["schemas"]["ReferenceSubstitution"]; + readonly lease?: components["schemas"]["lease"]; + readonly construction?: components["schemas"]["construction"]; + /** @description Grupo de informações de operações relacionadas a bens imóveis, exceto obras. (imovel) */ + readonly realEstate?: components["schemas"]["realEstate"]; + readonly foreignTrade?: components["schemas"]["foreignTrade"]; + readonly deduction?: components["schemas"]["deduction"]; + readonly benefit?: components["schemas"]["benefit"]; + readonly suspension?: components["schemas"]["suspension"]; + /** @description Detalhes dos valores do serviço, incluindo encargos. (RetornoComplementarIBSCBS) */ + readonly serviceAmountDetails?: components["schemas"]["serviceAmountDefinitions"]; + readonly ibsCbs?: components["schemas"]["ibsCbs"]; + readonly approximateTax?: components["schemas"]["approximateTax"]; + readonly approximateTotals?: components["schemas"]["approximateTotals"]; + }; + /** @description Definição de endereço reutilizável, alinhada com as estruturas nacionais e complementada para endereços no exterior (tpEnderecoExterior, tpEstadoProvinciaRegiao). */ + readonly addressDefinition: { + /** + * @description Sigla do País (padrão ISO 3166-1). Exemplo: BRA, USD, ARG (country do partyDefinition, cPais da NFS-e NAC). + * @default BRA + */ + readonly country: string; + /** @description CEP (Exemplo: 99999-999) ou Código de Endereçamento Postal no exterior (cEndPost). */ + readonly postalCode?: string; + /** @description Logradouro (xLgr, tpLogradouro) */ + readonly street?: string; + /** @description Número (Exemplo: 185 ou S/N) (nro, tpNumeroEndereco) */ + readonly number?: string; + /** @description Complemento (Exemplo: BLC A; APT 10) (xCpl, tpComplementoEndereco) */ + readonly additionalInformation?: string; + /** @description Bairro (xBairro, tpBairro) */ + readonly district?: string; + /** @description Cidade */ + readonly city?: { + /** @description Código do IBGE (cMun, tpCidade) */ + readonly code?: string; + /** @description Nome da Cidade (tpNomeCidade) - Usado para endereços no exterior. */ + readonly name?: string; + }; + /** @description Estado/UF (tpUF) ou Estado, Província, Região no exterior (xEstProvReg, tpEstadoProvinciaRegiao) */ + readonly state?: string; + }; + /** @description Definição reutilizável para um participante (Tomador, Intermediário, Fornecedor). Baseado em partyDefinition da NFSe Nacional, com campos alinhados aos tipos de São Paulo (tpInformacoesPessoa, gpCPFCNPJNIF). */ + readonly partyDefinition: { + /** + * @description Tipo do participante. + * Valores possíveis: + * - `Undefined`: Não definido + * - `NaturalPerson`: Pessoa Física + * - `LegalEntity`: Pessoa Jurídica + * @enum {string} + */ + readonly type?: "Undefined" | "NaturalPerson" | "LegalEntity"; + /** @description Nome / Razão Social (xNome, tpRazaoSocial), obrigatório quando aplicável (tpRazaoSocialObrigatorio) */ + readonly name?: string; + /** @description CNPJ, CPF ou NIF (Número de Identificação Fiscal) (tpCNPJ, tpCPF, tpNIF) */ + readonly federalTaxNumber?: number; + /** @description Inscrição Municipal (tpInscricaoMunicipal, tamanho 8 ou 12 na v2.0 SP) */ + readonly municipalTaxNumber?: string; + /** @description Inscrição Estadual (Opcional, não se aplica a todos) (tpInscricaoEstadual) */ + readonly stateTaxNumber?: string; + /** + * @description Tipo do Regime Tributário. + * Valores possíveis: + * - `Isento`: Isento + * - `MicroempreendedorIndividual`: Microempreendedor Individual (MEI) + * - `SimplesNacional`: Simples Nacional + * - `LucroPresumido`: Lucro Presumido + * - `LucroReal`: Lucro Real + * @enum {string} + */ + readonly taxRegime?: "Isento" | "MicroempreendedorIndividual" | "SimplesNacional" | "LucroPresumido" | "LucroReal"; + /** @description Número do Cadastro de Atividade Econômica da Pessoa Física (CAEPF). */ + readonly caepf?: string; + /** @description Telefone */ + readonly phoneNumber?: string; + /** @description Endereço eletrônico (email, tpEmail) */ + readonly email?: string; + /** + * @description Justificativa para ausência de NIF (tpNaoNIF, 0 - Não informado na nota de origem, 1 - Dispensado do NIF, 2 - Não exigência do NIF, além dos padrões nacionais). + * Valores possíveis: + * - `NotInformedOriginal`: Não informado na nota de origem + * - `Exempted`: Dispensado do NIF + * - `NotRequired`: Não exigência do NIF + * @enum {string} + */ + readonly noTaxIdReason?: "NotInformedOriginal" | "Exempted" | "NotRequired"; + readonly address?: components["schemas"]["addressDefinition"]; + }; + /** @description Detalhes dos valores do serviço, incluindo encargos. (tpRetornoComplementarIBSCBS) */ + readonly serviceAmountDefinitions: { + /** @description Valor Inicial Cobrado (ValorInicialCobrado) - Valor dos serviços antes de tributos, multa e juros. (Versão 2.0 SP) */ + readonly initialChargedAmount?: number; + /** @description Valor Final Cobrado (ValorFinalCobrado) - Valor total cobrado pela prestação do serviço, incluindo todos os tributos. (Versão 2.0 SP) */ + readonly finalChargedAmount?: number; + /** @description Valor da Multa (ValorMulta) (Versão 2.0 SP) */ + readonly fineAmount?: number; + /** @description Valor dos Juros (ValorJuros) (Versão 2.0 SP) */ + readonly interestAmount?: number; + }; + /** @description Detalhes da atividade do evento (atvEvento, tpAtividadeEvento) */ + readonly activityEvent: { + /** @description Nome do evento (xNomeEvt, tpXNomeEvt) */ + readonly name?: string; + /** + * Format: date-time + * @description Data de início do evento no formato YYYY-MM-DDTHH:MM:SS.SSSSSS-03:00 (dtiniEvt) + */ + readonly beginOn?: string; + /** + * Format: date-time + * @description Data do fim do evento no formato YYYY-MM-DDTHH:MM:SS.SSSSSS-03:00 (dtFimEvt) + */ + readonly endOn?: string; + /** @description Código da atividade do evento */ + readonly Code?: string; + /** @description Endereço do evento (end, tpEnderecoSimplesIBSCBS) */ + readonly address?: components["schemas"]["addressDefinition"]; + }; + /** + * @description Totais aproximados dos tributos (Lei 12.741/2012). (totTrib) - Define os totais aproximados de tributos. **Prioridade**: Se informado, o valor enviado será utilizado, ignorando o cálculo automático. **Automático**: Caso o campo não seja enviado (nulo), os valores serão calculados automaticamente pelo sistema. + * + * **Estrutura Simplificada vs. Detalhada (`approximateTax` vs. `approximateTotals`)** + * + * Ambos os campos, `approximateTax` e `approximateTotals`, servem para atender à Lei 12.741/2012 ("Lei De Olho no Imposto"), que exige a informação da carga tributária aproximada na nota fiscal. A principal diferença entre eles está na **granularidade** da informação. + * + * * **`approximateTax` (Estrutura Simplificada):** + * Permite informar a carga tributária de forma consolidada, através de um percentual (`totalRate`) ou valor monetário (`totalAmount`) totais. É ideal para sistemas que não possuem a decomposição dos tributos por esfera governamental. + * + * * **`approximateTotals` (Estrutura Detalhada):** + * Permite um detalhamento completo, separando os valores por esfera: `federal`, `state` e `municipal`. É a escolha ideal quando o sistema possui essa informação decomposta, oferecendo maior transparência. + * + * **Flexibilidade e Comportamento Automático:** + * A coexistência dos dois campos oferece **flexibilidade**. O `approximateTax` simplifica a integração para sistemas com dados consolidados, enquanto o `approximateTotals` atende a sistemas que desejam fornecer o detalhamento completo. O sistema de emissão priorizará a informação mais detalhada, se disponível. + * + * **Importante:** Se nenhum dos grupos (`approximateTax` ou `approximateTotals`) for preenchido, o sistema realizará o cálculo automático e preencherá o grupo `approximateTax`. + */ + readonly approximateTax: { + /** @description Fonte de informação da carga tributária (tpFonteCargaTributaria) */ + readonly source?: string; + readonly version?: string; + /** @description Valor percentual da carga tributária (tpPercentualCargaTributaria) */ + readonly totalRate?: number; + /** @description Valor da carga tributária total em R$ (ValorCargaTributaria, tpValor) */ + readonly totalAmount?: number; + }; + /** @description Grupo de informações relativas à NFS-e a ser substituída */ + readonly ReferenceSubstitution: { + /** @description Identificador da NFS-e a ser substituída (chSubstda). */ + readonly id?: string; + /** + * @description Motivo da substituição (equivale ao cMotivo). + * Valores possíveis: + * - `SnOut`: Desenquadramento de NFS-e do Simples Nacional + * - `SnIn`: Enquadramento de NFS-e no Simples Nacional + * - `ImmunityAddRetro`: Inclusão Retroativa de Imunidade/Isenção para NFS-e + * - `ImmunityRemoveRetro`: Exclusão Retroativa de Imunidade/Isenção para NFS-e + * - `RejectionBuyerOrIntermediary`: Rejeição de NFS-e pelo tomador ou pelo intermediário se responsável pelo recolhimento do tributo + * - `Other`: Outros + * @enum {string} + */ + readonly reason?: "SnOut" | "SnIn" | "ImmunityAddRetro" | "ImmunityRemoveRetro" | "RejectionBuyerOrIntermediary" | "Other"; + /** @description Descrição quando reason = 'other' (equivale ao xMotivo). */ + readonly reasonText?: string; + }; + /** @description Grupo de informações relativas a atividades de Locação, sublocação, arrendamento, direito de passagem ou permissão de uso, compartilhado ou não, de ferrovia, rodovia, postes, cabos, dutos e condutos de qualquer natureza. */ + readonly lease: { + /** + * @description Categoria do serviço (categ). + * Valores possíveis: + * - `Lease`: Locação + * - `Sublease`: Sublocação + * - `Leasehold`: Arrendamento + * - `RightOfWay`: Direito de passagem + * - `UsePermission`: Permissão de uso + * @enum {string} + */ + readonly category?: "Lease" | "Sublease" | "Leasehold" | "RightOfWay" | "UsePermission"; + /** + * @description Objeto da locação/sublocação/arrendamento/etc. (objeto). + * Valores possíveis: + * - `Railway`: Ferrovia + * - `Road`: Rodovia + * - `Poles`: Postes + * - `Cables`: Cabos + * - `Pipelines`: Dutos + * - `Conduits`: Condutos + * @enum {string} + */ + readonly objectType?: "Railway" | "Road" | "Poles" | "Cables" | "Pipelines" | "Conduits"; + /** @description Comprimento total de ferrovia/rodovia/cabos/dutos/condutos (extensao). */ + readonly totalLength?: number; + /** @description Número total de postes (nPostes). */ + readonly polesCount?: number; + }; + /** @description Grupo de informações relativas à obras de construção civil e congêneres. Apenas uma das opções (workId, cibCode ou siteAddress) deve ser preenchida. (obra) */ + readonly construction: { + /** @description Inscrição imobiliária fiscal (inscImobFisc), exemplos: SQL ou INCRA. (tpinsclmobFisc) */ + readonly propertyFiscalRegistration?: string; + /** @description Identificação da obra (CNO/CEI) — cObra. */ + readonly workId?: { + /** + * @description Tipo de cadastro da obra (CNO/CEI). (tpCObra) - Cadastro Nacional de Obras, ou do Cadastro Específico do INSS. + * Valores possíveis: + * - `bra.cno`: Cadastro Nacional de Obras + * - `bra.cei`: Cadastro Específico do INSS + * @enum {string} + */ + readonly scheme?: "bra.cno" | "bra.cei"; + /** @description Número da obra no cadastro selecionado. (tpCObra, tpNumero) */ + readonly value?: string; + }; + /** @description Código do Cadastro Imobiliário Brasileiro (cCIB). (tpCCIB) */ + readonly cibCode?: string; + /** @description Endereço da obra (nacional ou exterior). (siteAddress) */ + readonly siteAddress?: components["schemas"]["addressDefinition"]; + /** + * @description Número do encapsulamento da obra (NumeroEncapsulamento). String numérica com 1 a 12 dígitos. + * Atualmente utilizado apenas pelo serializador do município de São Paulo (Paulistana): quando informado, é emitido como o elemento XML `` nos blocos `` e ``, imediatamente antes de ``. Zeros à esquerda são preservados. (tpNumero: 1-12 dígitos) + * @example 123456 + */ + readonly encapsulationNumber?: string; + } & (unknown | unknown | unknown); + /** @description Grupo de informações de operações relacionadas a bens imóveis, exceto obras. Apenas uma das opções (cibCode ou siteAddress) deve ser preenchida. (imovel) */ + readonly realEstate: { + /** @description Inscrição imobiliária fiscal (inscImobFisc), exemplos: SQL ou INCRA. (tpinsclmobFisc) */ + readonly propertyFiscalRegistration?: string; + /** @description Código do Cadastro Imobiliário Brasileiro (cCIB). (tpCCIB) */ + readonly cibCode?: string; + /** @description Endereço do imóvel (nacional ou exterior). (siteAddress) */ + readonly siteAddress?: components["schemas"]["addressDefinition"]; + } & (unknown | unknown); + /** @description Grupo de informações sobre transações entre residentes ou domiciliados no Brasil com residentes ou domiciliados no exterior */ + readonly foreignTrade: { + /** + * @description Modo de prestação (mdPrestacao). + * Valores possíveis: + * - `Unknown`: Desconhecido (tipo não informado na nota de origem) + * - `CrossBorder`: Transfronteiriço + * - `ConsumptionInBrazil`: Consumo no Brasil + * - `TemporaryPersonnel`: Movimento Temporário de Pessoas Físicas + * - `ConsumptionAbroad`: Consumo no Exterior + * @default unknown + * @enum {string} + */ + readonly serviceMode: "Unknown" | "CrossBorder" | "ConsumptionInBrazil" | "TemporaryPersonnel" | "ConsumptionAbroad"; + /** + * @description Vínculo entre as partes (vincPrest). + * Valores possíveis: + * - `NoLink`: Sem vínculo com o Tomador/Prestador + * - `Controlled`: Controlada + * - `Controller`: Controladora + * - `Affiliate`: Coligada + * - `HeadOffice`: Matriz + * - `Branch`: Filial ou sucursal + * - `OtherLink`: Outro vínculo + * @default NoLink + * @enum {string} + */ + readonly relationShip: "NoLink" | "Controlled" | "Controller" | "Affiliate" | "HeadOffice" | "Branch" | "OtherLink"; + /** @description Moeda da transação (tabela de moedas do Banco Central do Brasil) — tpMoeda. */ + readonly currency?: string; + /** @description Valor do serviço na moeda informada em currency — vServMoeda. */ + readonly serviceAmountInCurrency?: number; + /** + * @description Mecanismo de apoio/fomento utilizado pelo prestador (mecAFComexP). + * Valores possíveis: + * - `Unknown`: Desconhecido + * - `None`: Nenhum + * - `Acc`: ACC - Adiantamento sobre Contrato de Câmbio + * - `Ace`: ACE – Adiantamento sobre Cambiais Entregues + * - `BndesEximPostShipServices`: BNDES-Exim Pós-Embarque – Serviços + * - `BndesEximPreShipServices`: BNDES-Exim Pré-Embarque - Serviços + * - `Fge`: FGE - Fundo de Garantia à Exportação + * - `ProexEqualization`: PROEX - EQUALIZAÇÃO + * - `ProexFinancing`: PROEX - Financiamento + * @default unknown + * @enum {string} + */ + readonly supportMechanismProvider: "Unknown" | "None" | "Acc" | "Ace" | "BndesEximPostShipServices" | "BndesEximPreShipServices" | "Fge" | "ProexEqualization" | "ProexFinancing"; + /** + * @description Mecanismo de apoio/fomento utilizado pelo tomador (mecAFComexT). + * Valores possíveis: + * - `Unknown`: Desconhecido + * - `None`: Nenhum + * - `PublicAdminAndInternationalRep`: Adm. Pública e Repr. Internacional + * - `LeasesMachineryShipsAircraft`: Alugueis e Arrend. Mercantil de maquinas, equip., embarc. e aeronaves + * - `AircraftLeaseAirTransportPublic`: Arrendamento Mercantil de aeronave para empresa de transporte aéreo público + * - `ExportAgentsCommission`: Comissão a agentes externos na exportação + * - `StorageHandlingTransportAbroad`: Despesas de armazenagem, mov. e transporte de carga no exterior + * - `FifaEventsSubsidiary`: Eventos FIFA (subsidiária) + * - `FifaEvents`: Eventos FIFA + * - `FreightsVesselAircraftRentalsOthers`: Fretes, arrendamentos de embarcações ou aeronaves e outros + * - `AeronauticalMaterial`: Material Aeronáutico + * - `PromotionGoodsAbroad`: Promoção de Bens no Exterior + * - `PromotionBrazilianTourism`: Promoção de Dest. Turísticos Brasileiros + * - `PromotionBrazilAbroad`: Promoção do Brasil no Exterior + * - `PromotionServicesAbroad`: Promoção Serviços no Exterior + * - `Recine`: RECINE + * - `Recopa`: RECOPA + * - `TrademarksPatentsCultivars`: Registro e Manutenção de marcas, patentes e cultivares + * - `Reicomp`: REICOMP + * - `Reidi`: REIDI + * - `Repenec`: REPENEC + * - `Repes`: REPES + * - `Retaero`: RETAERO + * - `Retid`: RETID + * - `RoyaltiesTechnicalAssistance`: Royalties, Assistência Técnica, Científica e Assemelhados + * - `ConformityAssessmentWto`: Serviços de avaliação da conformidade vinculados aos Acordos da OMC + * - `Zpe`: ZPE + * @default unknown + * @enum {string} + */ + readonly supportMechanismReceiver: "Unknown" | "None" | "PublicAdminAndInternationalRep" | "LeasesMachineryShipsAircraft" | "AircraftLeaseAirTransportPublic" | "ExportAgentsCommission" | "StorageHandlingTransportAbroad" | "FifaEventsSubsidiary" | "FifaEvents" | "FreightsVesselAircraftRentalsOthers" | "AeronauticalMaterial" | "PromotionGoodsAbroad" | "PromotionBrazilianTourism" | "PromotionBrazilAbroad" | "PromotionServicesAbroad" | "Recine" | "Recopa" | "TrademarksPatentsCultivars" | "Reicomp" | "Reidi" | "Repenec" | "Repes" | "Retaero" | "Retid" | "RoyaltiesTechnicalAssistance" | "ConformityAssessmentWto" | "Zpe"; + /** + * @description Vínculo à movimentação temporária de bens (movTempBens). + * Valores possíveis: + * - `Unknown`: Desconhecido + * - `No`: Não + * - `LinkedImportDeclaration`: Vinculada - Declaração de Importação + * - `LinkedExportDeclaration`: Vinculada - Declaração de Exportação + * @enum {string} + */ + readonly temporaryGoods?: "Unknown" | "No" | "LinkedImportDeclaration" | "LinkedExportDeclaration"; + /** @description Número da Declaração de Importação (DI/DSI/DA/DRI-E) averbado — nDI. */ + readonly importDeclaration?: string; + /** @description Número do Registro de Exportação (RE) averbado — nRE. */ + readonly exportRegistration?: string; + /** @description Indicador de envio da NFS-e ao MDIC (mdic). */ + readonly mdicDelivery?: boolean; + }; + /** + * @description Grupo de informações relativas ao valores para dedução/redução do valor da base de cálculo (vDedRed). Dedução/Redução aplicada SOMENTE à base de cálculo do ISSQN. + * + * **Deduções: Simples vs. Estruturado (`deductionsAmount` vs. `deduction`)** + * + * O layout oferece duas formas de registrar deduções da base de cálculo do ISSQN, visando flexibilidade e conformidade com os diferentes padrões (municipal legado vs. nacional). + * + * * **`deductionsAmount` (Valor Simples):** + * Um campo numérico para informar o **valor total consolidado** das deduções. É uma abordagem direta, compatível com layouts mais antigos que não exigem o detalhamento dos documentos que comprovam a dedução. + * + * * **`deduction` (Grupo Estruturado):** + * Um objeto complexo que permite justificar cada dedução com base em documentos fiscais, alinhando-se ao **padrão da NFS-e Nacional**. Ele detalha a origem de cada valor (chave do documento, tipo, valor, fornecedor). + * + * **Relação e Recomendação:** + * A coexistência dos campos oferece compatibilidade. `deductionsAmount` é uma opção simplificada, enquanto o grupo `deduction` é a forma mais completa e recomendada para garantir rastreabilidade e conformidade fiscal com o padrão nacional. O sistema de emissão priorizará os dados detalhados do grupo `deduction`, se fornecidos. + * + * **Recomendação de Uso:** + * * Use `deductionsAmount` para integrações simples ou quando não há detalhamento dos documentos. + * * Use o grupo `deduction` sempre que a informação detalhada dos documentos estiver disponível para garantir a máxima conformidade. + */ + readonly deduction: { + /** @description Percentual padrão de dedução/redução (pDR, %). */ + readonly rate?: number; + /** @description Valor monetário padrão de dedução/redução (vDR, R$). */ + readonly amount?: number; + readonly documents?: readonly components["schemas"]["deductionDocument"][]; + } & (unknown | unknown | unknown); + /** + * @description Documento base que justifica um item de dedução/redução (docDedRed, TCDocDedRed). + * Pelo menos **um** identificador deve ser preenchido — `nfseKey`, `nfeKey`, + * `municipalNfse`, `fiscalDocumentNumber` ou `nonFiscalDocumentNumber`. Se mais + * de um for enviado, o backend aplica a precedência nessa ordem. Quando + * `deductionType = Other`, o campo `otherDeductionDescription` passa a ser obrigatório. + */ + readonly deductionDocument: ({ + /** @description Chave de acesso da NFS-e nacional, exatamente 50 dígitos (chNFSe, TSChaveNFSe). */ + readonly nfseKey?: string; + /** @description Chave de acesso da NF-e (produto), exatamente 44 dígitos (chNFe). */ + readonly nfeKey?: string; + /** @description Referência a uma NFS-e municipal no padrão legado (NFSeMun). */ + readonly municipalNfse?: { + /** @description Código IBGE do município emissor (cMunNFSeMun). */ + readonly cityCode: string; + /** @description Número da NFS-e municipal (nNFSeMun). */ + readonly number: string; + /** @description Código de verificação (cVerifNFSeMun). */ + readonly verificationCode: string; + }; + /** @description Identificador de outro documento fiscal não eletrônico (nDocFisc). */ + readonly fiscalDocumentNumber?: string; + /** @description Identificador de documento não fiscal, ex: nota de débito interna (nDoc). */ + readonly nonFiscalDocumentNumber?: string; + /** + * @description Tipo da dedução/redução (tpDedRed). + * Valores aceitos (nome do enum — código numérico — descrição): + * - `FoodAndBeverages` (1): Alimentação e bebidas/frigobar + * - `Materials` (2): Materiais + * - `ConsortiumPassThrough` (5): Repasse consorciado + * - `HealthPlanPassThrough` (6): Repasse plano de saúde + * - `Services` (7): Serviços + * - `Subcontracting` (8): Subempreitada de mão de obra + * - `Other` (99): Outras deduções — exige `otherDeductionDescription` + * @enum {string} + */ + readonly deductionType: "FoodAndBeverages" | "Materials" | "ConsortiumPassThrough" | "HealthPlanPassThrough" | "Services" | "Subcontracting" | "Other"; + /** @description Obrigatório quando deductionType = 'Other' (xDescOutDed). */ + readonly otherDeductionDescription?: string; + /** + * Format: date + * @description Data de emissão do documento de origem (dtEmiDoc, AAAA-MM-DD). + */ + readonly issueDate: string; + /** @description Valor total dedutível/redutível no documento de origem (vDedutivelRedutivel). */ + readonly deductibleTotal: number; + /** @description Valor efetivamente utilizado como dedução nesta NFS-e (vDeducaoReducao). Deve ser ≤ deductibleTotal. */ + readonly usedAmount: number; + /** @description Fornecedor do documento (fornec). */ + readonly supplier?: components["schemas"]["partyDefinition"]; + } | unknown | unknown | unknown | unknown | unknown) & ({ + /** @enum {string} */ + readonly deductionType?: "Other"; + } | { + readonly deductionType?: unknown; + }); + /** @description Benefício Municipal aplicado à base de cálculo do ISSQN (BM). */ + readonly benefit: { + /** @description Identificador do benefício (nBM: IBGE[7] + tipo[2] + seq[5]). */ + readonly id: string; + /** @description Redução da BC por valor (vRedBCBM, R$). */ + readonly amount?: number; + /** @description Redução da BC por percentual (pRedBCBM, %). */ + readonly rate?: number; + } & (unknown | unknown); + /** @description Suspensão da exigibilidade do ISSQN (exigSusp). */ + readonly suspension: { + /** + * @description Motivo da suspensão (tpSusp). + * Valores possíveis: + * - `Judicial`: Exigibilidade Suspensa por Decisão Judicial + * - `Administrative`: Exigibilidade Suspensa por Processo Administrativo + * @enum {string} + */ + readonly reason: "Judicial" | "Administrative"; + /** @description Número do processo (nProcesso). */ + readonly processNumber: string; + }; + /** + * @description Totais aproximados dos tributos (Lei 12.741/2012). (totTrib) - Define os totais aproximados de tributos. **Prioridade**: Se informado, o valor enviado será utilizado, ignorando o cálculo automático. **Automático**: Caso o campo não seja enviado (nulo), os valores serão calculados automaticamente pelo sistema. + * + * **Estrutura Simplificada vs. Detalhada (`approximateTax` vs. `approximateTotals`)** + * + * Ambos os campos, `approximateTax` e `approximateTotals`, servem para atender à Lei 12.741/2012 ("Lei De Olho no Imposto"), que exige a informação da carga tributária aproximada na nota fiscal. A principal diferença entre eles está na **granularidade** da informação. + * + * * **`approximateTax` (Estrutura Simplificada):** + * Permite informar a carga tributária de forma consolidada, através de um percentual (`totalRate`) ou valor monetário (`totalAmount`) totais. É ideal para sistemas que não possuem a decomposição dos tributos por esfera governamental. + * + * * **`approximateTotals` (Estrutura Detalhada):** + * Permite um detalhamento completo, separando os valores por esfera: `federal`, `state` e `municipal`. É a escolha ideal quando o sistema possui essa informação decomposta, oferecendo maior transparência. + * + * **Flexibilidade e Comportamento Automático:** + * A coexistência dos dois campos oferece **flexibilidade**. O `approximateTax` simplifica a integração para sistemas com dados consolidados, enquanto o `approximateTotals` atende a sistemas mais robustos que desejam fornecer o detalhamento completo. O sistema de emissão priorizará a informação mais detalhada, se disponível. + * + * **Importante:** Se nenhum dos grupos (`approximateTax` ou `approximateTotals`) for preenchido, o sistema realizará o cálculo automático e preencherá o grupo `approximateTax`. + */ + readonly approximateTotals: { + /** @description Tributos federais. */ + readonly federal?: { + /** @description Valor percentual total aproximado dos tributos federais (%). (pTotTribFed) */ + readonly rate?: number; + /** @description Valor monetário total aproximado dos tributos federais (R$). (vTotTribFed) */ + readonly amount?: number; + }; + /** @description Tributos estaduais. */ + readonly state?: { + /** @description Valor percentual total aproximado dos tributos estaduais (%). (vTotTribEst) */ + readonly rate?: number; + /** @description Valor monetário total aproximado dos tributos estaduais (R$). (vTotTribEst) */ + readonly amount?: number; + }; + /** @description Tributos municipais. */ + readonly municipal?: { + /** @description Valor percentual total aproximado dos tributos municipais (%). (pTotTribMun) */ + readonly rate?: number; + /** @description Valor monetário total aproximado dos tributos municipais (R$). (vTotTribMun) */ + readonly amount?: number; + }; + /** @description Valor percentual total aproximado dos tributos (%). */ + readonly rate?: number; + /** @description Valor monetário total aproximado dos tributos (R$). */ + readonly amount?: number; + }; + /** @description Informações referentes ao IBS e à CBS por item (NFSe/infNFSe/DPS/infDPS/IBSCBS). (tpIBSCBS) */ + readonly ibsCbs: { + /** + * @description Finalidade da emissão da NFS-e (finNFe). (tpfinNFSe) - default: 'regular'. + * Valores possíveis: + * - `regular`: Regular + * @default regular + * @enum {string} + */ + readonly purpose: "regular"; + /** + * @description Relação entre destinatário e comprador/tomador indicado na NFS-e (indDest). (tpindDest) - default: 'sameAsBuyer'. Quando o valor for 'differentFromBuyer', o preenchimento do objeto 'recipient' se torna obrigatório. + * Valores possíveis: + * - `SameAsBuyer`: O destinatário é o mesmo que o comprador/tomador + * - `DifferentFromBuyer`: O destinatário é diferente do comprador/tomador + * @default SameAsBuyer + * @enum {string} + */ + readonly destinationIndicator: "SameAsBuyer" | "DifferentFromBuyer"; + /** @description Base de cálculo antes de reduções para cálculo do tributo bruto (vBC). */ + readonly basis?: number; + /** @description Montante relativo a reembolso/repasse/ressarcimento não integrante da BC (vReembRepasse). */ + readonly reimbursedResuppliedAmount?: number; + /** @description Valor monetário (R$) total relativo aos valores de dedução e redução da Base de Cálculo do IBS e da CBS referentes às operações de locação, cessão onerosa ou arrendamento de bens imóveis, e serviços médicos (vCalcDedRedIBSCBS). */ + readonly ibscbsDeductionReductionAmount?: number; + /** @description Indica se a operação é uma doação (indDoacao). (tpNaoSim) */ + readonly isDonation?: boolean | null; + /** + * @description **Indicador de Uso ou Consumo Pessoal** + * + * Indica se a operação é para uso ou consumo pessoal do adquirente. Este campo é crucial para a aplicação de regras tributárias específicas, como a não geração de crédito de IBS/CBS para o tomador. (indFinal) + */ + readonly personalUse?: boolean | null; + /** + * @description **Indicador da Operação (operationIndicator / cIndOp)** + * + * O campo `operationIndicator` (ou `cIndOp` nos schemas XSD) é um código obrigatório que define a natureza e a característica da operação de fornecimento do serviço ou bem, sendo um dos campos mais importantes introduzidos pela Reforma Tributária. + * + * Sua principal finalidade é determinar o **local de incidência** (onde o imposto é devido) para os novos tributos, o IBS (Imposto sobre Bens e Serviços) e a CBS (Contribuição sobre Bens e Serviços). Diferente do modelo anterior, onde o local da prestação era muitas vezes suficiente, este código traz uma regra específica para cada tipo de operação. + * + * **Como funciona?** + * + * Com base no código informado, o sistema tributário nacional identifica se o imposto deve ser recolhido: + * * No município onde um imóvel está localizado (para serviços de construção ou sobre imóveis). + * * No endereço do estabelecimento do fornecedor. + * * No endereço do adquirente (comprador/tomador) do serviço. + * * No local onde um evento ocorre. + * * Entre outras regras específicas. + * + * A escolha do código correto é, portanto, fundamental para garantir a conformidade fiscal e o recolhimento do IBS para o estado e município corretos. A seleção de um código inadequado pode levar a apurações de impostos incorretas. + * + * **Exemplo prático:** + * Para um serviço de reforma de um imóvel (`código 020201`), o imposto será devido no município onde o imóvel está localizado, independentemente de onde o prestador ou o tomador do serviço estejam estabelecidos. Já para um serviço de consultoria prestado à distância (`código 100301`), a regra geral aponta para o domicílio do adquirente. + * + * A lista completa de valores pode ser consultada na Tabela de [operationIndicator](https://nfe.io/docs/documentacao/reforma-tributaria/conceitos-funcionais/documentacao-layout-nfse-rtc/#tabela-de-operationindicator) da documentação funcional. + */ + readonly operationIndicator: string; + /** + * @description Tipo de Operação com Entes Governamentais ou outros serviços sobre bens imóveis (tpOper). + * Valores possíveis: + * - `SupplyFirstPayLater`: Fornecimento com pagamento posterior + * - `PayForPastSupply`: Recebimento do pagamento com fornecimento já realizado + * - `SupplyForPastPay`: Fornecimento com pagamento já realizado + * - `PayFirstSupplyLater`: Recebimento do pagamento com fornecimento posterior + * - `SupplyPayTogether`: Fornecimento e recebimento do pagamento concomitantes + * @enum {string} + */ + readonly operationType?: "SupplyFirstPayLater" | "PayForPastSupply" | "SupplyForPastPay" | "PayFirstSupplyLater" | "SupplyPayTogether"; + /** + * @description Código de Situação Tributária do IBS/CBS. Opcional. + * Quando não preenchido, o valor será obtido através dos 3 primeiros caracteres do valor informado no campo `classCode`. + * A lista completa de valores pode ser consultada na Tabela de [situationCode](hhttps://nfe.io/docs/documentacao/reforma-tributaria/conceitos-funcionais/documentacao-layout-nfse-rtc/#tabela-de-situationcode) da documentação funcional. + */ + readonly situationCode?: string; + /** + * @description Código de Classificação Tributária do IBS/CBS. (cClassTrib, tpClassificacaoTributaria) + * A lista completa de valores pode ser consultada na Tabela de [classCode](https://nfe.io/docs/documentacao/reforma-tributaria/conceitos-funcionais/documentacao-layout-nfse-rtc/#tabela-de-classcode) da documentação funcional. + */ + readonly classCode: string; + /** @description Grupo de NFS-e referenciadas (gRefNFSe). */ + readonly relatedDocs?: { + /** @description Chaves de NFS-e referenciadas (refNFSe). */ + readonly items?: readonly string[]; + }; + /** @description Grupo de informações relativas aos bens móveis objetos de locação (gLocBensMoveis). Grupo só é admitido quando se tratar de locação de bens móveis. cTribNac = 99.04.01. */ + readonly leasedMovableAssets?: readonly { + /** @description Código da Nomenclatura Comum do Mercosul (NCM) do bem móvel objeto da locação (cNCMBemMovel). */ + readonly ncmCode?: string; + /** @description Descrição do Bem Móvel objeto da locação (xNCMBemMovel). */ + readonly description?: string; + /** @description Quantidade do Bem Móvel objeto da locação (qtdNCMBemMovel). */ + readonly quantity?: number; + }[]; + /** @description Alíquotas e total do IBS por esfera subnacional. */ + readonly ibs?: { + /** @description Total do IBS (vIBSTot = vIBSUF + vIBSMun). */ + readonly totalAmount: number; + readonly state: { + /** @description Alíquota IBS (UF) (%) (pAliqIBSUF). */ + readonly rate: number; + /** + * @description Redução de alíquota estadual (%). + * @default 0 + */ + readonly rateReduction: number; + /** @description pAliqEfetUF = rate × (1 − rateReduction). Se rateReduction ausente, usar rate. */ + readonly effectiveRate: number; + /** @description Deferimento do IBS na esfera estadual. */ + readonly deferment?: { + /** @description Percentual de diferimento (pDif, %). */ + readonly rate?: number; + /** @description Valor do diferimento (vDif). */ + readonly amount?: number; + }; + /** @description Valor de imposto devolvido/devolução (vDevTrib) na esfera estadual. */ + readonly returnedAmount?: number; + /** @description Valor do IBS da UF (vIBSUF). */ + readonly amount?: number; + }; + readonly municipal: { + /** @description Alíquota IBS (Município) (%) (pAliqIBSMun). */ + readonly rate: number; + /** + * @description Redução de alíquota municipal (%). + * @default 0 + */ + readonly rateReduction: number; + /** @description pAliqEfetMun = rate × (1 − rateReduction). Se rateReduction ausente, usar rate. */ + readonly effectiveRate: number; + /** @description Deferimento do IBS na esfera municipal. */ + readonly deferment?: { + /** @description Percentual de diferimento (pDif, %). */ + readonly rate?: number; + /** @description Valor do diferimento (vDif). */ + readonly amount?: number; + }; + /** @description Valor de imposto devolvido/devolução (vDevTrib) na esfera municipal. */ + readonly returnedAmount?: number; + /** @description Valor do IBS do Município (vIBSMun). */ + readonly amount?: number; + }; + }; + /** @description Alíquotas da CBS (esfera federal). */ + readonly cbs?: { + /** @description Alíquota da CBS (%) (pAliqCBS). */ + readonly rate: number; + /** + * @description Redução de alíquota da CBS (%). + * @default 0 + */ + readonly rateReduction: number; + /** @description pAliqEfetCBS = rate × (1 − rateReduction). Se rateReduction ausente, usar rate. */ + readonly effectiveRate: number; + /** @description Deferimento da CBS. */ + readonly deferment?: { + /** @description Percentual de diferimento (pDif, %). */ + readonly rate?: number; + /** @description Valor do diferimento (vDif). */ + readonly amount?: number; + }; + /** @description Valor de imposto devolvido/devolução da CBS (vDevTrib). */ + readonly returnedAmount?: number; + /** @description Valor total da CBS (vCBS). */ + readonly amount?: number; + }; + /** @description Tributação regular hipotética caso condição resolutória/suspensiva não se aplique. (tpGTribRegular) */ + readonly regularTaxation?: { + /** @description CST regular (CSTReg), 3 dígitos. */ + readonly situationCode?: string; + /** @description Classificação tributária regular (cClassTribReg), 6 dígitos. */ + readonly classCode?: string; + readonly ibs?: { + /** @description Total do IBS (vIBSTotReg). */ + readonly totalAmount?: number; + readonly state?: { + /** @description Alíquota efetiva IBS UF (pAliqEfetRegIBSUF). */ + readonly effectiveRate?: number; + /** @description IBS UF regular (vTribRegIBSUF). */ + readonly amount?: number; + }; + readonly municipal?: { + /** @description Alíquota efetiva IBS Município (pAliqEfetRegIBSMun). */ + readonly effectiveRate?: number; + /** @description IBS Município regular (vTribRegIBSMun). */ + readonly amount?: number; + }; + }; + readonly cbs?: { + /** @description Alíquota efetiva CBS (pAliqEfetRegCBS). */ + readonly effectiveRate?: number; + /** @description CBS regular (vTribRegCBS). */ + readonly amount?: number; + }; + }; + /** @description Créditos presumidos de IBS e CBS quando aplicáveis. */ + readonly presumedCredits?: { + readonly ibs?: { + /** @description Código do crédito presumido IBS (cCredPres). */ + readonly code?: string; + /** @description Percentual do crédito presumido IBS (pCredPres). */ + readonly rate?: number; + /** @description Valor do crédito presumido IBS (vCredPres). */ + readonly amount?: number; + /** @description Valor do crédito presumido IBS sob condição suspensiva (vCredPresCondSus). */ + readonly conditionalAmount?: number; + }; + readonly cbs?: { + /** @description Código do crédito presumido CBS (cCredPres). */ + readonly code?: string; + /** @description Percentual do crédito presumido CBS (pCredPres). */ + readonly rate?: number; + /** @description Valor do crédito presumido CBS (vCredPres). */ + readonly amount?: number; + /** @description Valor do crédito presumido CBS sob condição suspensiva (vCredPresCondSus). */ + readonly conditionalAmount?: number; + }; + }; + /** @description Composição do valor de IBS e CBS em compras governamentais. */ + readonly governmentPurchase?: { + /** + * @description Tipo do ente da compra governamental (tpEnteGov). + * Valores possíveis: + * - `Union`: União + * - `State`: Estado + * - `FederalDistrict`: Distrito Federal + * - `Municipality`: Município + * @enum {string} + */ + readonly entityType?: "Union" | "State" | "FederalDistrict" | "Municipality"; + /** + * @description Tipo de Operação com Entes Governamentais (tpOperGov). + * Valores possíveis: + * - `SupplyFirstPayLater`: Fornecimento com pagamento posterior + * - `PayForPastSupply`: Pagamento de fornecimento anterior + * - `SupplyForPastPay`: Fornecimento com pagamento antecipado + * - `SupplyPayTogether`: Fornecimento e pagamento concomitantes + * @enum {string} + */ + readonly operationType?: "SupplyFirstPayLater" | "PayForPastSupply" | "SupplyForPastPay" | "SupplyPayTogether"; + readonly ibs?: { + /** @description Total do IBS em compras governamentais (vIBSTotGov). */ + readonly totalAmount?: number; + readonly state?: { + /** @description Alíquota IBS UF em compras governamentais (pAliqIBSUF). */ + readonly rate?: number; + /** @description Valor IBS UF em compras governamentais (vTribIBSUF). */ + readonly amount?: number; + }; + readonly municipal?: { + /** @description Alíquota IBS Município em compras governamentais (pAliqIBSMun). */ + readonly rate?: number; + /** @description Valor IBS Município em compras governamentais (vTribIBSMun). */ + readonly amount?: number; + }; + }; + readonly cbs?: { + /** @description Alíquota CBS em compras governamentais (pAliqCBS). */ + readonly rate?: number; + /** @description Valor CBS em compras governamentais (vTribCBS). */ + readonly amount?: number; + }; + }; + /** @description Transferência de créditos de IBS e CBS. */ + readonly creditTransfer?: { + /** @description Valor de IBS a transferir (vIBS). */ + readonly ibsAmount?: number; + /** @description Valor de CBS a transferir (vCBS). */ + readonly cbsAmount?: number; + }; + /** @description Valores de reembolso/repasse/ressarcimento já tributados e aqui referenciados. (tpGrupoReeRepRes) */ + readonly thirdPartyReimbursements?: { + readonly documents?: readonly components["schemas"]["thirdPartyReimbursementDocument"][]; + }; + }; + /** @description Documento utilizado para justificar exclusão da BC de IBS/CBS/ISS. (tpDocumento) */ + readonly thirdPartyReimbursementDocument: { + /** @description Chave NFS-e (padrão nacional). */ + readonly nfseKey?: string; + /** @description Chave NF-e (44 dígitos). */ + readonly nfeKey?: string; + /** @description Chave CT-e (44 dígitos). */ + readonly cteKey?: string; + /** @description Outro DF-e presente no repositório nacional. (tpDFeNacional) */ + readonly otherNationalDfe?: { + /** @description Chave do DF-e (chDFE). */ + readonly dfeKey: string; + /** @description Tipo do DF-e (xTpDFE, xTipoChaveDFe). */ + readonly dfeTypeText: string; + /** @description Documento fiscal a que se refere a chaveDfe (tipoChaveDFE, tpTipoChaveDFE). */ + readonly dfeType?: string; + }; + /** @description Documento fiscal que não está no repositório nacional (eletrônico ou não). (tpDocFiscalOutro) */ + readonly otherFiscalDoc?: { + /** @description Código IBGE do município emissor do documento fiscal (cMunEmiDocFisc). (cMunDocFiscal) */ + readonly issuerCityCode: string; + /** @description Número do documento fiscal (nDocFisc). (nDocFiscal, tpNumeroDescricaoDocumento) */ + readonly fiscalDocNumber: string; + /** @description Descrição do documento fiscal (xDocFisc). (xDocFiscal, tpNumeroDescricaoDocumento) */ + readonly fiscalDocDescription?: string; + }; + /** @description Documento não fiscal. (tpDocOutro) */ + readonly otherDoc?: { + /** @description Número do documento (nDoc). (nDoc, tpNumeroDescricaoDocumento) */ + readonly docNumber: string; + /** @description Descrição do documento (xDoc). (xDoc, tpNumeroDescricaoDocumento) */ + readonly docDescription: string; + }; + /** @description Fornecedor/emitente do documento referenciado (quando aplicável). (fornec, tpFornecedor) */ + readonly supplier?: components["schemas"]["partyDefinition"]; + /** + * Format: date + * @description Data de emissão (AAAA-MM-DD) (dtEmiDoc). + */ + readonly issueDate: string; + /** + * Format: date + * @description Data de competência (AAAA-MM-DD) (dtCompDoc). + */ + readonly accrualOn: string; + /** + * @description Motivo do reembolso/repasse/ressarcimento (tpReemb). (tpReeRepRes) + * Valores possíveis: + * - `RealEstateBrokerPassThrough`: Repasse de corretagem de imóveis + * - `TravelAgencySupplierPassThrough`: Repasse de valores de fornecedores em agência de viagens + * - `AdAgencyExternalProductionReimbursement`: Reembolso de produção externa em agência de publicidade + * - `AdAgencyMediaReimbursement`: Reembolso de despesas com mídia em agência de publicidade + * - `OtherReimbursement`: Outros reembolsos ou ressarcimentos + * @enum {string} + */ + readonly reimbursementType: "RealEstateBrokerPassThrough" | "TravelAgencySupplierPassThrough" | "AdAgencyExternalProductionReimbursement" | "AdAgencyMediaReimbursement" | "OtherReimbursement"; + /** @description Obrigatório quando reimbursementType = 'otherReimbursement' (xReembOutros). (tpXTpReeRepRes) */ + readonly reimbursementTypeText?: string; + /** @description Valor considerado para exclusão da BC (total ou parcial, R$) (vReemb). (vlrReembRepasse) */ + readonly amount: number; + } | unknown | unknown | unknown | unknown | unknown | unknown; + }; + responses: never; + parameters: never; + requestBodies: never; + headers: never; + pathItems: never; +} +export type $defs = Record; +export type operations = Record; diff --git a/src/index.ts b/src/index.ts index 9a8a05a..8a14967 100644 --- a/src/index.ts +++ b/src/index.ts @@ -32,7 +32,7 @@ * ``` * * @module @nfe-io/sdk - * @version 3.0.0-beta.1 + * @version 5.0.0 * @author NFE.io * @license MIT */ @@ -79,7 +79,6 @@ export type { Address, AddressCity, AddressLookupResponse, - AddressSearchOptions, // CT-e (Transportation Invoice) types TransportationInvoiceInboundSettings, @@ -317,6 +316,27 @@ export type { // Utility types ResourceId, ApiErrorResponse, + + // RTC (Reforma Tributária do Consumo) emission request types + NFSeRtcRequest, + ProductInvoiceRtcRequest, + + // Empresas (contribuintes-v2) spec-backed types + CompanyResourceItem, + CompanyResourceV1, + CreateCompanyResourceItem, + UpdateCompanyResourceItem, + CertificateMetadataResource, + CompanyAddress, + MunicipalTax, + CreateMunicipalTaxData, + UpdateMunicipalTaxData, + MunicipalTaxListResponse, + ConsumerInvoiceData, + ConsumerInvoice, + ConsumerInvoiceListResponse, + ConsumerInvoiceDisablementData, + CertificatesMetadataResource, } from './core/types.js'; /** @@ -420,6 +440,21 @@ export { TaxCalculationResource, createTaxCalculationResource } from './core/res export { TaxCodesResource, createTaxCodesResource } from './core/resources/tax-codes.js'; export { ProductInvoicesResource } from './core/resources/product-invoices.js'; export { StateTaxesResource } from './core/resources/state-taxes.js'; +export { ServiceInvoicesRtcResource } from './core/resources/service-invoices-rtc.js'; +export { ProductInvoicesRtcResource } from './core/resources/product-invoices-rtc.js'; +export { MunicipalTaxesResource } from './core/resources/municipal-taxes.js'; +export { ConsumerInvoicesResource } from './core/resources/consumer-invoices.js'; +export type { + ConsumerInvoiceListOptions, + ConsumerInvoiceEnvironment, +} from './core/resources/consumer-invoices.js'; +export { CertificatesResource } from './core/resources/certificates.js'; +export { NotificationsResource } from './core/resources/notifications.js'; +export type { Notification, NotificationListResponse } from './core/resources/notifications.js'; +export type { + CreateInvoiceResponse, + CancelInvoiceResponse, +} from './core/resources/service-invoices.js'; // ============================================================================ // Default Export (maintains v2 compatibility) @@ -472,7 +507,7 @@ export const PACKAGE_NAME = '@nfe-io/sdk'; * Current SDK version * @constant */ -export const PACKAGE_VERSION = '3.0.0-beta.1'; +export const PACKAGE_VERSION = '5.0.0'; /** * NFE.io API version supported by this SDK diff --git a/tests/integration/addresses.integration.test.ts b/tests/integration/addresses.integration.test.ts index 864540a..58adbd5 100644 --- a/tests/integration/addresses.integration.test.ts +++ b/tests/integration/addresses.integration.test.ts @@ -1,21 +1,18 @@ /** * Integration tests for AddressesResource * - * These tests require a valid API key and make real API calls. - * Skip these tests in CI/CD unless API key is available. + * These tests require a valid API key and make real API calls against the live + * address.api.nfe.io/v2 host (postal-code lookup only). * * To run these tests: * 1. Set NFE_DATA_API_KEY or NFE_API_KEY environment variable - * 2. Run: npm run test:integration + * 2. Run: RUN_INTEGRATION_TESTS=true npm run test:integration */ -import { describe, it, expect, beforeAll, afterAll } from 'vitest'; +import { describe, it, expect, beforeAll } from 'vitest'; import { NfeClient } from '../../src/core/client.js'; -import { NotFoundError } from '../../src/core/errors/index.js'; -import type { AddressLookupResponse } from '../../src/core/types.js'; import { shouldRunIntegrationTests, INTEGRATION_TEST_CONFIG } from './setup.js'; -// Use the shared integration test check const shouldRun = shouldRunIntegrationTests(); describe.skipIf(!shouldRun)('AddressesResource Integration', () => { @@ -24,122 +21,68 @@ describe.skipIf(!shouldRun)('AddressesResource Integration', () => { beforeAll(() => { client = new NfeClient({ apiKey: process.env.NFE_API_KEY, - dataApiKey: - process.env.NFE_DATA_API_KEY || - process.env.INTEGRATION_TEST_API_KEY, - environment: 'production', + dataApiKey: process.env.NFE_DATA_API_KEY || process.env.INTEGRATION_TEST_API_KEY, + environment: INTEGRATION_TEST_CONFIG.environment, }); }); - afterAll(() => { - // Cleanup if needed - }); - describe('lookupByPostalCode', () => { - it('should lookup a valid São Paulo postal code', async () => { - // CEP 01310-100 is Avenida Paulista, São Paulo - const result = await client.addresses.lookupByPostalCode('01310100'); - - expect(result).toBeDefined(); - expect(result.postalCode).toBe('01310100'); - expect(result.state).toBe('SP'); - expect(result.city?.name).toContain('São Paulo'); + it('looks up a valid São Paulo postal code (Avenida Paulista)', async () => { + const address = await client.addresses.lookupByPostalCode('01310100'); + + expect(address).toBeDefined(); + // API returns the CEP formatted with a hyphen + expect(address.postalCode).toBe('01310-100'); + expect(address.state).toBe('SP'); + expect(address.city.name).toContain('São Paulo'); + expect(address.street).toBeTruthy(); }); - it('should lookup a valid Rio de Janeiro postal code', async () => { - // CEP 20040-020 is Centro, Rio de Janeiro - const result = await client.addresses.lookupByPostalCode('20040-020'); + it('looks up a valid Rio de Janeiro postal code (with hyphen input)', async () => { + const address = await client.addresses.lookupByPostalCode('20040-020'); - expect(result).toBeDefined(); - expect(result.postalCode).toBe('20040020'); - expect(result.state).toBe('RJ'); + expect(address).toBeDefined(); + expect(address.postalCode).toBe('20040-020'); + expect(address.state).toBe('RJ'); }); - it('should handle non-existent postal code gracefully', async () => { - // CEP 00000-000 should not exist - await expect( - client.addresses.lookupByPostalCode('00000000') - ).rejects.toThrow(); + it('rejects a non-existent postal code', async () => { + await expect(client.addresses.lookupByPostalCode('00000000')).rejects.toThrow(); }); }); - describe('search', () => { - it('should search addresses by state filter', async () => { - const result = await client.addresses.search({ - filter: "state eq 'SP'", - }); - - expect(result).toBeDefined(); - // Response structure may vary - if (result.addresses) { - expect(Array.isArray(result.addresses)).toBe(true); - } - }); - - it('should search addresses by city filter', async () => { - const result = await client.addresses.search({ - filter: "city.name eq 'São Paulo'", - }); - - expect(result).toBeDefined(); - }); - }); - - describe('lookupByTerm', () => { - it('should lookup addresses by street name', async () => { - const result = await client.addresses.lookupByTerm('Paulista'); - - expect(result).toBeDefined(); - // Response structure may vary based on API - }); - - it('should handle search term with no results', async () => { - // Search for something unlikely to exist - const result = await client.addresses.lookupByTerm('ZZZZNONEXISTENTZZZ123'); - - expect(result).toBeDefined(); - // May return empty results or throw - }); - }); - - describe('API response format', () => { - it('should return address with expected structure', async () => { - const result = await client.addresses.lookupByPostalCode('01310100'); - - // Verify essential fields are present - expect(result).toHaveProperty('postalCode'); - expect(result).toHaveProperty('state'); - expect(result).toHaveProperty('city'); - - // City should have code and name - if (result.city) { - expect(result.city).toHaveProperty('code'); - expect(result.city).toHaveProperty('name'); - } + describe('response shape', () => { + it('returns a single Address with the expected fields (no envelope, no array)', async () => { + const address = await client.addresses.lookupByPostalCode('01310100'); + + expect(address).toHaveProperty('postalCode'); + expect(address).toHaveProperty('state'); + expect(address).toHaveProperty('city'); + expect(address.city).toHaveProperty('code'); + expect(address.city).toHaveProperty('name'); + // Must NOT be the old wrong shapes + expect(address).not.toHaveProperty('address'); + expect(address).not.toHaveProperty('addresses'); }); }); }); /** - * Tests for multi-API key configuration in integration + * Multi-API key configuration against the live API. */ describe.skipIf(!shouldRun)('Multi-API Key Integration', () => { - it('should create client with only dataApiKey', () => { + it('creates a client with only dataApiKey and can access addresses', () => { const client = new NfeClient({ dataApiKey: process.env.NFE_DATA_API_KEY || process.env.NFE_API_KEY, }); - - // Should be able to access addresses expect(() => client.addresses).not.toThrow(); }); - it('should make address API call with separate dataApiKey', async () => { + it('makes an address API call with a separate dataApiKey', async () => { const client = new NfeClient({ dataApiKey: process.env.NFE_DATA_API_KEY || process.env.NFE_API_KEY, }); - - // This should work because we have dataApiKey - const result = await client.addresses.lookupByPostalCode('01310100'); - expect(result).toBeDefined(); + const address = await client.addresses.lookupByPostalCode('01310100'); + expect(address.state).toBe('SP'); }); }); diff --git a/tests/integration/service-invoices.integration.test.ts b/tests/integration/service-invoices.integration.test.ts index 6c93d90..a139fc4 100644 --- a/tests/integration/service-invoices.integration.test.ts +++ b/tests/integration/service-invoices.integration.test.ts @@ -1,12 +1,19 @@ /** * Integration tests for ServiceInvoices resource * Tests complete workflow: create company → issue invoice → poll → cancel + * + * Requires an EMISSION-CAPABLE company (one with a municipal enrollment) via + * NFE_COMPANY_ID. NFS-e emission needs an inscrição municipal; without it the API + * returns 503. When the configured company cannot emit, the suite self-skips (so it + * never fails for a configuration reason). If NFE_COMPANY_ID is unset, a throwaway + * test company is created (and deleted) instead. */ -import { describe, it, expect, beforeAll, afterEach } from 'vitest'; +import { describe, it, expect, beforeAll, afterEach, afterAll } from 'vitest'; import { createIntegrationClient, skipIfNoApiKey, + shouldRunIntegrationTests, TEST_COMPANY_DATA, cleanupTestCompany, logTestInfo, @@ -16,9 +23,36 @@ import { NfeClient } from '../../src/core/client.js'; const hasApiKey = !!process.env.NFE_API_KEY; -describe.skipIf(!hasApiKey)('ServiceInvoices Integration Tests', () => { +// Determine emission capability once, at collection time, so the whole suite self-skips +// when the configured company has no municipal enrollment (the NFS-e prerequisite). +async function companyCanEmit(): Promise { + const companyId = process.env.NFE_COMPANY_ID?.trim(); + if (!companyId) return true; // a throwaway company is created in beforeAll + try { + const probe = createIntegrationClient(); + const res = (await probe.municipalTaxes.list(companyId)) as unknown as { + municipalTaxes?: unknown[]; + }; + return (res.municipalTaxes?.length ?? 0) > 0; + } catch { + return false; + } +} + +const canEmit = shouldRunIntegrationTests() ? await companyCanEmit() : false; + +if (hasApiKey && !canEmit && process.env.NFE_COMPANY_ID) { + console.warn( + `[Integration] Company ${process.env.NFE_COMPANY_ID} has no municipal enrollment; ` + + 'ServiceInvoices emission tests will be skipped. Set NFE_COMPANY_ID to an emission-capable company.' + ); +} + +describe.skipIf(!hasApiKey || !canEmit)('ServiceInvoices Integration Tests', () => { let client: NfeClient; let testCompanyId: string; + // Only true when WE created the company (so cleanup must not delete a real one) + let companyWasCreated = false; const createdInvoiceIds: string[] = []; beforeAll(async () => { @@ -32,18 +66,27 @@ describe.skipIf(!hasApiKey)('ServiceInvoices Integration Tests', () => { environment: INTEGRATION_TEST_CONFIG.environment, }); - // Create test company for all invoice tests - const companyData = { - ...TEST_COMPANY_DATA, - name: `Test Company for Invoices ${Date.now()}`, - }; - const company = await client.companies.create(companyData); - testCompanyId = company.id; - logTestInfo('Created test company', { id: testCompanyId }); - }, { timeout: INTEGRATION_TEST_CONFIG.timeout }); + // Prefer an existing, emission-capable company (municipal enrollment + certificate). + // Fall back to creating a throwaway test company only when none is provided. + const existingCompanyId = process.env.NFE_COMPANY_ID?.trim(); + if (existingCompanyId) { + testCompanyId = existingCompanyId; + logTestInfo('Using existing company from NFE_COMPANY_ID', { id: testCompanyId }); + } else { + const companyData = { + ...TEST_COMPANY_DATA, + name: `Test Company for Invoices ${Date.now()}`, + }; + const company = await client.companies.create(companyData); + testCompanyId = company.id; + companyWasCreated = true; + logTestInfo('Created test company', { id: testCompanyId }); + } + // NOTE: hooks take the timeout as a NUMBER (not an options object). + }, INTEGRATION_TEST_CONFIG.timeout); afterEach(async () => { - // Cleanup invoices (cancel them) + // Cleanup invoices (cancel them) after each test for (const invoiceId of createdInvoiceIds) { try { await client.serviceInvoices.cancel(testCompanyId, invoiceId); @@ -56,26 +99,26 @@ describe.skipIf(!hasApiKey)('ServiceInvoices Integration Tests', () => { createdInvoiceIds.length = 0; }); - afterEach(async () => { - // Cleanup test company after all tests - if (testCompanyId) { + afterAll(async () => { + // Only delete the company if THIS suite created it — never a real one passed via env + if (companyWasCreated && testCompanyId) { await cleanupTestCompany(client, testCompanyId); logTestInfo('Cleaned up test company', { id: testCompanyId }); } - }); + }, INTEGRATION_TEST_CONFIG.timeout); const createTestInvoiceData = () => ({ borrower: { - federalTaxNumber: 12345678901, + federalTaxNumber: 52998224725, // Valid CPF (correct check digits) name: 'Cliente Teste', email: 'cliente@example.com', }, cityServiceCode: '10677', // Código de serviço genérico description: 'Serviço de teste SDK v3', - servicesAmount: 100.00, + servicesAmount: 100.0, }); - it.skipIf(skipIfNoApiKey())('should create a service invoice (sync)', { timeout: INTEGRATION_TEST_CONFIG.timeout }, async () => { + it.skipIf(skipIfNoApiKey())('should create a service invoice (discriminated union)', { timeout: INTEGRATION_TEST_CONFIG.timeout }, async () => { const invoiceData = createTestInvoiceData(); logTestInfo('Creating service invoice', invoiceData); @@ -83,26 +126,19 @@ describe.skipIf(!hasApiKey)('ServiceInvoices Integration Tests', () => { expect(result).toBeDefined(); - // Check if sync (201) or async (202) - if ('id' in result) { - // Synchronous creation (201) - expect(result.id).toBeDefined(); - expect(result.number).toBeDefined(); - createdInvoiceIds.push(result.id); - logTestInfo('Invoice created synchronously', { id: result.id, number: result.number }); + // create() returns a discriminated union keyed on `status` + if (result.status === 'immediate') { + // Synchronous issuance (201) + expect(result.invoice.id).toBeDefined(); + createdInvoiceIds.push(result.invoice.id); + logTestInfo('Invoice created synchronously', { id: result.invoice.id }); } else { - // Asynchronous creation (202) - has flowStatus and location - expect(result.flowStatus).toBeDefined(); - expect(['pending', 'processing']).toContain(result.flowStatus); - - // Extract invoice ID from location if available - if (result.location) { - const match = result.location.match(/serviceinvoices\/([^/]+)/); - if (match) { - createdInvoiceIds.push(match[1]); - } - } - logTestInfo('Invoice created asynchronously', { status: result.flowStatus }); + // Asynchronous issuance (202 + Location) + expect(result.status).toBe('async'); + expect(result.response.location).toBeTruthy(); + expect(result.response.invoiceId).toBeTruthy(); + createdInvoiceIds.push(result.response.invoiceId); + logTestInfo('Invoice created asynchronously', { invoiceId: result.response.invoiceId }); } }); @@ -176,16 +212,16 @@ describe.skipIf(!hasApiKey)('ServiceInvoices Integration Tests', () => { }); createdInvoiceIds.push(created.id); - // List invoices + // List invoices — the API returns a `{ serviceInvoices, page }` envelope logTestInfo('Listing invoices for company', { companyId: testCompanyId }); - const invoices = await client.serviceInvoices.list(testCompanyId); + const result = await client.serviceInvoices.list(testCompanyId); - expect(invoices).toBeDefined(); - expect(Array.isArray(invoices)).toBe(true); - expect(invoices.length).toBeGreaterThan(0); + expect(result).toBeDefined(); + expect(Array.isArray(result.serviceInvoices)).toBe(true); + expect(result.serviceInvoices!.length).toBeGreaterThan(0); // Should include our created invoice - const found = invoices.find(inv => inv.id === created.id); + const found = result.serviceInvoices!.find((inv) => inv.id === created.id); expect(found).toBeDefined(); }); @@ -197,21 +233,38 @@ describe.skipIf(!hasApiKey)('ServiceInvoices Integration Tests', () => { }); createdInvoiceIds.push(created.id); - // Cancel it + // Cancel it — cancellation is asynchronous (202 + Location) logTestInfo('Cancelling invoice', { id: created.id }); - const cancelled = await client.serviceInvoices.cancel(testCompanyId, created.id); + const result = await client.serviceInvoices.cancel(testCompanyId, created.id); - expect(cancelled).toBeDefined(); - expect(cancelled.id).toBe(created.id); - expect(cancelled.status).toBe('cancelled'); + expect(result.status).toBe('async'); + if (result.status === 'async') { + expect(result.response.invoiceId).toBeTruthy(); + expect(result.response.location).toContain('/serviceinvoices/'); + } - // Remove from cleanup since already cancelled + // Cancellation already requested; remove from cleanup to avoid a double cancel const index = createdInvoiceIds.indexOf(created.id); if (index > -1) { createdInvoiceIds.splice(index, 1); } }); + it.skipIf(skipIfNoApiKey())('should cancel and wait until settled', { timeout: 120000 }, async () => { + const created = await client.serviceInvoices.createAndWait(testCompanyId, createTestInvoiceData(), { + timeout: 60000, + }); + + logTestInfo('Cancelling invoice and waiting', { id: created.id }); + const settled = await client.serviceInvoices.cancelAndWait(testCompanyId, created.id, { + timeout: 60000, + }); + + expect(settled).toBeDefined(); + expect(settled.id).toBe(created.id); + expect(settled.flowStatus).toBe('Cancelled'); + }); + it.skipIf(skipIfNoApiKey())('should send invoice email', { timeout: 90000 }, async () => { // Create invoice first const invoiceData = createTestInvoiceData(); @@ -267,8 +320,10 @@ describe.skipIf(!hasApiKey)('ServiceInvoices Integration Tests', () => { expect(Buffer.isBuffer(xmlBuffer)).toBe(true); expect(xmlBuffer.length).toBeGreaterThan(0); - // XML should start with { + it('keeps the permissive index signature (arbitrary reads compile as unknown)', () => { + const c = {} as Company; + expectTypeOf(c.someUndocumentedField).toBeUnknown(); + }); + + it('preserves existing required field types', () => { + const c = {} as Company; + expectTypeOf(c.name).toEqualTypeOf(); + expectTypeOf(c.federalTaxNumber).toEqualTypeOf(); + }); + + it('exposes the strict spec-backed types for opt-in use', () => { + expectTypeOf().not.toBeNever(); + expectTypeOf().not.toBeNever(); + }); +}); diff --git a/tests/unit/client-update-config.test.ts b/tests/unit/client-update-config.test.ts new file mode 100644 index 0000000..1072ffa --- /dev/null +++ b/tests/unit/client-update-config.test.ts @@ -0,0 +1,35 @@ +/** + * Regression test for updateConfig cache invalidation (fix-repo-bugs). + * + * Before the fix, updateConfig() reset only ~10 of the cached resources/clients, + * leaving productInvoices/stateTaxes/taxCalculation/query/lookup resources stale + * (holding an HTTP client built with the old config). This asserts every getter + * rebuilds after updateConfig. + */ + +import { describe, it, expect } from 'vitest'; +import { NfeClient } from '../../src/core/client.js'; + +describe('NfeClient.updateConfig — full cache invalidation', () => { + it('rebuilds previously-missed resources after updateConfig', () => { + const nfe = new NfeClient({ apiKey: 'fiscal-key', dataApiKey: 'data-key' }); + + // Resources that were NOT in the old reset list: + const before = { + productInvoices: nfe.productInvoices, + stateTaxes: nfe.stateTaxes, + taxCalculation: nfe.taxCalculation, + taxCodes: nfe.taxCodes, + // ...and one that always was reset, as a control: + serviceInvoices: nfe.serviceInvoices, + }; + + nfe.updateConfig({ timeout: 45000 }); + + expect(nfe.productInvoices).not.toBe(before.productInvoices); + expect(nfe.stateTaxes).not.toBe(before.stateTaxes); + expect(nfe.taxCalculation).not.toBe(before.taxCalculation); + expect(nfe.taxCodes).not.toBe(before.taxCodes); + expect(nfe.serviceInvoices).not.toBe(before.serviceInvoices); + }); +}); diff --git a/tests/unit/core/resources/service-invoices.test.ts b/tests/unit/core/resources/service-invoices.test.ts index 3afc1e9..9e239e7 100644 --- a/tests/unit/core/resources/service-invoices.test.ts +++ b/tests/unit/core/resources/service-invoices.test.ts @@ -6,7 +6,7 @@ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; import { ServiceInvoicesResource } from '../../../../src/core/resources/service-invoices.js'; import type { HttpClient } from '../../../../src/core/http/client.js'; import type { HttpResponse } from '../../../../src/core/types.js'; -import { NotFoundError, InvoiceProcessingError, TimeoutError } from '../../../../src/core/errors/index.js'; +import { NotFoundError, InvoiceProcessingError } from '../../../../src/core/errors/index.js'; describe('ServiceInvoicesResource', () => { let mockHttp: HttpClient; @@ -256,7 +256,7 @@ describe('ServiceInvoicesResource', () => { const companyId = 'company-123'; const invoiceId = 'invoice-456'; - it('should cancel invoice successfully', async () => { + it('should return an immediate result when the API cancels synchronously (200)', async () => { const mockCancelledInvoice = { id: invoiceId, flowStatus: 'Cancelled', @@ -270,12 +270,32 @@ describe('ServiceInvoicesResource', () => { const result = await resource.cancel(companyId, invoiceId); - expect(result).toEqual(mockCancelledInvoice); + expect(result.status).toBe('immediate'); + if (result.status === 'immediate') { + expect(result.invoice).toEqual(mockCancelledInvoice); + } expect(mockHttp.delete).toHaveBeenCalledWith( `/companies/${companyId}/serviceinvoices/${invoiceId}` ); }); + it('should return an async result with invoiceId from Location (202)', async () => { + vi.mocked(mockHttp.delete).mockResolvedValue({ + data: { code: 202, status: 'pending', location: `/v1/companies/${companyId}/serviceinvoices/${invoiceId}` }, + status: 202, + headers: { location: `/v1/companies/${companyId}/serviceinvoices/${invoiceId}` }, + } as HttpResponse); + + const result = await resource.cancel(companyId, invoiceId); + + expect(result.status).toBe('async'); + if (result.status === 'async') { + expect(result.response.code).toBe(202); + expect(result.response.invoiceId).toBe(invoiceId); + expect(result.response.location).toContain('/serviceinvoices/'); + } + }); + it('should throw NotFoundError when trying to cancel non-existent invoice', async () => { vi.mocked(mockHttp.delete).mockRejectedValue(new NotFoundError('Invoice not found')); @@ -283,6 +303,49 @@ describe('ServiceInvoicesResource', () => { }); }); + describe('cancelAndWait()', () => { + const companyId = 'company-123'; + const invoiceId = 'invoice-456'; + const fastPoll = { initialDelay: 0, maxDelay: 0, timeout: 2000 }; + const loc = `/v1/companies/${companyId}/serviceinvoices/${invoiceId}`; + + it('polls until the async cancellation settles as Cancelled', async () => { + vi.mocked(mockHttp.delete).mockResolvedValue({ + data: { code: 202, status: 'pending', location: loc }, status: 202, headers: { location: loc }, + } as HttpResponse); + vi.mocked(mockHttp.get).mockResolvedValue({ + data: { id: invoiceId, flowStatus: 'Cancelled' }, status: 200, headers: {}, + } as HttpResponse); + + const invoice = await resource.cancelAndWait(companyId, invoiceId, fastPoll); + expect(invoice.id).toBe(invoiceId); + expect(invoice.flowStatus).toBe('Cancelled'); + }); + + it('returns immediately when cancellation is synchronous (200 body)', async () => { + vi.mocked(mockHttp.delete).mockResolvedValue({ + data: { id: invoiceId, flowStatus: 'Cancelled' }, status: 200, headers: {}, + } as HttpResponse); + + const invoice = await resource.cancelAndWait(companyId, invoiceId, fastPoll); + expect(invoice.flowStatus).toBe('Cancelled'); + expect(mockHttp.get).not.toHaveBeenCalled(); + }); + + it('throws InvoiceProcessingError when cancellation fails (CancelFailed)', async () => { + vi.mocked(mockHttp.delete).mockResolvedValue({ + data: { code: 202, status: 'pending', location: loc }, status: 202, headers: { location: loc }, + } as HttpResponse); + vi.mocked(mockHttp.get).mockResolvedValue({ + data: { id: invoiceId, flowStatus: 'CancelFailed', flowMessage: 'x' }, status: 200, headers: {}, + } as HttpResponse); + + await expect( + resource.cancelAndWait(companyId, invoiceId, fastPoll) + ).rejects.toBeInstanceOf(InvoiceProcessingError); + }); + }); + describe('sendEmail()', () => { const companyId = 'company-123'; const invoiceId = 'invoice-456'; diff --git a/tests/unit/generation.test.ts b/tests/unit/generation.test.ts index 2fb33c7..76b74be 100644 --- a/tests/unit/generation.test.ts +++ b/tests/unit/generation.test.ts @@ -56,9 +56,9 @@ describe('OpenAPI Type Generation', () => { const indexPath = join(GENERATED_DIR, 'index.ts'); const content = readFileSync(indexPath, 'utf8'); - // Basic syntax checks + // Basic syntax checks (the index re-exports namespaces: `export * as X`) expect(content).not.toContain('undefined'); - expect(content).toMatch(/export (type|interface|const)/); + expect(content).toMatch(/export (type|interface|const|\*)/); // Should not have syntax errors (checked by typecheck in CI) // Here we just verify it's not empty and has exports @@ -66,33 +66,29 @@ describe('OpenAPI Type Generation', () => { }); }); - describe('Generated Type Exports', () => { - it('should export ServiceInvoice type', () => { - const indexPath = join(GENERATED_DIR, 'index.ts'); - const content = readFileSync(indexPath, 'utf8'); + describe('Public Domain Type Exports', () => { + // Public domain types live in src/core/types.ts (the barrel re-exports from + // there). The generated index intentionally no longer defines placeholder + // `{ [key: string]: unknown }` interfaces (sync-openapi-specs-from-docs, 0B). + const TYPES_PATH = 'src/core/types.ts'; - // Check that type is exported in source code + it('should export ServiceInvoice type', () => { + const content = readFileSync(TYPES_PATH, 'utf8'); expect(content).toMatch(/export.*ServiceInvoice/); }); it('should export Company type', () => { - const indexPath = join(GENERATED_DIR, 'index.ts'); - const content = readFileSync(indexPath, 'utf8'); - + const content = readFileSync(TYPES_PATH, 'utf8'); expect(content).toMatch(/export.*Company/); }); it('should export LegalPerson type', () => { - const indexPath = join(GENERATED_DIR, 'index.ts'); - const content = readFileSync(indexPath, 'utf8'); - + const content = readFileSync(TYPES_PATH, 'utf8'); expect(content).toMatch(/export.*LegalPerson/); }); it('should export NaturalPerson type', () => { - const indexPath = join(GENERATED_DIR, 'index.ts'); - const content = readFileSync(indexPath, 'utf8'); - + const content = readFileSync(TYPES_PATH, 'utf8'); expect(content).toMatch(/export.*NaturalPerson/); }); @@ -199,20 +195,26 @@ describe('OpenAPI Type Generation', () => { expect(existsSync(scriptPath)).toBe(true); }); - it('npm run generate should work', () => { + it('npm run generate should work and apply the known-skipped allowlist', () => { // This test actually runs generation (slow test) // Skip in watch mode to avoid regeneration loops if (process.env.VITEST_WATCH === 'true') { return; } + let output = ''; expect(() => { - execSync('npm run generate', { + output = execSync('npm run generate', { encoding: 'utf8', stdio: 'pipe', timeout: 30000, // 30 second timeout }); }).not.toThrow(); + + // Allowlist semantics: the 5 legacy Swagger 2.0 specs are reported as + // known-skipped (not errors), and the count is reconciled. + expect(output).toContain('Known-skipped (hand-typed)'); + expect(output).toMatch(/Generated \d+ of \d+ spec file\(s\) \(\d+ known-skipped\)/); }, 35000); // 35 second test timeout it('npm run validate:spec should work', () => { diff --git a/tests/unit/http-client.test.ts b/tests/unit/http-client.test.ts index a199978..29a685a 100644 --- a/tests/unit/http-client.test.ts +++ b/tests/unit/http-client.test.ts @@ -180,6 +180,47 @@ describe('HttpClient', () => { }); }); + describe('PATCH Requests', () => { + it('should make successful PATCH request', async () => { + const patchData = { foo: 'bar' }; + const responseBody = { id: '123', ...patchData }; + + fetchMock.mockResolvedValue({ + ok: true, + status: 200, + headers: createMockHeaders([['content-type', 'application/json']]), + json: async () => responseBody, + }); + + const response = await httpClient.patch('/companies/123/municipaltaxes/1/updateprefecture', patchData); + + expect(response.data).toEqual(responseBody); + expect(fetchMock.mock.calls[0][1].method).toBe('PATCH'); + }); + + it('inherits retry behavior (503 then success), proving verb parity', async () => { + fetchMock + .mockResolvedValueOnce({ + ok: false, + status: 503, + statusText: 'Service Unavailable', + headers: createMockHeaders([['content-type', 'application/json']]), + json: async () => ({ error: 'Temporarily unavailable' }), + }) + .mockResolvedValueOnce({ + ok: true, + status: 200, + headers: createMockHeaders([['content-type', 'application/json']]), + json: async () => ({ ok: true }), + }); + + const response = await httpClient.patch<{ ok: boolean }>('/test', {}); + + expect(response.data).toEqual({ ok: true }); + expect(fetchMock).toHaveBeenCalledTimes(2); + }); + }); + describe('DELETE Requests', () => { it('should make successful DELETE request', async () => { fetchMock.mockResolvedValue({ diff --git a/tests/unit/resources/addresses.test.ts b/tests/unit/resources/addresses.test.ts index ea3c538..b38d0cc 100644 --- a/tests/unit/resources/addresses.test.ts +++ b/tests/unit/resources/addresses.test.ts @@ -1,12 +1,15 @@ /** * Unit tests for AddressesResource - * Tests address lookup, search, and validation + * + * The live address.api.nfe.io/v2 API supports postal-code lookup only and returns the + * single address wrapped in an `{ address }` envelope. The resource unwraps it and + * returns a plain `Address`. */ import { describe, it, expect, beforeEach, vi } from 'vitest'; import { AddressesResource } from '../../../src/core/resources/addresses.js'; import { HttpClient } from '../../../src/core/http/client.js'; -import type { HttpResponse, Address, AddressSearchOptions } from '../../../src/core/types.js'; +import type { HttpResponse, Address, AddressLookupResponse } from '../../../src/core/types.js'; import { ValidationError } from '../../../src/core/errors/index.js'; describe('AddressesResource', () => { @@ -29,226 +32,80 @@ describe('AddressesResource', () => { }); describe('lookupByPostalCode', () => { + // Shape mirrors the real API payload (single address, alpha-3 country, formatted CEP) const mockAddress: Address = { - postalCode: '01310100', - street: 'Avenida Paulista', + postalCode: '01310-100', + streetSuffix: 'Avenida', + street: 'Paulista', + number: 'de 612 a 1510 - lado par', district: 'Bela Vista', + additionalInformation: '', city: { code: '3550308', name: 'São Paulo', }, state: 'SP', - country: 'BR', + country: 'BRA', }; - it('should lookup address by postal code (without hyphen)', async () => { - const mockResponse: HttpResponse
= { - data: mockAddress, - status: 200, - headers: {}, - }; - mockHttpClient.get.mockResolvedValue(mockResponse); + const mockEnvelope = (address: Address): HttpResponse => ({ + data: { address }, + status: 200, + headers: {}, + }); + + it('unwraps the { address } envelope and returns the single Address', async () => { + mockHttpClient.get.mockResolvedValue(mockEnvelope(mockAddress)); const result = await resource.lookupByPostalCode('01310100'); expect(result).toEqual(mockAddress); + // Direct field access works (no `.addresses[0]`) + expect(result.street).toBe('Paulista'); + expect(result.city.name).toBe('São Paulo'); expect(mockHttpClient.get).toHaveBeenCalledWith('/addresses/01310100'); }); - it('should lookup address by postal code (with hyphen)', async () => { - const mockResponse: HttpResponse
= { - data: mockAddress, - status: 200, - headers: {}, - }; - mockHttpClient.get.mockResolvedValue(mockResponse); + it('normalizes a hyphenated CEP to the 8-digit path', async () => { + mockHttpClient.get.mockResolvedValue(mockEnvelope(mockAddress)); const result = await resource.lookupByPostalCode('01310-100'); expect(result).toEqual(mockAddress); - // CEP should be normalized to remove hyphen expect(mockHttpClient.get).toHaveBeenCalledWith('/addresses/01310100'); }); - it('should throw ValidationError for postal code with less than 8 digits', async () => { + it('trims surrounding whitespace before normalizing', async () => { + mockHttpClient.get.mockResolvedValue(mockEnvelope(mockAddress)); + + const result = await resource.lookupByPostalCode(' 01310-100 '); + + expect(result).toEqual(mockAddress); + expect(mockHttpClient.get).toHaveBeenCalledWith('/addresses/01310100'); + }); + + it('throws ValidationError for a CEP with fewer than 8 digits', async () => { await expect(resource.lookupByPostalCode('1234567')).rejects.toThrow(ValidationError); await expect(resource.lookupByPostalCode('1234567')).rejects.toThrow(/Invalid postal code/); }); - it('should throw ValidationError for postal code with more than 8 digits', async () => { + it('throws ValidationError for a CEP with more than 8 digits', async () => { await expect(resource.lookupByPostalCode('123456789')).rejects.toThrow(ValidationError); - await expect(resource.lookupByPostalCode('123456789')).rejects.toThrow(/Invalid postal code/); }); - it('should throw ValidationError for postal code with non-numeric characters', async () => { + it('throws ValidationError for non-numeric characters', async () => { await expect(resource.lookupByPostalCode('1234567a')).rejects.toThrow(ValidationError); await expect(resource.lookupByPostalCode('abcd-efg')).rejects.toThrow(ValidationError); }); - it('should throw ValidationError for empty postal code', async () => { + it('throws ValidationError for empty / whitespace-only input', async () => { await expect(resource.lookupByPostalCode('')).rejects.toThrow(ValidationError); - }); - - it('should throw ValidationError for whitespace-only postal code', async () => { await expect(resource.lookupByPostalCode(' ')).rejects.toThrow(ValidationError); }); - it('should handle postal code with leading/trailing whitespace', async () => { - const mockResponse: HttpResponse
= { - data: mockAddress, - status: 200, - headers: {}, - }; - mockHttpClient.get.mockResolvedValue(mockResponse); - - const result = await resource.lookupByPostalCode(' 01310-100 '); - - expect(result).toEqual(mockAddress); - // Implementation trims whitespace and removes hyphen - expect(mockHttpClient.get).toHaveBeenCalledWith('/addresses/01310100'); - }); - - it('should handle API error responses', async () => { + it('propagates API errors (e.g. 404 NotFoundError)', async () => { mockHttpClient.get.mockRejectedValue(new Error('Not Found')); - await expect(resource.lookupByPostalCode('00000000')).rejects.toThrow('Not Found'); }); }); - - describe('search', () => { - const mockAddresses: Address[] = [ - { - postalCode: '01310100', - street: 'Avenida Paulista', - district: 'Bela Vista', - city: { code: '3550308', name: 'São Paulo' }, - state: 'SP', - country: 'BR', - }, - { - postalCode: '01310200', - street: 'Avenida Paulista', - district: 'Bela Vista', - city: { code: '3550308', name: 'São Paulo' }, - state: 'SP', - country: 'BR', - }, - ]; - - it('should search addresses with filter options', async () => { - const mockResponse: HttpResponse<{ addresses: Address[] }> = { - data: { addresses: mockAddresses }, - status: 200, - headers: {}, - }; - mockHttpClient.get.mockResolvedValue(mockResponse); - - const options: AddressSearchOptions = { - filter: "city.name eq 'São Paulo'", - }; - - const result = await resource.search(options); - - expect(result.addresses).toEqual(mockAddresses); - // Implementation passes params object directly to http.get - expect(mockHttpClient.get).toHaveBeenCalledWith('/addresses', { - $filter: "city.name eq 'São Paulo'", - }); - }); - - it('should search addresses without filter (list all)', async () => { - const mockResponse: HttpResponse<{ addresses: Address[] }> = { - data: { addresses: mockAddresses }, - status: 200, - headers: {}, - }; - mockHttpClient.get.mockResolvedValue(mockResponse); - - const result = await resource.search({}); - - expect(result.addresses).toEqual(mockAddresses); - // When no filter is provided, an empty object is passed - expect(mockHttpClient.get).toHaveBeenCalledWith('/addresses', {}); - }); - - it('should handle empty results', async () => { - const mockResponse: HttpResponse<{ addresses: Address[] }> = { - data: { addresses: [] }, - status: 200, - headers: {}, - }; - mockHttpClient.get.mockResolvedValue(mockResponse); - - const result = await resource.search({ filter: "state eq 'XX'" }); - - expect(result.addresses).toEqual([]); - }); - }); - - describe('lookupByTerm', () => { - const mockAddresses: Address[] = [ - { - postalCode: '01310100', - street: 'Avenida Paulista', - district: 'Bela Vista', - city: { code: '3550308', name: 'São Paulo' }, - state: 'SP', - country: 'BR', - }, - ]; - - it('should lookup addresses by term', async () => { - const mockResponse: HttpResponse<{ addresses: Address[] }> = { - data: { addresses: mockAddresses }, - status: 200, - headers: {}, - }; - mockHttpClient.get.mockResolvedValue(mockResponse); - - const result = await resource.lookupByTerm('Avenida Paulista'); - - expect(result.addresses).toEqual(mockAddresses); - // Implementation uses URL path with encoded term - expect(mockHttpClient.get).toHaveBeenCalledWith('/addresses/Avenida%20Paulista'); - }); - - it('should throw ValidationError for empty term', async () => { - await expect(resource.lookupByTerm('')).rejects.toThrow(ValidationError); - await expect(resource.lookupByTerm('')).rejects.toThrow(/Search term is required/); - }); - - it('should throw ValidationError for whitespace-only term', async () => { - await expect(resource.lookupByTerm(' ')).rejects.toThrow(ValidationError); - }); - - it('should handle term with leading/trailing whitespace', async () => { - const mockResponse: HttpResponse<{ addresses: Address[] }> = { - data: { addresses: mockAddresses }, - status: 200, - headers: {}, - }; - mockHttpClient.get.mockResolvedValue(mockResponse); - - const result = await resource.lookupByTerm(' Paulista '); - - expect(result.addresses).toEqual(mockAddresses); - // Term should be trimmed before encoding - expect(mockHttpClient.get).toHaveBeenCalledWith('/addresses/Paulista'); - }); - - it('should handle special characters in term', async () => { - const mockResponse: HttpResponse<{ addresses: Address[] }> = { - data: { addresses: [] }, - status: 200, - headers: {}, - }; - mockHttpClient.get.mockResolvedValue(mockResponse); - - // Special characters should be URL encoded - await resource.lookupByTerm("D'Água"); - - // encodeURIComponent encodes single quote as %27 but leaves it unchanged in most implementations - expect(mockHttpClient.get).toHaveBeenCalledWith("/addresses/D'%C3%81gua"); - }); - }); }); diff --git a/tests/unit/resources/certificates.test.ts b/tests/unit/resources/certificates.test.ts new file mode 100644 index 0000000..efa830b --- /dev/null +++ b/tests/unit/resources/certificates.test.ts @@ -0,0 +1,58 @@ +/** + * Unit tests for CertificatesResource (digital certificate management). + */ + +import { describe, it, expect, beforeEach, vi } from 'vitest'; +import { CertificatesResource } from '../../../src/core/resources/certificates.js'; +import { HttpClient } from '../../../src/core/http/client.js'; +import { ValidationError } from '../../../src/core/errors/index.js'; + +describe('CertificatesResource', () => { + let resource: CertificatesResource; + let http: { + get: ReturnType; + post: ReturnType; + put: ReturnType; + delete: ReturnType; + }; + + beforeEach(() => { + http = { get: vi.fn(), post: vi.fn(), put: vi.fn(), delete: vi.fn() }; + resource = new CertificatesResource(http as unknown as HttpClient); + }); + + const companyId = 'company-123'; + const thumb = 'AB12CD34'; + const v2 = `/v2/companies/${companyId}/certificates`; + const v1 = `/v1/companies/${companyId}/certificate`; + + it('list GETs the v2 plural collection', async () => { + http.get.mockResolvedValue({ status: 200, headers: {}, data: {} }); + await resource.list(companyId); + expect(http.get).toHaveBeenCalledWith(v2); + }); + + it('get/delete by thumbprint hit the v2 path', async () => { + http.get.mockResolvedValue({ status: 200, headers: {}, data: {} }); + http.delete.mockResolvedValue({ status: 204, headers: {}, data: undefined }); + await resource.getByThumbprint(companyId, thumb); + await resource.deleteByThumbprint(companyId, thumb); + expect(http.get).toHaveBeenCalledWith(`${v2}/${thumb}`); + expect(http.delete).toHaveBeenCalledWith(`${v2}/${thumb}`); + }); + + it('v1 variants hit the v1 path', async () => { + http.get.mockResolvedValue({ status: 200, headers: {}, data: {} }); + http.delete.mockResolvedValue({ status: 204, headers: {}, data: undefined }); + await resource.getByThumbprintV1(companyId, thumb); + await resource.deleteByThumbprintV1(companyId, thumb); + expect(http.get).toHaveBeenCalledWith(`${v1}/${thumb}`); + expect(http.delete).toHaveBeenCalledWith(`${v1}/${thumb}`); + }); + + it('validates inputs', async () => { + await expect(resource.list('')).rejects.toBeInstanceOf(ValidationError); + await expect(resource.getByThumbprint(companyId, '')).rejects.toBeInstanceOf(ValidationError); + await expect(resource.deleteByThumbprint('', thumb)).rejects.toBeInstanceOf(ValidationError); + }); +}); diff --git a/tests/unit/resources/companies-certificates.test.ts b/tests/unit/resources/companies-certificates.test.ts index 1618a95..eb76208 100644 --- a/tests/unit/resources/companies-certificates.test.ts +++ b/tests/unit/resources/companies-certificates.test.ts @@ -29,7 +29,8 @@ describe('CompaniesResource - Certificate Management', () => { const result = await companies.validateCertificate(validBuffer, 'password'); expect(result.valid).toBe(true); - expect(result.metadata).toBeDefined(); + // Pre-flight does not fabricate metadata (verified server-side on upload). + expect(result.metadata).toBeUndefined(); }); it('should reject invalid certificate format', async () => { diff --git a/tests/unit/resources/consumer-invoices.test.ts b/tests/unit/resources/consumer-invoices.test.ts new file mode 100644 index 0000000..84f2d5b --- /dev/null +++ b/tests/unit/resources/consumer-invoices.test.ts @@ -0,0 +1,120 @@ +/** + * Unit tests for ConsumerInvoicesResource (NFC-e issuance). + */ + +import { describe, it, expect, beforeEach, vi } from 'vitest'; +import { ConsumerInvoicesResource } from '../../../src/core/resources/consumer-invoices.js'; +import { HttpClient } from '../../../src/core/http/client.js'; +import type { + ConsumerInvoiceData, + ConsumerInvoiceDisablementData, +} from '../../../src/core/types.js'; +import { ValidationError } from '../../../src/core/errors/index.js'; + +describe('ConsumerInvoicesResource', () => { + let resource: ConsumerInvoicesResource; + let http: { + get: ReturnType; + post: ReturnType; + put: ReturnType; + delete: ReturnType; + }; + + beforeEach(() => { + http = { get: vi.fn(), post: vi.fn(), put: vi.fn(), delete: vi.fn() }; + resource = new ConsumerInvoicesResource(http as unknown as HttpClient); + }); + + const companyId = 'company-123'; + const invoiceId = 'nfce-1'; + const base = `/v2/companies/${companyId}/consumerinvoices`; + + it('create POSTs to consumerinvoices and is webhook-driven (returns data, no poll)', async () => { + const enqueued = { id: invoiceId, flowStatus: 'Processing' }; + http.post.mockResolvedValue({ status: 202, headers: {}, data: enqueued }); + + const result = await resource.create(companyId, { items: [] } as unknown as ConsumerInvoiceData); + + expect(http.post).toHaveBeenCalledWith(base, { items: [] }); + expect(result).toEqual(enqueued); + expect(http.get).not.toHaveBeenCalled(); + }); + + it('list / retrieve / cancel hit the right paths', async () => { + http.get.mockResolvedValue({ status: 200, headers: {}, data: {} }); + http.delete.mockResolvedValue({ status: 200, headers: {}, data: { id: invoiceId } }); + + await resource.list(companyId, { environment: 'Test' }); + await resource.retrieve(companyId, invoiceId); + await resource.cancel(companyId, invoiceId); + + expect(http.get).toHaveBeenCalledWith(base, { environment: 'Test' }); + expect(http.get).toHaveBeenCalledWith(`${base}/${invoiceId}`, undefined); + expect(http.delete).toHaveBeenCalledWith(`${base}/${invoiceId}`); + }); + + it('list requires environment', async () => { + await expect( + resource.list(companyId, {} as unknown as { environment: 'Test' }) + ).rejects.toBeInstanceOf(ValidationError); + }); + + it('items / events use sub-resource paths', async () => { + http.get.mockResolvedValue({ status: 200, headers: {}, data: {} }); + await resource.getItems(companyId, invoiceId); + await resource.getEvents(companyId, invoiceId); + expect(http.get).toHaveBeenCalledWith(`${base}/${invoiceId}/items`, undefined); + expect(http.get).toHaveBeenCalledWith(`${base}/${invoiceId}/events`, undefined); + }); + + it('downloads send the right Accept and path (pdf/xml/rejection)', async () => { + http.get.mockResolvedValue({ status: 200, headers: {}, data: Buffer.from('x') }); + + await resource.downloadPdf(companyId, invoiceId); + await resource.downloadXml(companyId, invoiceId); + await resource.downloadRejectionXml(companyId, invoiceId); + + expect(http.get).toHaveBeenCalledWith(`${base}/${invoiceId}/pdf`, undefined, { Accept: 'application/pdf' }); + expect(http.get).toHaveBeenCalledWith(`${base}/${invoiceId}/xml`, undefined, { Accept: 'application/xml' }); + expect(http.get).toHaveBeenCalledWith(`${base}/${invoiceId}/xml/rejection`, undefined, { Accept: 'application/xml' }); + }); + + it('forwards environment on reads/downloads when provided', async () => { + http.get.mockResolvedValue({ status: 200, headers: {}, data: {} }); + + await resource.retrieve(companyId, invoiceId, 'Test'); + await resource.getItems(companyId, invoiceId, 'Test'); + await resource.getEvents(companyId, invoiceId, 'Test'); + await resource.downloadPdf(companyId, invoiceId, 'Test'); + + expect(http.get).toHaveBeenCalledWith(`${base}/${invoiceId}`, { environment: 'Test' }); + expect(http.get).toHaveBeenCalledWith(`${base}/${invoiceId}/items`, { environment: 'Test' }); + expect(http.get).toHaveBeenCalledWith(`${base}/${invoiceId}/events`, { environment: 'Test' }); + expect(http.get).toHaveBeenCalledWith(`${base}/${invoiceId}/pdf`, { environment: 'Test' }, { Accept: 'application/pdf' }); + }); + + it('list forwards optional filters (startingAfter/endingBefore/limit/q)', async () => { + http.get.mockResolvedValue({ status: 200, headers: {}, data: {} }); + + await resource.list(companyId, { + environment: 'Test', startingAfter: 'a', endingBefore: 'b', limit: 5, q: 'buyer.name:X', + }); + + expect(http.get).toHaveBeenCalledWith(base, { + environment: 'Test', startingAfter: 'a', endingBefore: 'b', limit: 5, q: 'buyer.name:X', + }); + }); + + it('disable POSTs to /disablement', async () => { + http.post.mockResolvedValue({ status: 200, headers: {}, data: {} }); + const data = { serie: 1, numberStart: 1, numberEnd: 10 } as unknown as ConsumerInvoiceDisablementData; + await resource.disable(companyId, data); + expect(http.post).toHaveBeenCalledWith(`${base}/disablement`, data); + }); + + it('validates inputs', async () => { + await expect(resource.create('', {} as ConsumerInvoiceData)).rejects.toBeInstanceOf(ValidationError); + await expect(resource.retrieve(companyId, '')).rejects.toBeInstanceOf(ValidationError); + await expect(resource.downloadPdf(companyId, '')).rejects.toBeInstanceOf(ValidationError); + }); +}); diff --git a/tests/unit/resources/municipal-taxes.test.ts b/tests/unit/resources/municipal-taxes.test.ts new file mode 100644 index 0000000..e6eef43 --- /dev/null +++ b/tests/unit/resources/municipal-taxes.test.ts @@ -0,0 +1,76 @@ +/** + * Unit tests for MunicipalTaxesResource (Inscrições Municipais). + */ + +import { describe, it, expect, beforeEach, vi } from 'vitest'; +import { MunicipalTaxesResource } from '../../../src/core/resources/municipal-taxes.js'; +import { HttpClient } from '../../../src/core/http/client.js'; +import type { CreateMunicipalTaxData, UpdateMunicipalTaxData } from '../../../src/core/types.js'; +import { ValidationError } from '../../../src/core/errors/index.js'; + +describe('MunicipalTaxesResource', () => { + let resource: MunicipalTaxesResource; + let http: { + get: ReturnType; + post: ReturnType; + put: ReturnType; + delete: ReturnType; + patch: ReturnType; + }; + + beforeEach(() => { + http = { get: vi.fn(), post: vi.fn(), put: vi.fn(), delete: vi.fn(), patch: vi.fn() }; + resource = new MunicipalTaxesResource(http as unknown as HttpClient); + }); + + const companyId = 'company-123'; + const id = 'mt-1'; + const base = `/v2/companies/${companyId}/municipaltaxes`; + + it('list GETs the municipaltaxes base path', async () => { + http.get.mockResolvedValue({ data: { municipalTaxes: [] }, status: 200, headers: {} }); + await resource.list(companyId); + expect(http.get).toHaveBeenCalledWith(base); + }); + + it('create POSTs wrapped as { municipalTax }', async () => { + http.post.mockResolvedValue({ data: { id }, status: 201, headers: {} }); + const data = { taxNumber: '123' } as unknown as CreateMunicipalTaxData; + await resource.create(companyId, data); + expect(http.post).toHaveBeenCalledWith(base, { municipalTax: data }); + }); + + it('retrieve/update/delete hit /{id}', async () => { + http.get.mockResolvedValue({ data: { id }, status: 200, headers: {} }); + http.put.mockResolvedValue({ data: { id }, status: 200, headers: {} }); + http.delete.mockResolvedValue({ data: undefined, status: 204, headers: {} }); + const upd = { issRate: 5 } as unknown as UpdateMunicipalTaxData; + + await resource.retrieve(companyId, id); + await resource.update(companyId, id, upd); + await resource.delete(companyId, id); + + expect(http.get).toHaveBeenCalledWith(`${base}/${id}`); + expect(http.put).toHaveBeenCalledWith(`${base}/${id}`, { municipalTax: upd }); + expect(http.delete).toHaveBeenCalledWith(`${base}/${id}`); + }); + + it('updatePrefecture uses PATCH on .../updateprefecture', async () => { + http.patch.mockResolvedValue({ data: { id }, status: 200, headers: {} }); + const upd = { loginPassword: 'x' } as unknown as UpdateMunicipalTaxData; + await resource.updatePrefecture(companyId, id, upd); + expect(http.patch).toHaveBeenCalledWith(`${base}/${id}/updateprefecture`, { municipalTax: upd }); + }); + + it('getSeries GETs .../series/{serie}', async () => { + http.get.mockResolvedValue({ data: {}, status: 200, headers: {} }); + await resource.getSeries(companyId, id, '1'); + expect(http.get).toHaveBeenCalledWith(`${base}/${id}/series/1`); + }); + + it('validates inputs', async () => { + await expect(resource.list('')).rejects.toBeInstanceOf(ValidationError); + await expect(resource.retrieve(companyId, '')).rejects.toBeInstanceOf(ValidationError); + await expect(resource.getSeries(companyId, id, '')).rejects.toBeInstanceOf(ValidationError); + }); +}); diff --git a/tests/unit/resources/p5-p7-additions.test.ts b/tests/unit/resources/p5-p7-additions.test.ts new file mode 100644 index 0000000..3e77dc1 --- /dev/null +++ b/tests/unit/resources/p5-p7-additions.test.ts @@ -0,0 +1,101 @@ +/** + * Unit tests for P5/P7 additions: companies.exists (HEAD), serviceInvoices + * retrieveByExternalId, stateTaxes switchAuthorizer, and NotificationsResource. + */ + +import { describe, it, expect, beforeEach, vi } from 'vitest'; +import { CompaniesResource } from '../../../src/core/resources/companies.js'; +import { ServiceInvoicesResource } from '../../../src/core/resources/service-invoices.js'; +import { StateTaxesResource } from '../../../src/core/resources/state-taxes.js'; +import { NotificationsResource } from '../../../src/core/resources/notifications.js'; +import { HttpClient } from '../../../src/core/http/client.js'; +import { NotFoundError, ValidationError } from '../../../src/core/errors/index.js'; + +function mockHttp() { + return { get: vi.fn(), post: vi.fn(), put: vi.fn(), delete: vi.fn(), patch: vi.fn(), head: vi.fn() }; +} + +const companyId = 'company-123'; + +describe('companies.exists (HEAD /v2/companies/{id})', () => { + it('returns true when HEAD succeeds (uses the v2 client)', async () => { + const main = mockHttp(); + const v2 = mockHttp(); + v2.head.mockResolvedValue({ status: 200, headers: {}, data: undefined }); + const companies = new CompaniesResource(main as unknown as HttpClient, v2 as unknown as HttpClient); + + expect(await companies.exists(companyId)).toBe(true); + expect(v2.head).toHaveBeenCalledWith(`/v2/companies/${companyId}`); + }); + + it('returns false on 404 (NotFoundError) without throwing', async () => { + const main = mockHttp(); + const v2 = mockHttp(); + v2.head.mockRejectedValue(new NotFoundError('not found')); + const companies = new CompaniesResource(main as unknown as HttpClient, v2 as unknown as HttpClient); + + expect(await companies.exists(companyId)).toBe(false); + }); + + it('validates companyId', async () => { + const companies = new CompaniesResource(mockHttp() as unknown as HttpClient); + await expect(companies.exists('')).rejects.toBeInstanceOf(ValidationError); + }); +}); + +describe('serviceInvoices.retrieveByExternalId', () => { + it('GETs the external/{id} path', async () => { + const http = mockHttp(); + http.get.mockResolvedValue({ status: 200, headers: {}, data: { id: 'inv-1' } }); + const res = new ServiceInvoicesResource(http as unknown as HttpClient); + + const inv = await res.retrieveByExternalId(companyId, 'ext-9'); + expect(http.get).toHaveBeenCalledWith(`/companies/${companyId}/serviceinvoices/external/ext-9`); + expect(inv.id).toBe('inv-1'); + }); +}); + +describe('stateTaxes.switchAuthorizer', () => { + it('POSTs to .../switch-authorizer', async () => { + const http = mockHttp(); + http.post.mockResolvedValue({ status: 200, headers: {}, data: { id: 'st-1' } }); + const res = new StateTaxesResource(http as unknown as HttpClient); + + await res.switchAuthorizer(companyId, 'st-1', { authorizer: 'SVRS' }); + expect(http.post).toHaveBeenCalledWith( + `/v2/companies/${companyId}/statetaxes/st-1/switch-authorizer`, + { authorizer: 'SVRS' } + ); + }); +}); + +describe('NotificationsResource', () => { + let http: ReturnType; + let res: NotificationsResource; + beforeEach(() => { + http = mockHttp(); + res = new NotificationsResource(http as unknown as HttpClient); + }); + const base = `/companies/${companyId}/notifications`; + + it('list/retrieve/delete/email hit the right paths', async () => { + http.get.mockResolvedValue({ status: 200, headers: {}, data: {} }); + http.delete.mockResolvedValue({ status: 204, headers: {}, data: undefined }); + http.post.mockResolvedValue({ status: 200, headers: {}, data: {} }); + + await res.list(companyId); + await res.retrieve(companyId, 'n1'); + await res.delete(companyId, 'n1'); + await res.sendEmail(companyId, { to: 'x@y.z' }); + + expect(http.get).toHaveBeenCalledWith(base); + expect(http.get).toHaveBeenCalledWith(`${base}/n1`); + expect(http.delete).toHaveBeenCalledWith(`${base}/n1`); + expect(http.post).toHaveBeenCalledWith(`${base}/email`, { to: 'x@y.z' }); + }); + + it('validates inputs', async () => { + await expect(res.retrieve(companyId, '')).rejects.toBeInstanceOf(ValidationError); + await expect(res.list('')).rejects.toBeInstanceOf(ValidationError); + }); +}); diff --git a/tests/unit/resources/product-invoices-rtc.test.ts b/tests/unit/resources/product-invoices-rtc.test.ts new file mode 100644 index 0000000..6fc4bf8 --- /dev/null +++ b/tests/unit/resources/product-invoices-rtc.test.ts @@ -0,0 +1,69 @@ +/** + * Unit tests for ProductInvoicesRtcResource (NF-e/NFC-e, Reforma Tributária). + */ + +import { describe, it, expect, beforeEach, vi } from 'vitest'; +import { ProductInvoicesRtcResource } from '../../../src/core/resources/product-invoices-rtc.js'; +import { HttpClient } from '../../../src/core/http/client.js'; +import type { ProductInvoiceRtcRequest } from '../../../src/core/types.js'; +import { ValidationError } from '../../../src/core/errors/index.js'; + +describe('ProductInvoicesRtcResource', () => { + let resource: ProductInvoicesRtcResource; + let http: { + get: ReturnType; + post: ReturnType; + put: ReturnType; + delete: ReturnType; + }; + + beforeEach(() => { + http = { get: vi.fn(), post: vi.fn(), put: vi.fn(), delete: vi.fn() }; + resource = new ProductInvoicesRtcResource(http as unknown as HttpClient); + }); + + const companyId = 'company-123'; + + function minimalRtcPayload(): ProductInvoiceRtcRequest { + return { + operationType: 'Outgoing', + destination: 'InternalOperation', + buyer: { name: 'NF-E HOMOLOGACAO', federalTaxNumber: 11223344000155 }, + items: [ + { + description: 'PRODUTO RTC', + ncm: '85171300', + cfop: 5102, + quantity: 1, + unitAmount: 150.0, + tax: { IBSCBS: { situationCode: '000', classCode: '000001' } }, + }, + ], + } as unknown as ProductInvoiceRtcRequest; + } + + describe('create()', () => { + it('posts the RTC payload to the v2 productinvoices endpoint (webhook-driven, returns data)', async () => { + const enqueued = { id: 'prod-1', flowStatus: 'Processing' }; + http.post.mockResolvedValue({ status: 202, headers: {}, data: enqueued }); + + const result = await resource.create(companyId, minimalRtcPayload()); + + expect(http.post).toHaveBeenCalledWith( + `/v2/companies/${companyId}/productinvoices`, + expect.objectContaining({ + items: expect.arrayContaining([ + expect.objectContaining({ tax: expect.objectContaining({ IBSCBS: expect.any(Object) }) }), + ]), + }) + ); + // Webhook-driven: returns the enqueued resource (no polling) + expect(result).toEqual(enqueued); + expect(http.get).not.toHaveBeenCalled(); + }); + + it('throws ValidationError on empty companyId', async () => { + await expect(resource.create('', minimalRtcPayload())).rejects.toBeInstanceOf(ValidationError); + }); + }); +}); diff --git a/tests/unit/resources/service-invoices-rtc.test.ts b/tests/unit/resources/service-invoices-rtc.test.ts new file mode 100644 index 0000000..c605a3c --- /dev/null +++ b/tests/unit/resources/service-invoices-rtc.test.ts @@ -0,0 +1,164 @@ +/** + * Unit tests for ServiceInvoicesRtcResource (NFS-e, Reforma Tributária). + */ + +import { describe, it, expect, beforeEach, vi } from 'vitest'; +import { ServiceInvoicesRtcResource } from '../../../src/core/resources/service-invoices-rtc.js'; +import { HttpClient } from '../../../src/core/http/client.js'; +import type { NFSeRtcRequest } from '../../../src/core/types.js'; +import { ValidationError, InvoiceProcessingError, NotFoundError } from '../../../src/core/errors/index.js'; + +describe('ServiceInvoicesRtcResource', () => { + let resource: ServiceInvoicesRtcResource; + let http: { + get: ReturnType; + post: ReturnType; + put: ReturnType; + delete: ReturnType; + }; + + beforeEach(() => { + http = { get: vi.fn(), post: vi.fn(), put: vi.fn(), delete: vi.fn() }; + resource = new ServiceInvoicesRtcResource(http as unknown as HttpClient); + }); + + const companyId = 'company-123'; + + function minimalRtcPayload(): NFSeRtcRequest { + return { + borrower: { name: 'CONSUMIDOR MINIMO LTDA', federalTaxNumber: 191 }, + cityServiceCode: '4444', + federalServiceCode: '01.01', + description: 'Serviço de consultoria (RTC)', + servicesAmount: 1000.0, + nbsCode: '101010100', + ibsCbs: { operationIndicator: '1005011', classCode: '000001' }, + } as unknown as NFSeRtcRequest; + } + + describe('create()', () => { + it('posts the RTC payload to the serviceinvoices endpoint WITHOUT a /v1 prefix', async () => { + http.post.mockResolvedValue({ status: 201, headers: {}, data: { id: 'inv-1' } }); + + await resource.create(companyId, minimalRtcPayload()); + + expect(http.post).toHaveBeenCalledWith( + `/companies/${companyId}/serviceinvoices`, + expect.objectContaining({ ibsCbs: expect.any(Object) }) + ); + }); + + it('returns an async union with invoiceId on 202 + Location', async () => { + http.post.mockResolvedValue({ + status: 202, + headers: { location: `/v1/companies/${companyId}/serviceinvoices/inv-async` }, + data: {}, + }); + + const result = await resource.create(companyId, minimalRtcPayload()); + + expect(result.status).toBe('async'); + if (result.status === 'async') { + expect(result.response.invoiceId).toBe('inv-async'); + expect(result.response.code).toBe(202); + } + }); + + it('returns immediate union on 201', async () => { + http.post.mockResolvedValue({ status: 201, headers: {}, data: { id: 'inv-1' } }); + + const result = await resource.create(companyId, minimalRtcPayload()); + + expect(result.status).toBe('immediate'); + if (result.status === 'immediate') { + expect(result.invoice.id).toBe('inv-1'); + } + }); + + it('throws ValidationError on empty companyId', async () => { + await expect(resource.create('', minimalRtcPayload())).rejects.toBeInstanceOf(ValidationError); + }); + }); + + describe('downloadCancellationXml()', () => { + it('GETs the cancellation-xml path (no /v1 prefix) and returns the buffer', async () => { + const xml = Buffer.from(''); + http.get.mockResolvedValue({ status: 200, headers: {}, data: xml }); + + const result = await resource.downloadCancellationXml(companyId, 'inv-1'); + + expect(http.get).toHaveBeenCalledWith( + `/companies/${companyId}/serviceinvoices/inv-1/cancellation-xml`, + undefined, + { Accept: 'application/xml' } + ); + expect(result).toBe(xml); + }); + + it('throws ValidationError on empty invoiceId', async () => { + await expect(resource.downloadCancellationXml(companyId, '')).rejects.toBeInstanceOf( + ValidationError + ); + }); + }); + + describe('retrieve()', () => { + it('GETs the invoice (no /v1 prefix) and returns it', async () => { + http.get.mockResolvedValue({ status: 200, headers: {}, data: { id: 'inv-1', flowStatus: 'Issued' } }); + + const inv = await resource.retrieve(companyId, 'inv-1'); + + expect(http.get).toHaveBeenCalledWith(`/companies/${companyId}/serviceinvoices/inv-1`); + expect(inv.id).toBe('inv-1'); + }); + + it('throws NotFoundError when the API returns no body', async () => { + http.get.mockResolvedValue({ status: 200, headers: {}, data: undefined }); + await expect(resource.retrieve(companyId, 'ghost')).rejects.toBeInstanceOf(NotFoundError); + }); + }); + + describe('createAndWait()', () => { + const fastPoll = { initialDelay: 0, maxDelay: 0, timeout: 2000 }; + + it('returns the invoice directly on immediate (201) creation', async () => { + http.post.mockResolvedValue({ status: 201, headers: {}, data: { id: 'inv-1', flowStatus: 'Issued' } }); + + const inv = await resource.createAndWait(companyId, minimalRtcPayload(), fastPoll); + + expect(inv.id).toBe('inv-1'); + expect(http.get).not.toHaveBeenCalled(); // no polling needed + }); + + it('polls until terminal on async (202 + Location) creation', async () => { + http.post.mockResolvedValue({ + status: 202, + headers: { location: `/v1/companies/${companyId}/serviceinvoices/inv-async` }, + data: {}, + }); + http.get.mockResolvedValue({ status: 200, headers: {}, data: { id: 'inv-async', flowStatus: 'Issued' } }); + + const onPoll = vi.fn(); + const inv = await resource.createAndWait(companyId, minimalRtcPayload(), { ...fastPoll, onPoll }); + + expect(inv.id).toBe('inv-async'); + expect(http.get).toHaveBeenCalledWith(`/companies/${companyId}/serviceinvoices/inv-async`); + expect(onPoll).toHaveBeenCalled(); + }); + + it('throws InvoiceProcessingError when the invoice settles as IssueFailed', async () => { + http.post.mockResolvedValue({ + status: 202, + headers: { location: `/v1/companies/${companyId}/serviceinvoices/inv-fail` }, + data: {}, + }); + http.get.mockResolvedValue({ + status: 200, headers: {}, data: { id: 'inv-fail', flowStatus: 'IssueFailed', flowMessage: 'rejected' }, + }); + + await expect( + resource.createAndWait(companyId, minimalRtcPayload(), fastPoll) + ).rejects.toBeInstanceOf(InvoiceProcessingError); + }); + }); +}); diff --git a/tests/unit/service-invoices.test.ts b/tests/unit/service-invoices.test.ts index 441db30..2cbda7d 100644 --- a/tests/unit/service-invoices.test.ts +++ b/tests/unit/service-invoices.test.ts @@ -157,7 +157,10 @@ describe('ServiceInvoicesResource', () => { expect(mockHttpClient.delete).toHaveBeenCalledWith( `/companies/${TEST_COMPANY_ID}/serviceinvoices/${TEST_INVOICE_ID}` ); - expect(result.flowStatus).toBe('Cancelled'); + expect(result.status).toBe('immediate'); + if (result.status === 'immediate') { + expect(result.invoice.flowStatus).toBe('Cancelled'); + } }); }); diff --git a/tests/unit/utils/certificate-validator.test.ts b/tests/unit/utils/certificate-validator.test.ts index 977e720..626887a 100644 --- a/tests/unit/utils/certificate-validator.test.ts +++ b/tests/unit/utils/certificate-validator.test.ts @@ -29,15 +29,15 @@ describe('CertificateValidator', () => { expect(result.error).toContain('Invalid certificate format'); }); - it('should accept valid PKCS#12 signature', async () => { + it('should accept valid PKCS#12 signature (format pre-flight only)', async () => { // Create buffer with PKCS#12 signature (0x3082) const validBuffer = Buffer.from([0x30, 0x82, 0x01, 0x00]); const result = await CertificateValidator.validate(validBuffer, 'password'); expect(result.valid).toBe(true); - expect(result.metadata).toBeDefined(); - expect(result.metadata?.subject).toBeDefined(); - expect(result.metadata?.issuer).toBeDefined(); + // Honest pre-flight: it must NOT fabricate subject/issuer/validity it never parsed. + // Subject/issuer/validity/password are verified server-side on upload. + expect(result.metadata).toBeUndefined(); }); }); diff --git a/tests/unit/webhooks.test.ts b/tests/unit/webhooks.test.ts index 867e3b9..1a67c45 100644 --- a/tests/unit/webhooks.test.ts +++ b/tests/unit/webhooks.test.ts @@ -166,6 +166,65 @@ describe('WebhooksResource', () => { }); }); + describe('account-scoped operations (host-root /v2/webhooks)', () => { + it('listAccountWebhooks GETs /webhooks and unwraps the {webHooks} envelope', async () => { + vi.mocked(mockHttpClient.get).mockResolvedValue({ + data: { webHooks: [{ id: 'w1' }, { id: 'w2' }] }, status: 200, headers: {}, + } as HttpResponse<{ webHooks: Webhook[] }>); + + const result = await webhooks.listAccountWebhooks(); + expect(mockHttpClient.get).toHaveBeenCalledWith('/webhooks'); + expect(result.data).toHaveLength(2); + expect(result.data[0]?.id).toBe('w1'); + }); + + it('createAccountWebhook POSTs /webhooks', async () => { + vi.mocked(mockHttpClient.post).mockResolvedValue({ + data: { id: 'w1' }, status: 201, headers: {}, + } as HttpResponse); + + await webhooks.createAccountWebhook({ url: 'https://x.test/hook' }); + expect(mockHttpClient.post).toHaveBeenCalledWith('/webhooks', { url: 'https://x.test/hook' }); + }); + + it('retrieve/update/delete by id hit /webhooks/{id}', async () => { + vi.mocked(mockHttpClient.get).mockResolvedValue({ data: { id: 'w1' }, status: 200, headers: {} } as HttpResponse); + vi.mocked(mockHttpClient.put).mockResolvedValue({ data: { id: 'w1' }, status: 200, headers: {} } as HttpResponse); + vi.mocked(mockHttpClient.delete).mockResolvedValue({ data: undefined, status: 204, headers: {} } as HttpResponse); + + await webhooks.retrieveAccountWebhook('w1'); + await webhooks.updateAccountWebhook('w1', { active: false }); + await webhooks.deleteAccountWebhook('w1'); + await webhooks.pingAccountWebhook('w1'); + + expect(mockHttpClient.get).toHaveBeenCalledWith('/webhooks/w1'); + expect(mockHttpClient.put).toHaveBeenCalledWith('/webhooks/w1', { active: false }); + expect(mockHttpClient.delete).toHaveBeenCalledWith('/webhooks/w1'); + expect(mockHttpClient.put).toHaveBeenCalledWith('/webhooks/w1/pings', {}); + }); + + it('deleteAllAccountWebhooks is a distinct method hitting DELETE /webhooks (no id)', async () => { + vi.mocked(mockHttpClient.delete).mockResolvedValue({ data: undefined, status: 204, headers: {} } as HttpResponse); + + await webhooks.deleteAllAccountWebhooks(); + expect(mockHttpClient.delete).toHaveBeenCalledWith('/webhooks'); + // It is NOT the same as the single-delete method + expect(webhooks.deleteAllAccountWebhooks).not.toBe(webhooks.deleteAccountWebhook); + }); + + it('fetchEventTypes GETs /webhooks/eventTypes and extracts ids from the {eventTypes} envelope', async () => { + vi.mocked(mockHttpClient.get).mockResolvedValue({ + data: { eventTypes: [{ id: 'invoice.issued' }, { id: 'invoice.cancelled' }, { id: 'some.new.event' }] }, + status: 200, headers: {}, + } as HttpResponse<{ eventTypes: Array<{ id: string }> }>); + + const types = await webhooks.fetchEventTypes(); + expect(mockHttpClient.get).toHaveBeenCalledWith('/webhooks/eventTypes'); + expect(types).toContain('some.new.event'); + expect(types).toHaveLength(3); + }); + }); + describe('Error Handling', () => { it('should propagate HTTP client errors', async () => { const error = new Error('Network error');