Skip to content

API reference

Generated from the docstrings. Everything not listed here is internal.

Top level

euinvoice.to_xml

to_xml(
    invoice: Invoice,
    *,
    profile: Profile | None = None,
    syntax: Syntax
    | Literal["ubl", "cii", "fatturapa"]
    | None = None,
    fatturapa_transmission: Transmission | None = None,
) -> bytes

Write an invoice as UBL 2.1 or CII D16B XML under a profile, after its pre-flight and calculation checks.

syntax=Syntax.FATTURAPA writes an FPR12 FatturaPA 1.2.3 file instead (#119), with no profile (FatturaPA has no BT-24) and the transmission header from fatturapa_transmission. Its checks are FatturaPA's (D8 as amended), not the CEN ones: :func:euinvoice.syntax.fatturapa.preflight runs first (what the invoice lacks, what FPR12 cannot carry, totals against the summaries), then the document is written and the offline SdI checks of Allegato A 1.9.1 (:mod:euinvoice.validation.sdi, the ones :func:euinvoice.validate runs after the XSD) run on it. An error from either refuses the write with :class:PreflightError; every set business term is either written or reported. The XSD step needs the fetched artifacts and does not run here; run :func:euinvoice.validate on the result for it.

The invoice is first set up for the profile with :meth:~euinvoice.profiles.Profile.prepare (BT-24 becomes the profile's, BT-23 gets its default; see there), so the written document reads back as profile.prepare(invoice), not as invoice. Then two checks run on the prepared invoice: :attr:~euinvoice.profiles.Profile.preflight (the profile's rules) and :func:euinvoice.calc.check with the target syntax (the CEN calculation, VAT category and period rules, each fatal exactly when that syntax's official CEN binding rejects it; tests/conformance/test_calc_oracle.py). Any fatal or error finding refuses the write with :class:PreflightError, because the official rules would reject the document. Warnings do not block and are not returned; call profile.preflight(prepared, syntax) and calc.check(prepared, syntax=syntax) to see them. Both checks are early, model-level messages; the official Schematron stays the oracle (D8), so run :func:euinvoice.validate on the result for the verdict.

Parameters:

Name Type Description Default
invoice Invoice

The invoice, e.g. from :func:euinvoice.calc.complete.

required
profile Profile | None

The profile to write under. None uses the profile registered for the invoice's own BT-24 (:func:euinvoice.profiles.get); a Factur-X level shares its BT-24 with another profile, so pass it explicitly (and see :func:euinvoice.facturx.embed for the PDF).

None
syntax Syntax | Literal['ubl', 'cii', 'fatturapa'] | None

Syntax.UBL / "ubl", Syntax.CII / "cii" or Syntax.FATTURAPA / "fatturapa". None is allowed only when the profile supports a single syntax, which is then used (e.g. FACTURX_EN16931 / FACTURX_XRECHNUNG: CII).

None
fatturapa_transmission Transmission | None

The FatturaPA transmission header (:class:euinvoice.syntax.fatturapa.Transmission), required with Syntax.FATTURAPA and refused with any other syntax.

None

Returns:

Type Description
bytes

The serialized XML document.

Raises:

Type Description
PreflightError

The pre-flight or calculation checks report a fatal or error finding; findings holds every finding of both. For FatturaPA, the FatturaPA pre-flight reports an error or an SdI check fails on the written document (findings then holds the pre-flight's and the SdI checks' findings; profile_id and syntax are "fatturapa").

UnsupportedDocumentError

profile is None and no profile is registered for the invoice's BT-24; a profile is given with Syntax.FATTURAPA; the profile does not support syntax; or it is a Factur-X level that is not generated (MINIMUM, BASIC WL, BASIC, EXTENDED, plan ยง1), whose official Schematron is not pinned (issue #42), so nothing could tell whether its rules accept the document.

ValueError

syntax is not a syntax, or is None for a profile that supports more than one; fatturapa_transmission is missing with Syntax.FATTURAPA or given with another syntax.

ModelError

The invoice holds something the target syntax cannot express, e.g. a set national extension (Invoice.it, D3 as amended) in UBL or CII; see :func:euinvoice.syntax.ubl.write, :func:euinvoice.syntax.cii.write and :func:euinvoice.syntax.fatturapa.write.

euinvoice.parse

parse(data: bytes) -> Invoice

Read a UBL, CII or FatturaPA invoice, or the invoice of a Factur-X / ZUGFeRD PDF, into the semantic model.

This is :func:parse_detailed without its unmapped list: input that has no business term in the model is discarded here. Use :func:parse_detailed to see it.

Parameters:

Name Type Description Default
data bytes

UBL 2.1 Invoice / CreditNote, CII D16B CrossIndustryInvoice or FatturaPA 1.2 FatturaElettronica XML, or a PDF with an embedded Factur-X / ZUGFeRD invoice.

required

Returns:

Type Description
Invoice

The invoice.

Raises:

Type Description
TypeError

data is not bytes.

ParseError

See :func:parse_detailed.

UnsupportedDocumentError

See :func:parse_detailed.

PdfError

See :func:parse_detailed.

ImportError

data is a PDF and the [pdf] extra (pypdf) is not installed.

euinvoice.parse_detailed

parse_detailed(data: bytes) -> ParseResult

Read a UBL, CII or FatturaPA invoice, or the invoice of a Factur-X / ZUGFeRD PDF, and list what was not mapped.

The syntax comes from the root element (:func:euinvoice.detection.detect_root); the BT-24 profile plays no part in reading. A PDF (%PDF- header, see :func:euinvoice.detection.is_pdf) is read with :func:euinvoice.facturx.extract, which needs the [pdf] extra, and its embedded XML is parsed like any other. A FatturaPA document is read per App. 4.1 of the SdI "Regole tecniche fatture europee" v2.6, with the data that has no business term in Invoice.it (see :mod:euinvoice.syntax.fatturapa); it must hold a single invoice: read a lotto with :func:parse_all.

Parameters:

Name Type Description Default
data bytes

UBL 2.1 Invoice / CreditNote, CII D16B CrossIndustryInvoice or FatturaPA 1.2 FatturaElettronica XML, or a PDF with an embedded Factur-X / ZUGFeRD invoice.

required

Returns:

Type Description
ParseResult

The invoice plus the XPath of every element or attribute that has no business term in the model

ParseResult

(:attr:~euinvoice.syntax.result.ParseResult.unmapped).

Raises:

Type Description
TypeError

data is not bytes.

ParseError

The XML is malformed, has a DOCTYPE or exceeds the parser limits (D10), or its content does not form a valid invoice (the message names the BT/BG id). Factur-X MINIMUM and BASIC WL documents raise it, as they lack terms EN 16931 requires (issue #69).

UnsupportedDocumentError

The root element is not a UBL 2.1 Invoice / CreditNote, a CII D16B CrossIndustryInvoice or a FatturaPA 1.2 FatturaElettronica; a FatturaPA document holds several invoices (a lotto, read it with :func:parse_all); or data is a signed FatturaPA (.p7m, CAdES), which is not read: extract its XML first.

PdfError

data is a PDF that does not name exactly one embedded invoice (see :func:euinvoice.facturx.extract).

ImportError

data is a PDF and the [pdf] extra (pypdf) is not installed.

euinvoice.parse_all

parse_all(data: bytes) -> tuple[ParseResult, ...]

Read every invoice of a document into ParseResult s, the detailed form of :func:parse_detailed.

Only a FatturaPA batch file (a lotto, #120) holds several invoices: one per FatturaElettronicaBody, sharing the header. Each result's unmapped lists the unmapped header content and that of its own body. UBL, CII and Factur-X documents hold one invoice, so the result has one entry, equal to :func:parse_detailed's.

Parameters:

Name Type Description Default
data bytes

As for :func:parse_detailed.

required

Returns:

Type Description
tuple[ParseResult, ...]

One result per invoice, in document order.

Raises:

Type Description
TypeError

data is not bytes.

ParseError

See :func:parse_detailed.

UnsupportedDocumentError

See :func:parse_detailed; a lotto is read, not refused.

PdfError

See :func:parse_detailed.

ImportError

See :func:parse_detailed.

euinvoice.validate

validate(
    data: bytes, profile: Profile | None = None
) -> ValidationReport

Validate an e-invoice against the official XSD and the Schematron rule sets of its profile.

Rule failures are findings, never exceptions (D9). Nothing is downloaded (D7): the artifacts must have been fetched with python -m euinvoice artifacts fetch.

Profile resolution when profile is None: :func:euinvoice.detection.detect_root reads BT-24 (XPath normalize-space) and looks it up exactly with :func:euinvoice.profiles.get. If there is no single non-empty BT-24, or no registered profile declares it (e.g. a CIUS whose profile is not implemented yet), the document is validated against :data:euinvoice.profiles.EN16931 (XSD and CEN rules) and the report starts with an information finding :data:PROFILE_FALLBACK_RULE_ID saying that only the core rules ran. The same fallback applies when the BT-24's profile does not support the document's syntax. A CII document whose BT-24 is one of euinvoice.profiles.facturx._UNREGISTERED_LEVEL_IDENTIFIERS (a ZUGFeRD 2.0 MINIMUM, BASIC or EXTENDED id, or a colon spelling of Factur-X BASIC / EXTENDED) does not fall back: it raises ArtifactsNotAvailableError like the Factur-X levels below. The official steps report the BT-24 problem itself: a missing or blank BT-24 fails BR-01 (and, in CII without the context parameter, CII-SR-009 and CII-SR-010); a repeated UBL cbc:CustomizationID or CII ram:ID fails the XSD (each allows at most one); a repeated CII ram:GuidelineSpecifiedDocumentContextParameter makes the CEN stylesheet fail at run time (SCHEMATRON-RUNTIME, fatal) before CII-SR-009 is evaluated.

An explicit profile is used as given, even if the document's BT-24 names another one; the profile's own rules check BT-24 where they require a value (e.g. XRechnung BR-DE-21).

A FatturaPA document is validated by syntax (profile must be None): the FatturaPA 1.2.3 XSD, then, if it reported nothing fatal / error, the offline SdI checks (:func:euinvoice.validation.sdi.check), each an error finding whose rule_id is the SdI error code. An FPA12 document's report starts with an information finding :data:FPA12_NOTE_RULE_ID.

Parameters:

Name Type Description Default
data bytes

The serialized XML document.

required
profile Profile | None

The profile to validate under; None auto-detects it from BT-24.

None

For the XRECHNUNG, XRECHNUNG_EXTENSION and XRECHNUNG_CVD profiles, kosit holds the KoSIT validator's verdict when a scenario of the pinned KoSIT configuration matches the document and runs the same rule sets (:func:euinvoice.validation.kosit.verdict); otherwise it is None. It never changes findings or ok.

Returns:

Type Description
ValidationReport

The XSD findings, then the findings of each rule set in profile order (none if the XSD step had

ValidationReport

a fatal or error finding), each tagged with its source; and the KoSIT verdict.

Raises:

Type Description
TypeError

data is not bytes.

ParseError

The XML is malformed, has a DOCTYPE or exceeds the parser limits (D10).

UnsupportedDocumentError

The root element is not a UBL 2.1 Invoice / CreditNote, a CII D16B CrossIndustryInvoice or a FatturaPA 1.2 FatturaElettronica, profile does not support the document's syntax (no profile supports FatturaPA), or data is a signed .p7m envelope (:func:euinvoice.detection.is_signed; extract its XML first).

ArtifactsNotAvailableError

An artifact the run needs is not in the cache (names the fetch command; XRechnung profiles also read xrechnung-validator-configuration), saxonche is not installed, or the profile (or, auto-detected, the BT-24's level) needs the Factur-X / ZUGFeRD Schematron, which is not pinned yet (issue #42).

euinvoice.detect

detect(data: bytes) -> Detection

Classify an e-invoice XML document by its root element and specification identifier (BT-24).

Parameters:

Name Type Description Default
data bytes

The serialized XML document.

required

Returns:

Type Description
Detection

The syntax, root element, BT-24 and matching profile, see :func:detect_root.

Raises:

Type Description
TypeError

data is not bytes.

ParseError

The XML is malformed, has a DOCTYPE or exceeds the parser limits.

UnsupportedDocumentError

data is a PDF or a signed .p7m envelope (:func:is_signed), or the root element is not a UBL 2.1 Invoice / CreditNote, a CII D16B CrossIndustryInvoice or a FatturaPA 1.2 FatturaElettronica.

Example

from euinvoice import _xml xml = ( ... f'' ... "urn:cen.eu:en16931:2017" ... ) detect(xml.encode()).profile.id 'en16931'

euinvoice.ValidationReport dataclass

ValidationReport(
    findings: tuple[Finding, ...] = (),
    kosit: KositAssessment | None = None,
)

The result of validating one document.

Attributes:

Name Type Description
findings tuple[Finding, ...]

Every finding, in the order the validation steps produced them.

kosit KositAssessment | None

The KoSIT verdict, for the XRechnung profiles when a KoSIT scenario matches the document; None otherwise.

ok property

ok: bool

Whether no finding is fatal or error (warnings and information do not count).

euinvoice.report.Finding dataclass

Finding(
    rule_id: str,
    severity: Severity,
    location: str | None,
    message: str,
    source: str,
)

One problem reported by a validation step.

Attributes:

Name Type Description
rule_id str

The official rule id (e.g. BR-02, PEPPOL-EN16931-R001, BR-DE-15), or XSD for a schema-validity error.

severity Severity

How serious the finding is; fatal and error make the report not ok.

location str | None

Where the problem is (an XPath for Schematron, the line number and element path for XSD, a model path such as vat_breakdown[0].tax_amount for calc or buyer.electronic_address for a pre-flight check), if known.

message str

The rule's human-readable text.

source str

The rule set that produced it, as the manifest source name plus what ran (e.g. cen-ubl, peppol-bis, xsd:ubl-2_1), calc, or a pre-flight source such as peppol-preflight.

euinvoice.report.Severity

Bases: StrEnum

Severity of a finding.

The values are the flag values used by the official Schematron rule sets.

euinvoice.report.KositAssessment dataclass

KositAssessment(
    scenario: str,
    overrides: tuple[SeverityOverride, ...] = (),
    blocking: tuple[Finding, ...] = (),
)

The verdict the KoSIT XRechnung validator would give, next to the raw official flags (issue #49).

The KoSIT validator configuration replaces the severity of some official rules per scenario (customLevel in its scenarios.xml) and rejects a document when any message is still at level error (resources/default-report.xsl, template rep:report in mode assessment). :func:euinvoice.validate builds this; :attr:ValidationReport.findings and :attr:ValidationReport.ok are never changed by it.

Attributes:

Name Type Description
scenario str

The <name> of the KoSIT scenario that matched the document.

overrides tuple[SeverityOverride, ...]

Every finding a customLevel of that scenario applies to, with the effective level.

blocking tuple[Finding, ...]

Every finding that is fatal or error after the overrides, with its official severity. A blocking XSD finding is among them, as KoSIT rejects a schema-invalid document.

accepted property

accepted: bool

Whether KoSIT would accept the document: no finding is blocking after the overrides.

euinvoice.report.SeverityOverride dataclass

SeverityOverride(finding: Finding, severity: Severity)

A finding whose severity a validator configuration replaces.

Attributes:

Name Type Description
finding Finding

The finding, unchanged: its severity is the official flag.

severity Severity

The severity the configuration gives it instead (for KoSIT, the customLevel).

euinvoice.Syntax

Bases: StrEnum

An XML syntax of an e-invoice.

UBL and CII are the EN 16931 syntaxes (CEN/TS 16931-3-2 and -3-3): every profile, writer and reader serves them. FATTURAPA (Italy's SdI format, XSD 1.2.3; ADR 0001) is not an EN 16931 syntax: it is detected and validated (#121) and written by to_xml without a profile (#119); no profile supports it, and its reader (#120) is :func:euinvoice.parse.

euinvoice.ParseResult dataclass

ParseResult(
    invoice: Invoice, unmapped: tuple[str, ...] = ()
)

An invoice read from XML, plus the input it could not map.

Attributes:

Name Type Description
invoice Invoice

The invoice built from the mapped business terms.

unmapped tuple[str, ...]

The location (an XPath in lxml's getpath format) of each element or attribute in the input that the reader did not map to a business term, in document order. An attribute is <element path>/@prefix:name; <element path>/text() means that only part of the element's text was mapped (e.g. the time zone of a UBL xs:date, which the model's calendar date cannot hold).

Calculation

euinvoice.calc.complete

complete(
    draft: InvoiceDraft,
    *,
    paid_amount: Amount | None = None,
    rounding_amount: Amount | None = None,
    vat_total_in_accounting_currency: Amount | None = None,
    exemption_reasons: Mapping[
        str, ExemptionReason
    ] = _NO_REASONS,
) -> Invoice

Derive BT-131, the DOCUMENT TOTALS (BG-22) and the VAT BREAKDOWN (BG-23) of a draft.

The keyword arguments are the inputs that cannot be derived. check(complete(draft, ...)) is empty, so the amounts pass the CEN rules in both UBL and CII, whenever the draft's VAT categories, rates and exemption reasons follow the CEN rules (BR--05..07, BR--10, BR-O-11..14, BR-B-02); the result is not checked for you.

Whole-unit VAT: the CEN example huf_example_cii.xml states the VAT of 69180.00 at 27 % as 18679 (whole forints), not 18678.60. :func:complete always rounds to cents (D11).

Derivations (CEN validation-1.3.16, EN16931-model.sch):

  • BT-131 per line: :func:line_net_amount.
  • BT-106 = ฮฃ BT-131 (BR-CO-10); BT-107 = ฮฃ BT-92 and BT-108 = ฮฃ BT-99, left out when there are no document level allowances or charges (BR-CO-11, BR-CO-12).
  • BT-109 = BT-106 - BT-107 + BT-108 (BR-CO-13); BT-110 = ฮฃ BT-117 (BR-CO-14); BT-112 = BT-109 + BT-110 (BR-CO-15); BT-115 = BT-112 - BT-113 + BT-114 (BR-CO-16).
  • BG-23: one group per VAT category code and rate, see :func:_breakdown (BR-CO-17, -08).
  • BT-111 is passed through; BT-6 needs it and must differ from BT-5 (BR-53).

Parameters:

Name Type Description Default
draft InvoiceDraft

The invoice without totals and VAT breakdown.

required
paid_amount Amount | None

Paid amount (BT-113).

None
rounding_amount Amount | None

Rounding amount (BT-114).

None
vat_total_in_accounting_currency Amount | None

Invoice total VAT amount in accounting currency (BT-111), required exactly when the draft has a VAT accounting currency code BT-6 (BR-53).

None
exemption_reasons Mapping[str, ExemptionReason]

VAT exemption reason (BT-120, BT-121) per VAT category code (BT-118), e.g. {"AE": ExemptionReason(code="VATEX-EU-AE", text="Reverse charge")}; keys are matched upper-cased. Every VAT breakdown of that category gets it.

_NO_REASONS

Returns:

Type Description
Invoice

The complete invoice, built through validation (Invoice.model_validate).

Raises:

Type Description
ModelError

exemption_reasons names a category the draft does not use (or one twice, in different case), or BT-6 and BT-111 do not go together: BT-6 without BT-111, BT-111 without BT-6, or BT-6 equal to BT-5 (BR-53).

ValidationError

An amount argument is not an Amount (e.g. a float, D3, or more than two decimals, BR-DEC-*).

euinvoice.calc.ExemptionReason

Bases: EuInvoiceModel

The VAT exemption reason of one VAT category, an input of :func:complete.

It fills BT-120 and BT-121 of every VAT breakdown (BG-23) of that category. BR-E-10, BR-AE-10, BR-IC-10, BR-G-10 and BR-O-10 require one of the two for categories E, AE, K, G and O.

text class-attribute instance-attribute

text: Text | None = None

VAT exemption reason text (BT-120).

code class-attribute instance-attribute

code: VatExemptionReasonCode | None = None

VAT exemption reason code (BT-121), CEF VATEX (BR-CL-22), stored upper-cased.

euinvoice.calc.check

check(
    invoice: Invoice, *, syntax: Syntax | None = None
) -> tuple[Finding, ...]

Report where a complete invoice breaks the CEN calculation, VAT and period rules.

Checked (CEN validation-1.3.16, every rule fatal in both bindings): BR-CO-10 โ€ฆ BR-CO-17, BR-48, BR-53; the period rules BR-29, BR-30, BR-CO-19 and BR-CO-20; per VAT category code S, Z, E, AE, K, G, O, L and M the rules BR-<x>-01 (breakdown present), -05/-06/-07 (line, allowance and charge VAT rates), -08 (taxable amount), -09 (tax amount) and -10 (exemption reason); BR-O-11 โ€ฆ BR-O-14 and BR-B-02. Each rule is evaluated as the UBL and the CII binding test it. With a target syntax, a rule its binding rejects is fatal under the rule's id, and the other binding is ignored. Without one, a rule is fatal when both bindings reject it and a warning :data:~euinvoice.calc.PORTABILITY when only one does (see :mod:euinvoice.calc). The official Schematron stays the oracle (D8): this is an early warning, not a replacement for validate().

Parameters:

Name Type Description Default
invoice Invoice

A complete invoice.

required
syntax Syntax | None

The syntax the invoice will be written in (a :class:~euinvoice.syntax.Syntax or its value, "ubl" / "cii"), or None for either.

None

Returns:

Type Description
Finding

The findings, each with the model path as location (e.g. vat_breakdown[0].tax_amount, see

...

mod:euinvoice.model.bt_index) and source "calc"; empty if every rule holds in the target

tuple[Finding, ...]

syntax (both, without one).

Raises:

Type Description
ValueError

syntax is not a syntax, or not an EN 16931 one (Syntax.FATTURAPA has no CEN binding).

euinvoice.calc.line_net_amount

line_net_amount(line: LineDraft | InvoiceLine) -> Decimal

Compute the invoice line net amount (BT-131): BT-129 * BT-146 / BT-149 + ฮฃ BT-141 - ฮฃ BT-136.

The product is rounded half up to two decimals per D11 (the line charges and allowances already have two). EN 16931 has no rule for this arithmetic; the formula is the one of Peppol BIS 3.0.21 rule PEPPOL-EN16931-R120 (rules/sch/PEPPOL-EN16931-UBL.sch), which compares the unrounded value with a slack of 0.02 (u:slack(โ€ฆ, 0.02)), so the rounded result is always consistent with it. A missing or zero base quantity (BT-149) counts as 1, as R120's $baseQuantity variable does; Peppol's PEPPOL-EN16931-R121 rejects a base quantity of 0, which is a profile pre-flight concern.

Parameters:

Name Type Description Default
line LineDraft | InvoiceLine

A line draft (or a complete line, whose own BT-131 is ignored).

required

Returns:

Type Description
Decimal

The line net amount, with two decimals.

Profiles

euinvoice.profiles.Profile dataclass

Profile(
    *,
    id: str,
    title: str,
    specification_identifier: str,
    syntaxes: frozenset[Syntax],
    rule_sets: tuple[str, ...],
    business_process_type: str | None = None,
    facturx_filename: str | None = None,
    facturx_conformance_level: str | None = None,
    preflight: Preflight = no_preflight,
    requires_vat_breakdown_rate: Callable[[Invoice], bool]
    | None = None,
)

A specification an invoice is written, read and validated under (IMPLEMENTATION_PLAN.md D5).

A profile is data plus :meth:prepare and an optional :attr:preflight. Pre-flight checks give early, model-level messages for a CIUS's rules; the official Schematron stays the oracle (D8).

Attributes:

Name Type Description
id str

Short stable name, e.g. "en16931".

title str

Human-readable name.

specification_identifier str

The BT-24 value the profile writes and is looked up by.

syntaxes frozenset[Syntax]

Supported syntaxes, a subset of :data:SYNTAXES.

rule_sets tuple[str, ...]

Names of the official Schematron rule sets to run after the XSD, in order. A name and a syntax select one euinvoice.validation.schematron constant: "cen" โ†’ CEN_UBL / CEN_CII, "peppol" โ†’ PEPPOL_UBL / PEPPOL_CII, "xrechnung" โ†’ XRECHNUNG_UBL / XRECHNUNG_CII; "facturx" has no pinned artifact yet (#42). Profiles do not import euinvoice.validation (dependency direction, plan ยง4), so the validator owns that mapping. Names come from :data:RULE_SETS; "cen" is not required (Factur-X MINIMUM and BASIC WL are not EN 16931).

business_process_type str | None

BT-23 default written when the invoice has none, or None.

facturx_filename str | None

Name of the embedded XML in a Factur-X / ZUGFeRD PDF; None otherwise.

facturx_conformance_level str | None

XMP fx:ConformanceLevel of a Factur-X / ZUGFeRD profile; None otherwise.

preflight Preflight

Checks an invoice for the profile's rules before it is written in a syntax, e.g. PEPPOL.preflight(PEPPOL.prepare(invoice), Syntax.UBL). Call contract: it runs on the invoice after :meth:prepare, right before writing; to_xml (#27) calls it. validate() never does, because there the official Schematron is the oracle (D8). A pre-flight never reports a fatal official rule id that the oracle would not raise for the written document; extra semantic checks use EUINV-* ids at warning severity. Defaults to :func:no_preflight.

requires_vat_breakdown_rate Callable[[Invoice], bool] | None

Predicate (invoice) -> bool: whether the profile's rules require the VAT category rate (BT-119) on every VAT breakdown of that invoice, e.g. XRechnung BR-DE-14 or Peppol DE-R-014. When it returns True, :meth:prepare writes BT-119 = 0 on each "Not subject to VAT" (O) breakdown that has none. None (EN 16931 core: BR-48 exempts category O) never asks. It is a per-invoice predicate, not a bool, because a bool would add a 0 rate to non-German Peppol invoices that their rules don't ask for; the predicate keeps that output byte-identical.

prepare

prepare(invoice: Invoice) -> Invoice

Return invoice set up for this profile, ready to be written.

Sets PROCESS CONTROL (BG-2): BT-24 always becomes :attr:specification_identifier, because the profile the caller writes under decides what the document claims; BT-23 gets :attr:business_process_type only when the invoice has none. When :attr:requires_vat_breakdown_rate says so, a "Not subject to VAT" (O) VAT breakdown without BT-119 gets BT-119 = 0, the value of the official XRechnung instance standard/01.04a-INVOICE_ubl.xml (cbc:Percent 0) and 01.04a-INVOICE_uncefact.xml (ram:RateApplicablePercent 0). The CEN rules accept it: BR-O-05/06/07 forbid a rate on O lines, allowances and charges only, and BR-CO-17 holds with BT-117 = 0 (BR-O-09) (UBL; in CII the $VATO context shadows BR-CO-17, which does not run on O breakdowns). A breakdown of another category keeps a missing rate, which has no default. The result is built through model validation (never model_copy(update=...), which skips it).

Parameters:

Name Type Description Default
invoice Invoice

The invoice to prepare.

required

Returns:

Type Description
Invoice

A new, validated invoice (equal to invoice when nothing changes).

Raises:

Type Description
ValidationError

The profile's values are invalid for the model.

euinvoice.profiles.get

get(specification_identifier: str) -> Profile

Return the profile whose specification identifier (BT-24) is specification_identifier.

The match is exact: a CIUS identifier such as urn:cen.eu:en16931:2017#compliant#... never resolves to the core profile.

Parameters:

Name Type Description Default
specification_identifier str

A BT-24 value.

required

Returns:

Type Description
Profile

The registered profile.

Raises:

Type Description
UnsupportedDocumentError

No registered profile has this BT-24; the message lists the known ones.

Factur-X / ZUGFeRD

euinvoice.facturx.embed

embed(
    pdf: bytes,
    invoice: Invoice | bytes,
    *,
    profile: Profile,
    relationship: Relationship | None = None,
) -> bytes

Embed an invoice's CII XML into a PDF/A-3 as a Factur-X / ZUGFeRD document.

The XML becomes an associated file named after the level (factur-x.xml, xrechnung.xml) and the XMP gets the fx properties and their PDF/A extension schema. The rest of the PDF is kept as it is, including the document information dictionary and the first file identifier; the second identifier is regenerated, because the content changed (ISO 32000-1 ยง14.4). The XML is embedded byte for byte.

Parameters:

Name Type Description Default
pdf bytes

A PDF/A-3 document (XMP pdfaid:part 3).

required
invoice Invoice | bytes

The CII XML as bytes, embedded unchanged, or an :class:~euinvoice.model.Invoice, written with :func:euinvoice.syntax.cii.write after profile.prepare (EN 16931 and XRECHNUNG levels only, plan ยง1). Pre-flight checks are not run; that is to_xml's job (#27).

required
profile Profile

The Factur-X level, e.g. :data:euinvoice.profiles.FACTURX_EN16931.

required
relationship Relationship | None

The file specification's /AFRelationship; by default Source for XRECHNUNG and Data otherwise (the corpus-attested choice, see the module docstring).

None

Returns:

Type Description
bytes

The Factur-X PDF.

Raises:

Type Description
UnsupportedDocumentError

profile is not a Factur-X level, invoice is an Invoice for a level that is not generated, or the XML is not CII or its BT-24 is not the level's.

ParseError

The XML is malformed or has a DOCTYPE (D10).

PdfError

pdf cannot be read or rewritten by pypdf (any pypdf failure on the untrusted input, with the cause chained), is encrypted or signed, is not PDF/A-3, has a non-stream or malformed XMP packet, already carries Factur-X XMP, or already has an attachment with the level's file name.

ValueError

relationship is not one of :data:Relationship.

ModelError

invoice is an Invoice holding something CII cannot express, e.g. a set national extension (Invoice.it, D3 as amended); see :func:euinvoice.syntax.cii.write.

euinvoice.facturx.extract

extract(pdf: bytes) -> Extracted

Take the invoice XML out of a Factur-X or ZUGFeRD (1.0, 2.0, 2.1+) PDF.

The XMP names the invoice attachment; the selection rule and its corpus evidence are in the module docstring. The PDF need not be PDF/A, and a signed PDF is read like any other.

Parameters:

Name Type Description Default
pdf bytes

The PDF.

required

Returns:

Type Description
Extracted

The XML and what the PDF and the XML say about it.

Raises:

Type Description
PdfError

pypdf cannot read pdf (any pypdf failure on the untrusted input, with the cause chained); it is encrypted; its XMP is missing, not a stream or not well-formed; or the XMP does not name exactly one invoice attachment by an attested name (see the module docstring).

Example

from euinvoice import facturx found = facturx.extract(pdf_bytes) # doctest: +SKIP found.filename, found.conformance_level # doctest: +SKIP ('factur-x.xml', 'EN 16931')

euinvoice.facturx.Extracted dataclass

Extracted(
    *,
    xml: bytes,
    filename: str,
    container: Container,
    conformance_level: str | None,
    profile: Profile | None,
)

The invoice found in a Factur-X / ZUGFeRD PDF.

Attributes:

Name Type Description
xml bytes

The embedded file, byte for byte. Not parsed beyond detection: parse() / validate() read it.

filename str

Its name, the XMP DocumentFileName.

container Container

The XMP invoice schema.

conformance_level str | None

The XMP ConformanceLevel as written ("EN 16931", "COMFORT"โ€ฆ), or None.

profile Profile | None

For Factur-X, the level's profile when the XML is CII with that level's BT-24 (the pairing embed enforces); otherwise the profile :func:euinvoice.detection.detect finds from the XML alone (a ZUGFeRD 2.0 EN 16931 invoice is the core profile), or None (no registered BT-24, not a supported invoice, not well-formed).

FatturaPA

euinvoice.model.it.ItalianExtension

Bases: EuInvoiceModel

FatturaPA data of an invoice with no EN 16931 business term (Invoice.it).

tax_regime instance-attribute

tax_regime: Annotated[RegimeFiscale, fatturapa('1.2.1.8')]

RegimeFiscale <1.1> of the seller (CedentePrestatore); FatturaPA requires it, EN 16931 has no term.

issuer class-attribute instance-attribute

issuer: Annotated[
    SoggettoEmittente | None, fatturapa(1.6)
] = None

SoggettoEmittente <0.1>: who issued the document when it is not the seller, e.g. CC (the buyer) for a TD17 self-billed integration.

document_type instance-attribute

document_type: Annotated[
    TipoDocumento, fatturapa("2.1.1.1")
]

TipoDocumento <1.1>. Required: App. 5.4 maps many codes to BT-3 380, so BT-3 cannot give it.

vat_summaries class-attribute instance-attribute

vat_summaries: Annotated[
    tuple[ItalianVatSummary, ...], fatturapa("2.2.2")
] = ()

DatiRiepilogo data, at most one per rate, Natura and split payment or not (rates compared by value).

payment class-attribute instance-attribute

payment: Annotated[
    ItalianPayment | None, fatturapa(2.4)
] = None

DatiPagamento data (BG-16 in App. 4.1).

seller_first_name class-attribute instance-attribute

seller_first_name: Annotated[
    Latin60Text | None, fatturapa("1.2.1.3.2")
] = None

Nome <1.1 with Cognome> of the seller, a natural person (Anagrafica). Set with :attr:seller_last_name or not at all. Allegato A 1.9.1 (pp. 33 and 38) reserves Denominazione (BT-27) for a "persona non fisica"; with the pair set the writer emits Nome and Cognome instead, and BT-27 must be Nome Cognome (the form the reader gives, #132 item 2a). App. 4.1 only describes the reverse concatenation into BT-27.

seller_last_name class-attribute instance-attribute

seller_last_name: Annotated[
    Latin60Text | None, fatturapa("1.2.1.3.3")
] = None

Cognome of the seller; see :attr:seller_first_name.

buyer_first_name class-attribute instance-attribute

buyer_first_name: Annotated[
    Latin60Text | None, fatturapa("1.4.1.3.2")
] = None

Nome of the buyer, a natural person; as :attr:seller_first_name, for BT-44.

buyer_last_name class-attribute instance-attribute

buyer_last_name: Annotated[
    Latin60Text | None, fatturapa("1.4.1.3.3")
] = None

Cognome of the buyer; see :attr:buyer_first_name.

stamp_duty_amount class-attribute instance-attribute

stamp_duty_amount: Annotated[
    Amount | None, fatturapa("2.1.1.6.2")
] = None

ImportoBollo of DatiBollo, the stamp duty charged to the buyer: above 0 and at most 99999999999.99 (Amount2DecimalType, XSD 1.2.3; the XSD type is signed, the sign is euinvoice's policy, #165). Regole tecniche v2.6 App. 4.1 row 2.1.1.6.2 (printed p. 84) says "Valorizzare a zero in caso di mancato addebito al cessionario-committente", which reads as a non-zero value for a charged duty; AdE FAQ 48 (compendium 22/12/2022, p. 19) fills DatiBollo "con l'importo dell'imposta". None (not charged) writes 0.00. For the EN-derived mapping the library follows the App. 4.1 row: BT-99 stays 0 / category Z (BR-IT-DC-480, p. 62) and no line is generated; the recharge is ordinary consideration (Interpello 428/2022, p. 4), which the caller models as an ordinary invoice line. Requires the zero SAE (credit note: 95) marker, else the writer refuses.

euinvoice.model.it.ItalianLineExtension

Bases: EuInvoiceModel

FatturaPA data of one line, 2.2.1 <DettaglioLinee> (InvoiceLine.it).

supply_type class-attribute instance-attribute

supply_type: Annotated[
    TipoCessionePrestazione | None, fatturapa("2.2.1.2")
] = None

TipoCessionePrestazione <0.1>: the line is a discount, premium, rebate or ancillary charge. App. 4.1 carries it in an ITEM ATTRIBUTE (BT-160 "TipoCessionePrestazione", BT-161 the code).

nature class-attribute instance-attribute

nature: Annotated[Natura | None, fatturapa("2.2.1.14")] = (
    None
)

Natura <0.1>: why the line carries no VAT. App. 4.1 carries it in an ITEM ATTRIBUTE (BT-160 "Natura") or, cross-border, derives it from BT-151 (App. 5.1); the category alone cannot tell N2.2 from N4.

total_price class-attribute instance-attribute

total_price: Annotated[
    UnitPriceAmount | None, fatturapa("2.2.1.11")
] = None

PrezzoTotale <1.1> as declared, when it has more than two decimals (Amount8DecimalType: 2 to 8). BT-131 is an EN amount (at most two decimals, BR-DEC-23), so the reader sets it to this value rounded half up (D11) and keeps the exact value here; the writer writes it back when rounding it gives BT-131. Leave it unset otherwise.

euinvoice.model.it.ItalianVatSummary

Bases: EuInvoiceModel

FatturaPA data of one 2.2.2 <DatiRiepilogo>, identified by its rate, Natura and split payment.

The amounts (ImponibileImporto, Imposta) are not here: the writer computes them (#119).

FatturaPA requires a separate summary per payment mode: split payment (EsigibilitaIVA S, from VAT category B, App. 4.1 2.2.2.7) versus ordinary (Allegato A 1.9.1, DatiRiepilogo: "per ogni aliquota IVA e modalitร  di versamento dell'imposta ("scissione dei pagamenti" od ordinaria), e/o per ogni natura"). The writer (#119) derives that split from the B/S categories. So the key is (:attr:rate, :attr:nature, whether :attr:vat_chargeability is S): a split-payment summary and an ordinary one can share a rate. Within those, splitting by D vs I is optional (Rappresentazione tabellare, row 2.2.2), and this key cannot express it.

rate instance-attribute

rate: Annotated[Rate, fatturapa('2.2.2.1')]

AliquotaIVA <1.1>, as a percentage (22 for 22 %). Part of the key.

nature class-attribute instance-attribute

nature: Annotated[Natura | None, fatturapa("2.2.2.2")] = (
    None
)

Natura <0.1>. Part of the key.

vat_chargeability class-attribute instance-attribute

vat_chargeability: Annotated[
    EsigibilitaIVA | None, fatturapa("2.2.2.7")
] = None

EsigibilitaIVA <0.1>. App. 4.1 derives it from BT-8 (3 and 35 โ†’ I, 432 โ†’ D) or category B (โ†’ S) for the whole document; this sets it per summary. Whether it is S (split payment) is part of the key.

legal_reference class-attribute instance-attribute

legal_reference: Annotated[
    Latin100Text | None, fatturapa("2.2.2.8")
] = None

RiferimentoNormativo <0.1>, the legal reference for :attr:nature. App. 4.1 concatenates Natura and this into BT-120 of the VAT breakdown.

euinvoice.model.it.ItalianPayment

Bases: EuInvoiceModel

FatturaPA data of 2.4 <DatiPagamento>, with one 2.4.2 <DettaglioPagamento>.

conditions instance-attribute

conditions: Annotated[
    CondizioniPagamento, fatturapa("2.4.1")
]

CondizioniPagamento <1.1> (App. 4.1 folds it into BT-20 as text).

method class-attribute instance-attribute

method: Annotated[
    ModalitaPagamento | None, fatturapa("2.4.2.2")
] = None

ModalitaPagamento <1.1>. App. 5.6 maps BT-81 to it many-to-one; set it to keep the exact code, or leave it unset to have it derived from BT-81.

euinvoice.syntax.fatturapa.write

write(
    invoice: Invoice, transmission: Transmission
) -> bytes

Serialize an invoice as an FPR12 FatturaPA 1.2.3 document with one body.

The pre-flight (:func:~euinvoice.syntax.fatturapa.preflight) runs first. The SdI checks of Allegato A 1.9.1 do not: run :func:euinvoice.validate on the result, or write through :func:euinvoice.to_xml, which runs them.

Parameters:

Name Type Description Default
invoice Invoice

The invoice, with Invoice.it set.

required
transmission Transmission

The transmission header (:class:~euinvoice.syntax.fatturapa.Transmission).

required

Returns:

Type Description
bytes

The UTF-8 encoded document with an XML declaration, unsigned.

Raises:

Type Description
ModelError

The pre-flight reports an error (the message lists every one), or a value does not fit its XSD type (length, characters, decimals, CAP, Provincia, line number, IBAN, โ€ฆ); that message starts with the business term and its model path.

euinvoice.syntax.fatturapa.preflight

preflight(
    invoice: Invoice,
    transmission: Transmission | None = None,
) -> tuple[Finding, ...]

Report what keeps an invoice from being written as FPR12, before :func:~euinvoice.syntax.fatturapa.write.

error findings block the write (write raises :class:~euinvoice.errors.ModelError, to_xml :class:~euinvoice.errors.PreflightError). Every set business term is either written or reported here: none is dropped.

Parameters:

Name Type Description Default
invoice Invoice

The invoice.

required
transmission Transmission | None

The transmission header it is written with. BT-49 is accepted only when it equals its recipient_code (the CodiceDestinatario, row 1.1.4), so without one it is refused.

None

Returns:

Type Description
Finding

The findings, located by model path, with source "fatturapa-preflight". warning findings do not

...

block the write.

euinvoice.syntax.fatturapa.Transmission

Bases: EuInvoiceModel

The transmission header of an FPR12 file: who sends it and to which SdI channel.

FormatoTrasmissione (1.1.3) is always FPR12 and is not a field.

transmitter_country instance-attribute

transmitter_country: Annotated[
    Text,
    AfterValidator(
        _match(
            _COUNTRY,
            "IdPaese must be two capital letters (NazioneType)",
        )
    ),
    fatturapa("1.1.1.1"),
]

IdTrasmittente/IdPaese: the country code of the transmitter's tax identifier, e.g. IT.

transmitter_code instance-attribute

transmitter_code: Annotated[
    Text,
    AfterValidator(
        _match(
            _CODE,
            "IdCodice must be 1 to 28 characters (CodiceType)",
        )
    ),
    fatturapa("1.1.1.2"),
]

IdTrasmittente/IdCodice: the transmitter's tax identifier (for Italy the codice fiscale).

transmission_number instance-attribute

transmission_number: Annotated[
    Text,
    AfterValidator(
        _match(
            _NUMBER,
            "ProgressivoInvio must be 1 to 10 Basic Latin characters, no tab or line break (String10Type)",
        )
    ),
    fatturapa("1.1.2"),
]

ProgressivoInvio: the transmitter's own identifier of this transmission (Allegato A: "progressivo che il soggetto trasmittente attribuisce al file"). Not the 1-5 character file_number of the SdI file name (:func:~euinvoice.syntax.fatturapa.file_name), which follows other rules.

recipient_code class-attribute instance-attribute

recipient_code: Annotated[
    Text,
    AfterValidator(
        _match(
            _RECIPIENT,
            "CodiceDestinatario of an FPR12 must be 7 capital letters or digits (CodiceDestinatarioType; 6 characters are FPA12 only, SdI 00427)",
        )
    ),
    fatturapa("1.1.4"),
] = RECIPIENT_UNKNOWN

CodiceDestinatario: the recipient's SdI channel code, :data:RECIPIENT_UNKNOWN (the default) or :data:RECIPIENT_FOREIGN.

recipient_pec class-attribute instance-attribute

recipient_pec: Annotated[
    Text | None, fatturapa("1.1.6")
] = None

PECDestinatario: the recipient's certified e-mail address. Allegato A uses it only with CodiceDestinatario :data:RECIPIENT_UNKNOWN, so it is refused with any other code.

euinvoice.syntax.fatturapa.RECIPIENT_UNKNOWN module-attribute

RECIPIENT_UNKNOWN: Final = '0000000'

CodiceDestinatario when the recipient's channel is unknown or is the PEC address in PECDestinatario (Allegato A 1.9.1, ยง2.1.1, CodiceDestinatario).

euinvoice.syntax.fatturapa.RECIPIENT_FOREIGN module-attribute

RECIPIENT_FOREIGN: Final = 'XXXXXXX'

CodiceDestinatario of an invoice to a party not established in Italy, sent to report its data (Allegato A 1.9.1, ยง2.1.1; SdI check 00313 requires the cessionario's IdPaese to differ from IT).

euinvoice.syntax.fatturapa.file_name

file_name(
    country: str,
    identifier: str,
    file_number: str,
    *,
    extension: str = ".xml",
) -> str

Build an SdI file name (Allegato A 1.9.1 ยง1.2.2), e.g. IT01234567890_00001.xml.

The name must also differ from every name sent to SdI before (00002), which only the sender can ensure.

Parameters:

Name Type Description Default
country str

The ISO 3166-1 alpha-2 code of the identifier, e.g. "IT".

required
identifier str

The tax identifier of the transmitter or another subject (11 to 16 characters for IT, 2 to 28 otherwise; letters and digits).

required
file_number str

The "progressivo univoco del file", 1 to 5 letters or digits. Not Transmission's transmission_number (1.1.2 ProgressivoInvio), which follows other rules.

required
extension str

One of :data:EXTENSIONS: .xml, .xml.p7m or .zip.

'.xml'

Returns:

Type Description
str

The file name.

Raises:

Type Description
ValueError

A part does not follow ยง1.2.2; the message says which.

euinvoice.syntax.fatturapa.check_file

check_file(name: str, size: int) -> tuple[Finding, ...]

Check a file about to be sent to SdI: its name (SdI 00001) and size (SdI 00003).

Parameters:

Name Type Description Default
name str

The file name, without a directory.

required
size int

The size of the transmitted file in bytes (the signed .xml.p7m or the .zip when that is what is sent).

required

Returns:

Type Description
Finding

An error finding per failed check, with the SdI code as rule_id, the file name as location and

...

source "sdi"; empty when both pass.

euinvoice.syntax.fatturapa.MAX_FILE_SIZE module-attribute

MAX_FILE_SIZE: Final = 5000000

The largest file accepted, in bytes: "5MB" read as 5 x 10^6 (see the module docstring).

euinvoice.syntax.fatturapa.EXTENSIONS module-attribute

EXTENSIONS: Final = ('.xml', '.xml.p7m', '.zip')

The extensions of Allegato A 1.9.1 ยง1.2.2.

Model

EN 16931 semantic model (IMPLEMENTATION_PLAN.md ยง4, D1, D3, D4).

:class:Invoice is the root (BG-0) of one immutable model for invoices and credit notes. Every field carries its EN 16931 business term or group id; :mod:euinvoice.model.bt_index maps each id to its path. docs/reference/bt-mapping.md lists every id with its cardinality, UBL and CII XPath and the sources they were taken from.

Amount module-attribute

Amount = t.Annotated[
    _Number, pydantic.AfterValidator(_check_amount)
]

EN 16931 data type Amount: at most two decimals (BR-DEC-*, UBL-DT-01), never rounded implicitly.

BT_INDEX module-attribute

BT_INDEX: Final[Mapping[str, str]] = types.MappingProxyType(
    build_index()
)

Every BT/BG id โ†’ its path in :class:Invoice, e.g. "BT-40" โ†’ "seller.postal_address.country_code".

Percentage module-attribute

Percentage = _Number

EN 16931 data type Percentage: precision is kept as given.

Rates are percentages, 19 rather than 0.19: BR-S-09 computes the VAT as Percent div 100.

Quantity module-attribute

Quantity = _Number

EN 16931 data type Quantity (BT-129, BT-149): precision is kept as given.

UnitPriceAmount module-attribute

UnitPriceAmount = _Number

EN 16931 data type Unit price amount (BT-146..148): precision is kept as given.

AdditionalSupportingDocument

Bases: EuInvoiceModel

ADDITIONAL SUPPORTING DOCUMENTS (BG-24).

attached_document class-attribute instance-attribute

attached_document: Annotated[
    BinaryObject | None, bt(BT - 125)
] = None

Attached document with its MIME code (BR-CL-24) and filename.

description class-attribute instance-attribute

description: Annotated[Text | None, bt(BT - 123)] = None

Supporting document description.

external_location class-attribute instance-attribute

external_location: Annotated[Text | None, bt(BT - 124)] = (
    None
)

External document location (a URL).

reference instance-attribute

reference: Annotated[NonBlankText, bt(BT - 122)]

Supporting document reference (BR-52).

BinaryObject

Bases: EuInvoiceModel

EN 16931 Binary object: the attached document BT-125 with its mime code and filename.

In JSON the content is base64 text. The content field is strict (bytes only), so a dict from model_dump(mode="json") that holds an attachment must be reloaded from JSON text with model_validate_json, which decodes the base64. The XRechnung 3.0.2 spec (ยง8.2, ยง11.2) gives mime code and filename cardinality 1, and UBL enforces both (UBL-DT-06/07, fatal), but the CEN CII rules and the CII XSD do not. The syntax-neutral model therefore keeps both optional, so a CEN-valid CII invoice always loads (D8); see "Decisions" (M3) in docs/reference/bt-mapping.md.

content instance-attribute

content: Annotated[bytes, Strict()]

The attached document's bytes.

filename class-attribute instance-attribute

filename: Text | None = None

The attached document's file name.

mime_code class-attribute instance-attribute

mime_code: Annotated[Text, AfterValidator(_mime)] | None = (
    None
)

MIME type, compared exactly as BR-CL-24 does (no whitespace stripping).

Buyer

Bases: EuInvoiceModel

BUYER (BG-7).

contact class-attribute instance-attribute

contact: Annotated[BuyerContact | None, bt(BG - 9)] = None

BUYER CONTACT.

electronic_address class-attribute instance-attribute

electronic_address: Annotated[
    Annotated[Identifier, EAS_SCHEME] | None, bt(BT - 49)
] = None

Buyer electronic address; its scheme is mandatory (BR-63) and a CEF EAS code (BR-CL-25).

identifier class-attribute instance-attribute

identifier: Annotated[
    Annotated[Identifier, ICD_SCHEME] | None, bt(BT - 46)
] = None

Buyer identifier, scheme from ISO 6523 ICD (BR-CL-10/11).

legal_registration_identifier class-attribute instance-attribute

legal_registration_identifier: Annotated[
    Annotated[Identifier, ICD_SCHEME] | None, bt(BT - 47)
] = None

Buyer legal registration identifier, scheme from ISO 6523 ICD (BR-CL-11).

name instance-attribute

name: Annotated[NonBlankText, bt(BT - 44)]

Buyer name (BR-07).

postal_address instance-attribute

postal_address: Annotated[BuyerPostalAddress, bt(BG - 8)]

BUYER POSTAL ADDRESS (BR-10).

trading_name class-attribute instance-attribute

trading_name: Annotated[Text | None, bt(BT - 45)] = None

Buyer trading name.

vat_identifier class-attribute instance-attribute

vat_identifier: Annotated[Text | None, bt(BT - 48)] = None

Buyer VAT identifier.

BuyerContact

Bases: EuInvoiceModel

BUYER CONTACT (BG-9).

contact_point class-attribute instance-attribute

contact_point: Annotated[Text | None, bt(BT - 56)] = None

Buyer contact point.

email class-attribute instance-attribute

email: Annotated[Text | None, bt(BT - 58)] = None

Buyer contact email address.

telephone class-attribute instance-attribute

telephone: Annotated[Text | None, bt(BT - 57)] = None

Buyer contact telephone number.

BuyerPostalAddress

Bases: EuInvoiceModel

BUYER POSTAL ADDRESS (BG-8). Required in the Buyer (BR-10).

address_line_1 class-attribute instance-attribute

address_line_1: Annotated[Text | None, bt(BT - 50)] = None

Buyer address line 1.

address_line_2 class-attribute instance-attribute

address_line_2: Annotated[Text | None, bt(BT - 51)] = None

Buyer address line 2.

address_line_3 class-attribute instance-attribute

address_line_3: Annotated[Text | None, bt(BT - 163)] = None

Buyer address line 3.

city class-attribute instance-attribute

city: Annotated[Text | None, bt(BT - 52)] = None

Buyer city.

country_code instance-attribute

country_code: Annotated[CountryCode, bt(BT - 55)]

Buyer country code (BR-11).

country_subdivision class-attribute instance-attribute

country_subdivision: Annotated[Text | None, bt(BT - 54)] = (
    None
)

Buyer country subdivision.

post_code class-attribute instance-attribute

post_code: Annotated[Text | None, bt(BT - 53)] = None

Buyer post code.

CreditTransfer

Bases: EuInvoiceModel

CREDIT TRANSFER (BG-17).

payment_account_identifier instance-attribute

payment_account_identifier: Annotated[
    NonBlankText, bt(BT - 84)
]

Payment account identifier, e.g. an IBAN (BR-50).

payment_account_name class-attribute instance-attribute

payment_account_name: Annotated[
    Text | None, bt(BT - 85)
] = None

Payment account name.

payment_service_provider_identifier class-attribute instance-attribute

payment_service_provider_identifier: Annotated[
    Text | None, bt(BT - 86)
] = None

Payment service provider identifier, e.g. a BIC.

DeliverToAddress

Bases: EuInvoiceModel

DELIVER TO ADDRESS (BG-15).

address_line_1 class-attribute instance-attribute

address_line_1: Annotated[Text | None, bt(BT - 75)] = None

Deliver to address line 1.

address_line_2 class-attribute instance-attribute

address_line_2: Annotated[Text | None, bt(BT - 76)] = None

Deliver to address line 2.

address_line_3 class-attribute instance-attribute

address_line_3: Annotated[Text | None, bt(BT - 165)] = None

Deliver to address line 3.

city class-attribute instance-attribute

city: Annotated[Text | None, bt(BT - 77)] = None

Deliver to city.

country_code instance-attribute

country_code: Annotated[CountryCode, bt(BT - 80)]

Deliver to country code (BR-57).

country_subdivision class-attribute instance-attribute

country_subdivision: Annotated[Text | None, bt(BT - 79)] = (
    None
)

Deliver to country subdivision.

post_code class-attribute instance-attribute

post_code: Annotated[Text | None, bt(BT - 78)] = None

Deliver to post code.

DeliveryInformation

Bases: EuInvoiceModel

DELIVERY INFORMATION (BG-13).

actual_delivery_date class-attribute instance-attribute

actual_delivery_date: Annotated[
    Date | None, bt(BT - 72)
] = None

Actual delivery date.

deliver_to_address class-attribute instance-attribute

deliver_to_address: Annotated[
    DeliverToAddress | None, bt(BG - 15)
] = None

DELIVER TO ADDRESS.

deliver_to_location_identifier class-attribute instance-attribute

deliver_to_location_identifier: Annotated[
    Annotated[Identifier, ICD_SCHEME] | None, bt(BT - 71)
] = None

Deliver to location identifier, scheme from ISO 6523 ICD (BR-CL-26).

deliver_to_party_name class-attribute instance-attribute

deliver_to_party_name: Annotated[
    Text | None, bt(BT - 70)
] = None

Deliver to party name.

invoicing_period class-attribute instance-attribute

invoicing_period: Annotated[
    InvoicingPeriod | None, bt(BG - 14)
] = None

INVOICING PERIOD.

DirectDebit

Bases: EuInvoiceModel

DIRECT DEBIT (BG-19). Every term is optional in EN 16931 (XRechnung adds BR-DE-29/30/31).

bank_assigned_creditor_identifier class-attribute instance-attribute

bank_assigned_creditor_identifier: Annotated[
    Text | None, bt(BT - 90)
] = None

Bank assigned creditor identifier.

debited_account_identifier class-attribute instance-attribute

debited_account_identifier: Annotated[
    Text | None, bt(BT - 91)
] = None

Debited account identifier.

mandate_reference_identifier class-attribute instance-attribute

mandate_reference_identifier: Annotated[
    Text | None, bt(BT - 89)
] = None

Mandate reference identifier.

DocumentLevelAllowance

Bases: _NeedsReason

DOCUMENT LEVEL ALLOWANCES (BG-20).

amount instance-attribute

amount: Annotated[Amount, bt(BT - 92)]

Document level allowance amount (BR-31).

base_amount class-attribute instance-attribute

base_amount: Annotated[Amount | None, bt(BT - 93)] = None

Document level allowance base amount.

percentage class-attribute instance-attribute

percentage: Annotated[Percentage | None, bt(BT - 94)] = None

Document level allowance percentage.

reason class-attribute instance-attribute

reason: Annotated[Text | None, bt(BT - 97)] = None

Document level allowance reason.

reason_code class-attribute instance-attribute

reason_code: Annotated[
    AllowanceReasonCode | None, bt(BT - 98)
] = None

Document level allowance reason code, UNTDID 5189 (BR-CL-19).

vat_category_code instance-attribute

vat_category_code: Annotated[VatCategoryCode, bt(BT - 95)]

Document level allowance VAT category code (BR-32).

vat_rate class-attribute instance-attribute

vat_rate: Annotated[Percentage | None, bt(BT - 96)] = None

Document level allowance VAT rate.

DocumentLevelCharge

Bases: _NeedsReason

DOCUMENT LEVEL CHARGES (BG-21).

amount instance-attribute

amount: Annotated[Amount, bt(BT - 99)]

Document level charge amount (BR-36).

base_amount class-attribute instance-attribute

base_amount: Annotated[Amount | None, bt(BT - 100)] = None

Document level charge base amount.

percentage class-attribute instance-attribute

percentage: Annotated[Percentage | None, bt(BT - 101)] = (
    None
)

Document level charge percentage.

reason class-attribute instance-attribute

reason: Annotated[Text | None, bt(BT - 104)] = None

Document level charge reason.

reason_code class-attribute instance-attribute

reason_code: Annotated[
    ChargeReasonCode | None, bt(BT - 105)
] = None

Document level charge reason code, UNTDID 7161 (BR-CL-20).

vat_category_code instance-attribute

vat_category_code: Annotated[VatCategoryCode, bt(BT - 102)]

Document level charge VAT category code (BR-37).

vat_rate class-attribute instance-attribute

vat_rate: Annotated[Percentage | None, bt(BT - 103)] = None

Document level charge VAT rate.

DocumentTotals

Bases: EuInvoiceModel

DOCUMENT TOTALS (BG-22). All amounts are in the invoice currency (BT-5) except BT-111.

amount_due instance-attribute

amount_due: Annotated[Amount, bt(BT - 115)]

Amount due for payment (BR-15).

paid_amount class-attribute instance-attribute

paid_amount: Annotated[Amount | None, bt(BT - 113)] = None

Paid amount.

rounding_amount class-attribute instance-attribute

rounding_amount: Annotated[Amount | None, bt(BT - 114)] = (
    None
)

Rounding amount.

sum_of_allowances class-attribute instance-attribute

sum_of_allowances: Annotated[
    Amount | None, bt(BT - 107)
] = None

Sum of allowances on document level.

sum_of_charges class-attribute instance-attribute

sum_of_charges: Annotated[Amount | None, bt(BT - 108)] = (
    None
)

Sum of charges on document level.

sum_of_line_net_amounts instance-attribute

sum_of_line_net_amounts: Annotated[Amount, bt(BT - 106)]

Sum of Invoice line net amount (BR-12).

total_vat class-attribute instance-attribute

total_vat: Annotated[Amount | None, bt(BT - 110)] = None

Invoice total VAT amount.

total_vat_in_accounting_currency class-attribute instance-attribute

total_vat_in_accounting_currency: Annotated[
    Amount | None, bt(BT - 111)
] = None

Invoice total VAT amount in accounting currency (BT-6); required with BT-6 (BR-53, checked by calc.check).

total_with_vat instance-attribute

total_with_vat: Annotated[Amount, bt(BT - 112)]

Invoice total amount with VAT (BR-14).

total_without_vat instance-attribute

total_without_vat: Annotated[Amount, bt(BT - 109)]

Invoice total amount without VAT (BR-13).

Identifier

Bases: EuInvoiceModel

EN 16931 Identifier with an optional scheme identifier.

Used for the business terms that may carry a scheme (XRechnung 3.0.2 spec chapter 11, rows "/Scheme identifier"): BT-18, BT-29, BT-30, BT-34, BT-46, BT-47, BT-49, BT-60, BT-61, BT-71, BT-128 and BT-157. Identifiers without a scheme are plain :data:Text. Which scheme list applies, and whether the scheme is mandatory, is checked on the field with :func:scheme.

scheme_id class-attribute instance-attribute

scheme_id: Text | None = None

The identification scheme (e.g. an ISO 6523 ICD or CEF EAS code). Stripped of XML whitespace when the field checks a scheme list.

value instance-attribute

value: Text

The identifier itself (content).

Invoice

Bases: _InvoiceBody

An EN 16931 invoice or credit note (the root, BG-0).

Every field carries its BT/BG id (see :mod:euinvoice.model.bt_index). Build an invoice with the constructor or model_validate: pydantic's model_copy(update=...) does not validate the updated values.

lines instance-attribute

lines: Annotated[
    tuple[InvoiceLine, ...],
    at_least_one(BR - 16),
    bt(BG - 25),
]

INVOICE LINE (1..n, BR-16).

totals instance-attribute

totals: Annotated[DocumentTotals, bt(BG - 22)]

DOCUMENT TOTALS. Required: the UBL 2.1 XSD has cac:LegalMonetaryTotal minOccurs=1 (UBL-Invoice-2.1.xsd), and in CII the CEN rule BR-CO-15 (fatal, invoice context) fails without it.

vat_breakdown instance-attribute

vat_breakdown: Annotated[
    tuple[VatBreakdown, ...],
    at_least_one(BR - CO - 18),
    bt(BG - 23),
]

VAT BREAKDOWN (1..n, BR-CO-18).

InvoiceDraft

Bases: _InvoiceBody

An invoice without its derived totals (BG-22) and VAT breakdown (BG-23), the input of calc.

It has every field of :class:Invoice except totals and vat_breakdown, with the same types and checks; its lines are :class:LineDraft (no BT-131).

lines instance-attribute

lines: Annotated[
    tuple[LineDraft, ...],
    at_least_one(BR - 16),
    bt(BG - 25),
]

INVOICE LINE drafts (1..n, BR-16).

InvoiceLine

Bases: _LineBody

INVOICE LINE (BG-25).

net_amount instance-attribute

net_amount: Annotated[Amount, bt(BT - 131)]

Invoice line net amount (BR-24).

InvoiceLineAllowance

Bases: _NeedsReason

INVOICE LINE ALLOWANCES (BG-27).

amount instance-attribute

amount: Annotated[Amount, bt(BT - 136)]

Invoice line allowance amount (BR-41).

base_amount class-attribute instance-attribute

base_amount: Annotated[Amount | None, bt(BT - 137)] = None

Invoice line allowance base amount.

percentage class-attribute instance-attribute

percentage: Annotated[Percentage | None, bt(BT - 138)] = (
    None
)

Invoice line allowance percentage.

reason class-attribute instance-attribute

reason: Annotated[Text | None, bt(BT - 139)] = None

Invoice line allowance reason.

reason_code class-attribute instance-attribute

reason_code: Annotated[
    AllowanceReasonCode | None, bt(BT - 140)
] = None

Invoice line allowance reason code, UNTDID 5189 (BR-CL-19).

InvoiceLineCharge

Bases: _NeedsReason

INVOICE LINE CHARGES (BG-28).

amount instance-attribute

amount: Annotated[Amount, bt(BT - 141)]

Invoice line charge amount (BR-43).

base_amount class-attribute instance-attribute

base_amount: Annotated[Amount | None, bt(BT - 142)] = None

Invoice line charge base amount.

percentage class-attribute instance-attribute

percentage: Annotated[Percentage | None, bt(BT - 143)] = (
    None
)

Invoice line charge percentage.

reason class-attribute instance-attribute

reason: Annotated[Text | None, bt(BT - 144)] = None

Invoice line charge reason.

reason_code class-attribute instance-attribute

reason_code: Annotated[
    ChargeReasonCode | None, bt(BT - 145)
] = None

Invoice line charge reason code, UNTDID 7161 (BR-CL-20).

InvoiceLinePeriod

Bases: EuInvoiceModel

INVOICE LINE PERIOD (BG-26).

BR-30 (end not before start) and BR-CO-20 (start or end present) are business rules, not checked by the model: :func:euinvoice.calc.check reports them, the official Schematron decides (D8).

end_date class-attribute instance-attribute

end_date: Annotated[Date | None, bt(BT - 135)] = None

Invoice line period end date.

start_date class-attribute instance-attribute

start_date: Annotated[Date | None, bt(BT - 134)] = None

Invoice line period start date.

InvoiceNote

Bases: EuInvoiceModel

INVOICE NOTE (BG-1).

The note text is optional here although the XRechnung and Peppol tables give it cardinality 1: no CEN rule requires it and the CII XSD allows a note with a subject code only (see "Open questions" in docs/reference/bt-mapping.md).

note class-attribute instance-attribute

note: Annotated[Text | None, bt(BT - 22)] = None

Invoice note.

subject_code class-attribute instance-attribute

subject_code: Annotated[
    TextSubjectCode | None, bt(BT - 21)
] = None

Invoice note subject code, UNTDID 4451 (BR-CL-08).

InvoicingPeriod

Bases: EuInvoiceModel

INVOICING PERIOD (BG-14).

BR-29 (end not before start) and BR-CO-19 (start or end present) are business rules, not checked by the model: :func:euinvoice.calc.check reports them, the official Schematron decides (D8).

end_date class-attribute instance-attribute

end_date: Annotated[Date | None, bt(BT - 74)] = None

Invoicing period end date.

start_date class-attribute instance-attribute

start_date: Annotated[Date | None, bt(BT - 73)] = None

Invoicing period start date.

ItemAttribute

Bases: EuInvoiceModel

ITEM ATTRIBUTES (BG-32).

name instance-attribute

name: Annotated[Text, bt(BT - 160)]

Item attribute name (BR-54).

value instance-attribute

value: Annotated[Text, bt(BT - 161)]

Item attribute value (BR-54).

ItemClassificationIdentifier

Bases: EuInvoiceModel

Item classification identifier (BT-158) with its mandatory scheme and optional scheme version.

The scheme identifier is required (BR-65) and is a UNTDID 7143 code (BR-CL-13). BT-158 is the only business term with a "Scheme version identifier" (XRechnung 3.0.2 spec ยง11.20).

scheme_id instance-attribute

scheme_id: Annotated[
    Text,
    AfterValidator(
        _code(
            UNTDID_7143_ITEM_CLASSIFICATION,
            "UNTDID 7143 item classification",
            BR - CL - 13,
        )
    ),
]

UNTDID 7143 list identifier (BR-65, BR-CL-13), stored stripped of XML whitespace.

scheme_version_id class-attribute instance-attribute

scheme_version_id: Text | None = None

Version of the classification scheme.

value instance-attribute

value: Text

The classification code (content).

ItemInformation

Bases: EuInvoiceModel

ITEM INFORMATION (BG-31).

attributes class-attribute instance-attribute

attributes: Annotated[
    tuple[ItemAttribute, ...], bt(BG - 32)
] = ()

ITEM ATTRIBUTES (0..n).

buyers_identifier class-attribute instance-attribute

buyers_identifier: Annotated[Text | None, bt(BT - 156)] = (
    None
)

Item Buyer's identifier.

classification_identifiers class-attribute instance-attribute

classification_identifiers: Annotated[
    tuple[ItemClassificationIdentifier, ...], bt(BT - 158)
] = ()

Item classification identifiers (0..n), each with a UNTDID 7143 scheme (BR-65, BR-CL-13).

country_of_origin class-attribute instance-attribute

country_of_origin: Annotated[
    CountryCode | None, bt(BT - 159)
] = None

Item country of origin (BR-CL-15).

description class-attribute instance-attribute

description: Annotated[Text | None, bt(BT - 154)] = None

Item description.

name instance-attribute

name: Annotated[NonBlankText, bt(BT - 153)]

Item name (BR-25).

sellers_identifier class-attribute instance-attribute

sellers_identifier: Annotated[Text | None, bt(BT - 155)] = (
    None
)

Item Seller's identifier.

standard_identifier class-attribute instance-attribute

standard_identifier: Annotated[
    Annotated[Identifier, STANDARD_ITEM_SCHEME] | None,
    bt(BT - 157),
] = None

Item standard identifier; its scheme is mandatory (BR-64) and an ISO 6523 ICD code (BR-CL-21).

LineDraft

Bases: _LineBody

An invoice line without its derived net amount (BT-131), the input of calc (#9).

It has every field of :class:InvoiceLine except net_amount, with the same types and checks.

LineVatInformation

Bases: EuInvoiceModel

LINE VAT INFORMATION (BG-30).

category_code instance-attribute

category_code: Annotated[VatCategoryCode, bt(BT - 151)]

Invoiced item VAT category code, UNTDID 5305 (BR-CO-04, BR-CL-18).

rate class-attribute instance-attribute

rate: Annotated[Percentage | None, bt(BT - 152)] = None

Invoiced item VAT rate as a percentage.

Payee

Bases: EuInvoiceModel

PAYEE (BG-10), used when the payee is not the Seller.

identifier class-attribute instance-attribute

identifier: Annotated[
    Annotated[Identifier, ICD_SCHEME] | None, bt(BT - 60)
] = None

Payee identifier, scheme from ISO 6523 ICD (BR-CL-10/11).

legal_registration_identifier class-attribute instance-attribute

legal_registration_identifier: Annotated[
    Annotated[Identifier, ICD_SCHEME] | None, bt(BT - 61)
] = None

Payee legal registration identifier, scheme from ISO 6523 ICD (BR-CL-11).

name instance-attribute

name: Annotated[Text, bt(BT - 59)]

Payee name (BR-17: the CEN rule tests that it exists).

PaymentCardInformation

Bases: EuInvoiceModel

PAYMENT CARD INFORMATION (BG-18).

The primary account number is optional here although the XRechnung and Peppol tables give it cardinality 1: no CEN rule requires it and the CII XSD makes it optional (see "Decisions" (M2) in docs/reference/bt-mapping.md). BR-51 (at most the first 6 and last 4 digits) is a warning in UBL and is left to the Schematron.

holder_name class-attribute instance-attribute

holder_name: Annotated[Text | None, bt(BT - 88)] = None

Payment card holder name.

primary_account_number class-attribute instance-attribute

primary_account_number: Annotated[
    Text | None, bt(BT - 87)
] = None

Payment card primary account number (masked, BR-51).

PaymentInstructions

Bases: EuInvoiceModel

PAYMENT INSTRUCTIONS (BG-16).

credit_transfers class-attribute instance-attribute

credit_transfers: Annotated[
    tuple[CreditTransfer, ...], bt(BG - 17)
] = ()

CREDIT TRANSFER (0..n).

direct_debit class-attribute instance-attribute

direct_debit: Annotated[DirectDebit | None, bt(BG - 19)] = (
    None
)

DIRECT DEBIT.

payment_card class-attribute instance-attribute

payment_card: Annotated[
    PaymentCardInformation | None, bt(BG - 18)
] = None

PAYMENT CARD INFORMATION.

payment_means_text class-attribute instance-attribute

payment_means_text: Annotated[Text | None, bt(BT - 82)] = (
    None
)

Payment means text.

payment_means_type_code instance-attribute

payment_means_type_code: Annotated[
    PaymentMeansCode, bt(BT - 81)
]

Payment means type code, UNTDID 4461 (BR-49, BR-CL-16).

remittance_information class-attribute instance-attribute

remittance_information: Annotated[
    Text | None, bt(BT - 83)
] = None

Remittance information.

PrecedingInvoiceReference

Bases: EuInvoiceModel

PRECEDING INVOICE REFERENCE (BG-3).

issue_date class-attribute instance-attribute

issue_date: Annotated[Date | None, bt(BT - 26)] = None

Preceding Invoice issue date.

reference instance-attribute

reference: Annotated[Text, bt(BT - 25)]

Preceding Invoice reference (BR-55).

PriceDetails

Bases: EuInvoiceModel

PRICE DETAILS (BG-29). Unit prices keep their precision (no BR-DEC limit, UBL-DT-01 exempts them).

base_quantity class-attribute instance-attribute

base_quantity: Annotated[Quantity | None, bt(BT - 149)] = (
    None
)

Item price base quantity.

base_quantity_unit_code class-attribute instance-attribute

base_quantity_unit_code: Annotated[
    UnitCode | None, bt(BT - 150)
] = None

Item price base quantity unit of measure code, UN/ECE Rec 20/21 (BR-CL-23).

item_gross_price class-attribute instance-attribute

item_gross_price: Annotated[
    Annotated[UnitPriceAmount, not_negative(BR - 28)]
    | None,
    bt(BT - 148),
] = None

Item gross price, not negative (BR-28).

item_net_price instance-attribute

item_net_price: Annotated[
    UnitPriceAmount, not_negative(BR - 27), bt(BT - 146)
]

Item net price (BR-26), not negative (BR-27).

item_price_discount class-attribute instance-attribute

item_price_discount: Annotated[
    UnitPriceAmount | None, bt(BT - 147)
] = None

Item price discount.

ProcessControl

Bases: EuInvoiceModel

PROCESS CONTROL (BG-2). Always present because BT-24 is mandatory (BR-01).

business_process_type class-attribute instance-attribute

business_process_type: Annotated[
    Text | None, bt(BT - 23)
] = None

Business process type (Peppol and XRechnung require it; EN 16931 does not).

specification_identifier instance-attribute

specification_identifier: Annotated[
    NonBlankText, bt(BT - 24)
]

Specification identifier, e.g. urn:cen.eu:en16931:2017 (BR-01).

Seller

Bases: EuInvoiceModel

SELLER (BG-4).

additional_legal_information: Annotated[
    Text | None, bt(BT - 33)
] = None

Seller additional legal information.

contact class-attribute instance-attribute

contact: Annotated[SellerContact | None, bt(BG - 6)] = None

SELLER CONTACT.

electronic_address class-attribute instance-attribute

electronic_address: Annotated[
    Annotated[Identifier, EAS_SCHEME] | None, bt(BT - 34)
] = None

Seller electronic address; its scheme is mandatory (BR-62) and a CEF EAS code (BR-CL-25).

identifiers class-attribute instance-attribute

identifiers: Annotated[
    tuple[Annotated[Identifier, ICD_SCHEME], ...],
    bt(BT - 29),
] = ()

Seller identifiers (0..n), scheme from ISO 6523 ICD (BR-CL-10/11).

legal_registration_identifier class-attribute instance-attribute

legal_registration_identifier: Annotated[
    Annotated[Identifier, ICD_SCHEME] | None, bt(BT - 30)
] = None

Seller legal registration identifier, scheme from ISO 6523 ICD (BR-CL-11).

name instance-attribute

name: Annotated[NonBlankText, bt(BT - 27)]

Seller name (BR-06).

postal_address instance-attribute

postal_address: Annotated[SellerPostalAddress, bt(BG - 5)]

SELLER POSTAL ADDRESS (BR-08).

tax_registration_identifier class-attribute instance-attribute

tax_registration_identifier: Annotated[
    Text | None, bt(BT - 32)
] = None

Seller tax registration identifier.

trading_name class-attribute instance-attribute

trading_name: Annotated[Text | None, bt(BT - 28)] = None

Seller trading name.

vat_identifier class-attribute instance-attribute

vat_identifier: Annotated[Text | None, bt(BT - 31)] = None

Seller VAT identifier.

SellerContact

Bases: EuInvoiceModel

SELLER CONTACT (BG-6).

contact_point class-attribute instance-attribute

contact_point: Annotated[Text | None, bt(BT - 41)] = None

Seller contact point.

email class-attribute instance-attribute

email: Annotated[Text | None, bt(BT - 43)] = None

Seller contact email address.

telephone class-attribute instance-attribute

telephone: Annotated[Text | None, bt(BT - 42)] = None

Seller contact telephone number.

SellerPostalAddress

Bases: EuInvoiceModel

SELLER POSTAL ADDRESS (BG-5). Required in the Seller (BR-08).

address_line_1 class-attribute instance-attribute

address_line_1: Annotated[Text | None, bt(BT - 35)] = None

Seller address line 1.

address_line_2 class-attribute instance-attribute

address_line_2: Annotated[Text | None, bt(BT - 36)] = None

Seller address line 2.

address_line_3 class-attribute instance-attribute

address_line_3: Annotated[Text | None, bt(BT - 162)] = None

Seller address line 3.

city class-attribute instance-attribute

city: Annotated[Text | None, bt(BT - 37)] = None

Seller city.

country_code instance-attribute

country_code: Annotated[CountryCode, bt(BT - 40)]

Seller country code (BR-09).

country_subdivision class-attribute instance-attribute

country_subdivision: Annotated[Text | None, bt(BT - 39)] = (
    None
)

Seller country subdivision.

post_code class-attribute instance-attribute

post_code: Annotated[Text | None, bt(BT - 38)] = None

Seller post code.

SellerTaxRepresentative

Bases: EuInvoiceModel

SELLER TAX REPRESENTATIVE PARTY (BG-11).

name instance-attribute

name: Annotated[NonBlankText, bt(BT - 62)]

Seller tax representative name (BR-18).

postal_address instance-attribute

postal_address: Annotated[
    TaxRepresentativePostalAddress, bt(BG - 12)
]

SELLER TAX REPRESENTATIVE POSTAL ADDRESS (BR-19).

vat_identifier instance-attribute

vat_identifier: Annotated[Text, bt(BT - 63)]

Seller tax representative VAT identifier (BR-56).

TaxRepresentativePostalAddress

Bases: EuInvoiceModel

SELLER TAX REPRESENTATIVE POSTAL ADDRESS (BG-12). Required in BG-11 (BR-19).

address_line_1 class-attribute instance-attribute

address_line_1: Annotated[Text | None, bt(BT - 64)] = None

Tax representative address line 1.

address_line_2 class-attribute instance-attribute

address_line_2: Annotated[Text | None, bt(BT - 65)] = None

Tax representative address line 2.

address_line_3 class-attribute instance-attribute

address_line_3: Annotated[Text | None, bt(BT - 164)] = None

Tax representative address line 3.

city class-attribute instance-attribute

city: Annotated[Text | None, bt(BT - 66)] = None

Tax representative city.

country_code instance-attribute

country_code: Annotated[CountryCode, bt(BT - 69)]

Tax representative country code (BR-20).

country_subdivision class-attribute instance-attribute

country_subdivision: Annotated[Text | None, bt(BT - 68)] = (
    None
)

Tax representative country subdivision.

post_code class-attribute instance-attribute

post_code: Annotated[Text | None, bt(BT - 67)] = None

Tax representative post code.

VatBreakdown

Bases: EuInvoiceModel

VAT BREAKDOWN (BG-23).

category_code instance-attribute

category_code: Annotated[VatCategoryCode, bt(BT - 118)]

VAT category code, UNTDID 5305 (BR-47, BR-CL-17).

exemption_reason class-attribute instance-attribute

exemption_reason: Annotated[Text | None, bt(BT - 120)] = (
    None
)

VAT exemption reason text.

exemption_reason_code class-attribute instance-attribute

exemption_reason_code: Annotated[
    VatExemptionReasonCode | None, bt(BT - 121)
] = None

VAT exemption reason code, CEF VATEX (BR-CL-22), stored upper-cased.

rate class-attribute instance-attribute

rate: Annotated[Percentage | None, bt(BT - 119)] = None

VAT category rate as a percentage, 19 for 19 % (BR-48, BR-S-09).

tax_amount instance-attribute

tax_amount: Annotated[Amount, bt(BT - 117)]

VAT category tax amount (BR-46).

taxable_amount instance-attribute

taxable_amount: Annotated[Amount, bt(BT - 116)]

VAT category taxable amount (BR-45).

bt_id

bt_id(model: type[BaseModel], field: str) -> str | None

Return the BT/BG id declared with :func:bt on model.field, or None if it has none.

Parameters:

Name Type Description Default
model type[BaseModel]

The model class.

required
field str

The field name.

required

Returns:

Type Description
str | None

The id, e.g. "BT-109", or None.

Raises:

Type Description
KeyError

model has no field called field.

extension_paths

extension_paths(
    model: BaseModel, prefix: str = ""
) -> tuple[str, ...]

Return the path of every extension hook that is set in model, at any depth.

Writers of a syntax with no place for an extension use it to refuse the invoice instead of dropping the extension silently (D3 as amended, plan ยง1); :func:without_extensions clears exactly these hooks.

Parameters:

Name Type Description Default
model BaseModel

An instance, e.g. an :class:~euinvoice.model.Invoice.

required
prefix str

Prepended to every path (used by the recursion).

''

Returns:

Type Description
tuple[str, ...]

The paths in field order, with indexes for repeated groups, e.g. ("it", "lines[1].it").

id_of

id_of(path: str) -> str

Return the BT/BG id of a model path.

Parameters:

Name Type Description Default
path str

A path as in :data:BT_INDEX, e.g. "seller.name".

required

Returns:

Type Description
str

E.g. "BT-27".

Raises:

Type Description
KeyError

No business term or group lives at path.

path_of

path_of(ident: str) -> str

Return the model path of a BT/BG id.

Parameters:

Name Type Description Default
ident str

E.g. "BT-153".

required

Returns:

Type Description
str

E.g. "lines[].item.name" ("" for BG-0).

Raises:

Type Description
KeyError

ident is not an EN 16931 id of the model.

quantize_amount

quantize_amount(value: Decimal) -> Decimal

Round a monetary amount to two decimals, half up (ties away from zero), per D11.

The result always has exactly two fraction digits (7 becomes 7.00). It does not depend on the active decimal context, so large values never raise or lose integer digits.

Parameters:

Name Type Description Default
value Decimal

A finite Decimal.

required

Returns:

Type Description
Decimal

value rounded with ROUND_HALF_UP to two decimals.

Raises:

Type Description
ModelError

value is a float or not finite.

without_extensions

without_extensions(model: M) -> M

Return model with every set extension hook cleared (the hooks :func:extension_paths lists).

A read FatturaPA invoice sets Invoice.it (and often InvoiceLine.it), so the UBL and CII writers refuse it (D3 as amended: an extension is never dropped silently). This drops them on request, e.g. before to_xml; call :func:extension_paths first to see what goes. It walks the model as :func:extension_paths does and rebuilds only the models on the way to a set hook.

Parameters:

Name Type Description Default
model M

An instance, e.g. an :class:~euinvoice.model.Invoice.

required

Returns:

Type Description
M

The model without extensions; model itself when none is set.

euinvoice.model.codes.DocumentType

Bases: StrEnum

Common invoice type codes (BT-3), a named subset of :data:UNTDID_1001_DOCUMENT_TYPE.

BT-3 accepts every code of :data:UNTDID_1001_DOCUMENT_TYPE; these members only give names to the codes whose meaning the pinned artifacts state: XRechnung Schematron 2.6.0 rule BR-DE-17 (schematron/ubl/XRechnung-UBL-validation.sch) and Peppol BIS 3.0.21 rule DE-R-017 list them as "326 (Partial invoice), 380 (Commercial invoice), 384 (Corrected invoice), 389 (Self-billed invoice) und 381 (Credit note), 875 (Partial construction invoice), 876 (Partial final construction invoice), 877 (Final construction invoice)".

Which UBL root a code needs is given by :data:UNTDID_1001_INVOICE_TYPE_UBL and :data:UNTDID_1001_CREDIT_NOTE_TYPE_UBL (CEN BR-CL-01, UBL): of these members only CREDIT_NOTE is a CreditNote code.

euinvoice.model.codes.VatCategory

Bases: StrEnum

VAT category code (BT-95, BT-102, BT-118, BT-151): UNTDID 5305 as restricted by EN 16931.

Equals :data:UNTDID_5305_VAT_CATEGORY (BR-CL-17 / BR-CL-18). The meanings are the names the CEN rules use for each code: the BR-<x>-01 asserts of schematron/UBL/EN16931-UBL-model.sch (validation-1.3.16) test cbc:ID = '<code>' and name the category in their message.

STANDARD_RATED class-attribute instance-attribute

STANDARD_RATED = 'S'

"Standard rated" (BR-S-01).

ZERO_RATED class-attribute instance-attribute

ZERO_RATED = 'Z'

"Zero rated" (BR-Z-01).

EXEMPT class-attribute instance-attribute

EXEMPT = 'E'

"Exempt from VAT" (BR-E-01).

REVERSE_CHARGE class-attribute instance-attribute

REVERSE_CHARGE = 'AE'

"Reverse charge", "VAT reverse charge" (BR-AE-01).

INTRA_COMMUNITY_SUPPLY class-attribute instance-attribute

INTRA_COMMUNITY_SUPPLY = 'K'

"Intra-community supply" (BR-IC-01).

EXPORT_OUTSIDE_EU class-attribute instance-attribute

EXPORT_OUTSIDE_EU = 'G'

"Export outside the EU" (BR-G-01).

NOT_SUBJECT_TO_VAT class-attribute instance-attribute

NOT_SUBJECT_TO_VAT = 'O'

"Not subject to VAT" (BR-O-01).

IGIC class-attribute instance-attribute

IGIC = 'L'

"IGIC" (BR-AF-01).

IPSI class-attribute instance-attribute

IPSI = 'M'

"IPSI" (BR-AG-01).

SPLIT_PAYMENT class-attribute instance-attribute

SPLIT_PAYMENT = 'B'

"Split payment", domestic Italian invoices only (BR-B-01).

Errors

Exception hierarchy of euinvoice.

Validation results are data, not exceptions (IMPLEMENTATION_PLAN.md D9): rule failures end up in a ValidationReport. These exceptions are raised only for misuse, malformed input or missing artifacts.

EuInvoiceError

Bases: Exception

Base class of every exception raised by euinvoice.

ModelError

Bases: EuInvoiceError, ValueError

An invoice model was built with invalid or inconsistent values.

Subclasses ValueError so that Pydantic validators raising it surface as validation errors.

ParseError

ParseError(message: str, *, location: str | None = None)

Bases: EuInvoiceError

Input could not be parsed safely (malformed XML, forbidden DOCTYPE, unexpected structure).

Attributes:

Name Type Description
location

Where the problem was found (an XPath or line:column), if known.

Create the error.

Parameters:

Name Type Description Default
message str

Human-readable description.

required
location str | None

Where the problem was found (an XPath or line:column), if known.

None

PreflightError

PreflightError(
    profile_id: str,
    syntax: str,
    findings: tuple[Finding, ...],
)

Bases: EuInvoiceError

to_xml refused an invoice: its pre-flight or calculation checks found fatal / error problems.

The checks (the profile's preflight and calc.check for the target syntax) run on the invoice after Profile.prepare; a blocking finding means the official rules would reject the written document, so nothing is written.

For FatturaPA, written without a profile, the check is the FatturaPA pre-flight (euinvoice.syntax.fatturapa.preflight), and profile_id and syntax are both "fatturapa".

Attributes:

Name Type Description
profile_id

The id of the profile the invoice was to be written under ("fatturapa" for FatturaPA).

syntax

The target syntax ("ubl", "cii" or "fatturapa").

findings

Every finding of both checks (warnings included), each naming its rule id and model location.

Create the error.

Parameters:

Name Type Description Default
profile_id str

The profile's id.

required
syntax str

The target syntax.

required
findings tuple[Finding, ...]

The pre-flight and calculation findings.

required

Raises:

Type Description
ValueError

No finding is fatal or error, so there is nothing to refuse.

UnsupportedDocumentError

Bases: EuInvoiceError

The document's syntax, type or profile (BT-24) is not supported.

ArtifactsNotAvailableError

ArtifactsNotAvailableError(
    message: str, *, fallback_profile_id: str | None = None
)

Bases: EuInvoiceError

The official validation artifacts a run needs are not available.

Either they are missing from the local cache, and the message names the command that fetches them (python -m euinvoice artifacts fetch), or the profile needs a rule set that is not pinned yet (the Factur-X / ZUGFeRD Schematron, issue #42), and the message says so; no fetch command can provide it.

Attributes:

Name Type Description
reason

The message without the fallback hint.

fallback_profile_id

The id of the profile that runs only the EN 16931 core rules on the same document ("en16931") when the refusal is for an unpinned rule set, else None. The message then ends with a hint naming it in Python spelling; the CLI builds its --profile hint from this attribute instead (#108).

Create the error.

Parameters:

Name Type Description Default
message str

Human-readable description, without the fallback hint.

required
fallback_profile_id str | None

The id of the EN 16931 core profile to suggest instead, if any.

None

ArtifactIntegrityError

Bases: EuInvoiceError

An official artifact cannot be trusted or used.

Raised when a download does not match the sha256 pinned in the manifest, a redirect leaves https, an archive member is unsafe (absolute path, .., symlink), a manifest entry is malformed, or a rule file cannot be compiled.

PdfError

Bases: EuInvoiceError

A PDF cannot be used for Factur-X / ZUGFeRD embedding or extraction.

Artifacts

euinvoice.validation.artifacts.fetch

fetch(
    names: Iterable[SourceName] | None = None,
    *,
    root: Path | None = None,
    sources: Mapping[str, Source] | None = None,
) -> dict[str, Path]

Download, verify and extract sources that are not in the cache yet.

Idempotent: sources whose cache entry is complete are skipped without any network access. A source with precompile entries pulls in the SchXslt source first.

Parameters:

Name Type Description Default
names Iterable[SourceName] | None

Manifest keys to fetch; all sources when None.

None
root Path | None

Cache root; defaults to :func:cache_dir.

None
sources Mapping[str, Source] | None

Manifest; defaults to the packaged manifest.

None

Returns:

Type Description
dict[str, Path]

The extracted directory per fetched (or already cached) source name.

Raises:

Type Description
TypeError

names is a single string instead of an iterable of names.

KeyError

An unknown source name was requested.

ArtifactIntegrityError

A download does not match its pinned sha256, a redirect leaves https, an archive member is unsafe (absolute path, .. component, symlink) or a Schematron cannot be compiled.

ArtifactsNotAvailableError

A download failed (network / HTTP error), or precompiling needs saxonche (the [validate] extra).

euinvoice.validation.artifacts.cache_dir

cache_dir() -> Path

Return the artifact cache root: $EUINVOICE_ARTIFACTS_DIR, else ~/.cache/euinvoice.