Skip to content

Invoice authoring API

The bidirectional CIUS-RO invoice models: flat document models with computed totals, the translated EN 16931 + CIUS-RO rule set, and the flat ⇄ UBL mapping. See the authoring guide for usage.

anafpy.efactura.authoring.models

Bidirectional flat models for CIUS-RO electronic invoices.

One semantic model — EN 16931's — covers both UBL document types: :class:InvoiceDocument authors and views an Invoice or a CreditNote (:attr:~InvoiceDocument.kind picks the render target). The same models are produced by :func:~.read.read_invoice and consumed by :func:~.build.build_invoice / :func:~.build.render_invoice, mirroring the e-Transport flat models.

Validation is two-tier:

  • Construction enforces what a single model can know unconditionally: field formats and lengths (the BR-RO-L* limits), closed code lists (BR-CL-*), decimal budgets (BR-DEC-*), and local consistency (a period needs a start or an end, a VAT category pins its rate shape, ...). Data violating these is rejected by ANAF with certainty, so failing fast is data hygiene.
  • :func:~.rules.validate runs the cross-aggregate EN 16931 / CIUS-RO rule set (totals arithmetic, VAT breakdown consistency, regime-dependent identifier requirements) and returns a findings report instead of raising.

Totals and the VAT breakdown are computed by default from the lines, allowances and charges (correct by construction); explicit values may be supplied — e.g. when reproducing an upstream document — and are then checked against the computed ones by :func:~.rules.validate.

Contact

Bases: BaseModel

A contact point (BG-6 seller / BG-9 buyer).

PostalAddress

Bases: BaseModel

A postal address (BG-5/8/12/15).

CIUS-RO requires the street and city everywhere an address appears (BR-RO-080/090/140/150/180/200); the county (CountrySubentity) is required for Romanian addresses and must be an ISO 3166-2:RO code (BR-RO-110), with Bucharest city names drawn from the SECTOR list (BR-RO-100) — "sector 3" is normalised to "SECTOR3".

ElectronicAddress

Bases: BaseModel

An electronic delivery address (BT-34/49) with its mandatory scheme (BR-62/63), e.g. ElectronicAddress(address="0000000000000", scheme="0088").

PartyIdentifier

Bases: BaseModel

A party identifier (BT-29/46/60); the scheme, when given, is an ISO 6523 ICD code (BR-CL-10).

Party

Bases: BaseModel

A buyer (BG-7); also the base of :class:Seller (BG-4).

name is the legal name (BT-27/44) and the address is mandatory (BR-08/10). The VAT identifier must carry its ISO 3166 country prefix (BR-CO-09; Greece may use EL).

Seller

Bases: Party

The seller (BG-4): a :class:Party plus the seller-only terms.

Payee

Bases: BaseModel

The payee (BG-10), when different from the seller (BR-17).

TaxRepresentative

Bases: BaseModel

The seller's tax representative (BG-11); the VAT identifier (BT-63) and postal address (BG-12) are mandatory (BR-56/19).

Period

Bases: BaseModel

An invoicing (BG-14) or line (BG-26) period: at least one bound (BR-CO-19/20), end not before start (BR-29/30).

DeliveryInformation

Bases: BaseModel

Delivery details (BG-13): recipient (BT-70), location (BT-71), actual delivery date (BT-72) and address (BG-15).

CreditTransfer

Bases: BaseModel

A credit-transfer account (BG-17); the account identifier is mandatory (BR-50).

PaymentCard

Bases: BaseModel

Payment card information (BG-18). Card-security standards allow at most the first 6 and last 4 digits of the account number (BR-51, a warning).

DirectDebit

Bases: BaseModel

Direct-debit terms (BG-19).

PaymentInstructions

Bases: BaseModel

Payment instructions (BG-16); the means code (BT-81) is mandatory (BR-49) and drawn from UNTDID 4461 — e.g. "30" credit transfer, "58" SEPA credit transfer, "42" bank account payment.

DocumentAllowance

Bases: _DocumentAllowanceChargeBase

A document-level allowance (BG-20).

DocumentCharge

Bases: _DocumentAllowanceChargeBase

A document-level charge (BG-21).

LineAllowance

Bases: _AllowanceChargeBase

An invoice-line allowance (BG-27).

LineCharge

Bases: _AllowanceChargeBase

An invoice-line charge (BG-28).

ItemAttribute

Bases: BaseModel

An item attribute (BG-32): name and value are both required (BR-54).

ItemClassification

Bases: BaseModel

An item classification (BT-158); the scheme is mandatory (BR-65).

InvoiceLine

Bases: BaseModel

One invoice / credit-note line (BG-25) with its item and price flattened in.

