docs/30-comptabilite/99-technique.md —
ce document se lit aussi dans le dépôt, sans serveur.
Comptabilité — le côté technique
La règle qui gouverne tout ce dossier
Les règles d'argent vivent dans src/Finance/ et ne sont jamais recopiées ailleurs.
Deux copies finissent par donner deux chiffres différents pour la même question, et plus personne ne sait lequel croire.
Elles ont des tests d'intégration sur MySQL, pas seulement sur le banc SQLite : certains défauts n'y sont pas visibles. Un bogue de GROUP BY qui faussait les frais de période n'a été vu que là.
Les fichiers
| Rôle | Fichier |
|---|---|
| Écran (liste, banque, rapprochement, TVA, avoirs) | public/erp/comptabilite.php |
| Fiche facture | public/erp/facture.php + public/erp/assets/js/pages/facture-2.js |
| LCR | public/erp/lcr.php |
| Variations | public/erp/variations.php |
| Scripts compta | public/erp/assets/js/compta/banque.js, rapprochement.js, tva.js, stock.js |
| API | src/Http/Controllers/BillController.php, BankController.php, RapprochementController.php, TvaController.php, AvoirController.php, LcrController.php, SalaryController.php, TarifVariationController.php |
| Données | src/Repository/BillRepository.php, BankStatementRepository.php, RapprochementRepository.php, AvoirRepository.php, LcrRepository.php, SalaryRepository.php, TarifVariationRepository.php |
Les règles d'argent
| Règle | Fichier |
|---|---|
| Statut « payée » | src/Finance/BillSettlement.php |
| Recalcul après rapprochement | src/Finance/LineReconciliation.php |
| Verrou de relevé | src/Finance/StatementLock.php |
| TVA | src/Finance/PeriodeTva.php |
| Marge et frais de période | src/Finance/Marge.php |
| Imputation d'un avoir | src/Finance/ImputationAvoir.php |
| Carte avoir → facture | src/Finance/AvoirAllocation.php |
| Échéancier | src/Finance/Echeancier.php |
| Trésorerie | src/Finance/Tresorerie.php |
| La tolérance | src/Support/Money.php — Money::within($a, $b, $toleranceCents = 2) |
Les tables
| Table | Contenu | Note |
|---|---|---|
bills | La facture | status : pending / paid / cancelled. categorie depuis la migration 036. |
bill_orders | Rattachement aux commandes | Pas de clé unique sur woo_order_id. |
bill_shipping_fees | Le port | Plusieurs lignes possibles. |
bill_irregularities | Les signalements | bill_id, note, resolved 0/1, resolved_at. |
bank_statements, bank_lines | Les relevés et leurs lignes | |
bank_line_bills | Les règlements | C'est cette table qui décide « payée ». |
bank_line_orders, bank_line_salaries, bank_line_custom | Les autres rapprochements | |
avoirs | avoir_type, supplier_id, amount, amount_ttc, vat_amount, status, bill_id | |
lcrs, lcr_bills, lcr_avoirs | Les lettres de change | |
salaries | user_id, period_month, period_year, gross_amount, net_amount, employer_charges | |
stock_movements | Les lignes de stock d'une facture | bill_id pointe la facture ; le mouvement est la ligne. |
Les routes
GET /v1/bills liste + statistiques + fournisseurs
GET /v1/bills/export ZIP des PDF d'une période
GET /v1/bills/irregularities toutes les irrégularités
GET /v1/bills/products catalogue, pour choisir un produit
POST /v1/bills créer
GET /v1/bills/{id} la fiche complète
PUT /v1/bills/{id} modifier (et ses lignes de commande)
DELETE /v1/bills/{id} supprimer
PUT /v1/bills/{id}/categorie classer
GET /v1/bills/{id}/stock les entrées en stock
POST /v1/bills/{id}/stock ajouter une ligne de stock
DELETE /v1/bills/{id}/stock/{mvt_id} retirer une ligne de stock
POST /v1/bills/{id}/shipping-fees ajouter du port
POST /v1/bills/{id}/irregularities signaler
POST /v1/bills/{id}/pdf téléverser le PDF
GET /v1/bills/{id}/pdf télécharger le PDF
Tout est derrière le module finance (can_view / can_update), sauf les salaires, qui sont derrière hr.
Les pièges connus
/v1/bills/products doit rester déclarée AVANT /v1/bills/{id}
FastRoute refuse une route statique posée après une route variable qui la couvre, et ce refus tombe à la construction du répartiteur — c'est-à-dire au premier appel de l'API, quel qu'il soit.
Le 21/09/2026, cette inversion a mis toute l'API en erreur 500, connexion comprise. TableDesRoutesTest le vérifie désormais.
Le catalogue des lignes de stock est servi par BillController
Pas par StockController. Parce que cette page est derrière finance et l'autre derrière orders : un comptable sans droit sur les commandes doit pouvoir saisir une facture.
bills.categorie porte un nom qui existe déjà comme alias
Dans Marge::fraisDePeriode(), s.category est aliasé categorie. Quand la colonne bills.categorie est arrivée, MySQL a donné raison à la colonne et le GROUP BY a cessé de grouper par catégorie de fournisseur. Les frais de période étaient faux sans que rien ne le dise.
Le GROUP BY porte désormais sur l'expression, jamais sur l'alias.
Les règles vivaient dans le contrôleur
AvoirAllocation était du code inline dans RapprochementController, à côté du décodage de la requête, sans test. Le service qui applique la carte était couvert ; la construction de la carte ne l'était pas.
Une forme d'API morte
avoir_ids: [int], qui imputait tout sur la première facture, n'a plus d'émetteur — relevé du 17/09/2026 sur public/, src/ et le plugin. Le seul écran qui envoie encore avoir_ids est celui des LCR, vers sa propre route. La forme n'est plus lue par le rapprochement.
Les tests
| Fichier | Ce qu'il tient |
|---|---|
tests/Unit/FraisDePeriodeTest.php | Frais de période, stock consommé, factures de stock exclues |
tests/Unit/AccountingTest.php | Les calculs comptables |
tests/Unit/MoneyTest.php | La tolérance |
tests/Unit/CategorieDeFactureTest.php | La liste PHP des catégories = l'ENUM de la migration |
tests/Unit/RechercheFacturesEchappementTest.php | La recherche n'accepte pas les jokers LIKE |
tests/Unit/ImputationAvoirTest.php, AvoirAllocationTest.php | Les avoirs |
tests/Unit/VariationsTarifairesDuStockTest.php | Les lignes de stock comme points exacts |
Sur MySQL : ReconciliationIntegrationTest.php, MargeIntegrationTest.php, TvaCollecteeIntegrationTest.php, BankImportIntegrationTest.php, MoneticoRemiseIntegrationTest.php, AllocationIntegrityIntegrationTest.php.
Finance\Echeanciern'a pas de test unitaire. Il n'est couvert qu'indirectement, partests/Integration/AnalyticsAvanceeIntegrationTest.php. C'est un trou connu, suivi dans le développement de Darsy.