La facturation
Étape 2 — La facturation
Surprise pour les novices : les factures ne sont pas créées quand l'acheteur paie, mais dès l'assignation. Elles servent justement à demander le paiement.
Les 8 types de documents (docTypes)
Le système ne connaît qu'un seul objet « Invoice », décliné en 8 types :
| docType | C'est quoi ? | Qui facture qui ? |
|---|---|---|
SALES | La facture de vente : le prix du lot (+ frais selon le mode). | Vendeur → Acheteur (courtage) ou Shop → Acheteur (commissaire-priseur) |
BUYER_FEES | La facture des frais acheteur, séparée (uniquement en facturation double). | Shop → Acheteur |
COMMISSION | La facture de commission : ce que le vendeur doit à la plateforme pour le service. | Shop → Vendeur |
INVOICE_SLIP | Le bordereau : document où le vendeur « facture » le shop pour les lots vendus (utilisé quand le shop encaisse pour le compte du vendeur, ex. commissaire-priseur). | Vendeur → Shop |
CREDIT_NOTE | L'avoir : l'inverse d'une facture, en montants négatifs (annulation totale ou partielle). | Inverse de la facture parente |
SERVICE_SHOP / SERVICE_SELLER / SERVICE_BUYER | Factures de prestations annexes (frais d'annulation, abonnements, services…). | Selon la prestation |
⭐ Facturation simple vs facturation double
C'est LA distinction à maîtriser. Elle est déterminée par l'invoiceMode du contrat du vendeur.
INTERMEDIATION_INVOICE_SIMPLE — une seule facture est envoyée à l'acheteur.
📄 Facture SALES : Vendeur → Acheteur
├── ligne(s) : prix d'adjudication du/des lot(s)
└── ligne(s) : frais acheteur (intégrés dans la même facture, marqués par un feesType)
Pour l'acheteur : un seul document, un seul montant à payer, une seule référence de paiement.
Pour le vendeur : la facture est émise en son nom (autofacturation, voir plus bas), il recevra séparément la facture de COMMISSION de la plateforme.
INTERMEDIATION_INVOICE_DOUBLE — deux factures distinctes sont envoyées à l'acheteur.
📄 Facture SALES : Vendeur → Acheteur
└── uniquement le prix d'adjudication du/des lot(s)
📄 Facture BUYER_FEES : Shop → Acheteur
└── uniquement les frais acheteur
Pour l'acheteur : deux documents, mais une expérience de paiement unifiée — la facture BUYER_FEES hérite de la référence de paiement et du code de confirmation de la facture SALES, pour qu'un seul virement règle les deux.
Pourquoi faire ça ? Séparation juridique et fiscale : la vente (entre vendeur et acheteur) et le service de la plateforme (entre shop et acheteur) sont deux opérations distinctes, chacune avec son émetteur, son régime de TVA et sa comptabilité.
preserveFeesVat du calcul. Les deux factures restent liées par le même invoiceGroup et leurs statuts sont synchronisés (quand la SALES passe PAID/CONFIRMED, la BUYER_FEES suit — syncBuyerFeesWithSaleInvoice).AUCTIONEER — le commissaire-priseur (le shop) vend au nom du vendeur et facture lui-même l'acheteur.
📄 Facture SALES : Shop (commissaire-priseur) → Acheteur
└── prix d'adjudication + frais, tout compris
📄 Bordereau INVOICE_SLIP : Vendeur → Shop
└── le vendeur « vend » au shop les lots adjugés (document établi par la plateforme)
📄 Facture COMMISSION : Shop → Vendeur (sa rémunération)
L'acheteur paie le commissaire-priseur (sur un wallet MangoPay dédié auctioneerWalletId), qui reverse ensuite le vendeur après déduction de sa commission. PURCHASE_RESELL (achat-revente) suit la même mécanique de facture unique émise par le shop.
L'autofacturation (selfBilling) et le mandat de facturation
En courtage (simple comme double), la facture SALES est juridiquement une facture du vendeur… mais c'est la plateforme qui l'émet à sa place. C'est l'autofacturation (mécanisme légal où un tiers, ici la plateforme, émet la facture au nom et pour le compte du vendeur, sur la base d'un mandat de facturation signé au contrat), autorisée par le paramètre de contrat INVOICING_MANDATE. Le PDF porte la mention correspondante, et la facture reçoit en plus un numéro de facture vendeur dans sa propre séquence (voir numérotation).
book-keeper/src/services/invoices/createFromSeance.js — intermediationInvoice() crée toujours la SALES (createSalesInvoice, organisations associées = seller, buyer) puis, uniquement si invoiceMode === INTERMEDIATION_INVOICE_DOUBLE, crée la BUYER_FEES (createBuyerFeesInvoice, organisations = owner, buyer, + organisation CREATOR du shop en rôle SELLER) en héritant paymentReference et confirmationCode de la SALES. auctioneerInvoice() crée la facture unique du commissaire-priseur. Le flag selfBilling est posé dans book-keeper/src/services/invoices/invoiceReportBuilder.js : docType === SALES && mode ∈ {SIMPLE, DOUBLE}.Comment les factures sont créées : createInvoicesFromSeance
À l'assignation (circuit V2), le book-keeper reçoit la séance et la liste d'items à facturer, puis :
- Validation : la séance existe, les items sont facturables ;
- Groupage : les items sont regroupés par contrat + vendeur + acheteur + bon de commande + code service (
groupItemsByContractSellerBuyer). Chaque groupe = une facture. Un acheteur qui remporte 3 lots du même vendeur reçoit donc une seule facture à 3 lignes ; - Création selon le mode :
intermediationInvoice()(simple/double) ouauctioneerInvoice(); - Lignes & totaux : construction des lignes depuis les items (
buildInvoiceLinesFromItem), calcul des totaux et de la TVA (computeTotalPrices) ; - Références : numéro de facture + référence de paiement générés ;
- Enregistrement en base (PostgreSQL), statut initial
TO_VALIDATE, puis validation (actionINVOICE) → statutINVOICEDet envoi à l'acheteur.
book-keeper/src/controllers/2_0/invoices/fromSeance.js. Le payInMode / payOutMode / contractShortId de l'item sont copiés dans invoice.indexedMeta. Le bon de commande (purchaseOrderNumber) et le code service (serviceCode) sont portés sur la facture pour permettre le groupage des commissions plus tard. Les factures d'un même couple vendeur/acheteur partagent un invoiceGroup (« {seller}-{buyer}-{timestamp} »).La facture de commission et le bordereau
Une fois la vente confirmée vient la rémunération de la plateforme :
- La facture
COMMISSIONest créée à partir des factures SALES (actionCOMMISSIONdu workflow, automatique siCOMMISSION_CREATE_AUTO). Elle est découpée par bon de commande / code service : un groupe = une facture de commission, pour que chaque commission reste rattachée à un seul ensemble de ventes ; - Le
INVOICE_SLIP(bordereau) est créé pour les circuits où le shop encaisse pour le compte du vendeur : il matérialise « le vendeur facture le shop » (associations : SELLER = le vendeur, BUYER = l'organisation créatrice du shop).
SELLER_COMMISSION), éventuels frais d'application, etc. Les taux peuvent dépendre de paliers calculés sur le montant adjugé de l'item, de la séance entière, de l'année ou du contrat (ITEM_BID_VALUE, SEANCE_BID_VALUE, YEAR_BID_VALUE, CONTRACT_BID_VALUE…). C'est pourquoi la création de commission va rechercher les montants agrégés d'enchères (getBiddingAmounts) avant de calculer.book-keeper/src/controllers/2_0/invoices/commission.js (création, groupage par BC/CS), book-keeper/src/controllers/2_0/invoices/slipInvoice.js (bordereau), book-keeper/src/services/invoices/buildCommissionInvoiceLines.js et buildSlipInvoiceLines.js (lignes), jobs/src/helpers/invoiceWorkflow.js → performCommission() (déclencheur ; en catégorie SELF_SERVICE la commission est déjà créée au performConfirm, en DIRECT_OFFER elle est créée ici).La numérotation des factures
Trois systèmes de numéros cohabitent — c'est souvent source de confusion :
| Numéro | Format | Exemple | À quoi il sert |
|---|---|---|---|
| Référence plateforme | YYYY-CNNNNNN (année + lettre catégorie + séquence sur 6 chiffres) | 2026-F000123 | Numéro officiel du document émis par la plateforme. Lettres : F = SALES & BUYER_FEES, C = COMMISSION, R = CREDIT_NOTE (avoir), B = INVOICE_SLIP (bordereau), A = factures de service. La séquence repart à 1 chaque année. |
| Numéro facture vendeur | PPYYNNNNNN (préfixe + année sur 2 chiffres + séquence sur 6 chiffres) | FC26000045 NC26000012 | Numéro attribué dans la séquence du vendeur pour les factures émises en son nom (autofacturation). FC = facture, NC = note de crédit. Ajouté lors des actions CONFIRM/INVOICE/PAY selon le cas (addSellerInvoiceNumber). |
| Référence de paiement | XXX/XXXX/XXXXX (communication structurée belge, 12 chiffres dont 2 de contrôle modulo 97) | 202/6000/12320 (pour 2026-F000123) | La référence que l'acheteur met dans son virement. Elle permet le rapprochement automatique paiement ↔ facture. Calculée à partir de la référence plateforme. |
book-keeper/src/services/invoices/references.js — buildInvoiceReference() (regex de contrôle /^\d{4}-[A-Z]\d{6}$/, lecture de la dernière facture en base pour incrémenter), buildSellerInvoiceReference(), buildPaymentReference() (base = année + 6 derniers chiffres du n°, contrôle = base % 97, 0 → 97).Le cycle de vie d'une facture
Comme les items, chaque facture suit un workflow d'actions. Voici le parcours d'une facture SALES :
flowchart LR
a["TO_VALIDATE"] --> b["INVOICED"]
b --> c["PAID"]
c --> d["CONFIRMED"]
d --> e["COMMISSIONED"]
e --> f["SUBMITTED / EXECUTED"]
f --> g["SETTLED"]
| Statut | Signification |
|---|---|
| TO_VALIDATE | La facture vient d'être créée, c'est encore un brouillon interne (non visible). |
| INVOICED (action INVOICE) | La facture est officialisée : numéro définitif, PDF généré, email envoyé à l'acheteur. Elle attend le paiement. |
| PAID (action PAY) | Le paiement a été reçu et rapproché. Déclenché par le book-keeper quand un mouvement bancaire couvre la facture. |
| CONFIRMED (action CONFIRM) | La vente est entérinée (souvent après vérification que l'enlèvement peut avoir lieu). En self-service, c'est ici que la facture de commission est créée. Le numéro vendeur est ajouté et la facture définitive est envoyée. |
| COMMISSIONED (action COMMISSION) | La commission est générée/déclenchée ; sur MangoPay c'est le moment où la répartition de l'argent (transfers) démarre. |
| SUBMITTED → EXECUTED (actions SUBMIT/EXECUTE) | Le reversement vendeur est préparé puis exécuté (virement SEPA via fichier Isabel, ou payout MangoPay). |
| SETTLED (action SETTLE) | L'argent est bien arrivé, la facture est définitivement soldée et réconciliée. |
Sorties possibles : CANCELED (annulée avant paiement), NOT_PAID (impayé constaté), REIMBURSED (remboursée après paiement).
jobs/src/helpers/invoiceWorkflow.js (les performXxx) et jobs/src/helpers/invoiceWorkFlowActions.js (matrice statut × docType → actions permises). Actions : INVOICE, BULK_INVOICE, CANCEL, PAY, NO_PAY, CONFIRM, BULK_CONFIRM, REIMBURSE, COMMISSION, SUBMIT, EXECUTE, SETTLE. Les factures COMMISSION et CREDIT_NOTE ont un workflow raccourci : TO_VALIDATE → INVOICED → EXECUTED/SUBMITTED → SETTLED.Le PDF et l'envoi
Chaque facture validée génère un PDF à partir de templates Handlebars, choisi selon le contexte : facture classique (invoice-v2-1f / invoice-v2-2f multi-taux), invoice-proforma (avant officialisation), invoice-intra (intracommunautaire), invoice-export, invoice-commission, invoice-receipt (reçu). Le PDF inclut les adresses et horaires d'enlèvement (pickupLines), les coordonnées bancaires du shop et les conditions de paiement. Il est stocké (service documents) et envoyé par email à l'acheteur ou au vendeur.
document-template/src/templates/pdfs/*.hbs ; assemblage des données : book-keeper/src/services/invoices/invoiceReportBuilder.js ; envoi : documents/src/helpers/documentNotification.js.