Send-readiness & fixing violations

Before TaxLens lets you export or issue an invoice, it runs a pre-check: a single gate that verifies every mandatory field is present and the totals reconcile. If something's missing, you get an HTTP 422 with the exact list of what to fix.

One gate, used everywhere

The pre-check lives in app/services/einvoice_validation.py and is the single source of truth for send-readiness. The same code runs in three places, so they can never disagree:

  • GET …/document returns a validation block (is_valid + violations[]) so you can preview readiness.
  • GET …/document/ubl blocks export with HTTP 422 if the document isn't valid.
  • POST …/issue runs it one final time before burning a fiscal number.
Detail
The pre-check is a focused EN 16931 / PEPPOL gate — the mandatory Business Terms an invoice can't be valid without, plus per-rate reconciliation — not the full PEPPOL Schematron. A green pre-check is strong assurance, but final Access Point validation is part of the deferred transmission work. See What's covered vs deferred.

What it checks

The mandatory Business Terms fall into three buckets — seller, buyer, and document:

FieldTypeDescription
Seller namerequiredBT-27The legal entity issuing the invoice. Comes from the legal issuer.
Seller countryrequiredBT-40The issuer's registered country.
Seller VAT / tax registrationrequiredBT-31At least one tax registration on the issuer. Add it under Legal issuers.
Seller PEPPOL endpointrequiredBT-34Endpoint id and EAS scheme — how the issuer is addressed on PEPPOL.
Buyer namerequiredBT-44Set via buyer_name on the booking or PATCH invoice-details.
Buyer postal countryrequiredBT-55The country in the buyer's address.
Buyer VAT idBT-48Required only when the invoice carries a reverse-charge (AE) line.
CurrencyrequiredBT-5The document currency.
VAT lines + per-rate subtotalsrequiredBG-23At least one VAT line; every standard-rated line carries a rate > 0; each VAT category has a matching tax subtotal that reconciles.

B2B (business buyer) also requires

When the buyer is a business (customer_type='business'), an Access Point needs a few extra Business Terms before it will route the invoice. These rules apply only to business buyers — a consumer (B2C) invoice that clears the table above is already send-ready:

FieldTypeDescription
Buyer reference / PO referencerequiredBT-10A routing reference for the buyer (PEPPOL-EN16931-R003). Set buyer_reference on the booking or via PATCH invoice-details.
Buyer electronic address + EAS schemerequiredBT-49The PEPPOL endpoint the network delivers to (PEPPOL-EN16931-R010) — an endpoint id plus a 4-digit EAS/ICD scheme code. Set buyer_endpoint_id and its scheme.
Payment terms / due daterequiredBT-20When the payable amount is positive, the invoice needs payment terms (BT-20) or a due date (BT-9) — BR-CO-25.

What a blocked export looks like

When the document isn't send-ready, the UBL endpoint refuses with HTTP 422 and lists each violation by Business Term, so you know exactly what to set:

422 — not send-ready
{
  "detail": {
    "message": "Booking is not PEPPOL send-ready. Resolve the listed violations and try again.",
    "violations": [
      { "code": "BT-31", "severity": "error", "field": "BT-31",
        "message": "Seller VAT/tax registration is required." },
      { "code": "BT-44", "severity": "error", "field": "BT-44",
        "message": "Buyer name is required. Pass buyer_name when creating the booking." },
      { "code": "BT-55", "severity": "error", "field": "BT-55",
        "message": "Buyer postal address country code is required. Pass buyer_address.country_code when creating the booking." }
    ]
  }
}
Warnings don't block
Occupancy or tourist tax on the booking produces a non-blocking warning (LODGING-OCCUPANCY-TAX-EXCLUDED) — those levies are excluded from the e-invoice by design, not an error. See VAT-only invoices.

How to clear the violations

  1. 1
    Configure the seller (once per org)
    In Settings, create a legal issuer with its legal name and country, add at least one tax registration (BT-31), and set its PEPPOL endpoint id + scheme (BT-34). This clears every seller-side violation for all future invoices.
  2. 2
    Attach the issuer to the booking
    Set legal_issuer_id when you create the booking, or later via PATCH invoice-details. Properties can default it (default_legal_issuer_id) so bookings inherit the right seller.
  3. 3
    Capture the buyer
    Provide buyer_name and a buyer_address with at least a country. The dashboard's "Edit invoice details" form does this without re-running the tax engine.
  4. 4
    Re-check
    Call GET …/document again and confirm validation.is_valid is true. Now the UBL export and issuance succeed.
The PATCH that usually clears it
// PATCH /v1/bookings/4821/invoice-details
{
  "legal_issuer_id": 7,
  "buyer_name": "Northwind Travel GmbH",
  "buyer_address": { "city": "Berlin", "country_code": "DE" }
}

Next

With a green pre-check you're ready to run the full invoice lifecycle. For the field-by-field API reference, see E-invoicing (API), and for common stumbles, Troubleshooting.