Invoicing
B2B invoice creation, lifecycle management, PDF generation, and dispute handling between accommodation providers and tour operators.
StableOverview
Ananas GDS includes a built-in B2B invoicing system for accommodation providers to bill tour operators and other partners. Invoices track line items, tax, due dates, and go through a structured lifecycle from draft to payment. PDF generation and email delivery are supported.
Invoice Model
The
Invoice
model represents a single billing document issued by an accommodation provider to a partner.
| Field | Type | Description |
|---|---|---|
id |
UUID | Unique identifier. Read-only. |
invoice_number |
string |
Auto-generated invoice number. Format:
INV-YYYY-NNNN
.
|
owner |
User FK | The user who issued the invoice (accommodation provider). |
partner |
User FK, nullable | The recipient of the invoice — a connected tour operator. |
issue_date |
date | The date the invoice was issued. |
due_date |
date | The payment due date. |
currency |
string |
3-character ISO currency code. e.g.
EUR
.
|
status |
string | Current lifecycle state. See Status Lifecycle . |
subtotal |
decimal | Sum of all line item totals before tax. |
tax_rate |
decimal |
Tax rate as a percentage, e.g.
19.00
for 19%.
|
tax_amount |
decimal |
Computed tax amount (
subtotal × tax_rate / 100
). Read-only.
|
total_amount |
decimal |
Computed total (
subtotal + tax_amount
). Read-only.
|
notes |
text | Optional free-text notes printed on the invoice. |
template |
FK InvoiceTemplate, nullable | Visual template applied when generating the PDF. |
created_at |
datetime | Record creation timestamp. Read-only. |
updated_at |
datetime | Last modification timestamp. Read-only. |
Invoice Line Items
Each invoice has one or more line items describing the services or goods being billed.
| Field | Type | Description |
|---|---|---|
invoice |
FK Invoice | Parent invoice this line item belongs to. |
description |
string | Description of the service or item. |
quantity |
decimal | Number of units. |
unit_price |
decimal | Price per unit. |
line_total |
decimal |
Computed total for this line (
quantity × unit_price
). Read-only.
|
Line items are sent as a nested array in invoice create and update requests. Example request body:
{
"partner": 42,
"issue_date": "2026-05-01",
"due_date": "2026-05-31",
"currency": "EUR",
"tax_rate": "19.00",
"notes": "Season 2026 distribution fee",
"line_items": [
{
"description": "Fact sheet distribution — Q2 2026",
"quantity": "1.00",
"unit_price": "450.00"
},
{
"description": "Photo library sync",
"quantity": "3.00",
"unit_price": "50.00"
}
]
}
Status Lifecycle
Invoices follow a strict state machine. The table below lists each status, its meaning, and the allowed transitions.
| Status | Description | Allowed Transitions |
|---|---|---|
DRAFT |
Invoice created but not yet sent. Fully editable. | SENT, CANCELLED |
SENT |
Sent to the recipient. No further edits permitted. | VIEWED, CANCELLED |
VIEWED |
Recipient has opened the invoice. | DISPUTED, CLEARED, CANCELLED |
DISPUTED |
Recipient has raised a dispute on the invoice. | CLEARED, CANCELLED |
CLEARED |
Dispute resolved. Awaiting payment. | PAID, CANCELLED |
PAID |
Invoice marked as paid. Terminal state. | — |
CANCELLED |
Invoice cancelled. Terminal state. | — |
OVERDUE |
System-set automatically when
due_date
passes and status is SENT, VIEWED, or DISPUTED.
|
PAID, CANCELLED |
Sent invoices are locked
Once an invoice is SENT, its line items and financial data cannot be edited. Create a new invoice or cancel and reissue.
API Endpoints
Invoice CRUD
/api/invoicing/invoices/
🔐 Auth required
List all invoices for the authenticated user. Supports
?status=
filter.
/api/invoicing/invoices/
🔐 Auth required
Create a new invoice. The invoice is created with status
DRAFT
. Include line items as a nested array in the request body.
/api/invoicing/invoices/{id}/
🔐 Auth required
Retrieve invoice details including line items and the full activity log.
/api/invoicing/invoices/{id}/
🔐 Auth required
Update an invoice. Only permitted when status is
DRAFT
.
/api/invoicing/invoices/{id}/
🔐 Auth required
Delete an invoice. Only permitted when status is
DRAFT
.
Invoice Actions
/api/invoicing/invoices/{id}/send/
🔐 Auth required
Transition the invoice from
DRAFT
to
SENT
. Sends a notification email to the partner.
/api/invoicing/invoices/{id}/dispute/
🔐 Auth required
Recipient marks the invoice as
DISPUTED
. Allowed from
VIEWED
or
SENT
.
/api/invoicing/invoices/{id}/mark-paid/
🔐 Auth required
Owner marks the invoice as
PAID
.
/api/invoicing/invoices/{id}/cancel/
🔐 Auth required
Cancel the invoice. Allowed from any non-terminal state.
PDF & Delivery
/api/invoicing/invoices/{id}/pdf/
🔐 Auth required
Download the invoice as a PDF. The PDF is rendered using the invoice's assigned template, or the default template if none is set.
/api/invoicing/invoices/{id}/send-reminder/
🔐 Auth required
Send a payment reminder email to the partner. See Reminders for details.
Templates
/api/invoicing/templates/
🔐 Auth required
List all invoice templates for the authenticated user.
/api/invoicing/templates/
🔐 Auth required
Create a new invoice template.
/api/invoicing/templates/{id}/
🔐 Auth required
Retrieve a specific template.
/api/invoicing/templates/{id}/
🔐 Auth required
Update a template.
/api/invoicing/templates/{id}/
🔐 Auth required
Delete a template.
Invoice Templates
Templates customise the visual appearance and default content of generated PDFs. A template can be assigned to any invoice; if none is assigned, the platform default is used.
| Field | Description |
|---|---|
name |
Human-readable template name. |
header_text |
Text printed at the top of the invoice, above line items. |
footer_text |
Text printed at the bottom of the invoice — payment terms, bank details, legal notices. |
payment_terms |
Short payment terms statement, e.g. Payment due within 30 days . |
bank_details |
Bank account information for wire transfer payments. |
primary_color |
Hex color code used for PDF accents and headings, e.g.
#c9a84c
.
|
Activity Log
Every state change and action on an invoice is recorded in the
InvoiceActivity
log. The log is immutable — entries cannot be edited or deleted.
| Field | Description |
|---|---|
user |
The user who performed the action. |
action |
Action code:
created
,
sent
,
viewed
,
disputed
,
cleared
,
paid
,
cancelled
,
reminder_sent
.
|
detail |
Optional text note attached to the action. |
timestamp |
When the action occurred. |
The full activity log is returned as a nested array in the invoice detail response (
GET /api/invoicing/invoices/{id}/
), ordered oldest to newest.
Reminders
Payment reminders can be sent for invoices with status
SENT
,
VIEWED
, or
OVERDUE
. Each reminder dispatched is recorded in an
InvoiceReminder
record.
| Field | Description |
|---|---|
sent_at |
Timestamp when the reminder was dispatched. |
reminder_type |
Reminder sequence:
first
,
second
, or
final
.
|
recipient_email |
Email address the reminder was sent to. |
Suggested reminder cadence
Set up a regular cadence: first reminder 3 days after due date, second at 7 days, final at 14 days.
EU E-Invoicing (EN 16931)
To meet the German B2B e-invoicing mandate (receive since Jan 2025; issue by 2027 for >€800k turnover, all businesses by 2028), any invoice can be exported as a structured, standards-compliant e-invoice. This lives in an isolated module that reads invoices read-only -- it does not change how invoices are created, stored, or rendered.
| Format | What it is | Availability |
|---|---|---|
| XRechnung (UBL 2.1 XML) | Pure structured XML, EN 16931 / XRechnung 3.0 compliant. The default. | Always -- built with the Python standard library, no third-party dependency, so it runs on any host. |
| ZUGFeRD / Factur-X (PDF + embedded XML) | Human-readable PDF carrying the same structured data -- what most German SME partners expect. |
When the optional
drafthorse
library is installed. If it is not, the system
automatically falls back to XRechnung
so export never fails.
|
Endpoint:
GET /api/einvoice/{invoice_id}/?format=xrechnung
(or
zugferd
) -- returns the file as a download. Only the issuing or receiving company may fetch it. The legal fields EN 16931 requires but the company profile lacks (VAT-ID, IBAN/BIC, ISO country, electronic address)
live on a dedicated
EInvoiceProfile
; where blank they fall back to the company record.
Accounting CSV export:
GET /api/einvoice/export/csv/?from=YYYY-MM-DD&to=YYYY-MM-DD
returns the account's invoices (issued and received, optionally date-filtered) as a semicolon-delimited, BOM-prefixed CSV that opens cleanly in German Excel and imports into Xero / QuickBooks / most
bookkeeping tools. The DATEV-specific EXTF
Buchungsstapel
export is a separate follow-on -- it additionally needs your chart-of-accounts numbers, so it is not emitted generically.
Inbound e-invoice receiver (Ananas as the hub):
POST /api/einvoice/import/
accepts a structured EN 16931 e-invoice as the raw XML body -- in
either
syntax any compliant software emits: UBL (XRechnung) or UN/CEFACT CII (ZUGFeRD/Factur-X). Ananas parses it (invoice number, dates, seller/buyer with VAT-IDs, line items, tax and totals), stores it as an
InboundEInvoice
record (which also satisfies the legal requirement to be able to
receive
structured e-invoices), infers direction from your VAT-ID (issued vs received), and soft-links it to the matching platform invoice when one exists. This makes Ananas the server that holds the data, not just a
client -- any external finance or partner system can push e-invoices in through one standards-based interface. (Auto-creating a full platform invoice from an off-platform counterparty is a reviewed
follow-on.)
E-invoice inbox:
GET /api/einvoice/inbox/
lists the account's received e-invoices (newest first; optional
?status=received|linked|error
and
?direction=issued|received
filters), and
GET /api/einvoice/inbox/{id}/
returns the full detail including line items and the raw XML. In the app both live under
Dev Tools → Connectivity → E-Invoicing
, alongside a browser upload for the import endpoint and the accounting CSV download.
Validation is required before real use
Generated e-invoices must be validated against the official KoSIT / EN 16931 validator and reviewed by your accountant before being sent as legal documents. The generator targets the mandatory fields; it is not a substitute for that compliance check. DATEV (EXTF CSV) export and Peppol delivery are planned follow-ons.