net_amount (BT-131) is computed from quantity x price and the line allowances/charges when omitted; supply it explicitly to reproduce an upstream document's own rounding.

effective_net_amount property

effective_net_amount: Decimal

BT-131: the explicit net amount, else quantity x unit price (per base quantity) minus line allowances plus line charges, rounded to 2.

VatBreakdownEntry

Bases: BaseModel

One VAT breakdown group (BG-23), keyed by category + rate.

Amounts left None are computed from the lines and document-level allowances/charges of the same category and rate. Exemption reasons cannot be computed: categories E/AE/K/G/O require one here (BR-E/AE/IC/G/O-10), which is the main reason to pass explicit entries when authoring an exempt invoice.

Totals

Bases: BaseModel

The document totals (BG-22). Every member is optional: whatever is left None is computed (see :meth:InvoiceDocument.effective_totals); whatever is supplied is preserved and checked by :func:~.rules.validate.

PrecedingInvoice

Bases: BaseModel

A preceding invoice reference (BG-3); the number is mandatory (BR-55). CIUS-RO requires at least one on credit notes referencing the corrected invoice in practice, and caps them at 500 (BR-RO-A500).

SupportingDocument

Bases: BaseModel

An additional supporting document (BG-24); the reference is mandatory (BR-52). Embedded content requires its MIME code and filename.

Note

Bases: BaseModel

An invoice note (BG-1): free text (BT-22) with an optional UNCL 4451 subject qualifier (BT-21); on the wire the two ride one cbc:Note as #CODE#text.

InvoiceDocument

Bases: BaseModel

A CIUS-RO electronic invoice or credit note, flat and bidirectional.

Author one with the business fields and let the totals and VAT breakdown be computed; or obtain one from wire XML via :func:~.read.read_invoice / :func:~.read.parse_invoice, with every wire amount preserved. Render with :func:~.build.render_invoice; check the full EN 16931 + CIUS-RO rule set with :func:~.rules.validate.

compute_vat_breakdown

compute_vat_breakdown() -> list[VatBreakdownEntry]

The VAT breakdown (BG-23) computed from lines, allowances and charges: one entry per (category, rate), tax = taxable x rate rounded to 2 (BR-CO-17), zero for the exempt-style categories. Exemption reasons are carried over from matching explicit :attr:vat_breakdown entries.

effective_vat_breakdown

effective_vat_breakdown() -> list[VatBreakdownEntry]

The breakdown to render: the computed one when no explicit entries were given; otherwise the explicit entries with any None amounts filled from the matching computed group.

compute_totals

compute_totals() -> Totals

The document totals (BG-22) computed per BR-CO-10..16.

prepaid and rounding are never computed (they are facts, not sums) and are read from the explicit :attr:totals; vat_total_tax_currency (BT-111) needs an exchange rate this library does not know, so it also stays explicit-only.

effective_totals

effective_totals() -> Totals

The totals to render: each member's explicit value when supplied, the computed value otherwise.

round2

round2(value: Decimal) -> Decimal

EN 16931 Annex-B rounding: two decimals, half away from zero.

anafpy.efactura.authoring.codes

Hand-curated codes for the CIUS-RO invoice models.

The big closed lists (currencies, countries, units, ...) are generated into :mod:._codelists; this module carries the small, semantically-named subsets the CIUS-RO Schematron restricts beyond the base UNTDID/ISO lists, plus the Romanian sub-national code lists that exist only in the RO rules.

DocumentKind

Bases: StrEnum

Which UBL document a :class:~.models.InvoiceDocument renders to.

InvoiceTypeCode

Bases: StrEnum

BT-3 document type codes CIUS-RO admits (BR-RO-020, UNTDID 1001 subset).

CREDIT_NOTE (381) is the only code valid on a UBL CreditNote; the other four are valid on a UBL Invoice.

VatCategory

Bases: StrEnum

BT-95/102/118/151 VAT category codes (UNCL 5305, EN16931 subset).

SPLIT_PAYMENT (B) is on the list but only valid on domestic Italian invoices (BR-B-01) — a CIUS-RO filing using it is rejected.

VatPointDateCode

Bases: StrEnum

BT-8 VAT point date codes (UNTDID 2005 restricted by BR-RO-040).

anafpy.efactura.authoring.rules

The cross-aggregate EN 16931 / CIUS-RO rule set over :class:InvoiceDocument.

:func:validate hand-translates the Schematron rules that construction-time shape checks cannot express — totals arithmetic (BR-CO-10..17), VAT breakdown consistency (BR-S/Z/E/AE/K/G/O/L/M-01..09), regime-dependent identifier requirements, and the RO-specific document rules — and returns a :class:ValidationReport of findings instead of raising. Rule ids and severities match the official CIUS-RO 1.0.9 Schematron so findings line up with what ANAF's validare endpoint reports; numeric comparisons mirror the Schematron's own tolerances (the per-rate sums and the tax formula accept a strict ±1 slack, the BR-CO totals are exact).

