You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Functional testing against the real Vendus API (test mode, plus one real
FR+NC cycle) surfaced several more wire-shape bugs the mocks hid, and the SDK
now matches the verified behaviour.
Fatura-Recibo (FR):
- create_invoice_receipt now requires `payments` — an FR records payment on
issue and Vendus rejects it otherwise ("o pagamento deve ser realizado no ato").
- New `Payment` input model and `list_payment_methods()` (+ `PaymentMethod`),
since payment-method ids are account-specific (GET /v1.0/payments).
Credit note (NC):
- create_credit_note(reference_document_id, reason) redesigned: it GETs the
original and credits its full set of lines, referencing each via
`reference_document` (number + row) + the original line id. The previous
top-level `reference_document_id` was rejected by the API (P001); the method
had never worked. No more inline items/client/register_id.
Cancel:
- cancel() fetches the document and refuses FT/FR/NC — fiscal documents cannot
be cancelled ("Não é permitido cancelar este tipo de documentos"); the SDK
raises ValidationError pointing to create_credit_note.
Robustness:
- _parse_document tolerates unknown type codes (the live account returned `RG`,
absent from documents/types) via a `DocumentType.UNKNOWN` sentinel; the raw
code is preserved in raw_response.
Docs/examples updated across EN + PT (FR payments, NC signature, "FT cannot be
cancelled"), README validation matrix refreshed, CLAUDE.md R13 reworked and R16
added with the live-verified wire facts. 79 unit tests + 4 live integration
tests; ruff/mypy/pytest/mkdocs --strict all green.
BREAKING CHANGE: create_invoice_receipt requires payments; create_credit_note
drops register_id/items/client; cancel refuses FT/FR/NC.
Copy file name to clipboardExpand all lines: CLAUDE.md
+17-8Lines changed: 17 additions & 8 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -294,16 +294,14 @@ client.documents.create_quote(...) # OR
294
294
295
295
Reason: each type has different mandatory/forbidden fields. A credit note **requires**`reference_document_id`; an invoice doesn't. Type-checking and autocomplete are better with dedicated methods.
296
296
297
-
### R13: Credit Notes Always Reference an Original Document
297
+
### R13: Credit Notes Credit a Real Original Document
298
298
299
-
Validated locally before any API call:
299
+
`create_credit_note(reference_document_id, reason)` credits the **full** original. Vendus requires each credit line to reference an existing line of the original (`reference_document` = document number + 1-based row) and to carry that line's id, so the SDK **GETs the original** (FT/FR) and replicates its lines. Consequences:
The original document must be a previously-issued invoice (FT) or invoice-receipt (FR). Vendus validates the rest server-side.
301
+
- The original must be **retrievable** → a real document, not a test-mode one (test docs aren't addressable).
302
+
- No inline `items`/`client` — they come from the original.
303
+
- Partial credits are not supported in v0.1.
304
+
- There is **no** top-level `reference_document_id` on the wire — Vendus rejects it (`P001`); the link lives per item.
307
305
308
306
### R14: Inline Client Only (v0.1.0)
309
307
@@ -318,6 +316,17 @@ If the app needs to track Vendus client IDs locally, that is its responsibility.
318
316
319
317
In Portuguese invoicing, "consumidor final" (final consumer) is represented by omitting the client entirely, NOT by passing `fiscal_id=999999990`. R7 enforces this at validation time.
320
318
319
+
### R16: Live-verified Wire Facts (do not regress)
320
+
321
+
These were confirmed against the **real** Vendus API; changing them silently re-breaks the SDK. Each was a bug the respx mocks hid until live-validation caught it:
322
+
323
+
-**Line items** send `tax_id` (a `TaxCategory` code — `NOR`/`INT`/`RED`/`ISE`/`OUT`), not `tax_rate`; `discount_percentage`, not `discount`; `id` for a product line, not `product_id`. Wrong names → `P001`.
324
+
-**FR (Fatura-Recibo) requires `payments`** — `[{"id": <method_id>, "amount": <gross>}]`. `method_id` is **account-specific**; list it via `list_payment_methods()` (`GET /v1.0/payments`). Missing → "o pagamento deve ser realizado no ato".
325
+
-**Cancel** = `PATCH /documents/{id}` with `{"status":"A"}` only (no reason field). **FT/FR/NC cannot be cancelled** ("Não é permitido cancelar este tipo de documentos") — the SDK fetches the type and refuses, pointing to a credit note.
326
+
-**Test-mode documents** live in a separate space: not retrievable/cancellable via `/documents/{id}` (404 "não existe").
327
+
-**Unknown type codes** (e.g. `RG`, seen live, absent from documents/types) must not crash parsing → `DocumentType.UNKNOWN`, raw code kept in `raw_response`.
328
+
-`tax_authority_id` is **empty in the POST response even for real fiscal documents** (ATCUD/hash mark a doc fiscal), so it is not a reliable test-vs-real discriminator at create time — the series prefix is (`FT T01P…` test vs `FT 01P…` real).
329
+
321
330
---
322
331
323
332
## How to Add a New Document Type (Future Versions)
The wire format of every operation is asserted by unit tests (respx mocks). Live validation runs against the real Vendus API in **test mode** (`mode=tests`) — non-fiscal documents that are never reported to the AT:
166
+
The wire format of every operation is asserted by unit tests (respx mocks), and validated against the real Vendus API — in **test mode** (`mode=tests`, non-fiscal) where possible, and once in real mode for the operations that test mode can't reach:
|`cancel`| ✅ | ⚠️ FT/FR/NC can't be cancelled — the SDK refuses them (reverse with a credit note) |
182
175
183
-
> Test-mode documents ("Modo de Formação") are non-fiscal and never reported to the AT. Vendus stores them in a separate space, so they can't be retrieved or cancelled via `/documents/{id}`. Live `get`/`list` are validated read-only against real documents; `cancel` is not live-validated because voiding a real fiscal document is destructive.
176
+
> Test-mode documents ("Modo de Formação") are non-fiscal and never reported to the AT, but Vendus stores them in a separate space — they can't be retrieved or cancelled via `/documents/{id}`. So credit notes (which must read the original) are validated in real mode, and `cancel` is not live-validated (FT/FR/NC are not cancellable; other types would require voiding a real document).
Copy file name to clipboardExpand all lines: docs/documents/credit-note.md
+17-31Lines changed: 17 additions & 31 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -2,12 +2,11 @@
2
2
3
3
## What it is
4
4
5
-
A Credit Note (NC) cancels or partially credits a previously issued document (FT or FS). It is the legal mechanism for returns, retroactive discounts, or correcting wrong invoices.
5
+
A Credit Note (NC) credits a previously issued invoice (FT or FR). It is the legal mechanism for returns, refunds, and corrections — and the **only** way to reverse a fiscal invoice, which cannot be cancelled.
6
6
7
7
-**Always references** an original document (`reference_document_id`)
8
8
-**Reason is mandatory** (`reason`) — required by AT
9
-
- Client should match the original document's client
10
-
- Can be partial (refunding only some items)
9
+
- Credits the **full** original document: the SDK fetches the original and replicates its lines, so the client and amounts come from it (partial credits are not supported in v0.1)
1.**Full vs partial NC:**if refunding the whole amount, replicate all items. For partial refunds, include only the items/quantities to credit.
87
-
2.**NC is not cancellation:**an NC **credits**the value but keeps the original document. To fully void, use `client.documents.cancel(id)` instead.
88
-
3.**Consistent client:**if the original was to Final Consumer, the NC should also omit `client`.
72
+
1.**Full credit only (v0.1):**the SDK credits every line of the original. Partial credits (some lines or quantities) are a future addition.
73
+
2.**NC is how you reverse an invoice:**fiscal invoices (FT/FR) **cannot be cancelled**— `cancel()` rejects them. Issue an NC to credit the original instead.
74
+
3.**Real documents only:** the original must be retrievable, so credit notes work on **real** documents, not test-mode ones (which are not addressable via `/documents/{id}`).
Copy file name to clipboardExpand all lines: docs/documents/credit-note.pt.md
+20-34Lines changed: 20 additions & 34 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -2,12 +2,11 @@
2
2
3
3
## O que é
4
4
5
-
A Nota de Crédito (NC) anula ou credita parcialmente um documento já emitido (FT ou FS). É o mecanismo legal para devoluções, descontos retroativos, ou correções de faturas erradas.
5
+
Uma Nota de Crédito (NC) credita uma fatura emitida anteriormente (FT ou FR). É o mecanismo legal para devoluções, reembolsos e correções — e a **única** forma de reverter uma fatura fiscal, que não pode ser cancelada.
6
6
7
7
-**Referencia sempre** um documento original (`reference_document_id`)
8
8
-**Motivo obrigatório** (`reason`) — exigido pela AT
9
-
- O cliente deve coincidir com o do documento original
10
-
- Pode ser parcial (devolução de só alguns itens)
9
+
- Credita o documento original **por inteiro**: o SDK vai buscar o original e replica as suas linhas, por isso o cliente e os valores vêm dele (créditos parciais não são suportados no v0.1)
11
10
12
11
## Fluxo
13
12
@@ -18,11 +17,11 @@ sequenceDiagram
18
17
participant API as Vendus API
19
18
participant AT
20
19
21
-
Note over App: Originalmente: cliente comprou 10 horas
1.**NC total vs parcial:**se devolves o total, replica todos os itens. Se devolves uma parte, inclui só os itens/quantidades a creditar.
87
-
2.**Não é cancelamento:**uma NC **credita** o valor mas mantém o documento original. Para cancelar completamente, usa `client.documents.cancel(id)` em alternativa.
88
-
3.**Cliente coerente:**se o original foi a Consumidor Final, a NC também deve omitir `client`.
72
+
1.**Só crédito total (v0.1):**o SDK credita todas as linhas do original. Créditos parciais (algumas linhas/quantidades) ficam para o futuro.
73
+
2.**A NC é como se reverte uma fatura:**faturas fiscais (FT/FR) **não podem ser canceladas** — o `cancel()` rejeita-as. Emite uma NC para creditar o original.
74
+
3.**Apenas documentos reais:** o original tem de ser consultável, por isso as notas de crédito funcionam sobre documentos **reais**, não sobre os de modo teste (que não são endereçáveis via `/documents/{id}`).
0 commit comments