From a11ec634ea8366fad4f71102afe86d78764be8c5 Mon Sep 17 00:00:00 2001 From: Gabriel Radureau Date: Fri, 14 Aug 2026 17:55:30 +0200 Subject: [PATCH] =?UTF-8?q?feat(erp):=20manifeste=20d'exercice=20=E2=80=94?= =?UTF-8?q?=20d=C3=A9crire=20les=20faits=20pour=20pouvoir=20les=20rejouer?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Un exercice OUVERT n'a d'engagement envers personne : aucun compte déposé, aucune liasse, aucun FEC remis. Tant qu'il en va ainsi, une erreur de MODÉLISATION se corrige mieux en rejouant depuis les pièces qu'en la contournant — la piste d'audit fiable demande que l'écriture reflète sa pièce, et rejouer resserre ce lien là où une rustine le distend. extractExercise.ts ne fait que LIRE. Il produit la description ordonnée des faits : tiers, factures client et fournisseur avec leurs lignes et règlements, et l'intégralité des mouvements bancaires. Les mouvements bancaires sont le RÉFÉRENT : Qonto et Wise sont hors de Dolibarr, on ne peut pas les réécrire. Le test d'acceptation d'un rejeu est donc qu'ils soient retrouvés au centime et à la date. La ligne de partage entre ce qui se rejoue et ce qui est figé n'est pas technique mais juridique : est figé tout ce qu'un tiers détient — facture client ENVOYÉE, dépôt au greffe, déclaration transmise — et tout exercice clos. Les factures client sont donc extraites avec delivered:true : un rejeu les reproduit à l'identique, il ne les recalcule pas. Les exclusions sont DÉCLARATIVES ET MOTIVÉES, jamais codées en dur : un auditeur doit lire pourquoi une référence manque sans avoir à le deviner. Deux pièces sont écartées — FAC001-CL00001, dont la référence était malformée par un défaut de masque, et l'avoir qui l'annulait. L'opérateur a établi qu'elle n'a jamais quitté la société : KissMetrics a été facturé via Wise en février, et le document qu'il détient référence FAC001-CL0001001. L'avoir ne documentait donc qu'un faux pas interne. La séquence FAC reste continue (FAC001 à FAC008), la réémission portant elle aussi FAC001 — rien à renuméroter. Deux constats que l'extraction met au jour : - 20 des 37 mouvements bancaires portent un libellé Dolibarr générique (« SupplierInvoicePayment », « CustomerInvoicePayment ») qui traverse le grand livre tel quel et y est illisible pour un repreneur comme pour un inspecteur ; - FAF2025001 (OVH, 7,30 EUR) est datée du 24/10/2025, trois mois avant l'immatriculation, et NE FIGURE PAS à l'annexe 1 des statuts qui recense les actes accomplis pour le compte de la société en formation. Une dépense antérieure à l'existence de la société ne peut être supportée par elle que si elle est reprise par cet état ou ratifiée ensuite. Co-Authored-By: Claude Opus 5 (1M context) --- .../adc-011-tenue-comptable-et-discretion.md | 14 +- fleet/profile/exercices/2026/manifest.json | 1117 +++++++++++++++++ test/extractExercise.ts | 245 ++++ 3 files changed, 1372 insertions(+), 4 deletions(-) create mode 100644 fleet/profile/exercices/2026/manifest.json create mode 100644 test/extractExercise.ts diff --git a/fleet/profile/decisions/adc-011-tenue-comptable-et-discretion.md b/fleet/profile/decisions/adc-011-tenue-comptable-et-discretion.md index 216c0a5..bfeee6c 100644 --- a/fleet/profile/decisions/adc-011-tenue-comptable-et-discretion.md +++ b/fleet/profile/decisions/adc-011-tenue-comptable-et-discretion.md @@ -40,10 +40,16 @@ Le document le dit expressément, et les pièces sont produites sur demande. La règle est donc *pouvoir répondre simplement en cas d'enquête*, jamais *anticiper l'enquête en publiant*. -**Limite qui inverse la règle :** un flux financier entre la société et son -dirigeant se déclare, même non demandé, dès lors que le document porte -précisément sur l'absence d'un tel flux. Taire ce qui contredirait le document -n'est pas de la discrétion, c'est une omission. +**Limite qui inverse la règle — et elle est étroite :** ne se déclare, même non +demandé, que ce qui rendrait le document **faux** si on le taisait. Le critère +est la véracité de ce qui est affirmé, pas l'exhaustivité. + +Une attestation d'absence de rémunération n'a pas à mentionner une indemnité +d'occupation : celle-ci n'est pas une rémunération, elle ne contredit donc rien. +La mentionner, c'est publier une relation financière que personne n'a demandée — +et un lecteur pressé y verrait une rémunération là où il n'y en a pas. Une +première rédaction l'avait fait, en s'abritant derrière la transparence ; c'était +une erreur d'analyse : la discrétion et la sincérité ne s'opposaient pas ici. ### 2. Permanence des méthodes diff --git a/fleet/profile/exercices/2026/manifest.json b/fleet/profile/exercices/2026/manifest.json new file mode 100644 index 0000000..6fe5e16 --- /dev/null +++ b/fleet/profile/exercices/2026/manifest.json @@ -0,0 +1,1117 @@ +{ + "meta": { + "exercice": 2026, + "source": "https://erp.arcodange.lab", + "extracted_at": "2026-08-14T15:54:36.583Z", + "replayable": true, + "note": "Manifeste d'exercice — description des faits, non des écritures comptables. Les factures client sont marquées delivered:true : elles existent chez le client et se reproduisent à l'identique, jamais ne se recalculent." + }, + "exclusions": [ + { + "ref": "FAC001-CL00001", + "date": "2026-01-29", + "total_ttc": "510.00000000", + "motif": "Référence malformée (CL00001 au lieu de CL0001001) — défaut de configuration du masque. JAMAIS REMISE AU CLIENT : vérifié le 2026-08-14, aucune trace d'envoi ; KissMetrics a été facturé via Wise en février 2026, et le document qu'il détient référence FAC001-CL0001001. La séquence FAC reste continue (FAC001 à FAC008), la réémission portant elle aussi FAC001." + }, + { + "ref": "AVC001-CL0001001", + "date": "2026-01-29", + "total_ttc": "-510.00000000", + "motif": "Avoir émis le 24/02/2026 pour annuler FAC001-CL00001. Sans objet dès lors que la facture annulée n'a jamais quitté la société : il ne documentait qu'un faux pas interne, non un événement commercial. La séquence AVC devient vide." + } + ], + "anomalies": { + "libelles_generiques": 20, + "detail": "lignes bancaires portant un libellé Dolibarr générique — à remplacer au rejeu" + }, + "tiers": [ + { + "id": "1", + "name": "KissMetrics", + "code_client": "CL0001", + "code_fournisseur": null, + "client": "1", + "fournisseur": "0", + "country_id": "11", + "tva_intra": "", + "code_compta": null, + "code_compta_fournisseur": null + }, + { + "id": "2", + "name": "Wise Europe SA", + "code_client": null, + "code_fournisseur": "FO0001", + "client": "0", + "fournisseur": "1", + "country_id": "2", + "tva_intra": "", + "code_compta": null, + "code_compta_fournisseur": "401WISEEU" + }, + { + "id": "3", + "name": "Greffe du tribunal de commerce Évry", + "code_client": null, + "code_fournisseur": "FO0002", + "client": "0", + "fournisseur": "1", + "country_id": "1", + "tva_intra": "FR09326467370", + "code_compta": null, + "code_compta_fournisseur": "401GREFFE" + }, + { + "id": "4", + "name": "YOLAW", + "code_client": null, + "code_fournisseur": "FO0003", + "client": "0", + "fournisseur": "1", + "country_id": "1", + "tva_intra": "FR18753892926", + "code_compta": null, + "code_compta_fournisseur": "401YOLAW" + }, + { + "id": "5", + "name": "Medialex", + "code_client": null, + "code_fournisseur": "FO0004", + "client": "0", + "fournisseur": "1", + "country_id": "1", + "tva_intra": "", + "code_compta": null, + "code_compta_fournisseur": "401MEDIAL" + }, + { + "id": "6", + "name": "Qonto", + "code_client": null, + "code_fournisseur": "FO0005", + "client": "0", + "fournisseur": "1", + "country_id": "1", + "tva_intra": "FR10819489626", + "code_compta": null, + "code_compta_fournisseur": "401QONTO" + }, + { + "id": "7", + "name": "OVH", + "code_client": null, + "code_fournisseur": "FO0006", + "client": "0", + "fournisseur": "1", + "country_id": "1", + "tva_intra": "FR22424761419", + "code_compta": null, + "code_compta_fournisseur": "401OVH" + }, + { + "id": "8", + "name": "La Poste", + "code_client": null, + "code_fournisseur": "FO0007", + "client": "0", + "fournisseur": "1", + "country_id": "1", + "tva_intra": "FR39356000000", + "code_compta": null, + "code_compta_fournisseur": "401LAPOST" + }, + { + "id": "9", + "name": "Infogreffe", + "code_client": null, + "code_fournisseur": "FO0008", + "client": "0", + "fournisseur": "1", + "country_id": "1", + "tva_intra": "FR69338885718", + "code_compta": null, + "code_compta_fournisseur": "401INFOGR" + }, + { + "id": "10", + "name": "Darnis Operations", + "code_client": null, + "code_fournisseur": "FO0009", + "client": "0", + "fournisseur": "1", + "country_id": "1", + "tva_intra": "FR51920267531", + "code_compta": null, + "code_compta_fournisseur": "401DARNIS" + }, + { + "id": "11", + "name": "Anthropic Ireland Limited", + "code_client": null, + "code_fournisseur": "FO0010", + "client": "0", + "fournisseur": "1", + "country_id": null, + "tva_intra": "", + "code_compta": null, + "code_compta_fournisseur": "401ANTHRO" + }, + { + "id": "12", + "name": "Mistral AI SAS", + "code_client": null, + "code_fournisseur": "FO0011", + "client": "0", + "fournisseur": "1", + "country_id": null, + "tva_intra": "", + "code_compta": null, + "code_compta_fournisseur": "401MISTRA" + } + ], + "invoices": [ + { + "ref": "FAC001-CL0001001", + "socid": "1", + "date": "2026-01-29", + "date_lim_reglement": "2026-02-08", + "type": "0", + "total_ht": "510.00000000", + "total_tva": "0.00000000", + "total_ttc": "510.00000000", + "multicurrency_code": "EUR", + "statut": "2", + "paye": "1", + "delivered": true, + "lines": [ + { + "desc": "TVA non applicable – Article 259-1 du CGI – Prestation de services localisée hors de France (USA)", + "subprice": "510.00000000", + "qty": "1", + "tva_tx": "0.0000", + "product_type": "1" + } + ], + "payments": [ + { + "amount": "510.00000000", + "type": "VIR", + "date": "2026-02-05 12:00:00", + "ref": "REC002-####002" + } + ] + }, + { + "ref": "FAC002-CL0001002", + "socid": "1", + "date": "2026-02-23", + "date_lim_reglement": "2026-03-30", + "type": "0", + "total_ht": "5100.00000000", + "total_tva": "0.00000000", + "total_ttc": "5100.00000000", + "multicurrency_code": "EUR", + "statut": "2", + "paye": "1", + "delivered": true, + "lines": [ + { + "desc": "TVA non applicable – Article 259-1 du CGI – Prestation de services localisée hors de France (USA)", + "subprice": "510.00000000", + "qty": "10", + "tva_tx": "0.0000", + "product_type": "1" + } + ], + "payments": [ + { + "amount": "5100.00000000", + "type": "VIR", + "date": "2026-03-12 12:00:00", + "ref": "REC003-CL00002" + } + ] + }, + { + "ref": "FAC003-CL0001003", + "socid": "1", + "date": "2026-02-23", + "date_lim_reglement": "2026-03-30", + "type": "0", + "total_ht": "2550.00000000", + "total_tva": "0.00000000", + "total_ttc": "2550.00000000", + "multicurrency_code": "EUR", + "statut": "2", + "paye": "1", + "delivered": true, + "lines": [ + { + "desc": "TVA non applicable – Article 259-1 du CGI – Prestation de services localisée hors de France (USA)", + "subprice": "510.00000000", + "qty": "5", + "tva_tx": "0.0000", + "product_type": "1" + } + ], + "payments": [ + { + "amount": "2550.00000000", + "type": "VIR", + "date": "2026-04-20 12:00:00", + "ref": "REC001-CL0001001" + } + ] + }, + { + "ref": "FAC004-CL0001004", + "socid": "1", + "date": "2026-05-22", + "date_lim_reglement": "2026-06-29", + "type": "0", + "total_ht": "2145.92000000", + "total_tva": "0.00000000", + "total_ttc": "2145.92000000", + "multicurrency_code": "EUR", + "statut": "2", + "paye": "1", + "delivered": true, + "lines": [ + { + "desc": "Conseil et accompagnement infrastructure cloud - Cycle M1 (part fixe) - periode du 23/04/2026 au 23/05/2026.\nTVA non applicable - Articles 259, 1 et 283-2 du CGI - prestation de services a un preneur assujetti etabli hors UE (Etats-Unis).", + "subprice": "2145.92000000", + "qty": "1", + "tva_tx": "0.0000", + "product_type": "1" + } + ], + "payments": [ + { + "amount": "2145.92000000", + "type": "VIR", + "date": "2026-05-29 00:00:00", + "ref": "REC002-######001" + } + ] + }, + { + "ref": "FAC005-CL0001005", + "socid": "1", + "date": "2026-05-22", + "date_lim_reglement": "2026-08-22", + "type": "0", + "total_ht": "2575.11000000", + "total_tva": "0.00000000", + "total_ttc": "2575.11000000", + "multicurrency_code": "EUR", + "statut": "1", + "paye": "0", + "delivered": true, + "lines": [ + { + "desc": "Conseil et accompagnement infrastructure cloud - Cycle M1 (part differee) - periode du 23/04/2026 au 23/05/2026.\nPaiement differe a 3 mois - echeance 23/08/2026.\nTVA non applicable - Articles 259, 1 et 283-2 du CGI - prestation de services a un preneur assujetti etabli hors UE (Etats-Unis).", + "subprice": "2575.11000000", + "qty": "1", + "tva_tx": "0.0000", + "product_type": "1" + } + ], + "payments": [] + }, + { + "ref": "FAC006-CL0001006", + "socid": "1", + "date": "2026-06-22", + "date_lim_reglement": "2026-07-30", + "type": "0", + "total_ht": "2145.92000000", + "total_tva": "0.00000000", + "total_ttc": "2145.92000000", + "multicurrency_code": "EUR", + "statut": "2", + "paye": "1", + "delivered": true, + "lines": [ + { + "desc": "Conseil et accompagnement infrastructure cloud - Cycle M2 (part fixe) - periode du 23/05/2026 au 23/06/2026.\nTVA non applicable - Articles 259, 1 et 283-2 du CGI - prestation de services a un preneur assujetti etabli hors UE (Etats-Unis).", + "subprice": "2145.92000000", + "qty": "1", + "tva_tx": "0.0000", + "product_type": "1" + } + ], + "payments": [ + { + "amount": "2145.92000000", + "type": "VIR", + "date": "2026-06-25 00:00:00", + "ref": "REC002-######002" + } + ] + }, + { + "ref": "FAC007-CL0001007", + "socid": "1", + "date": "2026-06-22", + "date_lim_reglement": "2026-09-22", + "type": "0", + "total_ht": "2575.11000000", + "total_tva": "0.00000000", + "total_ttc": "2575.11000000", + "multicurrency_code": "EUR", + "statut": "1", + "paye": "0", + "delivered": true, + "lines": [ + { + "desc": "Conseil et accompagnement infrastructure cloud - Cycle M2 (part differee) - periode du 23/05/2026 au 23/06/2026.\nPaiement differe a 3 mois - echeance 23/09/2026.\nTVA non applicable - Articles 259, 1 et 283-2 du CGI - prestation de services a un preneur assujetti etabli hors UE (Etats-Unis).", + "subprice": "2575.11000000", + "qty": "1", + "tva_tx": "0.0000", + "product_type": "1" + } + ], + "payments": [] + }, + { + "ref": "FAC008-CL0001008", + "socid": "1", + "date": "2026-07-22", + "date_lim_reglement": "2026-08-21", + "type": "0", + "total_ht": "2185.00000000", + "total_tva": "0.00000000", + "total_ttc": "2185.00000000", + "multicurrency_code": "EUR", + "statut": "2", + "paye": "1", + "delivered": true, + "lines": [ + { + "desc": "Conseil et accompagnement infrastructure cloud — Cycle M3 (part fixe) — période d'exécution du 23/06/2026 au 23/07/2026. Contrat cadre du 23/04/2026, art. 6 ; avenant art. 2 et 4. Montant contractuel 2 500,00 USD converti à 2 185,00 € (taux du jour du paiement).\nPrestation de services au sens des articles 259, 1° et 283-2 du Code général des impôts. TVA française non applicable — preneur assujetti établi hors de l'Union européenne (États-Unis). Auto-liquidation par le preneur dans son pays d'établissement le cas échéant. / Services rendered pursuant to Articles 259, 1° and 283-2 of the French General Tax Code. French VAT not applicable — recipient is a taxable person established outside the European Union (United States). Reverse charge by the recipient in its country of establishment if applicable.", + "subprice": "2185.00000000", + "qty": "1", + "tva_tx": "0.0000", + "product_type": "1" + } + ], + "payments": [ + { + "amount": "2185.00000000", + "type": "VIR", + "date": "2026-07-20 00:00:00", + "ref": "REC003-######003" + } + ] + } + ], + "supplierInvoices": [ + { + "ref": "FAF2026001", + "ref_supplier": "PLAN_ORDER_CHECKOUT-invoice-23917601", + "socid": "2", + "date": "2026-01-25", + "total_ht": "50.00000000", + "total_tva": "0.00000000", + "total_ttc": "50.00000000", + "statut": "2", + "paye": "1", + "lines": [ + { + "desc": "Ouverture compte - Exonération de TVA – Article 261 C du CGI", + "subprice": "50.00000000", + "qty": "1", + "tva_tx": "0.0000", + "product_type": "1" + } + ], + "payments": [ + { + "amount": "50.00000000", + "type": "VIR", + "date": "2026-01-26 12:00:00", + "ref": "REF2026001" + } + ] + }, + { + "ref": "FAF2026002", + "ref_supplier": "26004975", + "socid": "3", + "date": "2026-01-08", + "total_ht": "47.69000000", + "total_tva": "9.54000000", + "total_ttc": "57.23000000", + "statut": "2", + "paye": "1", + "lines": [ + { + "desc": "Déclaration relative au bénéficiaire effectif lors de la demande
\r\nd'immatriculation 16,951", + "subprice": "16.95000000", + "qty": "1", + "tva_tx": "20.0000", + "product_type": "1" + }, + { + "desc": "Immatriculation Personne Morale sur création", + "subprice": "23.30000000", + "qty": "1", + "tva_tx": "20.0000", + "product_type": "1" + }, + { + "desc": "Dépôts d'actes ou de pièces", + "subprice": "6.36000000", + "qty": "1", + "tva_tx": "20.0000", + "product_type": "1" + }, + { + "desc": "Acte de greffe (Réclamation de renseignements ou pièces manquantes (C.
\r\ncom., R 123-97 al 2))", + "subprice": "1.08000000", + "qty": "1", + "tva_tx": "20.0000", + "product_type": "1" + } + ], + "payments": [ + { + "amount": "55.93000000", + "type": "CB", + "date": "2026-01-09 12:00:00", + "ref": "REF2026002" + }, + { + "amount": "1.30000000", + "type": "CB", + "date": "2026-01-15 12:00:00", + "ref": "REF2026003" + } + ] + }, + { + "ref": "FAF2026003", + "ref_supplier": "1091237202601", + "socid": "4", + "date": "2026-01-03", + "total_ht": "1.66000000", + "total_tva": "0.33000000", + "total_ttc": "1.99000000", + "statut": "2", + "paye": "1", + "lines": [ + { + "desc": "Modèle Statuts EURL", + "subprice": "1.66000000", + "qty": "1", + "tva_tx": "20.0000", + "product_type": "1" + } + ], + "payments": [ + { + "amount": "1.99000000", + "type": "CB", + "date": "2026-01-04 12:00:00", + "ref": "REF2026004" + } + ] + }, + { + "ref": "FAF2026004", + "ref_supplier": "74389798", + "socid": "5", + "date": "2026-01-08", + "total_ht": "124.00000000", + "total_tva": "24.80000000", + "total_ttc": "148.80000000", + "statut": "2", + "paye": "1", + "lines": [ + { + "desc": "AVIS DE CONSTITUTION SARLU ARCODANGE
\r\nIdentifiant annonce: 22565141
\r\nparution le 10/01/2026 sur 20 Minutes Essonne", + "subprice": "124.00000000", + "qty": "1", + "tva_tx": "20.0000", + "product_type": "1" + } + ], + "payments": [ + { + "amount": "148.80000000", + "type": "CB", + "date": "2026-01-09 12:00:00", + "ref": "REF2026005" + } + ] + }, + { + "ref": "FAF2026005", + "ref_supplier": "06-01-26-payment-366753", + "socid": "6", + "date": "2026-01-05", + "total_ht": "169.00000000", + "total_tva": "33.80000000", + "total_ttc": "202.80000000", + "statut": "2", + "paye": "1", + "lines": [ + { + "desc": "Pack Qonto Basic
\r\nVotre pack inclut :
\r\n- Prestation dépôt de capital en ligne- 12 mois d'abonnement Qonto au
\r\nforfait Basic (à partir de la date devérification réglementaire de votre
\r\nnouvelle entreprise", + "subprice": "169.00000000", + "qty": "1", + "tva_tx": "20.0000", + "product_type": "1" + } + ], + "payments": [ + { + "amount": "202.80000000", + "type": "CB", + "date": "2026-01-06 12:00:00", + "ref": "REF2026006" + } + ] + }, + { + "ref": "FAF2026006", + "ref_supplier": "210000003184C", + "socid": "8", + "date": "2026-01-11", + "total_ht": "8.43000000", + "total_tva": "0.00000000", + "total_ttc": "8.43000000", + "statut": "2", + "paye": "1", + "lines": [ + { + "desc": "Lettre recommandée: 22 g
\r\nExonération de TVA, article 261-4-1° du CGI", + "subprice": "8.43000000", + "qty": "1", + "tva_tx": "0.0000", + "product_type": "1" + } + ], + "payments": [ + { + "amount": "8.43000000", + "type": "CB", + "date": "2026-01-12 12:00:00", + "ref": "REF2026007" + } + ] + }, + { + "ref": "FAF2026007", + "ref_supplier": "60117-ZMHMT", + "socid": "9", + "date": "2026-01-16", + "total_ht": "2.67000000", + "total_tva": "0.53000000", + "total_ttc": "3.20000000", + "statut": "2", + "paye": "1", + "lines": [ + { + "desc": "Extrait KBIS et frais d'emission
\r\nFormat electronique", + "subprice": "2.67000000", + "qty": "1", + "tva_tx": "20.0000", + "product_type": "1" + } + ], + "payments": [ + { + "amount": "3.20000000", + "type": "CB", + "date": "2026-01-17 12:00:00", + "ref": "REF2026008" + } + ] + }, + { + "ref": "FAF2026008", + "ref_supplier": "F1040", + "socid": "10", + "date": "2026-02-27", + "total_ht": "510.00000000", + "total_tva": "102.00000000", + "total_ttc": "612.00000000", + "statut": "2", + "paye": "1", + "lines": [ + { + "desc": "Apport d'affaire Arcodange ", + "subprice": "510.00000000", + "qty": "1", + "tva_tx": "20.0000", + "product_type": "1" + } + ], + "payments": [ + { + "amount": "612.00000000", + "type": "VIR", + "date": "2026-03-13 12:00:00", + "ref": "REF2026009" + } + ] + }, + { + "ref": "FAF2026009", + "ref_supplier": "F1042", + "socid": "10", + "date": "2026-02-27", + "total_ht": "255.00000000", + "total_tva": "51.00000000", + "total_ttc": "306.00000000", + "statut": "2", + "paye": "1", + "lines": [ + { + "desc": "Apport d'affaire Arcodange ", + "subprice": "510.00000000", + "qty": "0.5", + "tva_tx": "20.0000", + "product_type": "1" + } + ], + "payments": [ + { + "amount": "306.00000000", + "type": "VIR", + "date": "2026-05-10 12:00:00", + "ref": "REF2026010" + } + ] + }, + { + "ref": "FAF2026010", + "ref_supplier": "F1046", + "socid": "10", + "date": "2026-06-28", + "total_ht": "219.50000000", + "total_tva": "43.90000000", + "total_ttc": "263.40000000", + "statut": "2", + "paye": "1", + "lines": [ + { + "desc": "Apport d affaire Arcodange - Projet — juin 2026 (Hiway F1046)", + "subprice": "219.50000000", + "qty": "1", + "tva_tx": "20.0000", + "product_type": "1" + } + ], + "payments": [ + { + "amount": "263.40000000", + "type": "VIR", + "date": "2026-06-29 00:00:00", + "ref": "REF2026012" + } + ] + }, + { + "ref": "FAF2026011", + "ref_supplier": "9BF0758D-695749", + "socid": "11", + "date": "2026-04-11", + "total_ht": "180.00000000", + "total_tva": "0.00000000", + "total_ttc": "180.00000000", + "statut": "2", + "paye": "1", + "lines": [ + { + "desc": "Claude Pro — abonnement avril 2026 (facture 9BF0758D-695749, autoliquidation UE)", + "subprice": "180.00000000", + "qty": "1", + "tva_tx": "0.0000", + "product_type": "1" + } + ], + "payments": [ + { + "amount": "180.00000000", + "type": "CB", + "date": "2026-04-13 00:00:00", + "ref": "REF2026014" + } + ] + }, + { + "ref": "FAF2026012", + "ref_supplier": "MSTRL-API-814045-001", + "socid": "12", + "date": "2026-04-01", + "total_ht": "143.90000000", + "total_tva": "28.78000000", + "total_ttc": "172.68000000", + "statut": "2", + "paye": "1", + "lines": [ + { + "desc": "", + "subprice": "0.00000000", + "qty": "0", + "tva_tx": "0.0000", + "product_type": "0" + } + ], + "payments": [ + { + "amount": "172.68000000", + "type": "CB", + "date": "2026-04-03 00:00:00", + "ref": "REF2026013" + } + ] + }, + { + "ref": "FAF2026013", + "ref_supplier": "F1045", + "socid": "10", + "date": "2026-06-29", + "total_ht": "214.70000000", + "total_tva": "42.94000000", + "total_ttc": "257.64000000", + "statut": "2", + "paye": "1", + "lines": [ + { + "desc": "Apport d'affaire Arcodange - Projet — juin 2026 (facture Hiway F1045 du 30/06/2026)", + "subprice": "214.70000000", + "qty": "1", + "tva_tx": "20.0000", + "product_type": "1" + } + ], + "payments": [ + { + "amount": "257.64000000", + "type": "VIR", + "date": "2026-06-29 00:00:00", + "ref": "REF2026011" + } + ] + }, + { + "ref": "FAF2026014", + "ref_supplier": "F1048", + "socid": "10", + "date": "2026-08-30", + "total_ht": "218.50000000", + "total_tva": "43.70000000", + "total_ttc": "262.20000000", + "statut": "1", + "paye": "0", + "lines": [ + { + "desc": "Apport d'affaire Arcodange — commission 10 % sur encaissement KM du 20/07/2026", + "subprice": "218.50000000", + "qty": "1", + "tva_tx": "20.0000", + "product_type": "1" + } + ], + "payments": [] + } + ], + "accounts": [ + { + "id": "1", + "label": "QONTO", + "ref": "QON1", + "account_number": "51211", + "accountancy_journal": "BQ1", + "currency_code": "EUR", + "balance": 4158.28, + "lines": [ + { + "date": "2026-01-06", + "amount": 1000, + "label": "(Solde initial)", + "generic_label": false, + "num_releve": "202601" + }, + { + "date": "2026-01-25", + "amount": -50, + "label": "ouverture compte WISE", + "generic_label": false, + "num_releve": "202601" + }, + { + "date": "2026-03-12", + "amount": 5000, + "label": "virement du compte international vers le compte principal", + "generic_label": false, + "num_releve": null + }, + { + "date": "2026-03-12", + "amount": -612, + "label": "(SupplierInvoicePayment)", + "generic_label": true, + "num_releve": null + }, + { + "date": "2026-04-02", + "amount": -172.68, + "label": "(SupplierInvoicePayment)", + "generic_label": true, + "num_releve": null + }, + { + "date": "2026-04-12", + "amount": -180, + "label": "(SupplierInvoicePayment)", + "generic_label": true, + "num_releve": null + }, + { + "date": "2026-05-09", + "amount": -306, + "label": "(SupplierInvoicePayment)", + "generic_label": true, + "num_releve": null + }, + { + "date": "2026-06-28", + "amount": -257.64, + "label": "(SupplierInvoicePayment)", + "generic_label": true, + "num_releve": null + }, + { + "date": "2026-06-28", + "amount": -263.4, + "label": "(SupplierInvoicePayment)", + "generic_label": true, + "num_releve": null + } + ] + }, + { + "id": "2", + "label": "WISE EURO", + "ref": "WIS2", + "account_number": "51212", + "accountancy_journal": "BQ2", + "currency_code": "EUR", + "balance": 9636.84, + "lines": [ + { + "date": "2026-01-25", + "amount": 0, + "label": "Solde initial", + "generic_label": false, + "num_releve": "202601" + }, + { + "date": "2026-01-25", + "amount": 50, + "label": "ouverture compte WISE", + "generic_label": false, + "num_releve": "202601" + }, + { + "date": "2026-01-25", + "amount": -50, + "label": "(SupplierInvoicePayment)", + "generic_label": true, + "num_releve": "202601" + }, + { + "date": "2026-02-04", + "amount": 510, + "label": "", + "generic_label": false, + "num_releve": null + }, + { + "date": "2026-02-04", + "amount": 510, + "label": "", + "generic_label": false, + "num_releve": null + }, + { + "date": "2026-02-04", + "amount": -510, + "label": "(CustomerInvoicePaymentBack)", + "generic_label": false, + "num_releve": null + }, + { + "date": "2026-03-11", + "amount": 5100, + "label": "(CustomerInvoicePayment)", + "generic_label": true, + "num_releve": null + }, + { + "date": "2026-03-12", + "amount": -5000, + "label": "virement du compte international vers le compte principal", + "generic_label": false, + "num_releve": null + }, + { + "date": "2026-04-19", + "amount": 2550, + "label": "(CustomerInvoicePayment)", + "generic_label": true, + "num_releve": null + }, + { + "date": "2026-05-28", + "amount": 2145.92, + "label": "(CustomerInvoicePayment)", + "generic_label": true, + "num_releve": null + }, + { + "date": "2026-06-24", + "amount": 2145.92, + "label": "(CustomerInvoicePayment)", + "generic_label": true, + "num_releve": null + }, + { + "date": "2026-07-19", + "amount": 2185, + "label": "(CustomerInvoicePayment)", + "generic_label": true, + "num_releve": null + } + ] + }, + { + "id": "3", + "label": "G.RADUREAU Compte Courant Asso", + "ref": "CCA1", + "account_number": "45511", + "accountancy_journal": "CCA1", + "currency_code": "EUR", + "balance": -1912.98, + "lines": [ + { + "date": "2025-10-23", + "amount": -7.3, + "label": "(SupplierInvoicePayment)", + "generic_label": true, + "num_releve": null + }, + { + "date": "2026-01-03", + "amount": -1.99, + "label": "(SupplierInvoicePayment)", + "generic_label": true, + "num_releve": null + }, + { + "date": "2026-01-05", + "amount": -202.8, + "label": "(SupplierInvoicePayment)", + "generic_label": true, + "num_releve": null + }, + { + "date": "2026-01-08", + "amount": -55.93, + "label": "(SupplierInvoicePayment)", + "generic_label": true, + "num_releve": null + }, + { + "date": "2026-01-08", + "amount": -148.8, + "label": "(SupplierInvoicePayment)", + "generic_label": true, + "num_releve": null + }, + { + "date": "2026-01-11", + "amount": -8.43, + "label": "(SupplierInvoicePayment)", + "generic_label": true, + "num_releve": null + }, + { + "date": "2026-01-14", + "amount": -1.3, + "label": "(SupplierInvoicePayment)", + "generic_label": true, + "num_releve": null + }, + { + "date": "2026-01-15", + "amount": 0, + "label": "Solde initial", + "generic_label": false, + "num_releve": null + }, + { + "date": "2026-01-16", + "amount": -3.2, + "label": "(SupplierInvoicePayment)", + "generic_label": true, + "num_releve": null + }, + { + "date": "2026-01-30", + "amount": -163.23, + "label": "Indemnité d'occupation — janvier 2026", + "generic_label": false, + "num_releve": null + }, + { + "date": "2026-02-27", + "amount": -220, + "label": "Indemnité d'occupation — février 2026", + "generic_label": false, + "num_releve": null + }, + { + "date": "2026-03-30", + "amount": -220, + "label": "Indemnité d'occupation — mars 2026", + "generic_label": false, + "num_releve": null + }, + { + "date": "2026-04-29", + "amount": -220, + "label": "Indemnité d'occupation — avril 2026", + "generic_label": false, + "num_releve": null + }, + { + "date": "2026-05-30", + "amount": -220, + "label": "Indemnité d'occupation — mai 2026", + "generic_label": false, + "num_releve": null + }, + { + "date": "2026-06-29", + "amount": -220, + "label": "Indemnité d'occupation — juin 2026", + "generic_label": false, + "num_releve": null + }, + { + "date": "2026-07-30", + "amount": -220, + "label": "Indemnité d'occupation — juillet 2026", + "generic_label": false, + "num_releve": null + } + ] + } + ] +} \ No newline at end of file diff --git a/test/extractExercise.ts b/test/extractExercise.ts new file mode 100644 index 0000000..b92264c --- /dev/null +++ b/test/extractExercise.ts @@ -0,0 +1,245 @@ +/* + Extrait d'une instance Dolibarr le MANIFESTE D'UN EXERCICE : la description + complète et ordonnée des faits qui le composent. + + POURQUOI. Un exercice ouvert n'a d'engagement envers personne — aucun compte + déposé, aucune liasse, aucun FEC remis. Tant qu'il en va ainsi, une erreur de + MODÉLISATION (mauvais compte, auxiliaire manquant, libellé illisible) se + corrige mieux en rejouant depuis les pièces qu'en la contournant : la piste + d'audit fiable demande que l'écriture reflète sa pièce, et rejouer resserre ce + lien là où une rustine le distend. + + Ce fichier ne fait que LIRE. Il produit le manifeste ; le rejeu est un autre + outil, et il ne s'exécute que sur une cible vierge. + + CE QUI EST REJOUABLE, ET CE QUI NE L'EST PAS. La ligne de partage n'est pas + technique, elle est juridique : + + rejouable : factures fournisseur enregistrées, lignes bancaires, paiements + divers, charges sociales, tiers — tout ce qui est interne ; + FIGÉ : toute pièce déjà remise à un tiers (facture client ENVOYÉE, + dépôt au greffe, déclaration transmise) et tout exercice clos. + + Une facture partie chez le client existe chez lui : aucun rejeu ne l'efface. + Le manifeste les extrait quand même — il décrit l'exercice — mais les marque + `delivered: true`, et le rejeu devra les reproduire à l'identique plutôt que + les recalculer. + + LE RÉFÉRENT DE VÉRIFICATION. Les mouvements bancaires sont la seule partie de + l'exercice qu'on ne peut pas réécrire : Qonto et Wise sont hors de Dolibarr. + Le manifeste les embarque donc intégralement, et le test d'acceptation d'un + rejeu est qu'ils soient retrouvés au centime et à la date. + + Usage : + DENO_TLS_CA_STORE=system \ + DOLIBARR_URL=https://erp.arcodange.lab DOLAPIKEY=$(cat test/....key) \ + deno run -A test/extractExercise.ts --year 2026 --out manifest.json + + `DENO_TLS_CA_STORE=system` n'est pas décoratif : les hôtes du lab portent un + certificat signé par une autorité interne, présente dans le trousseau système + mais absente du magasin que Deno embarque. Sans lui, `fetch` échoue sur + « invalid peer certificate: UnknownIssuer » — alors que curl et Playwright, + qui lisent le trousseau système, passent sans rien dire. +*/ +import "load_dotenv"; + +const argv = Deno.args; +const pick = (f: string, d = "") => (argv.includes(f) ? argv[argv.indexOf(f) + 1] : d); + +const base = pick("--url", Deno.env.get("DOLIBARR_URL") ?? "https://erp.arcodange.lab"); +const key = Deno.env.get("DOLAPIKEY") ?? ""; +const year = Number(pick("--year", "2026")); +const out = pick("--out", `manifest-${year}.json`); + +/** + * Pièces volontairement écartées du rejeu, avec leur motif. + * + * Une exclusion n'est légitime que si la pièce N'A JAMAIS ÉTÉ REMISE À UN TIERS. + * Le motif est consigné dans le manifeste : un auditeur doit pouvoir lire + * pourquoi une référence manque, sans avoir à le deviner ni à nous croire. + */ +const EXCLUSIONS: Record = { + "FAC001-CL00001": + "Référence malformée (CL00001 au lieu de CL0001001) — défaut de configuration du masque. " + + "JAMAIS REMISE AU CLIENT : vérifié le 2026-08-14, aucune trace d'envoi ; KissMetrics a été " + + "facturé via Wise en février 2026, et le document qu'il détient référence FAC001-CL0001001. " + + "La séquence FAC reste continue (FAC001 à FAC008), la réémission portant elle aussi FAC001.", + "AVC001-CL0001001": + "Avoir émis le 24/02/2026 pour annuler FAC001-CL00001. Sans objet dès lors que la facture " + + "annulée n'a jamais quitté la société : il ne documentait qu'un faux pas interne, non un " + + "événement commercial. La séquence AVC devient vide.", +}; + +if (!key) { + console.error("DOLAPIKEY requis (lecture seule)."); + Deno.exit(2); +} + +async function api(path: string): Promise { + const r = await fetch(`${base}/api/index.php${path}`, { headers: { DOLAPIKEY: key } }); + if (!r.ok) return null; + try { + return await r.json() as T; + } catch { + return null; + } +} + +/** Dolibarr rend des epochs ; on les fige en ISO dans le fuseau du serveur. */ +const iso = (ts: unknown): string | null => { + const n = Number(ts); + if (!n) return null; + return new Date(n * 1000).toISOString().slice(0, 10); +}; + +const inYear = (ts: unknown) => { + const d = iso(ts); + return !!d && d.startsWith(String(year)); +}; + +type Any = Record; + +console.log(`source : ${base}`); +console.log(`exercice : ${year}\n`); + +// --- tiers ------------------------------------------------------------------ +const tiers = (await api("/thirdparties?limit=500") ?? []).map((t) => ({ + id: t.id, + name: t.name, + code_client: t.code_client, + code_fournisseur: t.code_fournisseur, + client: t.client, + fournisseur: t.fournisseur, + country_id: t.country_id, + tva_intra: t.tva_intra, + code_compta: t.code_compta, + code_compta_fournisseur: t.code_compta_fournisseur, +})); +console.log(`tiers : ${tiers.length}`); + +// --- factures client -------------------------------------------------------- +// `delivered: true` : elles sont parties chez le client. Un rejeu les reproduit +// à l'identique — référence, date, montant — il ne les recalcule pas. +const invoices = []; +const excluded: Any[] = []; +for (const f of await api("/invoices?limit=500") ?? []) { + if (!inYear(f.date)) continue; + const motif = EXCLUSIONS[String(f.ref)]; + if (motif) { + excluded.push({ ref: f.ref, date: iso(f.date), total_ttc: f.total_ttc, motif }); + continue; + } + const payments = await api(`/invoices/${f.id}/payments`) ?? []; + invoices.push({ + ref: f.ref, + socid: f.socid, + date: iso(f.date), + date_lim_reglement: iso(f.date_lim_reglement), + type: f.type, + total_ht: f.total_ht, + total_tva: f.total_tva, + total_ttc: f.total_ttc, + multicurrency_code: f.multicurrency_code, + statut: f.statut, + paye: f.paye, + delivered: true, + lines: (f.lines as Any[] ?? []).map((l) => ({ + desc: l.desc, subprice: l.subprice, qty: l.qty, tva_tx: l.tva_tx, product_type: l.product_type, + })), + payments: payments.map((p) => ({ amount: p.amount, type: p.type, date: p.date, ref: p.ref })), + }); +} +console.log(`factures client : ${invoices.length} (figées — déjà remises au client)`); +for (const e of excluded) console.log(` ⊘ exclue : ${e.ref} (${e.total_ttc} EUR) — ${String(e.motif).slice(0, 72)}…`); + +// --- factures fournisseur --------------------------------------------------- +const supplierInvoices = []; +for (const f of await api("/supplierinvoices?limit=500") ?? []) { + if (!inYear(f.date)) continue; + const payments = await api(`/supplierinvoices/${f.id}/payments`) ?? []; + supplierInvoices.push({ + ref: f.ref, + ref_supplier: f.ref_supplier, + socid: f.socid, + date: iso(f.date), + total_ht: f.total_ht, + total_tva: f.total_tva, + total_ttc: f.total_ttc, + statut: f.statut, + paye: f.paye, + lines: (f.lines as Any[] ?? []).map((l) => ({ + desc: l.desc, subprice: l.subprice, qty: l.qty, tva_tx: l.tva_tx, product_type: l.product_type, + })), + payments: payments.map((p) => ({ amount: p.amount, type: p.type, date: p.date, ref: p.ref })), + }); +} +console.log(`factures fournisseur : ${supplierInvoices.length}`); + +// --- comptes et mouvements bancaires --------------------------------------- +// LE RÉFÉRENT. Tout le reste se vérifie contre ces lignes. +const accounts = []; +let bankLineCount = 0; +for (const a of await api("/bankaccounts") ?? []) { + const lines = (await api(`/bankaccounts/${a.id}/lines?limit=1000`) ?? []) + .map((l) => ({ + date: iso(l.datev ?? l.dateo), + amount: Number(l.amount), + label: l.label, + // Les libellés générés par Dolibarr — « SupplierInvoicePayment », + // « CustomerInvoicePayment » — traversent le grand livre tels quels et y + // sont illisibles. Le rejeu doit les remplacer par le libellé de la pièce. + generic_label: /^\(?(Customer|Supplier)InvoicePayment\)?$/.test(String(l.label ?? "")), + num_releve: l.num_releve, + })) + .filter((l) => l.date) + .sort((x, y) => String(x.date).localeCompare(String(y.date))); + bankLineCount += lines.length; + accounts.push({ + id: a.id, + label: a.label, + ref: a.ref, + account_number: a.account_number, + accountancy_journal: a.accountancy_journal, + currency_code: a.currency_code, + balance: Number(lines.reduce((s, l) => s + l.amount, 0).toFixed(2)), + lines, + }); +} +console.log(`comptes bancaires : ${accounts.length} (${bankLineCount} mouvements — référent de vérification)`); + +const generics = accounts.flatMap((a) => a.lines).filter((l) => l.generic_label).length; + +const manifest = { + meta: { + exercice: year, + source: base, + extracted_at: new Date().toISOString(), + // Un exercice clos ne se rejoue pas : la clôture est la limite dure. + replayable: true, + note: "Manifeste d'exercice — description des faits, non des écritures comptables. " + + "Les factures client sont marquées delivered:true : elles existent chez le client " + + "et se reproduisent à l'identique, jamais ne se recalculent.", + }, + exclusions: excluded, + anomalies: { + libelles_generiques: generics, + detail: generics + ? "lignes bancaires portant un libellé Dolibarr générique — à remplacer au rejeu" + : "aucune", + }, + tiers, + invoices, + supplierInvoices, + accounts, +}; + +await Deno.writeTextFile(out, JSON.stringify(manifest, null, 2)); + +const totalClient = invoices.reduce((s, f) => s + Number(f.total_ttc ?? 0), 0); +const totalFourn = supplierInvoices.reduce((s, f) => s + Number(f.total_ttc ?? 0), 0); +console.log(`\n--- récapitulatif ---`); +console.log(` produits facturés : ${totalClient.toFixed(2)} EUR`); +console.log(` achats facturés : ${totalFourn.toFixed(2)} EUR`); +for (const a of accounts) console.log(` ${String(a.label).padEnd(32)} ${a.balance.toFixed(2)} EUR`); +console.log(` libellés génériques : ${generics} ← à corriger au rejeu`); +console.log(`\nmanifeste écrit : ${out}`);