Browse the docs

DocsReference

Mapping: Ekwo, EN 16931 and the FEC

Each Ekwo column that has a counterpart in a standard, lined up against it. It lets an integrator write a connector in a day instead of reading the schema and the standards in parallel for a week.

  • EN 16931 references are business term identifiers (BT-) and business groups (BG-) of the European semantic standard for electronic invoicing, as used by Peppol BIS Billing 3.0 and Factur-X.
  • FEC refers to the eighteen columns of the French fichier des écritures comptables, arrêté du 29 juillet 2013 (art. A. 47 A-1 du LPF). The column is produced by fec_lines().

An empty cell means there is no counterpart, which is itself information.

Company and period

Ekwo EN 16931 FEC
companies.name BT-27 seller name
companies.legal_name BT-27
companies.vat_number BT-31 seller VAT identifier
companies.registration_number BT-30 seller legal registration
companies.country BT-40 seller country
companies.peppol_scheme, .peppol_identifier BT-34 seller electronic address, with its scheme (BT-34-1)
companies.address_line1, .postal_code, .city BG-5 seller postal address
companies.currency_code BT-5 invoice currency Idevise (when it differs)

Chart of accounts

Ekwo EN 16931 FEC
accounts.code CompteNum
accounts.name CompteLib

Journals

Ekwo EN 16931 FEC
journals.code JournalCode
journals.name JournalLib

Contacts

Ekwo EN 16931 FEC
contacts.name BT-44 buyer name / BT-27 seller name CompAuxLib
contacts.vat_number BT-48 buyer VAT identifier
contacts.registration_number BT-47 buyer legal registration
contacts.auxiliary_code CompAuxNum
contacts.email BT-43 buyer contact email
contacts.address_line1, .postal_code, .city, .country BG-8 buyer postal address
contacts.payment_terms_days BT-20 payment terms
contacts.iban BT-84 payment account identifier
contacts.peppol_scheme, .peppol_identifier BT-49 buyer electronic address, with its scheme (BT-49-1)

Taxes

Ekwo EN 16931 FEC
taxes.amount BT-119 category rate, through the snapshot the line keeps
taxes.vat_category BT-118 category code, through the snapshot the line keeps
taxes.exemption_code BT-121 exemption reason code
taxes.legal_reference (the article the tax rests on, written for a reviewer; BT-120 is document_tax_summary.exemption_reason)

Entries and ledger lines

Ekwo EN 16931 FEC
entries.number EcritureNum
entries.entry_date EcritureDate
entries.reference PieceRef
entries.description EcritureLib (fallback)
entries.posted_at ValidDate
entries.journal_id JournalCode, JournalLib
entry_lines.account_id CompteNum, CompteLib
entry_lines.debit, .credit Debit, Credit
entry_lines.name EcritureLib
entry_lines.contact_id CompAuxNum, CompAuxLib
entry_lines.date_maturity BT-9 due date
entry_lines.matching_number EcritureLet
entry_lines.amount_currency, .currency_code Montantdevise, Idevise

Documents

Ekwo EN 16931 FEC
documents BG-2 process control
documents.doc_type BT-3 invoice type code
documents.number BT-1 invoice number PieceRef
documents.supplier_reference BT-1 on the supplier's side
documents.contact_id BG-7 buyer
documents.document_date BT-2 issue date PieceDate
documents.accounting_date EcritureDate
documents.due_date BT-9 due date
documents.currency_code BT-5 Idevise
documents.buyer_reference BT-10 buyer reference
documents.project_reference BT-11 project reference
documents.contract_reference BT-12 contract reference
documents.order_reference BT-13 purchase order reference
documents.delivery_date BT-72 actual delivery date
documents.tax_point_date BT-7 value added tax point date
documents.delivery_address_line1, .delivery_postal_code, .delivery_city, .delivery_country BG-15 deliver-to address
documents.payment_terms BT-20 payment terms
documents.payment_means_code BT-81 payment means type code
documents.payment_reference BT-83 remittance information
documents.payee_iban BT-84 payment account identifier
documents.note BT-22 invoice note
documents.amount_untaxed BT-109 total without VAT
documents.amount_tax BT-110 total VAT
documents.amount_total BT-112 total with VAT
documents.amount_paid BT-113 paid amount
documents.amount_residual BT-115 amount due for payment
documents.reversed_document_id BT-25 preceding invoice reference

Products

Ekwo has one table for a product and no variant of it, because variants are a commerce feature and a variant with no distinct price, account or tax is a row that carries nothing. Nothing here is stock: there is no qty_available, no valuation and no move, and there is deliberately no counterpart to them.

Ekwo EN 16931 FEC
products BG-31 item information
products.code BT-155 item seller identifier
products.name BT-153 item name
products.description BT-154 item description
products.unit_code BT-130 unit of measure code
products.sale_price BT-146 item net price
products.sale_account_id CompteNum
products.purchase_account_id CompteNum

Document lines

