This page covers the e-invoicing-specific architectural decisions and flows. It assumes familiarity with the general CDC, RPC, and Kafka patterns used across the codebase.
Communication Overview
E-invoicing adds a third async channel (Invopop webhooks) alongside the existing CDC and RPC patterns:
Mechanism
E-Invoicing Use
CDC
Syncs invoice data from LVDAM into Abacus. Triggers Raise, Issue, and lifecycle commands
RPC
Abacus calls LVDAM to transition document state (issuing, reject, issue)
Webhooks
Invopop delivers tax authority results via Kafka. Triggers MarkAsAccepted or MarkAsRejected
E-Invoicing RPC Endpoints
These are the LVDAM endpoints specific to e-invoicing (standard issue is reused):
RPC call
LVDAM endpoint
When
issuing_document/1
PUT /invoicing/rpc/documents/:uuid/issuing
Issue command for e-invoicing-eligible document
mark_as_rejected/1
PUT /invoicing/rpc/documents/:uuid/reject
After MarkAsRejectedByTaxAuthority
mark_as_issued/1
(stub, not yet wired)
After MarkAsAcceptedByTaxAuthority
Invopop Webhook Pipeline
The webhook flow adds an e-invoicing-specific Kafka consumer and event handler chain:
E-invoicing enabled: calls issuing_document/1 RPC → LVDAM transitions to issuing, reserves invoice number → best-effort sync_document to refresh CDC data
Not enabled: calls issue_document/1 (standard flow, skips all e-invoicing logic)
Phase 3: Submit to Tax Authority
Document entity sets issuing_at, emits Issuing event with send_to_tax_authority?: true
E-invoicing is configured per workspace. The WorkspaceConfiguration entity holds an e_invoicing_provider field (Invopop struct with API token, workflow ID, and supplier/company ID). Credentials are encrypted at rest via Shared.Infrastructure.Vault.
A denormalized WorkspaceEInvoicingConfiguration read model provides configured?/1 for fast eligibility checks during the Issue command. Handle nil workspace IDs gracefully — documents may lack them during CDC backfill.
No PMS UI exists yet (PLU-4223). Configuration is manual via IEx console in staging.
Eventual Consistency
CDC events are asynchronous, so Abacus may act on stale data when the Issue command fires. The DocumentHandler makes a best-effort sync_document call after the issuing_document RPC succeeds, refetching the document from LVDAM. If the sync fails, it logs a warning but does not block the flow.
A more comprehensive version of sync_document in the CDC handler was rolled back because it blocked the Kafka queue on bad data. Re-enabling requires the Kafka inbox pattern (PLU-4309), which moves messages into Oban jobs for independent processing.
Suggested Reading Order
For a new engineer, this order traces the end-to-end flow:
Issue decision:document_handler.ex (before_fetch_entity clauses for Issue)
Submit to Invopop:on_issuing/send_to_tax_authority.ex → infrastructure/e_invoicing.ex → e_invoicing_providers/invopop.ex