ANAF's validator remains the authority: a clean local report is a strong signal, never a guarantee.

Severity

Bases: StrEnum

Finding severity, matching the Schematron flag.

Finding

Bases: BaseModel

One rule violation.

ValidationReport

Bases: BaseModel

The outcome of :func:validate: all findings, worst first.

ok property

ok: bool

True when no fatal finding was raised (warnings may remain).

InvoiceValidationError

InvoiceValidationError(report: ValidationReport)

Bases: AnafError

Raised by :func:~.build.render_invoice when the document fails :func:validate and validation was not explicitly skipped.

validate

validate(document: InvoiceDocument) -> ValidationReport

Run the translated EN 16931 + CIUS-RO rule set and report findings.

Covers the cross-aggregate rules construction cannot enforce: totals arithmetic, VAT breakdown presence/consistency per category, VAT-regime identifier requirements, payment-instruction coherence. Field formats, lengths, code lists and decimal budgets were already enforced when the models were constructed.

Returns:

Name Type Description
A ValidationReport

class:ValidationReport; report.ok is True when no fatal

ValidationReport

finding remains. ANAF's server-side validare stays authoritative.

anafpy.efactura.authoring.build

Compose a flat :class:~.models.InvoiceDocument into UBL wire XML.

:func:build_invoice produces the generated UBL model (Invoice or CreditNote per the document's :attr:~.models.InvoiceDocument.kind); :func:render_invoice serializes it to upload-ready bytes, running :func:~.rules.validate first unless explicitly skipped.

Syntax-mapping notes (the EN 16931 UBL binding's quirks, mirrored by :mod:.read):

  • BT-21/22 ride one cbc:Note as #SUBJECTCODE#text.
  • BT-8 (VAT point date code) rides cac:InvoicePeriod/cbc:DescriptionCode.
  • BT-32 (seller tax registration) is a second cac:PartyTaxScheme whose TaxScheme/ID is not VAT (rendered as FC).
  • BT-90 (bank-assigned creditor id) is a cac:PartyIdentification with schemeID="SEPA" on the payee (or the seller when there is no payee).
  • On a credit note, BT-9 (due date) rides cac:PaymentMeans and BT-11 (project reference) an AdditionalDocumentReference with type code 50.
  • UBL requires cac:CardAccount/cbc:NetworkID, which EN 16931 does not model; it is rendered as ZZZ and dropped on read.

render_invoice

render_invoice(document: InvoiceDocument, *, skip_validation: bool = False) -> bytes

Compose and serialize a document to upload-ready UTF-8 XML bytes.

Runs :func:~.rules.validate first and raises :class:~.rules.InvoiceValidationError on fatal findings; pass skip_validation=True to render regardless (e.g. to reproduce a faulty upstream document byte-for-byte for ANAF's own validator to judge).

build_invoice

build_invoice(document: InvoiceDocument) -> Invoice | CreditNote

Compose the flat document into its generated UBL model.

Pure translation — no validation, no defaulting beyond what the flat model already computed (line ids, totals, VAT breakdown).

anafpy.efactura.authoring.read

Read UBL wire XML back into the flat :class:~.models.InvoiceDocument.

The inverse of :mod:.build, sharing its syntax-mapping conventions (#CODE# note prefixes, the FC tax-registration scheme, SEPA-schemed creditor ids, type-130/50 document references). Every wire amount — line nets, totals, the VAT breakdown — is read into the explicit fields, so a round-trip preserves the source document's own arithmetic instead of recomputing it; run :func:~.rules.validate on the result to judge that arithmetic.

The reader is strict by design: a document that violates the construction-time CIUS-RO shape checks (a seller without a city, an unknown currency, ...) raises a pydantic ValidationError. That is safe for inbox content — every filed document already passed ANAF's validation, whose rules the checks mirror — and DownloadedMessage.view wraps the reader to return None instead of raising; the raw bytes and the full UBL model remain the fallback tiers.

parse_invoice

parse_invoice(xml: bytes) -> InvoiceDocument

Parse e-Factura wire XML straight into an :class:InvoiceDocument.

Raises:

Type Description
ValueError

when the bytes are not a parseable UBL invoice/credit note.

ValidationError

when the document violates the flat models' construction-time shape checks.

read_invoice

read_invoice(doc: Invoice | CreditNote) -> InvoiceDocument

Translate a generated UBL model into the flat :class:InvoiceDocument.

Wire totals, line net amounts and the VAT breakdown land in the explicit fields (never recomputed); :func:~.rules.validate afterwards tells whether the source document's arithmetic holds up.