Ekwo EN 16931 FEC
document_lines BG-25 invoice line
document_lines.sequence BT-126 line identifier
document_lines.name BT-153 item name
document_lines.description BT-154 item description
document_lines.quantity BT-129 invoiced quantity
document_lines.unit_code BT-130 unit of measure code
document_lines.unit_price BT-146 item net price
document_lines.discount_percent BT-138 document level allowance percentage
document_lines.amount_untaxed BT-131 line net amount
document_lines.account_id CompteNum
document_lines.vat_category BT-151 line VAT category code
document_lines.vat_rate BT-152 line VAT rate
document_tax_summary (view) BG-23 VAT breakdown, one row per tax: two taxes of one category and rate are one group of the standard
document_tax_summary.vat_category BT-118 category code, as the lines carry it
document_tax_summary.tax_rate BT-119 category rate, as the lines carry it
document_tax_summary.base_amount BT-116 category taxable amount
document_tax_summary.tax_charged BT-117 category tax amount: what the buyer is charged, nothing where the tax is self-assessed
document_tax_summary.tax_amount (the tax the return reports, which a reverse charge has and its invoice does not)
document_tax_summary.exemption_code BT-121 exemption reason code
document_tax_summary.exemption_reason BT-120 exemption reason text
document_header (view) BG-2 to BG-19, everything above the lines
document_header.tax_point_date BT-7 value added tax point date
document_header.delivery_address_line1, .delivery_postal_code, .delivery_city, .delivery_country BG-15 deliver-to address (BT-75, BT-78, BT-77, BT-80)
document_header.seller_peppol_scheme, .seller_peppol_identifier BT-34-1, BT-34
document_header.buyer_peppol_scheme, .buyer_peppol_identifier BT-49-1, BT-49
document_line_items (view) BG-25 with BG-31
document_line_items.seller_item_identifier BT-155 item seller identifier

To the Peppol invoice

@ekwo-ai/peppol-ubl reads one row of document_header, the rows of document_line_items and the rows of document_tax_summary, under the column names above, and nothing else: no join to a table and no option is needed for a posted sale to come out with no rule broken. Dates are selected ::text. What the views do not say yet, and the brick therefore reports rather than writes: the net price of a line keyed with its tax in it (BT-146, BR-26), document and line allowances and charges, and a reason for a supply outside the scope of the tax where the pack gives it no code (BR-O-10).

To the Factur-X invoice object

@ekwo-ai/factur-x takes a plain invoice object and emits EN 16931 CII XML. It lives in this repository under packages/formats/, but @ekwo-ai/core does not depend on it: a format is something you add on top of the ledger, not something the ledger needs. The mapping is therefore written here and asserted in tests/mcp/products.test.ts without the library.

Read document_line_items and build one line per row:

Factur-X line field Ekwo EN 16931
name item_name BT-153
description item_description BT-154
sellerItemId seller_item_identifier — the product code, null on a free-text line BT-155
quantity quantity BT-129
unitCode unit_code BT-130
unitPrice unit_price BT-146
vatRate vat_rate BT-152
vatCategory vat_category BT-151

The header comes from documents and companies as the Documents table above says: numbernumber (BT-1), document_dateissueDate (BT-2), due_datedueDate (BT-9), buyer_referencebuyerReference (BT-10), order_referenceorderReference (BT-13), payee_ibanpayment.iban (BT-84).

Payments, matching, bank

Ekwo EN 16931 FEC
reconciliations.matched_at DateLet
reconciliations.matching_number EcritureLet

Analytics and attachments

Ekwo EN 16931 FEC
attachments BG-24 additional supporting documents

Deliberately absent

What Why not
A line type telling invoice lines from ledger lines Documents and entries are two tables, so a line never has to say which one it belongs to.
A product catalogue with variants A catalogue is an application concern, not an accounting one.
Analytic columns created at runtime A schema that alters itself cannot be migrated or secured.
Per-company defaults stored as JSON A plain column per company default is simpler and joinable.
An XML-RPC or JSON-RPC endpoint Supabase exposes the schema as REST with an OpenAPI description.

Producing a FEC

fec_lines(company_id, from, to) returns the eighteen columns: the opening balances of the financial year first, then its movements in chronological order. generateFec() in @ekwo-ai/fec turns them into the file, and checkFec() applies the file-level rules — mandatory fields, one side per line, each entry balancing — before you hand it over.

The opening lines are computed from the ledger and never posted, one per balance-sheet account, plus one for the result of a year that has not been closed yet. They appear only when the period is exactly a financial year. The sources below are those of a movement line; an opening line takes its journal from country_defaults.opening_journal_code, its account and amount from the cumulative balance of the day before the year opens, and its label from country_defaults.opening_entry_label.

FEC column Source
JournalCode, JournalLib journals.code, journals.name
EcritureNum entries.number
EcritureDate entries.entry_date
CompteNum, CompteLib accounts.code, accounts.name
CompAuxNum, CompAuxLib contacts.auxiliary_code, contacts.name
PieceRef documents.number, else documents.supplier_reference, else entries.reference
PieceDate documents.document_date, else entries.entry_date
EcritureLib entry_lines.name, else entries.description, else accounts.name
Debit, Credit entry_lines.debit, entry_lines.credit
EcritureLet entry_lines.matching_number
DateLet latest reconciliations.matched_at on that line
ValidDate entries.posted_at
Montantdevise, Idevise entry_lines.amount_currency, entry_lines.currency_code, when the currency differs from the company's

Rendered from docs/mapping.md, in the repository, when the site was built. Read or change it on GitHub