From ac40f66bac6add9628190ce61b2267ad7fb936b5 Mon Sep 17 00:00:00 2001 From: Guy Corbaz Date: Sat, 3 Oct 2026 11:25:35 +0200 Subject: [PATCH 01/13] =?UTF-8?q?docs(25-5-b):=20fiche=20cr=C3=A9=C3=A9e?= =?UTF-8?q?=20=E2=80=94=20balance=20avec=20solde=20d'ouverture=20(refs=20#?= =?UTF-8?q?385)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- .../25-5-b-balance-solde-ouverture.md | 194 ++++++++++++++++++ .../sprint-status.yaml | 2 +- 2 files changed, 195 insertions(+), 1 deletion(-) create mode 100644 _bmad-output/implementation-artifacts/25-5-b-balance-solde-ouverture.md diff --git a/_bmad-output/implementation-artifacts/25-5-b-balance-solde-ouverture.md b/_bmad-output/implementation-artifacts/25-5-b-balance-solde-ouverture.md new file mode 100644 index 000000000..b20ed8aae --- /dev/null +++ b/_bmad-output/implementation-artifacts/25-5-b-balance-solde-ouverture.md @@ -0,0 +1,194 @@ +# Story 25.5-b : La balance des comptes porte un solde d'ouverture — et concorde enfin avec le bilan + +Status: ready-for-dev + +**Issue : [#385]**, que cette story **ferme** : la PR porte `closes #385` dans le **titre ET le corps**. Seconde moitié de +la 25-5, découpée le 2026-09-23 (la 25-5-a a fermé #386). Branche `story/25-5-b-balance-solde-ouverture`, partie de +`main` (`27c61def`) : la story ne dépend pas de la chaîne 25-4, qui ne touche pas `trial_balance.rs`. + +## Story + +En tant que **personne qui tient ses livres dans Kesh, ou qui les révise**, +je veux que **la balance des comptes montre pour chaque compte son solde d'ouverture, ses mouvements et son solde de +clôture**, +afin que **le solde de clôture d'un compte de bilan soit le même nombre que celui du bilan** — un compte, un nombre. + +## Les faits, vérifiés dans le code + +- **La balance actuelle est une balance des mouvements.** `crates/kesh-report/src/trial_balance.rs:60-104` filtre les + lignes sur `je.fiscal_year_id = ?` **et** `je.entry_date BETWEEN start AND end`. `balance` est le **net des mouvements** + de la période, signé par type (`Asset`/`Expense` : débit − crédit ; sinon crédit − débit). +- **Le bilan est cumulatif depuis l'origine** (`balance_sheet.rs:1-30`) : comptes de bilan = Σ de toutes les écritures + `entry_date ≤ fin`, tous exercices confondus ; plus deux **lignes calculées** : `retained_earnings` (P&L + `entry_date < fy_start`) et `equity_result` (P&L `[fy_start, fin]`). +- ⚠️ **L'écran avoue l'écart** : `TrialBalanceView.svelte:26-32` affiche `reports-trial-balance-period-note` — « Le total + par compte n'est pas comparable au solde cumulé du même compte au bilan ». Cette note devient **fausse** avec la story. +- **Le moteur existe — dans le grand livre** (Story 24-1). `general_ledger.rs:11-35` pose **la règle d'ouverture** : + `Asset`/`Liability` = cumul `entry_date < from`, tous exercices ; `Revenue`/`Expense` = cumul depuis le **début de + l'exercice contenant `from`** (0 si `from` est ce premier jour). Implémentée par `opening_balance` (`:220-275`), avec + `is_debit_natured` / `signed` (`:182-192`) et `fiscal_year_start_containing` (`:194-217`) — **privés au module**, et + **requête par compte**. +- **Un test de concordance existe déjà** entre grand livre et balance (`crates/kesh-report/tests/general_ledger.rs:150-205`) : + il ne compare que `total_debit` / `total_credit` — les mouvements, la seule chose que la balance porte aujourd'hui. +- **Règle d'inclusion actuelle** (`trial_balance.rs:3-6`) : comptes actifs toujours ; archivés **seulement s'ils ont une + écriture dans la période**. Un compte archivé portant un solde d'ouverture mais sans mouvement **disparaîtrait** — + alors que le bilan le compte. +- **Rendus** : CSV `csv.rs:249-296` (`NumeroCompte;NomCompte;TotalDebit;TotalCredit;Solde`, ligne `Total`) ; PDF + `pdf.rs:699-850` (5 colonnes, A4 portrait, nom tronqué à 30 caractères, positions `MARGIN_LEFT_MM + 20/90/125/160`). + Les libellés `ledger_opening`, `ledger_closing` et `retained_result_label` existent déjà dans `SectionLabels` + (`pdf.rs:74-140`) ; côté écran, `reports-ledger-opening`, `reports-ledger-closing` et + `reports-retained-earnings-calculated` / `-loss` existent déjà dans les 4 locales. +- **Consommateurs de `TrialBalance` / `TrialBalanceRow`** (construction littérale à compléter) : `csv.rs` (tests + `:820`, `:1128`), `pdf.rs` (`fixture_tb`, `:1810`), `benches/export.rs:100-125`. Appels de `generate` : + `kesh-api/src/routes/reports.rs:399`, `:991` ; tests `kesh-db/tests/report_aggregates.rs` (4 sites), + `kesh-api/tests/reports_e2e.rs`, `reports_export_e2e.rs`, `kesh-report/tests/general_ledger.rs`. +- **Le manuel est faux, et pas seulement sur l'ouverture** : `user-manual.tex:1557-1559` — « Liste tous les comptes avec + leurs soldes débiteurs et créditeurs. Filtres : période, **niveau de détail (comptes principaux uniquement, ou tous + sous-comptes)** ». Aucun filtre de niveau n'existe (`grep -n "detail\|niveau" reports/+page.svelte routes/reports.rs` : + rien). Glossaire `:2043` : « … à une date donnée ». + +## Acceptance Criteria + +**AC 1 — Le solde d'ouverture, selon la règle du grand livre.** `TrialBalanceRow` gagne `opening_balance` et +`closing_balance` (`Decimal`, même convention de signe que `balance`). Ouverture : comptes de bilan (`Asset`, +`Liability`) = Σ des lignes `entry_date < period.start_date`, **tous exercices confondus** ; comptes de résultat +(`Revenue`, `Expense`) = Σ des lignes `entry_date ∈ [début de l'exercice de la période, period.start_date[` — 0 quand la +période commence le premier jour de l'exercice. Le début de l'exercice est celui de `period.fiscal_year_id` (la +`ReportPeriod` est déjà bornée à cet exercice). **Clôture = ouverture + net des mouvements** (`balance`). + +**AC 2 — Une seule règle, pas deux copies.** `is_debit_natured`, `signed` et la règle de borne basse sont **extraits** +de `general_ledger.rs` dans un module partagé de `kesh-report` (ex. `crate::opening`) que les deux rapports appellent ; +le grand livre garde son comportement (ses tests restent verts sans modification de leurs assertions). La balance +calcule ses ouvertures en **une requête agrégée** (pas une requête par compte) : les sommes conditionnelles sur +`entry_date` suffisent. + +**AC 3 — Les mouvements ne changent pas.** `total_debit`, `total_credit` (par ligne et au total), `balance` (net des +mouvements) et `balanced` gardent **exactement** leur sens et leurs valeurs actuels. `balance` est documenté au champ +comme « net des mouvements de la période » — l'API publique (clés PAT) ne voit qu'**ajouter** des champs. + +**AC 4 — La ligne calculée du résultat reporté.** Sans écriture de clôture, le résultat des exercices antérieurs n'est +porté par **aucun compte** : sans lui, ni la colonne d'ouverture ni celle de clôture ne s'équilibrent. `TrialBalance` +gagne `retained_earnings: Decimal` — **même valeur** que `BalanceSheet::retained_earnings` pour l'exercice de la période +(P&L `entry_date < fy_start`, signe crédit-positif), obtenue en appelant **la même fonction** : +`fetch_retained_earnings` (`balance_sheet.rs:333`, privée) est rendue visible au crate (`pub(crate)`), pas recopiée. Il est rendu comme une **ligne +à part**, après les comptes, libellée « Résultat reporté (calculé) » (« Perte reportée » si négatif, comme au bilan), +ouverture = clôture = `retained_earnings`, mouvements vides. Ce n'est **pas** une `TrialBalanceRow` (pas +d'`account_id`, pas de lien vers le grand livre). + +**AC 5 — Un contrôle, pas une tautologie.** `TrialBalance` gagne `opening_balanced` et `closing_balanced` (`bool`) : +Σ des ouvertures (resp. clôtures) **exprimées en sens débit** (positif pour un compte à nature débitrice, négatif sinon) +moins `retained_earnings` vaut zéro. Le résultat reporté est calculé **indépendamment** (comme au bilan), donc +l'égalité est une vérification réelle, pas une identité de construction. Un déséquilibre n'est **pas** une erreur (à la +différence de `TrialBalanceUnbalanced` sur les mouvements) : il est **journalisé** (`tracing::error!`) et affiché (⚠️), +comme `equation_holds` au bilan. + +**AC 6 — La règle d'inclusion.** Un compte est rendu s'il est **actif**, **ou** s'il a un mouvement dans la période, +**ou** si son **solde d'ouverture est non nul**. Un compte archivé à solde d'ouverture non nul sans mouvement apparaît +donc (marqueur `active: false`), sinon la clôture ne concorderait pas avec le bilan. Doc du module mise à jour. + +**AC 7 — Les rendus.** +- **Écran** (`TrialBalanceView.svelte`) : colonnes N° | Intitulé | **Ouverture** | Débit | Crédit | **Clôture** ; la + colonne « Solde » (net des mouvements) disparaît de l'écran ; pied : totaux débit/crédit et ✓/⚠️ des mouvements comme + aujourd'hui, plus un indicateur ✓/⚠️ pour l'ouverture et la clôture (AC 5) ; la ligne calculée (AC 4) avant le pied. + La note `reports-trial-balance-period-note` est **retirée** (clé supprimée des 4 locales) — elle deviendrait fausse. +- **CSV** : `NumeroCompte;NomCompte;SoldeOuverture;TotalDebit;TotalCredit;SoldeCloture` — la colonne `Solde` est + **remplacée**, pas renommée en silence (CHANGELOG). Ligne de résultat reporté (libellé fr fixe, comme les autres en-têtes + CSV), puis ligne `Total` (débit, crédit ; ouverture et clôture vides). +- **PDF** : six colonnes en A4 portrait — nom tronqué plus court (la troncature existante s'adapte), libellés + `ledger_opening` / `ledger_closing` réutilisés, ligne `retained_result_label`. Garde « vide » inchangée + (`rows.is_empty()`). +- `isReportEmpty('trial-balance', …)` (`reports.api.ts:169-172`) : inchangé — les comptes actifs sont toujours rendus. + +**AC 8 — L'API et les types.** `openingBalance`, `closingBalance` sur chaque ligne ; `retainedEarnings`, +`openingBalanced`, `closingBalanced` au niveau du rapport — dans la réponse de `GET /api/v1/reports/trial-balance` et dans +`reports.types.ts`. Libellés neufs éventuels dans les 4 locales, `sitesTotal` recompté depuis la source. + +**AC 9 — La concordance, prouvée en appelant les autres rapports.** Le motif d'échec redouté est **muet** : +`clôture = ouverture + mouvements` resterait vrai, le rapport serait intérieurement cohérent et extérieurement faux. +Les tests appellent donc **réellement** `generate_balance_sheet`, `generate_income_statement` et le grand livre : +- deux exercices, des écritures sur les deux (dont un compte de bilan, un de produits, un de charges) ; balance de + l'exercice 2 → **clôture de chaque compte de bilan = son solde au bilan** à la fin de la période ; **clôture de chaque + compte de résultat = son montant au compte de résultat** ; `retained_earnings` = celui du bilan ; ouverture du compte + de produits **= 0** au premier jour de l'exercice 2 (et non le cumul de l'exercice 1) ; +- une période **en cours d'exercice** (mars–juin de l'exercice 2) : ouverture du compte de produits = cumul janvier– + février ; clôture = celle du grand livre sur la même période (`section.closing`) ; +- `opening_balanced` et `closing_balanced` vrais dans ces scénarios ; +- un compte **archivé** à solde d'ouverture non nul, sans mouvement → présent, clôture = bilan ; +- non-régression : mouvements, `balance`, `balanced` identiques à avant sur le jeu existant (`report_aggregates.rs`) ; +- le test existant `general_ledger.rs:150-205` étendu à `opening` / `closing`. +- Rendus : CSV (en-tête à six colonnes, ligne de résultat reporté, ligne `Total`) ; PDF (rendu non vide avec la ligne + calculée) ; **Vitest** de la vue (`TrialBalanceView.test.ts`, à créer : colonnes, ligne calculée, ✓/⚠️, note absente) ; + test d'API sur la forme de la réponse (`reports_e2e.rs`) ; **E2E** : la balance affiche les colonnes Ouverture / + Clôture (`reports.spec.ts`, sélecteurs ancrés). + +Chacun aurait échoué avant le patch ; chaque test de concordance nomme la mutation qu'il tue (ex. « ouverture des +comptes de résultat cumulée depuis l'origine », « règle d'inclusion sans le solde d'ouverture »). + +**AC 10 — Le manuel et le CHANGELOG.** `user-manual.tex` § *Balance des comptes* (`:1557-1559`) réécrit : les quatre +colonnes, la règle d'ouverture (bilan depuis l'origine, résultat depuis le début de l'exercice), la ligne calculée, +la concordance avec le bilan et le grand livre ; la phrase sur le **filtre de niveau de détail**, qui n'existe pas, +**retirée**. Le grand livre (`:1561-1565`, « là où la balance dit la même chose autrement ») relu. Glossaire `:2043` +corrigé (« à une date donnée » → sur une période, ouverture et clôture). PDF régénéré, contrôlé **aplati** +(`pdftotext … | tr '\n' ' ' | tr -s ' '`). CHANGELOG `[Unreleased]` : `Changed` (colonnes de l'écran, du PDF et du CSV — +`Solde` remplacé) et `Fixed` (#385). README et site relus (`grep -rn "balance" README.md website/`). + +## Tasks / Subtasks + +- [ ] **T1 — le module partagé** (AC 2) : extraire `is_debit_natured`, `signed`, la borne basse de l'ouverture ; le grand + livre l'appelle ; ses tests verts sans retouche d'assertion. +- [ ] **T2 — la balance** (AC 1, 3, 4, 5, 6) : requête agrégée, champs neufs, ligne calculée, contrôles, inclusion. +- [ ] **T3 — les rendus** CSV et PDF (AC 7), fixtures et bench complétés. +- [ ] **T4 — l'écran, les types, l'i18n** (AC 7, 8) : vue, `reports.types.ts`, clé de note retirée des 4 locales, + `sitesTotal`. +- [ ] **T5 — tests** (AC 9). +- [ ] **T6 — manuel, CHANGELOG** (AC 10). +- [ ] **T7 — gates** : backend complet, frontend complet, **E2E complet**. + +## Dev Notes + +### Ce qu'il ne faut pas faire + +- **Ne pas** recopier `opening_balance` du grand livre dans `trial_balance.rs` (DRY, AC 2) — ni l'appeler compte par + compte (une requête par compte sur un plan de 300 comptes). +- **Ne pas** changer le sens de `balance` : l'API est publique (clés PAT, Story 15-x). On **ajoute**. +- **Ne pas** fusionner la ligne calculée avec un compte physique de rôle `RetainedEarnings` (collision D1 de la 14-3c, + `balance_sheet.rs:40-55`) : le compte physique reste une ligne de compte, la ligne calculée reste à part. +- **Ne pas** borner l'ouverture des comptes de bilan par `fiscal_year_id` : c'est exactement le défaut de #385. +- **Ne pas** introduire de filtre de niveau de détail pour rendre le manuel vrai : c'est le manuel qu'on corrige. + +### Limites assumées (à écrire au manuel et en doc de module) + +- **Signe lu sur le type courant** du compte : retyper un compte mouvementé re-signe tout son historique — limite + préexistante du grand livre (#274, #382), héritée telle quelle. +- **Pas d'écriture de clôture** : la remise à zéro des comptes de résultat n'existe que comme borne basse du `SUM` ; + c'est ce qui rend les tests de concordance (AC 9) indispensables. + +### Modules + +`kesh-report` (balance, module partagé, rendus), `frontend` (vue, types, test), `kesh-i18n` (clé retirée, libellés +éventuels), + `docs`, `CHANGELOG`. `kesh-api` : aucun changement de code attendu (la route sérialise la structure ; +les libellés PDF existent) — tests seulement. **Trois modules de code**, sous le seuil de la § *Règle de splitting +préventif*. + +### References + +- Issue [#385] ; audit du 2026-08-26 (comptable § 5.2, DAF § 1.3). +- `crates/kesh-report/src/general_ledger.rs:1-60`, `:182-275` — la règle et son implémentation. +- `crates/kesh-report/src/balance_sheet.rs:1-120`, `:224-228`, `:333` — le calcul de `retained_earnings`. +- Story 24-1 (grand livre) — `_bmad-output/implementation-artifacts/24-1-grand-livre.md`. + +## Dev Agent Record + +### Agent Model Used + +### Debug Log References + +### Completion Notes List + +### File List + +## Change Log + +- **2026-10-03** — Créée (Guy : « pousse et continue »). + +[#385]: https://github.com/guycorbaz/kesh/issues/385 diff --git a/_bmad-output/implementation-artifacts/sprint-status.yaml b/_bmad-output/implementation-artifacts/sprint-status.yaml index a4f7f76fb..a3d934fb7 100644 --- a/_bmad-output/implementation-artifacts/sprint-status.yaml +++ b/_bmad-output/implementation-artifacts/sprint-status.yaml @@ -368,7 +368,7 @@ development_status: 25-4-d-solder-le-reste: backlog # [#384] ramenee de la 25-6 le 2026-09-27 (Guy) — perte sur debiteur, escompte, frais : clore une facture partiellement reglee ; la part TVA reduit la TVA due. 25-4-propager-le-residuel: split # 2026-09-26 DECOUPEE (six modules > 5) en 25-4-a / 25-4-b / 25-4-c, puis 25-4-d (#384, 2026-09-27) ; #455 et #456 rattachees. # [#416] [#420] — la 24-2 a introduit le reglement PARTIEL ; le residuel n'est propage ni a la balance agee ni a l'echeancier, et **le score de rapprochement compare au TTC et non au residuel** : le virement du solde d'une facture partiellement reglee **score 0**. 25-5-a-export-souverainete: done # 2026-09-26 **MERGEE (PR #452, #386 fermee) SANS REVUE DE CODE** ; revue a posteriori CLOSE en 3 passes sur `story/25-5-a-revue-code` (4 HIGH P1, 1 HIGH orchestrateur P2 — colonnes manquantes, garde de colonnes —, P3 ciblee test seul) ; gates 2466/2466, E2E 221/8/19 sans regression ; reports #465, #466. # 2026-09-23 **SPECIFIEE et VALIDEE en 1 passe** (0 CRITICAL, 4 HIGH, 3 MEDIUM, 3 LOW, tous corriges). [#386] — issue du DECOUPAGE de la 25-5 (arbitrage de Guy) : #385 (Balance vs Bilan) et #386 n'ont en commun que le mot « etat », et touchent des modules disjoints. ⛔ **Trois faits qui corrigent l'issue** : 21 tables absentes et non 10 ; le trou s'est CREUSE depuis son ecriture (`invoice_settlements`, Epic 24) ; et SIX repositories sur huit n'ont aucune lecture exhaustive — le vrai cout, absent de l'issue. Arbitrage de Guy : l'export CSV ne porte que les donnees COMPTABLES (11 dedans), les backups portent tout ; 10 exclusions AVEC motif ecrit, dont 3 tables de SECRETS. Les justificatifs (fichiers) partent en story separee sur la SAUVEGARDE. ⛔ La vraie livraison est la GARDE D'EXHAUSTIVITE (patron D4-ter + P7), non les 11 exports : trois epics ont rouvert le trou en silence. ⚠️ Pieges releves par la validation : le nombre 19 code en dur a SEPT endroits dont deux `debug_assert_eq!` qui PANIQUENT ; `audit_log` n'a AUCUN dump exhaustif (ses fonctions sont bornees, la route rejette au-dela de 10 000 lignes) ; `csv_sanitize` existe et n'est pas employee par cet export ; deux sites de manuel DEJA faux (un dossier `reports/` invente, un « 38 » pour 39). - 25-5-b-balance-solde-ouverture: backlog # [#385] — la **Balance des comptes ne concorde pas avec le Bilan**, il manque la colonne « solde d'ouverture » (III.4). Detachee de la 25-5 le 2026-09-23 : voir 25-5-a. + 25-5-b-balance-solde-ouverture: ready-for-dev # 2026-10-03 CREEE (partie de main). ouverture + mouvements + cloture, ligne calculee du resultat reporte, closes #385. [#385] — la **Balance des comptes ne concorde pas avec le Bilan**, il manque la colonne « solde d'ouverture » (III.4). Detachee de la 25-5 le 2026-09-23 : voir 25-5-a. 25-5-etats-et-export: split # [#385] [#386] — la **Balance des comptes ne concorde pas avec le Bilan**, il manque la colonne « solde d'ouverture » (III.4) ; et **l'export de souverainete omet les achats, les avoirs, les projets et la piste d'audit** (V.6) — *il promet plus qu'il ne tient*. 25-6-tableau-de-bord-et-dettes: backlog # [#388] [#389] [#387] (#384 ramenee en 25-4-d le 2026-09-27) — la tuile « Dernieres ecritures » **n'appelle rien** et affiche toujours « Aucune ecriture » (*une tuile qui est un decor*) ; le « solde bancaire » est le solde COMPTABLE, donc faux ; imputer l'ECART d'un reglement partiel (escompte, frais bancaires, perte sur debiteur) ; et **le PDF d'une facture n'est jamais archive**, il est regenere a la volee (IV.9) — *un document reemis n'est pas le document envoye*. epic-16: done # 2026-08-11 (nuit) MERGÉ — dernière story 16-3b via PR #296 (squash `593bb845`). #152, #144 et #151 fermées. v0.9.0 prête à taguer. # 2026-07-26 KICKOFF (rétro Epic 14, décision D-D ; GO Guy après publication de v0.8.0). Branche `story/16-1-compte-produit-par-ligne` créée sur main `ef6cdf52`. Séquence : 16-1 (#152 socle par ligne) → 16-2 (#144 défaut catalogue) → 16-3 (#151 reliquat PDF). # ÉLU PROCHAIN EPIC — kickoff dès le merge de la PR groupée Epic 14. Reste #144 (compte produit au catalogue) + #152 (compte produit par ligne) ; #151 (récap TVA facture) déjà mergé hors-bande via PR #267 (bb3c8d64, partiel : coord. émetteur / n° client restants). Capte aussi les CR utilisateurs #265/#266/#268. # Facturation avancée (#144/#152/#151) — Epic dédié acté 2026-06-04 From 7e529131eb405c960c149ad1588b47905728da3e Mon Sep 17 00:00:00 2001 From: Guy Corbaz Date: Sat, 3 Oct 2026 11:25:56 +0200 Subject: [PATCH 02/13] docs(25-5-b): prompt de validation P1 (refs #385) Co-Authored-By: Claude Opus 5.5 --- .../25-5-b-validate-prompt-p1.md | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 _bmad-output/implementation-artifacts/25-5-b-validate-prompt-p1.md diff --git a/_bmad-output/implementation-artifacts/25-5-b-validate-prompt-p1.md b/_bmad-output/implementation-artifacts/25-5-b-validate-prompt-p1.md new file mode 100644 index 000000000..e426cb8df --- /dev/null +++ b/_bmad-output/implementation-artifacts/25-5-b-validate-prompt-p1.md @@ -0,0 +1,48 @@ +# Prompt — validation P1, Story 25-5-b (la balance porte un solde d'ouverture) + +*Versionné le 2026-10-03. Trois lentilles (Sonnet), contexte frais chacune.* + +Dépôt `/home/gcorbaz/devel/kesh`, branche `story/25-5-b-balance-solde-ouverture`. **Fiche à valider :** +`_bmad-output/implementation-artifacts/25-5-b-balance-solde-ouverture.md` (commit `ac40f66b`). Issue : `gh issue view 385`. +Règles : `CLAUDE.md`. La fiche décrit du travail **à faire** : un finding qui reproche au code de ne pas encore le porter +sera rejeté. + +## Lentilles + +- **A — Exactitude comptable.** La règle d'ouverture (bilan depuis l'origine, résultat depuis le début de l'exercice) + est-elle juste pour une balance suisse ? La ligne calculée du résultat reporté rend-elle **réellement** les colonnes + d'ouverture et de clôture équilibrées — refais le calcul sur deux exercices (vente et charge en exercice 1, vente en + exercice 2, balance de l'exercice 2 entière puis d'une période mars–juin) : Σ des ouvertures en sens débit − + `retained_earnings` = 0 ? Et la clôture ? Le résultat **de l'exercice en cours avant la période** (janvier–février) + est-il compté une fois, pas deux (dans les ouvertures des comptes de résultat ET dans `retained_earnings`) ? Le + contrôle de l'AC 5 est-il une vraie vérification ou une identité ? La concordance avec le bilan tient-elle quand la + période ne finit pas au dernier jour de l'exercice ? Un compte physique de rôle `RetainedEarnings` (report d'ouverture + d'un migrant) est-il compté deux fois avec la ligne calculée ? +- **B — Faisabilité dans le code.** Chaque référence `fichier:ligne` de la fiche existe-t-elle et dit-elle ce que la fiche + affirme (`grep -nF`, `sed -n`) ? L'extraction de l'AC 2 est-elle possible sans changer le comportement du grand livre + (lis `general_ledger.rs` en entier) ? `fetch_retained_earnings` prend-il la bonne date ? La requête agrégée unique + est-elle écrivable (règle d'inclusion sur un solde d'ouverture non nul : `HAVING` ou sous-requête ?) ? Les six colonnes + tiennent-elles en A4 portrait avec la police et les positions actuelles (`pdf.rs`) ? Tous les consommateurs de + `TrialBalance`/`TrialBalanceRow` et de la clé `reports-trial-balance-period-note` sont-ils listés (`grep -rn`) ? Le + périmètre (trois modules de code) est-il exact ? +- **C — Complétude et tests.** Chaque AC est-il testable, et le test prévu aurait-il échoué avant le patch ? Manque-t-il + un cas (période d'un seul jour ; premier exercice, `retained_earnings` = 0 ; exercice sans écriture ; compte de bilan + sans aucun mouvement ni ouverture ; écriture datée le jour même du début de période) ? Le manuel : relis + `docs/manual/fr/user-manual.tex` § *Balance des comptes*, § *Grand livre*, glossaire **et le PDF aplati** + (`pdftotext docs/manual/fr/user-manual.pdf - | tr '\n' ' ' | tr -s ' '` vers + `/tmp/claude-1000/-home-gcorbaz-devel-kesh/379e6f94-8029-42cb-9720-fa27c2fb204c/scratchpad/`) : la fiche liste-t-elle + **tous** les sites que le patch rendra faux ? Pars du symptôme : `grep -rn "mouvement de la période\|balance de vérification\|Balance des comptes" docs website README.md crates/kesh-i18n frontend/src`. + CHANGELOG, README, site. + +## Ce que tu rends + +Findings avec sévérité (CRITICAL/HIGH/MEDIUM/LOW), endroit, **preuve** (commande ET sortie, `grep -nF` pour toute +présence ou absence ; calcul chiffré pour la lentille A), correction proposée. ⛔ La liste des axes exercés ET non +exercés. + +## Interdits + +⛔ N'écris aucun fichier du dépôt ; aucune commande qui écrit dans le dépôt ou dans une base : `scripts/*` (dont +`scripts/prepare-release.sh`), `make`, `latexmk`, `git commit`/`add`/`checkout`, `sqlx`, `cargo test`/`nextest`, +`npm run`, `npx`, `gh issue create`/`comment`/`edit`. Autorisés : lecture, `grep`, `sed`, `git log`/`show`/`diff`, +`gh issue view`, `pdftotext` vers le scratchpad. From b6c1d5f415a35cf5b81f22d6b681ff951f5b3dac Mon Sep 17 00:00:00 2001 From: Guy Corbaz Date: Sat, 3 Oct 2026 11:34:19 +0200 Subject: [PATCH 03/13] =?UTF-8?q?docs(25-5-b):=20validation=20P1=20?= =?UTF-8?q?=E2=80=94=20fronti=C3=A8re,=20contr=C3=B4le=20test=C3=A9=20?= =?UTF-8?q?=C3=A0=20faux,=20sens=20d=C3=A9bit,=20PDF=20chiffr=C3=A9=20(ref?= =?UTF-8?q?s=20#385)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- .../25-5-b-balance-solde-ouverture.md | 45 +++++++++++++++---- 1 file changed, 37 insertions(+), 8 deletions(-) diff --git a/_bmad-output/implementation-artifacts/25-5-b-balance-solde-ouverture.md b/_bmad-output/implementation-artifacts/25-5-b-balance-solde-ouverture.md index b20ed8aae..4e6291e90 100644 --- a/_bmad-output/implementation-artifacts/25-5-b-balance-solde-ouverture.md +++ b/_bmad-output/implementation-artifacts/25-5-b-balance-solde-ouverture.md @@ -26,7 +26,7 @@ afin que **le solde de clôture d'un compte de bilan soit le même nombre que ce - **Le moteur existe — dans le grand livre** (Story 24-1). `general_ledger.rs:11-35` pose **la règle d'ouverture** : `Asset`/`Liability` = cumul `entry_date < from`, tous exercices ; `Revenue`/`Expense` = cumul depuis le **début de l'exercice contenant `from`** (0 si `from` est ce premier jour). Implémentée par `opening_balance` (`:220-275`), avec - `is_debit_natured` / `signed` (`:182-192`) et `fiscal_year_start_containing` (`:194-217`) — **privés au module**, et + `is_debit_natured` / `signed` (`:182-193`) et `fiscal_year_start_containing` (`:195-218`) — **privés au module**, et **requête par compte**. - **Un test de concordance existe déjà** entre grand livre et balance (`crates/kesh-report/tests/general_ledger.rs:150-205`) : il ne compare que `total_debit` / `total_credit` — les mouvements, la seule chose que la balance porte aujourd'hui. @@ -76,8 +76,12 @@ ouverture = clôture = `retained_earnings`, mouvements vides. Ce n'est **pas** u d'`account_id`, pas de lien vers le grand livre). **AC 5 — Un contrôle, pas une tautologie.** `TrialBalance` gagne `opening_balanced` et `closing_balanced` (`bool`) : -Σ des ouvertures (resp. clôtures) **exprimées en sens débit** (positif pour un compte à nature débitrice, négatif sinon) -moins `retained_earnings` vaut zéro. Le résultat reporté est calculé **indépendamment** (comme au bilan), donc +`Σ debit_sense(row.opening_balance) − retained_earnings = 0` (resp. `closing_balance`), où +`debit_sense(v) = if is_debit_natured(row.account_type) { v } else { −v }` — les soldes stockés sont **déjà signés par +type** : les additionner sans re-signer les comptes à nature créditrice donnerait un faux ⚠️ sur des livres exacts +(validation P1, calcul : 1000 − 600 ≠ 0 sur un jeu équilibré). Vérification chiffrée : ventes 1000 et charge 400 en +exercice 1, balance de l'exercice 2 → ouvertures en sens débit 1000 (clients) − 400 (caisse) = 600, `retained_earnings` +600, contrôle nul. Le résultat reporté est calculé **indépendamment** (comme au bilan), donc l'égalité est une vérification réelle, pas une identité de construction. Un déséquilibre n'est **pas** une erreur (à la différence de `TrialBalanceUnbalanced` sur les mouvements) : il est **journalisé** (`tracing::error!`) et affiché (⚠️), comme `equation_holds` au bilan. @@ -94,8 +98,11 @@ donc (marqueur `active: false`), sinon la clôture ne concorderait pas avec le b - **CSV** : `NumeroCompte;NomCompte;SoldeOuverture;TotalDebit;TotalCredit;SoldeCloture` — la colonne `Solde` est **remplacée**, pas renommée en silence (CHANGELOG). Ligne de résultat reporté (libellé fr fixe, comme les autres en-têtes CSV), puis ligne `Total` (débit, crédit ; ouverture et clôture vides). -- **PDF** : six colonnes en A4 portrait — nom tronqué plus court (la troncature existante s'adapte), libellés - `ledger_opening` / `ledger_closing` réutilisés, ligne `retained_result_label`. Garde « vide » inchangée +- **PDF** : six colonnes en A4 portrait. La mise en page à cinq colonnes occupe **déjà** toute la largeur + (20 + 70 + 3 × 35 = 195 mm = 210 − 15, validation P1) : positions imposées `MARGIN_LEFT_MM +` **0** (N°), **20** + (intitulé), **75** (ouverture), **105** (débit), **135** (crédit), **165** (clôture) — colonnes de montant de 30 mm + (« -1'234'567.89 » y tient), intitulé de 55 mm tronqué à **22** caractères (au lieu de 30, constante nommée). + Libellés `ledger_opening` / `ledger_closing` réutilisés, ligne `retained_result_label`. Garde « vide » inchangée (`rows.is_empty()`). - `isReportEmpty('trial-balance', …)` (`reports.api.ts:169-172`) : inchangé — les comptes actifs sont toujours rendus. @@ -112,12 +119,26 @@ Les tests appellent donc **réellement** `generate_balance_sheet`, `generate_inc de produits **= 0** au premier jour de l'exercice 2 (et non le cumul de l'exercice 1) ; - une période **en cours d'exercice** (mars–juin de l'exercice 2) : ouverture du compte de produits = cumul janvier– février ; clôture = celle du grand livre sur la même période (`section.closing`) ; -- `opening_balanced` et `closing_balanced` vrais dans ces scénarios ; +- **la frontière** : dans la période mars–juin, une écriture datée **le 1er mars** (comptée dans les mouvements, + absente de l'ouverture) et une datée **le 28 février** (dans l'ouverture, absente des mouvements) — la mutation + `<` → `<=` compterait la première deux fois et seule la comparaison au bilan la voit ; +- une période **d'un seul jour** (`start = end`), une écriture ce jour-là et une la veille ; +- le **premier exercice** d'une société (aucune écriture antérieure) : `retained_earnings = 0`, ouvertures nulles, + contrôles vrais ; un exercice **sans aucune écriture** : tout à zéro, contrôles vrais ; +- des **pertes cumulées** (charges > produits en exercice 1) : `retained_earnings < 0`, libellé « Perte reportée », + contrôles vrais ; +- `opening_balanced` et `closing_balanced` vrais dans ces scénarios — et **faux** quand l'égalité est réellement + cassée : une ligne d'écriture **déséquilibrée insérée en SQL brut** (hors `create_in_tx`) datée dans l'exercice 1 → + balance de l'exercice 2 rendue (pas d'erreur : les mouvements de la période restent équilibrés), `opening_balanced` + et `closing_balanced` à `false`. Sans ce test, le contrôle reproduit l'angle mort d'`equation_holds`, jamais testé à + `false` dans le dépôt (validation P1) ; +- un test **unitaire** de `debit_sense` (compte à nature créditrice re-signé) ; - un compte **archivé** à solde d'ouverture non nul, sans mouvement → présent, clôture = bilan ; - non-régression : mouvements, `balance`, `balanced` identiques à avant sur le jeu existant (`report_aggregates.rs`) ; - le test existant `general_ledger.rs:150-205` étendu à `opening` / `closing`. -- Rendus : CSV (en-tête à six colonnes, ligne de résultat reporté, ligne `Total`) ; PDF (rendu non vide avec la ligne - calculée) ; **Vitest** de la vue (`TrialBalanceView.test.ts`, à créer : colonnes, ligne calculée, ✓/⚠️, note absente) ; +- Rendus : CSV (en-tête à six colonnes, **valeurs** d'ouverture et de clôture dans l'ordre des colonnes, ligne de + résultat reporté, ligne `Total`) ; PDF (rendu non vide avec la ligne calculée ; la troncature à 22 caractères testée + sur un intitulé de 38) ; **Vitest** de la vue (`TrialBalanceView.test.ts`, à créer : colonnes, ligne calculée, ✓/⚠️, note absente) ; test d'API sur la forme de la réponse (`reports_e2e.rs`) ; **E2E** : la balance affiche les colonnes Ouverture / Clôture (`reports.spec.ts`, sélecteurs ancrés). @@ -155,6 +176,8 @@ corrigé (« à une date donnée » → sur une période, ouverture et clôture) `balance_sheet.rs:40-55`) : le compte physique reste une ligne de compte, la ligne calculée reste à part. - **Ne pas** borner l'ouverture des comptes de bilan par `fiscal_year_id` : c'est exactement le défaut de #385. - **Ne pas** introduire de filtre de niveau de détail pour rendre le manuel vrai : c'est le manuel qu'on corrige. +- La « requête agrégée unique » (AC 2) n'est tenue par **aucun test** (pas de compteur de requêtes dans le dépôt) : + c'est un contrôle de **revue de code**, assumé comme tel. ### Limites assumées (à écrire au manuel et en doc de module) @@ -189,6 +212,12 @@ préventif*. ## Change Log +- **2026-10-03** — Validation P1 (Sonnet ×3, prompt `25-5-b-validate-prompt-p1.md`) : **1 HIGH, 4 MED**, LOW. Retenus : + la frontière `entry_date = start` testée, et la veille (C1, HIGH) ; la formule du sens débit écrite et chiffrée (A) ; + le contrôle testé à `false` par une ligne déséquilibrée insérée en SQL brut (C2) ; premier exercice (C3) ; positions + des six colonnes du PDF chiffrées — la largeur est déjà saturée à cinq (B) ; période d'un jour, exercice vide, pertes + cumulées, valeurs CSV, troncature, requête unique laissée à la revue, deux références décalées (LOW). Règle, ligne + calculée et contrôle **confirmés par le calcul** sur trois jeux (lentille A, mutation #385 détectée : −1000 ≠ 0). - **2026-10-03** — Créée (Guy : « pousse et continue »). [#385]: https://github.com/guycorbaz/kesh/issues/385 From 6fc79cf5c4fa67248c17324e65e44287b5b40d99 Mon Sep 17 00:00:00 2001 From: Guy Corbaz Date: Sat, 3 Oct 2026 11:34:46 +0200 Subject: [PATCH 04/13] docs(25-5-b): prompt de validation P2 (refs #385) Co-Authored-By: Claude Opus 5.5 --- .../25-5-b-validate-prompt-p2.md | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 _bmad-output/implementation-artifacts/25-5-b-validate-prompt-p2.md diff --git a/_bmad-output/implementation-artifacts/25-5-b-validate-prompt-p2.md b/_bmad-output/implementation-artifacts/25-5-b-validate-prompt-p2.md new file mode 100644 index 000000000..f7a91a9a1 --- /dev/null +++ b/_bmad-output/implementation-artifacts/25-5-b-validate-prompt-p2.md @@ -0,0 +1,42 @@ +# Prompt — validation P2, Story 25-5-b (la balance porte un solde d'ouverture) + +*Versionné le 2026-10-03. Une passe (Opus), contexte frais, les trois lentilles. Trend : P1 1H/4M (Sonnet ×3).* + +Dépôt `/home/gcorbaz/devel/kesh`, branche `story/25-5-b-balance-solde-ouverture`. **Fiche :** +`_bmad-output/implementation-artifacts/25-5-b-balance-solde-ouverture.md` (tête `b6c1d5f4`). Remédiation de P1 : +`git diff 7e529131 b6c1d5f4`. Issue : `gh issue view 385`. Règles : `CLAUDE.md`. La fiche décrit du travail **à faire** : +un finding qui reproche au code de ne pas encore le porter sera rejeté. + +## Ce que tu vérifies + +1. **La remédiation de P1** (le diff) : chaque ajout est-il juste ? En particulier : + - la formule `debit_sense` et la vérification chiffrée de l'AC 5 — refais le calcul ; + - le test du contrôle à `false` : une ligne déséquilibrée insérée en SQL brut dans l'exercice 1 casse-t-elle + **réellement** les deux égalités (ouverture ET clôture) sans faire échouer le rapport ? `fetch_retained_earnings` + lit-il cette ligne (selon le type du compte choisi, elle tombe dans le résultat reporté ou dans une ouverture — + l'écart survit-il ?) ; `TrialBalanceUnbalanced` (mouvements de la période) reste-t-il muet ? + - les positions des six colonnes du PDF (`pdf.rs`, police Helvetica 10 pt, `MARGIN_LEFT_MM`, `PAGE_WIDTH_MM`, + `truncate_with_ellipsis`) : chevauchement ? la colonne de clôture déborde-t-elle de la page ? + - les scénarios ajoutés à l'AC 9 contredisent-ils un autre AC ? +2. **Ce que P1 n'a pas regardé** — cohérence interne de la fiche (AC, tâches, Dev Notes, *Modules*) ; la règle d'ouverture + pour une période **dont le début n'est pas le début de l'exercice** combinée à la règle d'inclusion (AC 6) ; un compte de + **résultat** archivé à ouverture non nulle en cours d'exercice ; la ligne calculée au CSV (libellé fixe fr — cohérent + avec les autres lignes CSV de `csv.rs` ?) ; la doc de l'API publique (`docs/` — `grep -rn "trial-balance" docs`) et le + manuel admin s'ils décrivent la réponse ; le test de concordance existant `general_ledger.rs:150-205` : étendu à + `opening`/`closing`, son scénario (exercice entier) prouve-t-il quelque chose sur l'ouverture ? +3. **Le manuel** — `docs/manual/fr/user-manual.tex` (§ *Balance des comptes*, § *Grand livre*, glossaire) **et le PDF + aplati** (`pdftotext docs/manual/fr/user-manual.pdf - | tr '\n' ' ' | tr -s ' '` vers + `/tmp/claude-1000/-home-gcorbaz-devel-kesh/379e6f94-8029-42cb-9720-fa27c2fb204c/scratchpad/`) : la fiche nomme-t-elle + tous les sites que le patch rendra faux ? + +## Ce que tu rends + +Findings avec sévérité (CRITICAL/HIGH/MEDIUM/LOW), endroit, **preuve** (commande ET sortie, `grep -nF` pour toute +présence ou absence, calcul chiffré), correction proposée. ⛔ La liste des axes exercés ET non exercés. + +## Interdits + +⛔ N'écris aucun fichier du dépôt ; aucune commande qui écrit dans le dépôt ou dans une base : `scripts/*` (dont +`scripts/prepare-release.sh`), `make`, `latexmk`, `git commit`/`add`/`checkout`, `sqlx`, `cargo test`/`nextest`, +`npm run`, `npx`, `gh issue create`/`comment`/`edit`. Autorisés : lecture, `grep`, `sed`, `git log`/`show`/`diff`, +`gh issue view`, `pdftotext` vers le scratchpad. From 013b2ae76b3c53fb410bd1f6dee342339cde9055 Mon Sep 17 00:00:00 2001 From: Guy Corbaz Date: Sat, 3 Oct 2026 11:44:11 +0200 Subject: [PATCH 05/13] =?UTF-8?q?docs(25-5-b):=20validation=20P2=20?= =?UTF-8?q?=E2=80=94=20un=20seul=20contr=C3=B4le,=20forme=20de=20la=20requ?= =?UTF-8?q?=C3=AAte,=20libell=C3=A9s=20courts=20(refs=20#385)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- .../25-5-b-balance-solde-ouverture.md | 112 ++++++++++++------ 1 file changed, 75 insertions(+), 37 deletions(-) diff --git a/_bmad-output/implementation-artifacts/25-5-b-balance-solde-ouverture.md b/_bmad-output/implementation-artifacts/25-5-b-balance-solde-ouverture.md index 4e6291e90..eccf75a97 100644 --- a/_bmad-output/implementation-artifacts/25-5-b-balance-solde-ouverture.md +++ b/_bmad-output/implementation-artifacts/25-5-b-balance-solde-ouverture.md @@ -37,7 +37,9 @@ afin que **le solde de clôture d'un compte de bilan soit le même nombre que ce `pdf.rs:699-850` (5 colonnes, A4 portrait, nom tronqué à 30 caractères, positions `MARGIN_LEFT_MM + 20/90/125/160`). Les libellés `ledger_opening`, `ledger_closing` et `retained_result_label` existent déjà dans `SectionLabels` (`pdf.rs:74-140`) ; côté écran, `reports-ledger-opening`, `reports-ledger-closing` et - `reports-retained-earnings-calculated` / `-loss` existent déjà dans les 4 locales. + `reports-retained-earnings-calculated` / `-loss` existent déjà dans les 4 locales. ⚠️ Les libellés « Solde + d'ouverture » / « Solde de clôture » sont **trop longs** pour les colonnes de la balance (AC 7) : seuls + `retained_result_label` et `reports-retained-earnings-*` sont réutilisés. - **Consommateurs de `TrialBalance` / `TrialBalanceRow`** (construction littérale à compléter) : `csv.rs` (tests `:820`, `:1128`), `pdf.rs` (`fixture_tb`, `:1810`), `benches/export.rs:100-125`. Appels de `generate` : `kesh-api/src/routes/reports.rs:399`, `:991` ; tests `kesh-db/tests/report_aggregates.rs` (4 sites), @@ -56,11 +58,17 @@ afin que **le solde de clôture d'un compte de bilan soit le même nombre que ce période commence le premier jour de l'exercice. Le début de l'exercice est celui de `period.fiscal_year_id` (la `ReportPeriod` est déjà bornée à cet exercice). **Clôture = ouverture + net des mouvements** (`balance`). -**AC 2 — Une seule règle, pas deux copies.** `is_debit_natured`, `signed` et la règle de borne basse sont **extraits** -de `general_ledger.rs` dans un module partagé de `kesh-report` (ex. `crate::opening`) que les deux rapports appellent ; -le grand livre garde son comportement (ses tests restent verts sans modification de leurs assertions). La balance -calcule ses ouvertures en **une requête agrégée** (pas une requête par compte) : les sommes conditionnelles sur -`entry_date` suffisent. +**AC 2 — Une seule règle, pas deux copies — et la forme qui le permet.** La règle type → borne vit **en Rust, une +fois** : un module partagé de `kesh-report` (ex. `crate::opening`) porte `is_debit_natured`, `signed` et +`opening_from(t, before_start: (debit, credit), since_fy_start: (debit, credit)) -> Decimal` (bilan : la première +paire ; résultat : la seconde ; signé par type). La balance fait **une requête agrégée** (pas une requête par compte) +qui rend, par compte, **trois paires de sommes brutes** — `entry_date < start`, `entry_date ∈ [fy_start, start[`, et les +mouvements de la période — **sans** `CASE` sur le type ni sur le signe en SQL (le `CASE` de signe actuel, +`trial_balance.rs:71-75`, disparaît au profit de `signed`). Le grand livre appelle `opening_from` lui aussi (sa +requête d'ouverture rend les deux paires) ; ses tests restent verts sans retouche d'assertion. ⚠️ `TrialBalanceRow` +dérive `sqlx::FromRow` (`trial_balance.rs:35`) : un champ calculé en Rust ferait échouer la lecture **à l'exécution** +(`ColumnNotFound`), invisible à la compilation — lire dans une structure brute intermédiaire, puis construire la +ligne. **AC 3 — Les mouvements ne changent pas.** `total_debit`, `total_credit` (par ligne et au total), `balance` (net des mouvements) et `balanced` gardent **exactement** leur sens et leurs valeurs actuels. `balance` est documenté au champ @@ -73,41 +81,51 @@ gagne `retained_earnings: Decimal` — **même valeur** que `BalanceSheet::retai `fetch_retained_earnings` (`balance_sheet.rs:333`, privée) est rendue visible au crate (`pub(crate)`), pas recopiée. Il est rendu comme une **ligne à part**, après les comptes, libellée « Résultat reporté (calculé) » (« Perte reportée » si négatif, comme au bilan), ouverture = clôture = `retained_earnings`, mouvements vides. Ce n'est **pas** une `TrialBalanceRow` (pas -d'`account_id`, pas de lien vers le grand livre). +d'`account_id`, pas de lien vers le grand livre). ⚠️ La bascule sur « Perte reportée » n'existe **qu'à l'écran**, comme +au bilan (`BalanceSheetView.svelte:97-100`) ; le CSV et le PDF du bilan portent un libellé fixe (`csv.rs:140-144`, +`pdf.rs:596-599`) — la balance fait de même. -**AC 5 — Un contrôle, pas une tautologie.** `TrialBalance` gagne `opening_balanced` et `closing_balanced` (`bool`) : -`Σ debit_sense(row.opening_balance) − retained_earnings = 0` (resp. `closing_balance`), où +**AC 5 — Un contrôle, pas une tautologie.** `TrialBalance` gagne **un** indicateur `opening_balanced` (`bool`) : +`Σ debit_sense(row.opening_balance) − retained_earnings = 0`, où `debit_sense(v) = if is_debit_natured(row.account_type) { v } else { −v }` — les soldes stockés sont **déjà signés par type** : les additionner sans re-signer les comptes à nature créditrice donnerait un faux ⚠️ sur des livres exacts (validation P1, calcul : 1000 − 600 ≠ 0 sur un jeu équilibré). Vérification chiffrée : ventes 1000 et charge 400 en exercice 1, balance de l'exercice 2 → ouvertures en sens débit 1000 (clients) − 400 (caisse) = 600, `retained_earnings` -600, contrôle nul. Le résultat reporté est calculé **indépendamment** (comme au bilan), donc +600, contrôle nul. **Pas de `closing_balanced`** (validation P2) : Σ debit_sense(clôture) = Σ debit_sense(ouverture) + +(total débit − total crédit des mouvements), et des mouvements déséquilibrés font déjà échouer le rapport +(`TrialBalanceUnbalanced`) — un second indicateur serait la copie du premier, une identité de construction. Le résultat reporté est calculé **indépendamment** (comme au bilan), donc l'égalité est une vérification réelle, pas une identité de construction. Un déséquilibre n'est **pas** une erreur (à la -différence de `TrialBalanceUnbalanced` sur les mouvements) : il est **journalisé** (`tracing::error!`) et affiché (⚠️), -comme `equation_holds` au bilan. +différence de `TrialBalanceUnbalanced` sur les mouvements) : il est **journalisé** en `tracing::warn!` — le niveau +d'`equation_holds` au bilan (`balance_sheet.rs:237`) — et affiché (⚠️). **AC 6 — La règle d'inclusion.** Un compte est rendu s'il est **actif**, **ou** s'il a un mouvement dans la période, **ou** si son **solde d'ouverture est non nul**. Un compte archivé à solde d'ouverture non nul sans mouvement apparaît donc (marqueur `active: false`), sinon la clôture ne concorderait pas avec le bilan. Doc du module mise à jour. **AC 7 — Les rendus.** -- **Écran** (`TrialBalanceView.svelte`) : colonnes N° | Intitulé | **Ouverture** | Débit | Crédit | **Clôture** ; la - colonne « Solde » (net des mouvements) disparaît de l'écran ; pied : totaux débit/crédit et ✓/⚠️ des mouvements comme - aujourd'hui, plus un indicateur ✓/⚠️ pour l'ouverture et la clôture (AC 5) ; la ligne calculée (AC 4) avant le pied. +- **Écran** (`TrialBalanceView.svelte`) : colonnes N° | Intitulé | **Ouverture** | Débit | Crédit | **Clôture** — clés + **neuves et courtes** `reports-column-opening` (« Ouverture ») et `reports-column-closing` (« Clôture ») dans les 4 + locales (les clés du grand livre rendent « Solde d'ouverture », trop long et non ancrable) ; la colonne « Solde » (net + des mouvements) disparaît de l'écran ; pied : totaux débit/crédit et ✓/⚠️ des mouvements comme aujourd'hui, plus + ✓/⚠️ d'`opening_balanced` sous la colonne Ouverture (AC 5) ; la ligne calculée (AC 4) avant le pied. La note `reports-trial-balance-period-note` est **retirée** (clé supprimée des 4 locales) — elle deviendrait fausse. - **CSV** : `NumeroCompte;NomCompte;SoldeOuverture;TotalDebit;TotalCredit;SoldeCloture` — la colonne `Solde` est - **remplacée**, pas renommée en silence (CHANGELOG). Ligne de résultat reporté (libellé fr fixe, comme les autres en-têtes - CSV), puis ligne `Total` (débit, crédit ; ouverture et clôture vides). + **remplacée**, pas renommée en silence (CHANGELOG — ⚠️ changement incompatible pour qui lit ce CSV, dans une version + 0.x : arbitrage de Guy à recueillir, défaut retenu : remplacer). Ligne de résultat reporté sur le patron du bilan + (`csv.rs:139-145`) : numéro **vide**, libellé fixe « Résultat reporté (calculé) » dans la colonne du nom, ouverture et + clôture remplies, débit et crédit vides ; puis ligne `Total` (débit, crédit ; ouverture et clôture vides). - **PDF** : six colonnes en A4 portrait. La mise en page à cinq colonnes occupe **déjà** toute la largeur (20 + 70 + 3 × 35 = 195 mm = 210 − 15, validation P1) : positions imposées `MARGIN_LEFT_MM +` **0** (N°), **20** (intitulé), **75** (ouverture), **105** (débit), **135** (crédit), **165** (clôture) — colonnes de montant de 30 mm (« -1'234'567.89 » y tient), intitulé de 55 mm tronqué à **22** caractères (au lieu de 30, constante nommée). - Libellés `ledger_opening` / `ledger_closing` réutilisés, ligne `retained_result_label`. Garde « vide » inchangée + En-têtes : deux champs **neufs** de `SectionLabels`, `col_opening` = « Ouverture » et `col_closing` = « Clôture » + (défauts fr de `PdfContext::fr_ch_default`) — « Solde d'ouverture » en Helvetica-Bold 10 pt mesure 29,85 mm et + toucherait « Débit », « Solde de clôture » finirait à 2,5 mm du bord (validation P2). Ligne `retained_result_label`. Garde « vide » inchangée (`rows.is_empty()`). - `isReportEmpty('trial-balance', …)` (`reports.api.ts:169-172`) : inchangé — les comptes actifs sont toujours rendus. **AC 8 — L'API et les types.** `openingBalance`, `closingBalance` sur chaque ligne ; `retainedEarnings`, -`openingBalanced`, `closingBalanced` au niveau du rapport — dans la réponse de `GET /api/v1/reports/trial-balance` et dans +`openingBalanced` au niveau du rapport — dans la réponse de `GET /api/v1/reports/trial-balance` et dans `reports.types.ts`. Libellés neufs éventuels dans les 4 locales, `sitesTotal` recompté depuis la source. **AC 9 — La concordance, prouvée en appelant les autres rapports.** Le motif d'échec redouté est **muet** : @@ -120,22 +138,30 @@ Les tests appellent donc **réellement** `generate_balance_sheet`, `generate_inc - une période **en cours d'exercice** (mars–juin de l'exercice 2) : ouverture du compte de produits = cumul janvier– février ; clôture = celle du grand livre sur la même période (`section.closing`) ; - **la frontière** : dans la période mars–juin, une écriture datée **le 1er mars** (comptée dans les mouvements, - absente de l'ouverture) et une datée **le 28 février** (dans l'ouverture, absente des mouvements) — la mutation - `<` → `<=` compterait la première deux fois et seule la comparaison au bilan la voit ; + absente de l'ouverture) et une datée **la veille du début de période** (dans l'ouverture, absente des mouvements) — + la mutation `<` → `<=` compterait la première deux fois, et seules les comparaisons au bilan et au grand livre la + voient (celle au grand livre seulement si la borne n'est pas dans le module partagé) ; - une période **d'un seul jour** (`start = end`), une écriture ce jour-là et une la veille ; - le **premier exercice** d'une société (aucune écriture antérieure) : `retained_earnings = 0`, ouvertures nulles, - contrôles vrais ; un exercice **sans aucune écriture** : tout à zéro, contrôles vrais ; -- des **pertes cumulées** (charges > produits en exercice 1) : `retained_earnings < 0`, libellé « Perte reportée », - contrôles vrais ; -- `opening_balanced` et `closing_balanced` vrais dans ces scénarios — et **faux** quand l'égalité est réellement - cassée : une ligne d'écriture **déséquilibrée insérée en SQL brut** (hors `create_in_tx`) datée dans l'exercice 1 → - balance de l'exercice 2 rendue (pas d'erreur : les mouvements de la période restent équilibrés), `opening_balanced` - et `closing_balanced` à `false`. Sans ce test, le contrôle reproduit l'angle mort d'`equation_holds`, jamais testé à + contrôle vrai ; une **société sans aucune écriture** : tout à zéro, contrôle vrai ; un **exercice 2 ouvert et encore + vide** après un exercice 1 mouvementé — la première vue réelle d'une année neuve : ouvertures = clôtures de + l'exercice 1 pour les comptes de bilan, nulles pour les comptes de résultat, mouvements nuls, contrôle vrai ; +- des **pertes cumulées** (charges > produits en exercice 1) : `retained_earnings < 0`, contrôle vrai ; le libellé + « Perte reportée » est vérifié **dans le Vitest de la vue** (seul rendu qui bascule, AC 4) ; +- `opening_balanced` vrai dans ces scénarios — et **faux** quand l'égalité est réellement cassée : une ligne + d'écriture **déséquilibrée insérée en SQL brut** (hors `create_in_tx`) datée dans l'exercice 1 — sur un compte de + bilan **et**, dans un second cas, sur un compte de résultat (l'écart passe alors par `retained_earnings`) → balance + de l'exercice 2 rendue (pas d'erreur : les mouvements de la période restent équilibrés), `opening_balanced` à + `false`. Sans ce test, le contrôle reproduit l'angle mort d'`equation_holds`, jamais testé à `false` dans le dépôt (validation P1) ; - un test **unitaire** de `debit_sense` (compte à nature créditrice re-signé) ; -- un compte **archivé** à solde d'ouverture non nul, sans mouvement → présent, clôture = bilan ; +- un compte **archivé** à solde d'ouverture non nul, sans mouvement → présent, clôture = bilan ; et un compte de + **résultat** archivé, mouvementé en janvier, balance de mars–juin → présent (ouverture non nulle), `opening_balanced` + vrai (une règle d'inclusion limitée aux comptes de bilan le ferait disparaître et le contrôle rougirait) ; - non-régression : mouvements, `balance`, `balanced` identiques à avant sur le jeu existant (`report_aggregates.rs`) ; -- le test existant `general_ledger.rs:150-205` étendu à `opening` / `closing`. +- le test existant `concordance_compte_de_resultat` (`general_ledger.rs:135-205`) étendu à `opening` / `closing` **et + passé à une période en cours d'exercice** — sur l'exercice entier, les deux ouvertures valent 0 par construction et + une mutation « ouverture toujours nulle » survivrait. - Rendus : CSV (en-tête à six colonnes, **valeurs** d'ouverture et de clôture dans l'ordre des colonnes, ligne de résultat reporté, ligne `Total`) ; PDF (rendu non vide avec la ligne calculée ; la troncature à 22 caractères testée sur un intitulé de 38) ; **Vitest** de la vue (`TrialBalanceView.test.ts`, à créer : colonnes, ligne calculée, ✓/⚠️, note absente) ; @@ -148,19 +174,23 @@ comptes de résultat cumulée depuis l'origine », « règle d'inclusion sans le **AC 10 — Le manuel et le CHANGELOG.** `user-manual.tex` § *Balance des comptes* (`:1557-1559`) réécrit : les quatre colonnes, la règle d'ouverture (bilan depuis l'origine, résultat depuis le début de l'exercice), la ligne calculée, la concordance avec le bilan et le grand livre ; la phrase sur le **filtre de niveau de détail**, qui n'existe pas, -**retirée**. Le grand livre (`:1561-1565`, « là où la balance dit la même chose autrement ») relu. Glossaire `:2043` +**retirée**. Le grand livre (`:1561-1565`, « là où la balance dit la même chose autrement ») relu. Le manuel dit aussi : (a) les +soldes de départ sont une écriture datée du premier jour du premier exercice (`:615`) — ils apparaissent donc en +**mouvements** de cet exercice, ouverture nulle ; (b) Ouverture et Clôture sont **signées selon la nature du compte** : +leur somme visible n'est pas nulle (1000 − 400 + 600 = 1200 dans l'exemple de l'AC 5) alors que l'écran affiche ✓ — +le contrôle porte sur les soldes en sens débit. Glossaire `:2043` corrigé (« à une date donnée » → sur une période, ouverture et clôture). PDF régénéré, contrôlé **aplati** -(`pdftotext … | tr '\n' ' ' | tr -s ' '`). CHANGELOG `[Unreleased]` : `Changed` (colonnes de l'écran, du PDF et du CSV — +(`pdftotext … | tr '\n' ' ' | tr -s ' '`). CHANGELOG, section `## [0.12.1] — Non publié` (il n'y a pas de section `[Unreleased]`) : `Changed` (colonnes de l'écran, du PDF et du CSV — `Solde` remplacé) et `Fixed` (#385). README et site relus (`grep -rn "balance" README.md website/`). ## Tasks / Subtasks - [ ] **T1 — le module partagé** (AC 2) : extraire `is_debit_natured`, `signed`, la borne basse de l'ouverture ; le grand livre l'appelle ; ses tests verts sans retouche d'assertion. -- [ ] **T2 — la balance** (AC 1, 3, 4, 5, 6) : requête agrégée, champs neufs, ligne calculée, contrôles, inclusion. +- [ ] **T2 — la balance** (AC 1, 3, 4, 5, 6) : requête agrégée, champs neufs, ligne calculée, contrôle, inclusion. - [ ] **T3 — les rendus** CSV et PDF (AC 7), fixtures et bench complétés. -- [ ] **T4 — l'écran, les types, l'i18n** (AC 7, 8) : vue, `reports.types.ts`, clé de note retirée des 4 locales, - `sitesTotal`. +- [ ] **T4 — l'écran, les types, l'i18n** (AC 7, 8) : vue, `reports.types.ts`, clé de note retirée et deux clés de + colonne ajoutées dans les 4 locales, `sitesTotal`. - [ ] **T5 — tests** (AC 9). - [ ] **T6 — manuel, CHANGELOG** (AC 10). - [ ] **T7 — gates** : backend complet, frontend complet, **E2E complet**. @@ -188,8 +218,8 @@ corrigé (« à une date donnée » → sur une période, ouverture et clôture) ### Modules -`kesh-report` (balance, module partagé, rendus), `frontend` (vue, types, test), `kesh-i18n` (clé retirée, libellés -éventuels), + `docs`, `CHANGELOG`. `kesh-api` : aucun changement de code attendu (la route sérialise la structure ; +`kesh-report` (balance, module partagé, rendus, deux libellés PDF), `frontend` (vue, types, test), `kesh-i18n` (clé +retirée, deux clés de colonne), + `docs`, `CHANGELOG`. `kesh-api` : aucun changement de code attendu (la route sérialise la structure ; les libellés PDF existent) — tests seulement. **Trois modules de code**, sous le seuil de la § *Règle de splitting préventif*. @@ -212,6 +242,14 @@ préventif*. ## Change Log +- **2026-10-03** — Validation P2 (Opus, prompt `25-5-b-validate-prompt-p2.md`) : **0 CRITICAL/HIGH, 4 MED, 9 LOW**, + retenus. `closing_balanced` retiré — copie d'`opening_balanced` par construction (M1) ; la forme de l'AC 2 prescrite + (sommes brutes en SQL, règle en Rust partagée avec le grand livre ; piège `FromRow`) (M2) ; libellés courts + « Ouverture » / « Clôture » — les en-têtes du grand livre débordent au PDF (calcul AFM) et ne s'ancrent pas en E2E + (M3) ; « Perte reportée » à l'écran seulement, comme au bilan (M4) ; test de concordance du grand livre en cours + d'exercice, « société sans écriture » et exercice 2 vide, compte de résultat archivé, `warn!`, CHANGELOG `[0.12.1]` + et incompatibilité CSV à arbitrer, « la veille », colonne du libellé CSV, deux phrases au manuel (L1–L8). Trend : + P1 1H/4M → P2 4M. - **2026-10-03** — Validation P1 (Sonnet ×3, prompt `25-5-b-validate-prompt-p1.md`) : **1 HIGH, 4 MED**, LOW. Retenus : la frontière `entry_date = start` testée, et la veille (C1, HIGH) ; la formule du sens débit écrite et chiffrée (A) ; le contrôle testé à `false` par une ligne déséquilibrée insérée en SQL brut (C2) ; premier exercice (C3) ; positions From 639cabf4f6c66d162a1e1661e1b902b189748dc2 Mon Sep 17 00:00:00 2001 From: Guy Corbaz Date: Sat, 3 Oct 2026 11:44:29 +0200 Subject: [PATCH 06/13] =?UTF-8?q?docs(25-5-b):=20prompt=20de=20validation?= =?UTF-8?q?=20P3=20cibl=C3=A9e=20(refs=20#385)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- .../25-5-b-validate-prompt-p3.md | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 _bmad-output/implementation-artifacts/25-5-b-validate-prompt-p3.md diff --git a/_bmad-output/implementation-artifacts/25-5-b-validate-prompt-p3.md b/_bmad-output/implementation-artifacts/25-5-b-validate-prompt-p3.md new file mode 100644 index 000000000..053297ec3 --- /dev/null +++ b/_bmad-output/implementation-artifacts/25-5-b-validate-prompt-p3.md @@ -0,0 +1,36 @@ +# Prompt — validation P3 ciblée, Story 25-5-b + +*Versionné le 2026-10-03. **Une lentille** (Haiku), contexte frais — passe ciblée sur la remédiation de P2. Trend : +P1 1H/4M (Sonnet ×3) → P2 4M/9L (Opus).* + +Dépôt `/home/gcorbaz/devel/kesh`, branche `story/25-5-b-balance-solde-ouverture`. **Diff à relire, aplati, un seul +commit : `git diff 6fc79cf5 013b2ae7`** (la fiche `_bmad-output/implementation-artifacts/25-5-b-balance-solde-ouverture.md`). +La fiche décrit du travail **à faire** : un finding qui reproche au code de ne pas encore le porter sera rejeté. + +## Lentille — regression hunter + +1. **Cohérence interne après le patch** : `closing_balanced` a été retiré — reste-t-il une phrase (AC 5, 7, 8, 9, tâches, + Dev Notes) qui suppose deux indicateurs ou un ✓/⚠️ sous la colonne Clôture ? `grep -nF "closing" ` et lis chaque + occurrence. +2. **L'AC 2 réécrit** : la forme prescrite (trois paires de sommes brutes, `opening_from` en Rust) est-elle compatible + avec le grand livre tel qu'il est ? Lis `crates/kesh-report/src/general_ledger.rs:194-275` : sa requête d'ouverture + peut-elle rendre les deux paires sans changer son résultat ? `fiscal_year_start_containing` y renvoie `None` quand + aucun exercice ne couvre `from` — la forme prescrite conserve-t-elle ce cas ? +3. **Les libellés** : les clés `reports-column-opening` / `reports-column-closing` existent-elles **déjà** dans + `crates/kesh-i18n/locales/fr-CH/messages.ftl` (collision) ? `grep -n "reports-column-" …`. Les champs `col_opening` / + `col_closing` existent-ils déjà dans `SectionLabels` (`crates/kesh-report/src/pdf.rs`) ? +4. **Les références ajoutées** existent-elles et disent-elles ce que la fiche affirme : `balance_sheet.rs:237`, + `BalanceSheetView.svelte:97-100`, `csv.rs:139-145`, `csv.rs:140-144`, `pdf.rs:596-599`, `general_ledger.rs:135-205`, + `user-manual.tex:615`, `CHANGELOG.md` (section `## [0.12.1] — Non publié`) ? + +## Ce que tu rends + +Findings avec sévérité, endroit, **preuve obligatoire : la commande ET sa sortie** — un finding sans sortie de commande +sera rejeté, un « 0 finding » sans les sorties des quatre vérifications aussi. ⛔ La liste des axes exercés ET non +exercés. + +## Interdits + +⛔ N'écris aucun fichier du dépôt ; aucune commande qui écrit dans le dépôt ou dans une base (`scripts/*` dont +`scripts/prepare-release.sh`, `make`, `latexmk`, `git commit`/`add`/`checkout`, `sqlx`, `cargo test`/`nextest`, +`npm run`, `npx`, `gh issue create`/`comment`/`edit`). Autorisés : lecture, `grep`, `sed`, `git log`/`show`/`diff`. From 42623926995e95298e0152d2bd3c52421c02ddee Mon Sep 17 00:00:00 2001 From: Guy Corbaz Date: Sat, 3 Oct 2026 11:46:57 +0200 Subject: [PATCH 07/13] =?UTF-8?q?docs(25-5-b):=20validation=20P3=20cibl?= =?UTF-8?q?=C3=A9e=20=E2=80=94=200=20finding,=20boucle=20close=20(refs=20#?= =?UTF-8?q?385)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- .../25-5-b-balance-solde-ouverture.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/_bmad-output/implementation-artifacts/25-5-b-balance-solde-ouverture.md b/_bmad-output/implementation-artifacts/25-5-b-balance-solde-ouverture.md index eccf75a97..4ccaa68e9 100644 --- a/_bmad-output/implementation-artifacts/25-5-b-balance-solde-ouverture.md +++ b/_bmad-output/implementation-artifacts/25-5-b-balance-solde-ouverture.md @@ -65,7 +65,9 @@ paire ; résultat : la seconde ; signé par type). La balance fait **une requêt qui rend, par compte, **trois paires de sommes brutes** — `entry_date < start`, `entry_date ∈ [fy_start, start[`, et les mouvements de la période — **sans** `CASE` sur le type ni sur le signe en SQL (le `CASE` de signe actuel, `trial_balance.rs:71-75`, disparaît au profit de `signed`). Le grand livre appelle `opening_from` lui aussi (sa -requête d'ouverture rend les deux paires) ; ses tests restent verts sans retouche d'assertion. ⚠️ `TrialBalanceRow` +requête d'ouverture rend les deux paires ; sa période est libre — quand aucun exercice ne couvre `from`, +`fiscal_year_start_containing` rend `None` et il passe une paire **nulle** « depuis le début d'exercice », ce qui garde +l'ouverture d'un compte de résultat à zéro comme aujourd'hui) ; ses tests restent verts sans retouche d'assertion. ⚠️ `TrialBalanceRow` dérive `sqlx::FromRow` (`trial_balance.rs:35`) : un champ calculé en Rust ferait échouer la lecture **à l'exécution** (`ColumnNotFound`), invisible à la compilation — lire dans une structure brute intermédiaire, puis construire la ligne. @@ -242,6 +244,12 @@ préventif*. ## Change Log +- **2026-10-03** — Validation P3 ciblée (Haiku, prompt `25-5-b-validate-prompt-p3.md`) : **0 finding**, sorties des quatre + vérifications fournies. Le point 2 (compatibilité de l'AC 2 avec le grand livre), établi par raisonnement, **repris + par l'orchestrateur** : la période libre du grand livre (`LedgerPeriod`) impose de passer une paire nulle quand aucun + exercice ne couvre `from` — écrit à l'AC 2 (LOW). **Boucle close** : P1 1H/4M (Sonnet ×3) → P2 4M/9L (Opus) → P3 0 + (Haiku). ⚠️ Arbitrage de Guy attendu sur le CSV (remplacer `Solde` ou ajouter deux colonnes en fin de ligne) — défaut + retenu : remplacer. - **2026-10-03** — Validation P2 (Opus, prompt `25-5-b-validate-prompt-p2.md`) : **0 CRITICAL/HIGH, 4 MED, 9 LOW**, retenus. `closing_balanced` retiré — copie d'`opening_balanced` par construction (M1) ; la forme de l'AC 2 prescrite (sommes brutes en SQL, règle en Rust partagée avec le grand livre ; piège `FromRow`) (M2) ; libellés courts From 46edd970d559ecefe4c9e322f040849b40ddcba3 Mon Sep 17 00:00:00 2001 From: Guy Corbaz Date: Sat, 3 Oct 2026 12:16:58 +0200 Subject: [PATCH 08/13] feat(25-5-b): la balance porte un solde d'ouverture et concorde avec le bilan (refs #385) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ouverture (bilan depuis l'origine, résultat depuis le début de l'exercice), mouvements, clôture ; ligne calculée du résultat reporté et contrôle d'ouverture. Règle partagée avec le grand livre (crate::opening). Écran, PDF et CSV à six colonnes ; manuel et CHANGELOG. Co-Authored-By: Claude Opus 5.5 --- CHANGELOG.md | 6 + .../25-5-b-balance-solde-ouverture.md | 93 ++- .../sprint-status.yaml | 2 +- crates/kesh-api/tests/reports_e2e.rs | 7 + crates/kesh-i18n/locales/de-CH/messages.ftl | 3 +- crates/kesh-i18n/locales/en-CH/messages.ftl | 3 +- crates/kesh-i18n/locales/fr-CH/messages.ftl | 3 +- crates/kesh-i18n/locales/it-CH/messages.ftl | 3 +- crates/kesh-report/benches/export.rs | 4 + crates/kesh-report/src/balance_sheet.rs | 2 +- crates/kesh-report/src/csv.rs | 45 +- crates/kesh-report/src/general_ledger.rs | 120 +--- crates/kesh-report/src/lib.rs | 1 + crates/kesh-report/src/opening.rs | 136 +++++ crates/kesh-report/src/pdf.rs | 241 ++++---- crates/kesh-report/src/trial_balance.rs | 286 +++++++-- crates/kesh-report/tests/general_ledger.rs | 41 ++ .../tests/trial_balance_opening.rs | 559 ++++++++++++++++++ docs/manual/fr/user-manual.pdf | Bin 299414 -> 301695 bytes docs/manual/fr/user-manual.tex | 32 +- .../features/reports/TrialBalanceView.svelte | 54 +- .../features/reports/TrialBalanceView.test.ts | 101 ++++ .../src/lib/features/reports/reports.types.ts | 9 + frontend/src/lib/shared/i18n-keys.test.ts | 9 +- .../lib/shared/i18n-libelle-en-dur.test.ts | 11 +- frontend/tests/e2e/reports.spec.ts | 9 + 26 files changed, 1474 insertions(+), 306 deletions(-) create mode 100644 crates/kesh-report/src/opening.rs create mode 100644 crates/kesh-report/tests/trial_balance_opening.rs create mode 100644 frontend/src/lib/features/reports/TrialBalanceView.test.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index b8049521f..38791bc1e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,10 @@ Le contenu est rédigé en français à destination des **fiduciaires, PME, ind ### Fixed +- **La balance des comptes ne concordait pas avec le bilan ([#385](https://github.com/guycorbaz/kesh/issues/385)).** Elle ne montrait que les **mouvements** de la période : le compte « clients » de la balance 2026 ne valait pas celui du bilan 2026, qui reporte les soldes depuis l'origine. Deux rapports, un compte, deux nombres — c'est ce qu'un réviseur trouve en dix minutes. + + **Désormais**, chaque compte porte son **solde d'ouverture**, ses mouvements et son **solde de clôture**. Un compte de bilan reporte son solde depuis l'origine ; un compte de produits ou de charges repart de zéro au début de l'exercice. La clôture d'un compte de bilan **est** son solde au bilan, celle d'un compte de résultat son montant au compte de résultat, et les deux concordent avec le grand livre. Une ligne **« Résultat reporté (calculé) »** porte le résultat des exercices antérieurs, qu'aucun compte ne porte faute d'écriture de clôture, et un **contrôle ✓/⚠️** vérifie que les ouvertures s'équilibrent avec elle. Un compte archivé figure tant qu'il porte un solde. + - **L'export de souveraineté ne portait que la moitié de votre comptabilité.** Il promettait de quoi « migrer vers un autre logiciel » et laissait dehors **les factures fournisseurs, les avoirs, les règlements, les projets, les ordres de virement, les personnes de contact, les pièces reçues et le journal d'audit** — onze tables comptables sur les trente que compte désormais l'archive. ⚠️ **Un cas était particulièrement trompeur** : les écritures exportaient bien un identifiant de projet, mais la table des projets manquait — l'identifiant ne renvoyait donc à rien. *Un fichier qui a l'air complet est pire qu'un fichier manifestement incomplet.* @@ -48,6 +52,8 @@ Le contenu est rédigé en français à destination des **fiduciaires, PME, ind ### Changed +- **La balance des comptes change de colonnes — écran, PDF et CSV ([#385](https://github.com/guycorbaz/kesh/issues/385)).** Ouverture | Débit | Crédit | Clôture : la colonne **« Solde »**, qui était le net des mouvements de la période et se prenait pour un solde de compte, disparaît de l'écran et du PDF. ⚠️ **Dans le CSV, la colonne `Solde` est remplacée** par `SoldeOuverture` (avant `TotalDebit`) et `SoldeCloture` (en dernier) : un outil qui lit ce fichier par position doit être adapté. Le CSV porte aussi la ligne du résultat reporté. *(API : `openingBalance` et `closingBalance` par compte, `retainedEarnings` et `openingBalanced` au rapport ; `balance` garde son sens — le net des mouvements.)* + - **Le bouton *Créer un avoir* s'affiche aussi sur une facture payée**, et c'est voulu. Il était masqué sur une facture payée mais pas sur une facture réglée en partie : l'écran ne couvrait que la moitié de la règle. Il reste désormais visible, et Kesh explique le refus dans le dialogue — comme le bouton *Dévalider*. - **Supprimer une facture validée n'est plus possible — il faut la « dévalider » d'abord.** Jusqu'ici, le bouton *Supprimer* d'une facture validée effaçait la facture **et son écriture comptable** d'un seul geste. La destruction d'une écriture était donc un **effet de bord du mot « supprimer »** : rien, dans le vocabulaire de l'écran, ne disait qu'on touchait aux livres. diff --git a/_bmad-output/implementation-artifacts/25-5-b-balance-solde-ouverture.md b/_bmad-output/implementation-artifacts/25-5-b-balance-solde-ouverture.md index 4ccaa68e9..de29ac802 100644 --- a/_bmad-output/implementation-artifacts/25-5-b-balance-solde-ouverture.md +++ b/_bmad-output/implementation-artifacts/25-5-b-balance-solde-ouverture.md @@ -1,6 +1,6 @@ # Story 25.5-b : La balance des comptes porte un solde d'ouverture — et concorde enfin avec le bilan -Status: ready-for-dev +Status: review **Issue : [#385]**, que cette story **ferme** : la PR porte `closes #385` dans le **titre ET le corps**. Seconde moitié de la 25-5, découpée le 2026-09-23 (la 25-5-a a fermé #386). Branche `story/25-5-b-balance-solde-ouverture`, partie de @@ -187,15 +187,15 @@ corrigé (« à une date donnée » → sur une période, ouverture et clôture) ## Tasks / Subtasks -- [ ] **T1 — le module partagé** (AC 2) : extraire `is_debit_natured`, `signed`, la borne basse de l'ouverture ; le grand +- [x] **T1 — le module partagé** (AC 2) : extraire `is_debit_natured`, `signed`, la borne basse de l'ouverture ; le grand livre l'appelle ; ses tests verts sans retouche d'assertion. -- [ ] **T2 — la balance** (AC 1, 3, 4, 5, 6) : requête agrégée, champs neufs, ligne calculée, contrôle, inclusion. -- [ ] **T3 — les rendus** CSV et PDF (AC 7), fixtures et bench complétés. -- [ ] **T4 — l'écran, les types, l'i18n** (AC 7, 8) : vue, `reports.types.ts`, clé de note retirée et deux clés de +- [x] **T2 — la balance** (AC 1, 3, 4, 5, 6) : requête agrégée, champs neufs, ligne calculée, contrôle, inclusion. +- [x] **T3 — les rendus** CSV et PDF (AC 7), fixtures et bench complétés. +- [x] **T4 — l'écran, les types, l'i18n** (AC 7, 8) : vue, `reports.types.ts`, clé de note retirée et deux clés de colonne ajoutées dans les 4 locales, `sitesTotal`. -- [ ] **T5 — tests** (AC 9). -- [ ] **T6 — manuel, CHANGELOG** (AC 10). -- [ ] **T7 — gates** : backend complet, frontend complet, **E2E complet**. +- [x] **T5 — tests** (AC 9). +- [x] **T6 — manuel, CHANGELOG** (AC 10). +- [x] **T7 — gates** : backend complet, frontend complet, **E2E complet**. ## Dev Notes @@ -236,14 +236,91 @@ préventif*. ### Agent Model Used +Claude Opus 5.5 (`claude-opus-5-5`). + ### Debug Log References +- Les dix tests de concordance ont passé **du premier coup** : aucun n'a donc été vu rouge avant le code. D'où les + mutations ci-dessous, qui tiennent lieu de phase rouge. +- Deux garde-fous du frontend ont rougi au gate, et ils avaient raison : + - `e2e-selecteurs-traduits` : l'E2E ancrait l'en-tête « Clôture » sur son libellé traduit. Remplacé par les + `data-testid` `tb-col-opening` et `tb-col-closing`, et l'absence de « Solde » par un compte de six en-têtes. + - `i18n-libelle-en-dur` : la déclaration neuve `retainedLabel` est **nommée** dans le relevé (44 → 45, + `conforme` 38 → 39). Ses deux branches délèguent à `i18nMsg`. +- Manuel : les glyphes ✓ et ⚠️ manquent dans la police (`Missing character`). Remplacés par des mots. Les dix + `Missing character` restants sont antérieurs à la story. +- `cargo fmt` a reformaté le fichier de test neuf. + ### Completion Notes List +- **T1, le module partagé** — `crate::opening` porte `is_debit_natured`, `is_bilan`, `signed`, `debit_sense` et + `opening_from`. Le grand livre lit désormais les deux paires de sommes en **une** requête par compte. Quand aucun + exercice ne couvre `from`, la borne basse vaut `from` lui-même et la paire est nulle. Ses tests passent sans + retouche d'assertion ; ses deux tests unitaires de signe et de nature ont migré dans `opening.rs`. +- **T2, la balance** — une requête agrégée rend trois paires de sommes brutes par compte, sans aucun `CASE` sur le + type ni sur le signe. Elle est lue dans une structure brute `RawRow`, la ligne étant construite en Rust. + - Règle d'inclusion : actif, ou mouvementé, ou porteur d'un solde d'ouverture. + - `retained_earnings` vient de `balance_sheet::fetch_retained_earnings`, passé en `pub(crate)` et non recopié. + - `opening_balanced` est calculé par `debit_sense` ; un écart est signalé par `tracing::warn!`. + - `balance`, `total_debit`, `total_credit` et `balanced` sont inchangés. + - Les trois tests unitaires tautologiques de `trial_balance.rs` (qui comparaient des constantes) sont remplacés + par trois tests de `is_opening_balanced`. +- **T3, les rendus** — CSV à six colonnes, avec la ligne du résultat reporté sur le patron du bilan. PDF à six + colonnes aux positions imposées, avec l'intitulé tronqué à 22 caractères et deux libellés neufs, `col_opening` et + `col_closing`. Le libellé de la ligne calculée n'est pas tronqué : environ 46 mm pour 55 mm disponibles. +- **T4, l'écran** — les colonnes Ouverture et Clôture, la ligne calculée (« Perte reportée » si elle est négative) + et le contrôle ✓/⚠️ d'ouverture, distinct de celui des mouvements. La note retirée a perdu sa clé dans les 4 + locales ; deux clés neuves sont ajoutées. `sitesTotal` passe de 1756 à 1758. +- **T5, les tests** — **périmètre : de `HEAD` = `42623926` (fiche validée, aucun code) à l'arbre de travail.** + - Rust, **+14 nets** : + - `trial_balance_opening.rs` : 10 tests neufs ; + - `opening.rs` : 4 tests, dont 2 migrés du grand livre (`general_ledger.rs` passe de 4 à 2) ; + - `pdf.rs` : +2 ; + - `trial_balance.rs` : 3 tests remplacés ; + - `csv.rs` : un test réécrit, qui porte maintenant les valeurs ; + - `concordance_compte_de_resultat` étendu à une période en cours d'exercice ; + - `reports_e2e.rs` : un test étendu à la forme de la réponse. + - Vitest : **+6** dans `TrialBalanceView.test.ts`, neuf. E2E : `reports.spec.ts` étendu. +- **Gates** (T7) : base remise à zéro, `scripts/test-fast.sh` **2545/2545** (fmt, clippy, nextest) ; frontend `check` + 0 erreur, `lint-i18n-ownership` PASS, `test:unit` **845/845**, `build` OK ; **E2E complet** sur `kesh_e2e` migrée à + neuf (run de 10:07 UTC) : **224 passés, 12 échoués** — les 10 attendus d'un run matinal (KF-029 ×7, KF-045 ×2 + `invoices.spec.ts:415/439`, KF-046 `sidebar-navigation:75`) et la paire de pollution documentée `products:166` + + `product-revenue-account:133`, **vertes rejouées seules** ; `reports.spec.ts` vert, extension 25-5-b comprise. +- **Mutations tuées** : + - `<` → `<=` sur la borne d'ouverture : 2 tests tombent ; + - ouverture de résultat cumulée depuis l'origine : 5 tests ; + - bilan borné par l'exercice (le défaut de #385) : 5 tests ; + - ancienne règle d'inclusion : 1 test ; + - contrôle tautologique : 2 tests ; + - à l'écran, contrôle lu sur `balanced` : 1 test ; + - ouverture affichée à la place de la clôture : 1 test. +- **T6, manuel et CHANGELOG** : + - § *Balance des comptes* réécrite. La phrase sur le filtre de niveau de détail, qui n'existait pas, est retirée. + - Les deux points de la validation P2 sont écrits : les soldes de départ en mouvements, les montants signés. + - Le renvoi vers le grand livre est corrigé, ainsi que le glossaire ; le label `sec:soldes-depart` est posé. + - PDF régénéré et contrôlé aplati. + - CHANGELOG `[0.12.1]` : une entrée `Fixed` et une entrée `Changed`, cette dernière signalant l'incompatibilité CSV. + - README et site relus, toujours vrais. + ### File List +- `CHANGELOG.md` +- `crates/kesh-api/tests/reports_e2e.rs` +- `crates/kesh-i18n/locales/{de-CH,en-CH,fr-CH,it-CH}/messages.ftl` +- `crates/kesh-report/benches/export.rs` +- `crates/kesh-report/src/{balance_sheet.rs,csv.rs,general_ledger.rs,lib.rs,pdf.rs,trial_balance.rs}` +- `crates/kesh-report/src/opening.rs` (neuf) +- `crates/kesh-report/tests/general_ledger.rs` +- `crates/kesh-report/tests/trial_balance_opening.rs` (neuf) +- `docs/manual/fr/user-manual.{tex,pdf}` +- `frontend/src/lib/features/reports/{TrialBalanceView.svelte,reports.types.ts}` +- `frontend/src/lib/features/reports/TrialBalanceView.test.ts` (neuf) +- `frontend/src/lib/shared/{i18n-keys.test.ts,i18n-libelle-en-dur.test.ts}` +- `frontend/tests/e2e/reports.spec.ts` + ## Change Log +- **2026-10-03** — Implémentée (dev-story) : T1–T7, gates ci-dessus. Statut → `review`. - **2026-10-03** — Validation P3 ciblée (Haiku, prompt `25-5-b-validate-prompt-p3.md`) : **0 finding**, sorties des quatre vérifications fournies. Le point 2 (compatibilité de l'AC 2 avec le grand livre), établi par raisonnement, **repris par l'orchestrateur** : la période libre du grand livre (`LedgerPeriod`) impose de passer une paire nulle quand aucun diff --git a/_bmad-output/implementation-artifacts/sprint-status.yaml b/_bmad-output/implementation-artifacts/sprint-status.yaml index a3d934fb7..a7423c77b 100644 --- a/_bmad-output/implementation-artifacts/sprint-status.yaml +++ b/_bmad-output/implementation-artifacts/sprint-status.yaml @@ -368,7 +368,7 @@ development_status: 25-4-d-solder-le-reste: backlog # [#384] ramenee de la 25-6 le 2026-09-27 (Guy) — perte sur debiteur, escompte, frais : clore une facture partiellement reglee ; la part TVA reduit la TVA due. 25-4-propager-le-residuel: split # 2026-09-26 DECOUPEE (six modules > 5) en 25-4-a / 25-4-b / 25-4-c, puis 25-4-d (#384, 2026-09-27) ; #455 et #456 rattachees. # [#416] [#420] — la 24-2 a introduit le reglement PARTIEL ; le residuel n'est propage ni a la balance agee ni a l'echeancier, et **le score de rapprochement compare au TTC et non au residuel** : le virement du solde d'une facture partiellement reglee **score 0**. 25-5-a-export-souverainete: done # 2026-09-26 **MERGEE (PR #452, #386 fermee) SANS REVUE DE CODE** ; revue a posteriori CLOSE en 3 passes sur `story/25-5-a-revue-code` (4 HIGH P1, 1 HIGH orchestrateur P2 — colonnes manquantes, garde de colonnes —, P3 ciblee test seul) ; gates 2466/2466, E2E 221/8/19 sans regression ; reports #465, #466. # 2026-09-23 **SPECIFIEE et VALIDEE en 1 passe** (0 CRITICAL, 4 HIGH, 3 MEDIUM, 3 LOW, tous corriges). [#386] — issue du DECOUPAGE de la 25-5 (arbitrage de Guy) : #385 (Balance vs Bilan) et #386 n'ont en commun que le mot « etat », et touchent des modules disjoints. ⛔ **Trois faits qui corrigent l'issue** : 21 tables absentes et non 10 ; le trou s'est CREUSE depuis son ecriture (`invoice_settlements`, Epic 24) ; et SIX repositories sur huit n'ont aucune lecture exhaustive — le vrai cout, absent de l'issue. Arbitrage de Guy : l'export CSV ne porte que les donnees COMPTABLES (11 dedans), les backups portent tout ; 10 exclusions AVEC motif ecrit, dont 3 tables de SECRETS. Les justificatifs (fichiers) partent en story separee sur la SAUVEGARDE. ⛔ La vraie livraison est la GARDE D'EXHAUSTIVITE (patron D4-ter + P7), non les 11 exports : trois epics ont rouvert le trou en silence. ⚠️ Pieges releves par la validation : le nombre 19 code en dur a SEPT endroits dont deux `debug_assert_eq!` qui PANIQUENT ; `audit_log` n'a AUCUN dump exhaustif (ses fonctions sont bornees, la route rejette au-dela de 10 000 lignes) ; `csv_sanitize` existe et n'est pas employee par cet export ; deux sites de manuel DEJA faux (un dossier `reports/` invente, un « 38 » pour 39). - 25-5-b-balance-solde-ouverture: ready-for-dev # 2026-10-03 CREEE (partie de main). ouverture + mouvements + cloture, ligne calculee du resultat reporte, closes #385. [#385] — la **Balance des comptes ne concorde pas avec le Bilan**, il manque la colonne « solde d'ouverture » (III.4). Detachee de la 25-5 le 2026-09-23 : voir 25-5-a. + 25-5-b-balance-solde-ouverture: review # 2026-10-03 CREEE, validee (3 passes), implementee, gates verts (partie de main). ouverture + mouvements + cloture, ligne calculee du resultat reporte, closes #385. [#385] — la **Balance des comptes ne concorde pas avec le Bilan**, il manque la colonne « solde d'ouverture » (III.4). Detachee de la 25-5 le 2026-09-23 : voir 25-5-a. 25-5-etats-et-export: split # [#385] [#386] — la **Balance des comptes ne concorde pas avec le Bilan**, il manque la colonne « solde d'ouverture » (III.4) ; et **l'export de souverainete omet les achats, les avoirs, les projets et la piste d'audit** (V.6) — *il promet plus qu'il ne tient*. 25-6-tableau-de-bord-et-dettes: backlog # [#388] [#389] [#387] (#384 ramenee en 25-4-d le 2026-09-27) — la tuile « Dernieres ecritures » **n'appelle rien** et affiche toujours « Aucune ecriture » (*une tuile qui est un decor*) ; le « solde bancaire » est le solde COMPTABLE, donc faux ; imputer l'ECART d'un reglement partiel (escompte, frais bancaires, perte sur debiteur) ; et **le PDF d'une facture n'est jamais archive**, il est regenere a la volee (IV.9) — *un document reemis n'est pas le document envoye*. epic-16: done # 2026-08-11 (nuit) MERGÉ — dernière story 16-3b via PR #296 (squash `593bb845`). #152, #144 et #151 fermées. v0.9.0 prête à taguer. # 2026-07-26 KICKOFF (rétro Epic 14, décision D-D ; GO Guy après publication de v0.8.0). Branche `story/16-1-compte-produit-par-ligne` créée sur main `ef6cdf52`. Séquence : 16-1 (#152 socle par ligne) → 16-2 (#144 défaut catalogue) → 16-3 (#151 reliquat PDF). # ÉLU PROCHAIN EPIC — kickoff dès le merge de la PR groupée Epic 14. Reste #144 (compte produit au catalogue) + #152 (compte produit par ligne) ; #151 (récap TVA facture) déjà mergé hors-bande via PR #267 (bb3c8d64, partiel : coord. émetteur / n° client restants). Capte aussi les CR utilisateurs #265/#266/#268. # Facturation avancée (#144/#152/#151) — Epic dédié acté 2026-06-04 diff --git a/crates/kesh-api/tests/reports_e2e.rs b/crates/kesh-api/tests/reports_e2e.rs index 58ef83ad6..88fdc3ec9 100644 --- a/crates/kesh-api/tests/reports_e2e.rs +++ b/crates/kesh-api/tests/reports_e2e.rs @@ -590,6 +590,13 @@ async fn trial_balance_total_debit_equals_total_credit(pool: MySqlPool) { assert_eq!(resp.status(), 200); let body: Value = resp.json().await.unwrap(); assert_eq!(body["balanced"], true); + // Story 25-5-b — les champs neufs traversent la frontière HTTP. + assert_eq!(body["openingBalanced"], true); + assert!(body["retainedEarnings"].is_string(), "{body}"); + let first = &body["rows"].as_array().expect("rows")[0]; + for key in ["openingBalance", "closingBalance", "balance"] { + assert!(first[key].is_string(), "{key} absent : {first}"); + } } // ============================================================ diff --git a/crates/kesh-i18n/locales/de-CH/messages.ftl b/crates/kesh-i18n/locales/de-CH/messages.ftl index 5f5cf114e..18dcf17a7 100644 --- a/crates/kesh-i18n/locales/de-CH/messages.ftl +++ b/crates/kesh-i18n/locales/de-CH/messages.ftl @@ -1096,6 +1096,8 @@ reports-column-account-name = Bezeichnung reports-column-debit = Soll reports-column-credit = Haben reports-column-balance = Saldo +reports-column-opening = Eröffnung +reports-column-closing = Abschluss reports-column-entry-date = Datum reports-column-description = Buchungstext @@ -1139,7 +1141,6 @@ reports-equity-result-loss = Verlust der Periode reports-retained-earnings = Gewinnvortrag reports-retained-earnings-calculated = Gewinnvortrag (berechnet) reports-retained-earnings-loss = Verlustvortrag -reports-trial-balance-period-note = Die Rohbilanz zeigt die Bewegung der Periode (pro Geschäftsjahr). Die Summe pro Konto ist nicht mit dem kumulierten Saldo desselben Kontos in der Bilanz vergleichbar (Saldovortrag seit Beginn). # Alerts + badges UI (2 — code review Pass 1 i18n leaks) reports-equation-warning = ⚠️ Bilanzgleichung ungültig (Quelldaten prüfen). diff --git a/crates/kesh-i18n/locales/en-CH/messages.ftl b/crates/kesh-i18n/locales/en-CH/messages.ftl index b08af60ee..9ccb420e9 100644 --- a/crates/kesh-i18n/locales/en-CH/messages.ftl +++ b/crates/kesh-i18n/locales/en-CH/messages.ftl @@ -1096,6 +1096,8 @@ reports-column-account-name = Account Name reports-column-debit = Debit reports-column-credit = Credit reports-column-balance = Balance +reports-column-opening = Opening +reports-column-closing = Closing reports-column-entry-date = Date reports-column-description = Description @@ -1139,7 +1141,6 @@ reports-equity-result-loss = Period loss reports-retained-earnings = Retained earnings reports-retained-earnings-calculated = Retained earnings (calculated) reports-retained-earnings-loss = Accumulated loss -reports-trial-balance-period-note = The trial balance shows the movement of the period (per fiscal year). The per-account total is not comparable to the cumulative balance of the same account in the balance sheet (carry-forward from inception). # Alerts + badges UI (2 — code review Pass 1 i18n leaks) reports-equation-warning = ⚠️ Balance sheet equation unbalanced (verify source data). diff --git a/crates/kesh-i18n/locales/fr-CH/messages.ftl b/crates/kesh-i18n/locales/fr-CH/messages.ftl index 0e17c9cfd..1da1b3e68 100644 --- a/crates/kesh-i18n/locales/fr-CH/messages.ftl +++ b/crates/kesh-i18n/locales/fr-CH/messages.ftl @@ -1151,6 +1151,8 @@ reports-column-account-name = Intitulé reports-column-debit = Débit reports-column-credit = Crédit reports-column-balance = Solde +reports-column-opening = Ouverture +reports-column-closing = Clôture reports-column-entry-date = Date reports-column-description = Libellé @@ -1199,7 +1201,6 @@ reports-equity-result-loss = Perte de l'exercice reports-retained-earnings = Résultat reporté reports-retained-earnings-calculated = Résultat reporté (calculé) reports-retained-earnings-loss = Perte reportée -reports-trial-balance-period-note = La balance de vérification affiche le mouvement de la période (par exercice). Le total par compte n'est pas comparable au solde cumulé du même compte au bilan (report à-nouveau depuis l'origine). # Alertes + badges UI (2 — code review Pass 1 i18n leaks) reports-equation-warning = ⚠️ Équation bilan déséquilibrée (vérifier données source). diff --git a/crates/kesh-i18n/locales/it-CH/messages.ftl b/crates/kesh-i18n/locales/it-CH/messages.ftl index 55c658f3e..4491a46e6 100644 --- a/crates/kesh-i18n/locales/it-CH/messages.ftl +++ b/crates/kesh-i18n/locales/it-CH/messages.ftl @@ -1096,6 +1096,8 @@ reports-column-account-name = Denominazione reports-column-debit = Dare reports-column-credit = Avere reports-column-balance = Saldo +reports-column-opening = Apertura +reports-column-closing = Chiusura reports-column-entry-date = Data reports-column-description = Causale @@ -1139,7 +1141,6 @@ reports-equity-result-loss = Perdita del periodo reports-retained-earnings = Risultato riportato reports-retained-earnings-calculated = Risultato riportato (calcolato) reports-retained-earnings-loss = Perdita riportata -reports-trial-balance-period-note = Il bilancio di verifica mostra il movimento del periodo (per esercizio). Il totale per conto non è comparabile con il saldo cumulato dello stesso conto nel bilancio (riporto a nuovo dall'origine). # Alerts + badges UI (2 — code review Pass 1 i18n leaks) reports-equation-warning = ⚠️ Equazione di bilancio non bilanciata (verificare i dati sorgente). diff --git a/crates/kesh-report/benches/export.rs b/crates/kesh-report/benches/export.rs index b5f188b96..c459deaf9 100644 --- a/crates/kesh-report/benches/export.rs +++ b/crates/kesh-report/benches/export.rs @@ -109,9 +109,11 @@ fn make_trial_balance(n: usize) -> TrialBalance { AccountType::Liability }, active: true, + opening_balance: Decimal::from(250), total_debit: Decimal::from(100), total_credit: Decimal::from(100), balance: Decimal::from(0), + closing_balance: Decimal::from(250), }) .collect(); let total_debit: Decimal = rows.iter().map(|r| r.total_debit).sum(); @@ -122,6 +124,8 @@ fn make_trial_balance(n: usize) -> TrialBalance { total_debit, total_credit, balanced: true, + retained_earnings: Decimal::ZERO, + opening_balanced: true, } } diff --git a/crates/kesh-report/src/balance_sheet.rs b/crates/kesh-report/src/balance_sheet.rs index 22562af7b..6c82b3242 100644 --- a/crates/kesh-report/src/balance_sheet.rs +++ b/crates/kesh-report/src/balance_sheet.rs @@ -330,7 +330,7 @@ async fn fetch_cumulative_section( /// /// **Couture (AC-G)** : même borne date `entry_date < before` que /// `fetch_cumulative_section` ; un snapshot de clôture stockera aussi ce report. -async fn fetch_retained_earnings( +pub(crate) async fn fetch_retained_earnings( pool: &MySqlPool, company_id: i64, before: NaiveDate, diff --git a/crates/kesh-report/src/csv.rs b/crates/kesh-report/src/csv.rs index 075070de1..32b7140c0 100644 --- a/crates/kesh-report/src/csv.rs +++ b/crates/kesh-report/src/csv.rs @@ -249,8 +249,11 @@ pub fn render_income_statement_csv( /// Génère le CSV de la balance des comptes (AC #14). /// -/// Colonnes : `NumeroCompte;NomCompte;TotalDebit;TotalCredit;Solde`. Ligne -/// finale totaux débit/crédit avec colonnes solde vide. +/// Colonnes : `NumeroCompte;NomCompte;SoldeOuverture;TotalDebit;TotalCredit;SoldeCloture` +/// — Story 25-5-b (#385) : la colonne `Solde` (net des mouvements, pris pour un +/// solde de compte) est **remplacée** par l'ouverture et la clôture. Après les +/// comptes, la ligne calculée du résultat reporté (numéro vide, comme au bilan), +/// puis la ligne `Total` (mouvements ; ouverture et clôture vides). pub fn render_trial_balance_csv( tb: &TrialBalance, mut writer: W, @@ -261,9 +264,10 @@ pub fn render_trial_balance_csv( wtr.write_record([ "NumeroCompte", "NomCompte", + "SoldeOuverture", "TotalDebit", "TotalCredit", - "Solde", + "SoldeCloture", ]) .map_err(map_csv_err)?; @@ -276,16 +280,30 @@ pub fn render_trial_balance_csv( wtr.write_record([ &row.account_number, &row.account_name, + &format_amount_iso(row.opening_balance), &format_amount_iso(row.total_debit), &format_amount_iso(row.total_credit), - &format_amount_iso(row.balance), + &format_amount_iso(row.closing_balance), ]) .map_err(map_csv_err)?; } + // Ligne calculée : libellé fixe, comme au CSV du bilan. + let retained = format_amount_iso(tb.retained_earnings); + wtr.write_record([ + "", + "Résultat reporté (calculé)", + &retained, + "", + "", + &retained, + ]) + .map_err(map_csv_err)?; + wtr.write_record([ "Total", "", + "", &format_amount_iso(tb.total_debit), &format_amount_iso(tb.total_credit), "", @@ -824,6 +842,8 @@ mod tests { total_debit: Decimal::ZERO, total_credit: Decimal::ZERO, balanced: true, + retained_earnings: Decimal::ZERO, + opening_balanced: true, }; let mut buf = Vec::new(); render_trial_balance_csv(&tb, &mut buf).unwrap(); @@ -1134,24 +1154,35 @@ mod tests { account_name: "Caisse".into(), account_type: AccountType::Asset, active: true, + opening_balance: dec!(1000), total_debit: dec!(200), total_credit: dec!(50), balance: dec!(150), + closing_balance: dec!(1150), }], total_debit: dec!(200), total_credit: dec!(50), balanced: true, + retained_earnings: dec!(-600), + opening_balanced: true, }; let mut buf = Vec::new(); render_trial_balance_csv(&tb, &mut buf).unwrap(); let body = String::from_utf8(buf[3..].to_vec()).unwrap(); let lines: Vec<&str> = body.split("\r\n").filter(|l| !l.is_empty()).collect(); - assert_eq!(lines.len(), 3, "1 header + 1 row + 1 total = 3 lines"); + assert_eq!( + lines.len(), + 4, + "1 header + 1 row + résultat reporté + 1 total = 4 lines" + ); assert_eq!( lines[0], - "NumeroCompte;NomCompte;TotalDebit;TotalCredit;Solde" + "NumeroCompte;NomCompte;SoldeOuverture;TotalDebit;TotalCredit;SoldeCloture" ); - assert!(lines[2].starts_with("Total;")); + // Story 25-5-b — l'ouverture et la clôture dans l'ordre des colonnes. + assert_eq!(lines[1], "1000;Caisse;1000.00;200.00;50.00;1150.00"); + assert_eq!(lines[2], ";Résultat reporté (calculé);-600.00;;;-600.00"); + assert_eq!(lines[3], "Total;;;200.00;50.00;"); } // Story 21-7 — CSV balance âgée. diff --git a/crates/kesh-report/src/general_ledger.rs b/crates/kesh-report/src/general_ledger.rs index 7825f95ec..3c6edca8f 100644 --- a/crates/kesh-report/src/general_ledger.rs +++ b/crates/kesh-report/src/general_ledger.rs @@ -10,6 +10,9 @@ //! //! # La règle du solde d'ouverture — le cœur du module //! +//! Elle vit dans [`crate::opening`], **partagée avec la balance des comptes** +//! (Story 25-5-b) : une seule règle, que les deux rapports appliquent. +//! //! Elle **diffère selon le type de compte**, et c'est ce qu'un lecteur pressé //! prendra de travers : //! @@ -53,6 +56,7 @@ use serde::Serialize; use sqlx::MySqlPool; use crate::errors::ReportError; +use crate::opening::{is_bilan, is_debit_natured, opening_from, signed}; /// Plafond de lignes rendues en une fois, aligné sur le patron du dépôt. /// @@ -178,20 +182,6 @@ struct RawAccount { active: bool, } -/// `true` si le compte a le débit pour côté naturel. -fn is_debit_natured(t: AccountType) -> bool { - matches!(t, AccountType::Asset | AccountType::Expense) -} - -/// Applique la convention de signe du dépôt — celle de la balance et du bilan. -fn signed(t: AccountType, debit: Decimal, credit: Decimal) -> Decimal { - if is_debit_natured(t) { - debit - credit - } else { - credit - debit - } -} - /// Début de l'exercice contenant `date`, s'il en existe un. /// /// Sert **uniquement** aux comptes de résultat : c'est la borne basse de leur @@ -217,10 +207,13 @@ async fn fiscal_year_start_containing( Ok(row.map(|r| r.0)) } -/// Solde d'ouverture d'un compte au `from` — **la règle centrale du module**. +/// Solde d'ouverture d'un compte au `from` — **la règle centrale du module**, +/// appliquée par [`opening_from`] (module partagé avec la balance des comptes). /// -/// Comptes de bilan : cumul depuis l'origine. Comptes de résultat : cumul depuis -/// le début de leur exercice. Voir la documentation du module pour le pourquoi. +/// Une requête lit les deux paires de sommes brutes : avant `from`, et depuis le +/// début de l'exercice contenant `from`. Quand aucun exercice ne couvre `from`, la +/// borne basse vaut `from` lui-même : la seconde paire est nulle, et l'ouverture +/// d'un compte de résultat aussi. async fn opening_balance( pool: &MySqlPool, company_id: i64, @@ -228,50 +221,28 @@ async fn opening_balance( account_type: AccountType, from: NaiveDate, ) -> Result { - // Borne basse : aucune pour un compte de bilan ; le début de l'exercice pour - // un compte de résultat. `None` ⇒ ouverture nulle, la borne haute étant - // exclusive et égale à la borne basse. - let lower: Option = match account_type { - AccountType::Asset | AccountType::Liability => None, - AccountType::Revenue | AccountType::Expense => { - match fiscal_year_start_containing(pool, company_id, from).await? { - Some(start) => Some(start), - // Aucun exercice ne couvre `from` : rien à reporter. - None => return Ok(Decimal::ZERO), - } - } - }; - - let sql = match lower { - Some(_) => { - "SELECT COALESCE(SUM(jel.debit), 0) AS d, COALESCE(SUM(jel.credit), 0) AS c \ - FROM journal_entry_lines jel \ - INNER JOIN journal_entries je ON je.id = jel.entry_id \ - WHERE jel.account_id = ? AND je.company_id = ? \ - AND je.entry_date >= ? AND je.entry_date < ?" - } - None => { - "SELECT COALESCE(SUM(jel.debit), 0) AS d, COALESCE(SUM(jel.credit), 0) AS c \ - FROM journal_entry_lines jel \ - INNER JOIN journal_entries je ON je.id = jel.entry_id \ - WHERE jel.account_id = ? AND je.company_id = ? \ - AND je.entry_date < ?" - } - }; - - let mut q = sqlx::query_as::<_, (Decimal, Decimal)>(sql) - .bind(account_id) - .bind(company_id); - if let Some(lo) = lower { - q = q.bind(lo); - } - let (d, c) = q - .bind(from) - .fetch_one(pool) - .await - .map_err(kesh_db::errors::map_db_error)?; + let fy_start = fiscal_year_start_containing(pool, company_id, from) + .await? + .unwrap_or(from); + + let (bd, bc, sd, sc) = sqlx::query_as::<_, (Decimal, Decimal, Decimal, Decimal)>( + "SELECT COALESCE(SUM(jel.debit), 0), COALESCE(SUM(jel.credit), 0), \ + COALESCE(SUM(CASE WHEN je.entry_date >= ? THEN jel.debit ELSE 0 END), 0), \ + COALESCE(SUM(CASE WHEN je.entry_date >= ? THEN jel.credit ELSE 0 END), 0) \ + FROM journal_entry_lines jel \ + INNER JOIN journal_entries je ON je.id = jel.entry_id \ + WHERE jel.account_id = ? AND je.company_id = ? AND je.entry_date < ?", + ) + .bind(fy_start) + .bind(fy_start) + .bind(account_id) + .bind(company_id) + .bind(from) + .fetch_one(pool) + .await + .map_err(kesh_db::errors::map_db_error)?; - Ok(signed(account_type, d, c)) + Ok(opening_from(account_type, (bd, bc), (sd, sc))) } /// Comptes à rendre : ceux qui ont un mouvement sur la période **ou** un solde @@ -560,39 +531,10 @@ pub async fn generate( }) } -/// `true` pour un compte de bilan — dont le solde se reporte d'un exercice à -/// l'autre. -fn is_bilan(t: AccountType) -> bool { - matches!(t, AccountType::Asset | AccountType::Liability) -} - #[cfg(test)] mod tests { use super::*; - fn d(v: i64) -> Decimal { - Decimal::from(v) - } - - #[test] - fn signe_suit_la_nature_du_compte() { - // Convention du dépôt, reprise telle quelle de la balance et du bilan. - assert_eq!(signed(AccountType::Asset, d(100), d(30)), d(70)); - assert_eq!(signed(AccountType::Expense, d(100), d(30)), d(70)); - assert_eq!(signed(AccountType::Liability, d(30), d(100)), d(70)); - assert_eq!(signed(AccountType::Revenue, d(30), d(100)), d(70)); - } - - #[test] - fn seuls_les_comptes_de_bilan_reportent_leur_solde() { - // C'est ce qui commande la borne basse du solde d'ouverture, et la - // remise à zéro du solde progressif au passage d'exercice. - assert!(is_bilan(AccountType::Asset)); - assert!(is_bilan(AccountType::Liability)); - assert!(!is_bilan(AccountType::Revenue)); - assert!(!is_bilan(AccountType::Expense)); - } - #[test] fn periode_inversee_est_refusee() { let from = NaiveDate::from_ymd_opt(2026, 3, 1).unwrap(); diff --git a/crates/kesh-report/src/lib.rs b/crates/kesh-report/src/lib.rs index 6760d3584..9f7b32f19 100644 --- a/crates/kesh-report/src/lib.rs +++ b/crates/kesh-report/src/lib.rs @@ -20,6 +20,7 @@ pub mod errors; pub mod general_ledger; pub mod income_statement; pub mod journal_report; +pub mod opening; pub mod pdf; pub mod period; pub mod project_report; diff --git a/crates/kesh-report/src/opening.rs b/crates/kesh-report/src/opening.rs new file mode 100644 index 000000000..19c3ec249 --- /dev/null +++ b/crates/kesh-report/src/opening.rs @@ -0,0 +1,136 @@ +//! La règle du solde d'ouverture, partagée par le grand livre et la balance des +//! comptes (Story 25-5-b, #385). +//! +//! # La règle +//! +//! Elle **diffère selon le type de compte** : +//! +//! - `Asset` / `Liability` (comptes de bilan) : cumul sur `entry_date < début`, +//! **tous exercices confondus** — même patron que le bilan. +//! - `Revenue` / `Expense` (comptes de résultat) : cumul depuis le **début de +//! l'exercice** contenant le début de la période seulement. Si la période +//! commence le premier jour d'un exercice, l'ouverture vaut 0. +//! +//! Un compte de bilan reporte son solde d'un exercice à l'autre ; un compte de +//! résultat est soldé au bouclement et repart de zéro. +//! +//! ⚠️ **Kesh ne passe aucune écriture de clôture** : cette remise à zéro n'existe +//! que comme borne basse d'une somme. Les appelants lisent donc, en SQL, **deux +//! paires de sommes brutes** — avant le début, et depuis le début de l'exercice — +//! et c'est [`opening_from`] qui choisit, **ici seulement**. Recopier ce choix en +//! `CASE` SQL dans un rapport en ferait une seconde règle, qui divergerait en +//! silence. +//! +//! # Limite connue +//! +//! Le signe est lu sur le `account_type` **courant** du compte : retyper un +//! compte mouvementé re-signe tout son historique (issues #274 et #382). + +use kesh_db::entities::AccountType; +use rust_decimal::Decimal; + +/// Une paire de sommes brutes `(débit, crédit)`, telle que la lit SQL. +pub type DebitCredit = (Decimal, Decimal); + +/// `true` si le compte a le débit pour côté naturel. +pub fn is_debit_natured(t: AccountType) -> bool { + matches!(t, AccountType::Asset | AccountType::Expense) +} + +/// `true` pour un compte de bilan — dont le solde se reporte d'un exercice à +/// l'autre. +pub fn is_bilan(t: AccountType) -> bool { + matches!(t, AccountType::Asset | AccountType::Liability) +} + +/// Applique la convention de signe du dépôt — celle de la balance, du bilan et +/// du grand livre : positif du côté naturel du compte. +pub fn signed(t: AccountType, debit: Decimal, credit: Decimal) -> Decimal { + if is_debit_natured(t) { + debit - credit + } else { + credit - debit + } +} + +/// Ramène un solde **signé par type** en sens débit (débit − crédit), quel que +/// soit le compte. C'est la grandeur qui s'additionne d'un compte à l'autre : +/// additionner des soldes signés par type sans les re-signer mélange les deux +/// conventions. +pub fn debit_sense(t: AccountType, signed_balance: Decimal) -> Decimal { + if is_debit_natured(t) { + signed_balance + } else { + -signed_balance + } +} + +/// Solde d'ouverture, signé par type, depuis les deux paires de sommes brutes : +/// `before_start` (`entry_date < début`) pour un compte de bilan, +/// `since_fy_start` (`entry_date ∈ [début de l'exercice, début[`) pour un compte +/// de résultat. +/// +/// Un appelant dont la période n'est couverte par aucun exercice passe une paire +/// `since_fy_start` **nulle** : l'ouverture d'un compte de résultat vaut alors 0. +pub fn opening_from( + t: AccountType, + before_start: DebitCredit, + since_fy_start: DebitCredit, +) -> Decimal { + let (debit, credit) = if is_bilan(t) { + before_start + } else { + since_fy_start + }; + signed(t, debit, credit) +} + +#[cfg(test)] +mod tests { + use super::*; + + fn d(v: i64) -> Decimal { + Decimal::from(v) + } + + #[test] + fn signe_suit_la_nature_du_compte() { + assert_eq!(signed(AccountType::Asset, d(100), d(30)), d(70)); + assert_eq!(signed(AccountType::Expense, d(100), d(30)), d(70)); + assert_eq!(signed(AccountType::Liability, d(30), d(100)), d(70)); + assert_eq!(signed(AccountType::Revenue, d(30), d(100)), d(70)); + } + + #[test] + fn seuls_les_comptes_de_bilan_reportent_leur_solde() { + assert!(is_bilan(AccountType::Asset)); + assert!(is_bilan(AccountType::Liability)); + assert!(!is_bilan(AccountType::Revenue)); + assert!(!is_bilan(AccountType::Expense)); + } + + /// Un compte à nature créditrice est re-signé : un passif de 70 (crédit) vaut + /// −70 en sens débit. Sans ce re-signe, le contrôle d'ouverture de la balance + /// rougirait sur des livres exacts. + #[test] + fn le_sens_debit_re_signe_les_comptes_a_nature_creditrice() { + assert_eq!(debit_sense(AccountType::Asset, d(70)), d(70)); + assert_eq!(debit_sense(AccountType::Expense, d(70)), d(70)); + assert_eq!(debit_sense(AccountType::Liability, d(70)), d(-70)); + assert_eq!(debit_sense(AccountType::Revenue, d(70)), d(-70)); + } + + /// Le bilan prend la paire « avant le début », le résultat la paire « depuis + /// le début de l'exercice » — jamais l'inverse. + #[test] + fn l_ouverture_choisit_la_paire_selon_le_type() { + let before = (d(1000), d(0)); + let since = (d(0), d(200)); + assert_eq!(opening_from(AccountType::Asset, before, since), d(1000)); + assert_eq!(opening_from(AccountType::Revenue, before, since), d(200)); + assert_eq!( + opening_from(AccountType::Revenue, before, (d(0), d(0))), + d(0) + ); + } +} diff --git a/crates/kesh-report/src/pdf.rs b/crates/kesh-report/src/pdf.rs index d12912adc..37158d353 100644 --- a/crates/kesh-report/src/pdf.rs +++ b/crates/kesh-report/src/pdf.rs @@ -3,7 +3,7 @@ //! 4 fonctions publiques pures (input DTO → `Result, ReportError>`) : //! - [`render_balance_sheet_pdf`] — Bilan (Actifs / Passifs / résultat de l'exercice). //! - [`render_income_statement_pdf`] — Compte de résultat (Produits / Charges / résultat net). -//! - [`render_trial_balance_pdf`] — Balance des comptes (5 colonnes : N°, Compte, Débit, Crédit, Solde). +//! - [`render_trial_balance_pdf`] — Balance des comptes (6 colonnes : N°, Compte, Ouverture, Débit, Crédit, Clôture). //! - [`render_journal_report_pdf`] — Journaux (5 sections fixes, filtre optionnel). //! //! Architecture (Pass 1 AA-M1 + Decision §swiss-amount-format) : @@ -114,6 +114,11 @@ pub struct SectionLabels { pub col_debit: String, pub col_credit: String, pub col_balance: String, + /// En-tête court de la colonne d'ouverture de la balance (Story 25-5-b) — + /// « Solde d'ouverture » ne tient pas dans une colonne de 30 mm. + pub col_opening: String, + /// En-tête court de la colonne de clôture de la balance (Story 25-5-b). + pub col_closing: String, pub col_entry_date: String, pub col_description: String, // Header @@ -168,6 +173,8 @@ impl SectionLabels { col_debit: "Débit".into(), col_credit: "Crédit".into(), col_balance: "Solde".into(), + col_opening: "Ouverture".into(), + col_closing: "Clôture".into(), col_entry_date: "Date".into(), col_description: "Libellé".into(), header_period: "Période".into(), @@ -696,7 +703,35 @@ pub fn render_income_statement_pdf( builder.finalize() } -/// Génère le PDF de la balance des comptes — 5 colonnes (AC #5). +/// Positions des six colonnes de la balance (Story 25-5-b), en mm depuis la marge +/// gauche. A4 portrait est déjà saturé à cinq colonnes (20 + 70 + 3 × 35 = 195 mm) : +/// quatre colonnes de montant de 30 mm (« -12'345'678.90 » ≈ 23 mm en Helvetica +/// 10 pt) laissent 55 mm à l'intitulé. +const TB_COL_X: [f32; 6] = [0.0, 20.0, 75.0, 105.0, 135.0, 165.0]; + +/// Longueur maximale de l'intitulé d'un compte dans la balance PDF : 22 +/// caractères tiennent dans 55 mm, même en capitales. +const TB_NAME_MAX_CHARS: usize = 22; + +/// Dessine une ligne de la balance : six cellules, `None` = cellule vide. +fn draw_tb_row(builder: &mut PdfBuilder, cells: [Option<&str>; 6], bold: bool) { + let y = builder.cursor_y; + let font = if bold { + builder.font_bold.clone() + } else { + builder.font.clone() + }; + let layer = builder.current_layer(); + for (cell, x) in cells.iter().zip(TB_COL_X) { + if let Some(text) = cell { + layer.use_text(*text, FONT_SIZE_PT, Mm(MARGIN_LEFT_MM + x), Mm(y), &font); + } + } +} + +/// Génère le PDF de la balance des comptes — 6 colonnes : N°, Intitulé, +/// Ouverture, Débit, Crédit, Clôture (Story 25-5-b, #385), puis la ligne calculée +/// du résultat reporté et les totaux des mouvements. pub fn render_trial_balance_pdf( tb: &TrialBalance, ctx: &PdfContext, @@ -715,131 +750,85 @@ pub fn render_trial_balance_pdf( return builder.finalize(); } - // En-têtes de colonnes (5 colonnes : N°, Compte, Débit, Crédit, Solde) - let col_num_x = MARGIN_LEFT_MM; - let col_name_x = MARGIN_LEFT_MM + 20.0; - let col_debit_x = MARGIN_LEFT_MM + 90.0; - let col_credit_x = MARGIN_LEFT_MM + 125.0; - let col_balance_x = MARGIN_LEFT_MM + 160.0; - - { - let y = builder.cursor_y; - let layer = builder.current_layer(); - layer.use_text( - &ctx.section_labels.col_account_number, - FONT_SIZE_PT, - Mm(col_num_x), - Mm(y), - &builder.font_bold, - ); - layer.use_text( - &ctx.section_labels.col_account_name, - FONT_SIZE_PT, - Mm(col_name_x), - Mm(y), - &builder.font_bold, - ); - layer.use_text( - &ctx.section_labels.col_debit, - FONT_SIZE_PT, - Mm(col_debit_x), - Mm(y), - &builder.font_bold, - ); - layer.use_text( - &ctx.section_labels.col_credit, - FONT_SIZE_PT, - Mm(col_credit_x), - Mm(y), - &builder.font_bold, - ); - layer.use_text( - &ctx.section_labels.col_balance, - FONT_SIZE_PT, - Mm(col_balance_x), - Mm(y), - &builder.font_bold, - ); - } + let l = &ctx.section_labels; + draw_tb_row( + &mut builder, + [ + Some(&l.col_account_number), + Some(&l.col_account_name), + Some(&l.col_opening), + Some(&l.col_debit), + Some(&l.col_credit), + Some(&l.col_closing), + ], + true, + ); builder.cursor_y -= LINE_HEIGHT_MM * 1.5; - // Lignes for row in &tb.rows { builder.ensure_space_for_row(); - let y = builder.cursor_y; - let layer = builder.current_layer(); - layer.use_text( - &row.account_number, - FONT_SIZE_PT, - Mm(col_num_x), - Mm(y), - &builder.font, - ); - // Pass 1 code-review H4 (BH1-H4) : la colonne `name` (largeur 70mm) peut - // déborder sur la colonne `débit` pour des libellés CH GAAP > 30 chars - // (e.g. « Provisions pour dépréciation d'actifs »). Truncate à 30 chars - // avec ellipsis Unicode pour préserver la lisibilité des colonnes - // débit/crédit/solde alignées. Refactor multi-ligne → v0.2 si feedback. - let display_name = truncate_with_ellipsis(&row.account_name, 30); - layer.use_text( - &display_name, - FONT_SIZE_PT, - Mm(col_name_x), - Mm(y), - &builder.font, - ); - layer.use_text( + // Pass 1 code-review H4 (BH1-H4) : un intitulé trop long déborderait sur la + // colonne suivante — tronqué avec ellipsis Unicode. + let name = truncate_with_ellipsis(&row.account_name, TB_NAME_MAX_CHARS); + let (opening, debit, credit, closing) = ( + format_swiss_amount(row.opening_balance), format_swiss_amount(row.total_debit), - FONT_SIZE_PT, - Mm(col_debit_x), - Mm(y), - &builder.font, - ); - layer.use_text( format_swiss_amount(row.total_credit), - FONT_SIZE_PT, - Mm(col_credit_x), - Mm(y), - &builder.font, + format_swiss_amount(row.closing_balance), ); - layer.use_text( - format_swiss_amount(row.balance), - FONT_SIZE_PT, - Mm(col_balance_x), - Mm(y), - &builder.font, + draw_tb_row( + &mut builder, + [ + Some(&row.account_number), + Some(&name), + Some(&opening), + Some(&debit), + Some(&credit), + Some(&closing), + ], + false, ); builder.cursor_y -= LINE_HEIGHT_MM; } - // Totaux + // Ligne calculée du résultat reporté : ouverture = clôture, sans mouvement. + builder.ensure_space_for_row(); + let retained = format_swiss_amount(tb.retained_earnings); + // Libellé fixe, non tronqué : « Résultat reporté (calculé) » ≈ 46 mm en casse + // mixte, dans les 55 mm de la colonne. + draw_tb_row( + &mut builder, + [ + None, + Some(&l.retained_result_label), + Some(&retained), + None, + None, + Some(&retained), + ], + false, + ); + builder.cursor_y -= LINE_HEIGHT_MM; + + // Totaux des mouvements. builder.cursor_y -= LINE_HEIGHT_MM * 0.5; builder.ensure_space_for_row(); - { - let y = builder.cursor_y; - let layer = builder.current_layer(); - layer.use_text( - &ctx.section_labels.grand_total, - FONT_SIZE_PT, - Mm(col_num_x), - Mm(y), - &builder.font_bold, - ); - layer.use_text( - format_swiss_amount(tb.total_debit), - FONT_SIZE_PT, - Mm(col_debit_x), - Mm(y), - &builder.font_bold, - ); - layer.use_text( - format_swiss_amount(tb.total_credit), - FONT_SIZE_PT, - Mm(col_credit_x), - Mm(y), - &builder.font_bold, - ); - } + let (total_debit, total_credit) = ( + format_swiss_amount(tb.total_debit), + format_swiss_amount(tb.total_credit), + ); + draw_tb_row( + &mut builder, + [ + Some(&l.grand_total), + None, + None, + Some(&total_debit), + Some(&total_credit), + None, + ], + true, + ); builder.cursor_y -= LINE_HEIGHT_MM; builder.finalize() @@ -1819,14 +1808,18 @@ mod tests { account_name: "Caisse".into(), account_type: AccountType::Asset, active: true, + opening_balance: dec!(500), total_debit: dec!(2000), total_credit: dec!(1000), balance: dec!(1000), + closing_balance: dec!(1500), }] }, total_debit: if empty { Decimal::ZERO } else { dec!(2000) }, total_credit: if empty { Decimal::ZERO } else { dec!(1000) }, balanced: true, + retained_earnings: if empty { Decimal::ZERO } else { dec!(500) }, + opening_balanced: true, } } @@ -1986,6 +1979,26 @@ mod tests { assert!(bytes.starts_with(b"%PDF-1.")); } + #[test] + fn trial_balance_pdf_with_retained_line_is_not_the_empty_render() { + // Story 25-5-b — la ligne calculée et les six colonnes : le rendu peuplé + // diffère du rendu vide (garde `rows.is_empty()` inchangée). + let ctx = PdfContext::fr_ch_default("CI Test Company"); + let full = render_trial_balance_pdf(&fixture_tb(false), &ctx).unwrap(); + let empty = render_trial_balance_pdf(&fixture_tb(true), &ctx).unwrap(); + assert!(full.starts_with(b"%PDF-1.")); + assert!(full.len() > empty.len()); + } + + #[test] + fn trial_balance_names_are_truncated_to_fit_six_columns() { + // Story 25-5-b — 55 mm pour l'intitulé : 22 caractères, ellipsis comprise. + let long = "Provisions pour dépréciation d'actifs"; // 37 caractères + let truncated = truncate_with_ellipsis(long, TB_NAME_MAX_CHARS); + assert_eq!(truncated.chars().count(), 22); + assert!(truncated.ends_with('…')); + } + #[test] fn journal_report_pdf_starts_with_pdf_signature() { let ctx = PdfContext::fr_ch_default("CI Test Company"); diff --git a/crates/kesh-report/src/trial_balance.rs b/crates/kesh-report/src/trial_balance.rs index 23a7b58a0..6bad7abef 100644 --- a/crates/kesh-report/src/trial_balance.rs +++ b/crates/kesh-report/src/trial_balance.rs @@ -1,20 +1,53 @@ -//! Balance des comptes : 1 ligne par compte avec total débit / total crédit / solde. +//! Balance des comptes : pour chaque compte, **solde d'ouverture, mouvements de la +//! période, solde de clôture** — et une ligne calculée du résultat reporté. //! -//! Règle d'inclusion (Pass 1 ECH-03) : -//! - Comptes **actifs** : inclus dans tous les cas (même sans écriture, balance=0) -//! - Comptes **archivés avec écritures dans la période** : inclus (marqueur `active: false`) -//! - Comptes **archivés sans écriture dans la période** : exclus +//! # Pourquoi l'ouverture (Story 25-5-b, #385) //! -//! Invariant : `total_debit == total_credit` (cohérence partie double, vérifiée à -//! l'INSERT des écritures dans `kesh-db::repositories::journal_entries::create_in_tx:201`). -//! Si déséquilibré → `ReportError::TrialBalanceUnbalanced` + log error! (defense in depth). +//! Jusqu'à la 25-5-b, la balance ne portait que les mouvements de la période : +//! le compte « clients » de la balance 2026 ne valait pas celui du bilan 2026, +//! qui est cumulatif depuis l'origine. Désormais `closing_balance` d'un compte de +//! bilan **est** son solde au bilan, et celui d'un compte de résultat son montant +//! au compte de résultat. La règle d'ouverture vit dans [`crate::opening`], +//! partagée avec le grand livre. +//! +//! # Règle d'inclusion +//! +//! Un compte est rendu s'il est **actif**, **ou** s'il a un mouvement dans la +//! période, **ou** si son **solde d'ouverture est non nul** — un compte archivé +//! qui porte encore un solde doit apparaître, sinon la clôture ne concorderait +//! plus avec le bilan. Un compte rendu archivé porte `active: false`. +//! +//! # La ligne calculée du résultat reporté +//! +//! Kesh ne passe aucune écriture de clôture : le résultat des exercices +//! antérieurs n'est porté par **aucun compte**. Sans lui, ni la colonne +//! d'ouverture ni celle de clôture ne s'équilibrent. [`TrialBalance::retained_earnings`] +//! le porte — la même valeur que le bilan, par la même fonction. +//! +//! # Deux contrôles +//! +//! - **Mouvements** : `total_debit == total_credit` (partie double, vérifiée à +//! l'INSERT des écritures dans `kesh-db::repositories::journal_entries::create_in_tx`). +//! Si déséquilibré → `ReportError::TrialBalanceUnbalanced` + log error! (defense +//! in depth). +//! - **Ouverture** : `opening_balanced` (voir le champ). Un déséquilibre n'est pas +//! une erreur : il est journalisé et affiché, comme `equation_holds` au bilan. +//! Il n'y a pas de contrôle de clôture : la clôture vaut l'ouverture plus des +//! mouvements équilibrés, ce serait la copie du premier. +//! +//! # Limite connue +//! +//! Le signe est lu sur le `account_type` **courant** du compte (issues #274, #382). use kesh_db::entities::AccountType; +use kesh_db::repositories::fiscal_years; use rust_decimal::Decimal; use serde::Serialize; use sqlx::MySqlPool; +use crate::balance_sheet::fetch_retained_earnings; use crate::errors::ReportError; +use crate::opening::{debit_sense, opening_from, signed}; use crate::period::ReportPeriod; /// Balance des comptes complète. @@ -23,28 +56,64 @@ use crate::period::ReportPeriod; pub struct TrialBalance { pub period: ReportPeriod, pub rows: Vec, + /// Total des mouvements débit de la période. pub total_debit: Decimal, + /// Total des mouvements crédit de la période. pub total_credit: Decimal, + /// Mouvements équilibrés — toujours vrai dans un rapport rendu (sinon + /// `TrialBalanceUnbalanced`). pub balanced: bool, + /// Résultat reporté **calculé** : cumul des résultats des exercices + /// strictement antérieurs à celui de la période (P&L `entry_date < fy_start`), + /// signe crédit-positif — négatif = pertes cumulées. Même valeur que + /// `BalanceSheet::retained_earnings`. Ouverture = clôture, sans mouvement. + pub retained_earnings: Decimal, + /// `Σ debit_sense(opening_balance) − retained_earnings == 0`. Le résultat + /// reporté étant calculé **indépendamment**, c'est une vérification réelle : + /// une ligne d'écriture déséquilibrée antérieure à la période la fait tomber. + pub opening_balanced: bool, } -/// Ligne de la balance des comptes. -/// -/// `account_number` (camelCase) mappe la colonne DB `accounts.number` -/// (Pass 4 AA4-01). -#[derive(Debug, Clone, Serialize, sqlx::FromRow)] +/// Ligne de la balance des comptes. Tous les soldes suivent la convention du +/// dépôt : positifs du côté naturel du compte (`Asset`/`Expense` : débit). +#[derive(Debug, Clone, Serialize)] #[serde(rename_all = "camelCase")] pub struct TrialBalanceRow { pub account_id: i64, - #[sqlx(rename = "number")] + /// `accounts.number`. pub account_number: String, - #[sqlx(rename = "name")] + /// `accounts.name`. pub account_name: String, pub account_type: AccountType, pub active: bool, + /// Solde au début de la période — règle de [`crate::opening`]. + pub opening_balance: Decimal, + /// Mouvements débit de la période. pub total_debit: Decimal, + /// Mouvements crédit de la période. pub total_credit: Decimal, + /// **Net des mouvements** de la période (pas un solde de compte). pub balance: Decimal, + /// `opening_balance + balance` — pour un compte de bilan, son solde au bilan. + pub closing_balance: Decimal, +} + +/// Ligne brute, telle que la lit SQL : trois paires de sommes, aucun choix de +/// règle ni de signe (ils sont en Rust, dans [`crate::opening`]). +#[derive(Debug, sqlx::FromRow)] +struct RawRow { + account_id: i64, + number: String, + name: String, + account_type: AccountType, + active: bool, + before_debit: Decimal, + before_credit: Decimal, + since_fy_debit: Decimal, + since_fy_credit: Decimal, + period_debit: Decimal, + period_credit: Decimal, + period_lines: i64, } /// Génère la balance des comptes pour la période donnée. @@ -53,12 +122,16 @@ pub async fn generate( company_id: i64, period: &ReportPeriod, ) -> Result { - // Règle d'inclusion (Pass 1 ECH-03) : - // (active=true OR EXISTS écritures dans la période) - // - // CORRECTION (Pass 4 dev) : la subquery `filtered_jel` filtre les lignes par - // fiscal_year + période AVANT le LEFT JOIN sur accounts, sinon SUM agrège - // les lignes de TOUS les fy (bug constaté avec fy2 sans écritures). + let fy = fiscal_years::find_by_id_in_company(pool, company_id, period.fiscal_year_id) + .await? + .ok_or(ReportError::FiscalYearNotFound { + fiscal_year_id: period.fiscal_year_id, + })?; + let fy_start = fy.start_date; + + // Une requête agrégée : par compte, les sommes avant le début, depuis le début + // de l'exercice, et sur la période. Les mouvements gardent leur filtre + // d'origine (exercice ET dates). let sql = " SELECT a.id AS account_id, @@ -66,52 +139,83 @@ pub async fn generate( a.name, a.account_type, a.active, - COALESCE(SUM(filtered_jel.debit), 0) AS total_debit, - COALESCE(SUM(filtered_jel.credit), 0) AS total_credit, - CASE - WHEN a.account_type IN ('Asset', 'Expense') - THEN COALESCE(SUM(filtered_jel.debit), 0) - COALESCE(SUM(filtered_jel.credit), 0) - ELSE COALESCE(SUM(filtered_jel.credit), 0) - COALESCE(SUM(filtered_jel.debit), 0) - END AS balance + COALESCE(SUM(CASE WHEN x.entry_date < ? THEN x.debit END), 0) AS before_debit, + COALESCE(SUM(CASE WHEN x.entry_date < ? THEN x.credit END), 0) AS before_credit, + COALESCE(SUM(CASE WHEN x.entry_date >= ? AND x.entry_date < ? THEN x.debit END), 0) + AS since_fy_debit, + COALESCE(SUM(CASE WHEN x.entry_date >= ? AND x.entry_date < ? THEN x.credit END), 0) + AS since_fy_credit, + COALESCE(SUM(CASE WHEN x.fiscal_year_id = ? AND x.entry_date BETWEEN ? AND ? + THEN x.debit END), 0) AS period_debit, + COALESCE(SUM(CASE WHEN x.fiscal_year_id = ? AND x.entry_date BETWEEN ? AND ? + THEN x.credit END), 0) AS period_credit, + CAST(COALESCE(SUM(CASE WHEN x.fiscal_year_id = ? AND x.entry_date BETWEEN ? AND ? + THEN 1 ELSE 0 END), 0) AS SIGNED) AS period_lines FROM accounts a LEFT JOIN ( - SELECT jel.account_id, jel.debit, jel.credit + SELECT jel.account_id, jel.debit, jel.credit, je.entry_date, je.fiscal_year_id FROM journal_entry_lines jel INNER JOIN journal_entries je ON je.id = jel.entry_id WHERE je.company_id = ? - AND je.fiscal_year_id = ? - AND je.entry_date BETWEEN ? AND ? - ) AS filtered_jel ON filtered_jel.account_id = a.id + AND je.entry_date <= ? + ) AS x ON x.account_id = a.id WHERE a.company_id = ? - AND ( - a.active = TRUE - OR EXISTS ( - SELECT 1 FROM journal_entry_lines jel2 - INNER JOIN journal_entries je2 ON je2.id = jel2.entry_id - WHERE jel2.account_id = a.id - AND je2.company_id = ? - AND je2.fiscal_year_id = ? - AND je2.entry_date BETWEEN ? AND ? - ) - ) GROUP BY a.id, a.number, a.name, a.account_type, a.active ORDER BY a.number ASC "; - let rows = sqlx::query_as::<_, TrialBalanceRow>(sql) - .bind(company_id) // filtered_jel.je.company_id + let (start, end) = (period.start_date, period.end_date); + let raw = sqlx::query_as::<_, RawRow>(sql) + .bind(start) // before_debit + .bind(start) // before_credit + .bind(fy_start) + .bind(start) // since_fy_debit + .bind(fy_start) + .bind(start) // since_fy_credit .bind(period.fiscal_year_id) - .bind(period.start_date) - .bind(period.end_date) - .bind(company_id) // a.company_id (WHERE) - .bind(company_id) // je2.company_id (EXISTS subquery) + .bind(start) + .bind(end) // period_debit .bind(period.fiscal_year_id) - .bind(period.start_date) - .bind(period.end_date) + .bind(start) + .bind(end) // period_credit + .bind(period.fiscal_year_id) + .bind(start) + .bind(end) // period_lines + .bind(company_id) // x: je.company_id + .bind(end) // x: borne haute + .bind(company_id) // a.company_id .fetch_all(pool) .await .map_err(kesh_db::errors::map_db_error)?; + let rows: Vec = raw + .into_iter() + .filter_map(|r| { + let opening = opening_from( + r.account_type, + (r.before_debit, r.before_credit), + (r.since_fy_debit, r.since_fy_credit), + ); + // Règle d'inclusion : actif, ou mouvementé, ou porteur d'un solde. + if !r.active && r.period_lines == 0 && opening.is_zero() { + return None; + } + let balance = signed(r.account_type, r.period_debit, r.period_credit); + Some(TrialBalanceRow { + account_id: r.account_id, + account_number: r.number, + account_name: r.name, + account_type: r.account_type, + active: r.active, + opening_balance: opening, + total_debit: r.period_debit, + total_credit: r.period_credit, + balance, + closing_balance: opening + balance, + }) + }) + .collect(); + let total_debit: Decimal = rows.iter().map(|r| r.total_debit).sum(); let total_credit: Decimal = rows.iter().map(|r| r.total_credit).sum(); let balanced = total_debit == total_credit; @@ -130,38 +234,92 @@ pub async fn generate( }); } + let retained_earnings = fetch_retained_earnings(pool, company_id, fy_start).await?; + let opening_balanced = is_opening_balanced(&rows, retained_earnings); + if !opening_balanced { + tracing::warn!( + company_id, + fiscal_year_id = period.fiscal_year_id, + retained_earnings = %retained_earnings, + "trial_balance : les soldes d'ouverture ne s'équilibrent pas avec le résultat reporté" + ); + } + Ok(TrialBalance { period: period.clone(), rows, total_debit, total_credit, balanced, + retained_earnings, + opening_balanced, }) } +/// `Σ debit_sense(opening_balance) − retained_earnings == 0` : les soldes signés +/// par type sont ramenés en sens débit avant d'être additionnés. +fn is_opening_balanced(rows: &[TrialBalanceRow], retained_earnings: Decimal) -> bool { + let sum: Decimal = rows + .iter() + .map(|r| debit_sense(r.account_type, r.opening_balance)) + .sum(); + sum - retained_earnings == Decimal::ZERO +} + #[cfg(test)] mod tests { + use super::*; use rust_decimal_macros::dec; + fn row(account_type: AccountType, opening: Decimal) -> TrialBalanceRow { + TrialBalanceRow { + account_id: 0, + account_number: String::new(), + account_name: String::new(), + account_type, + active: true, + opening_balance: opening, + total_debit: Decimal::ZERO, + total_credit: Decimal::ZERO, + balance: Decimal::ZERO, + closing_balance: opening, + } + } + + /// Ventes 1000 et charge 400 en exercice 1 (clients 1000, caisse −400, tous + /// deux à l'actif) : ouvertures en sens débit 600 = résultat reporté 600. #[test] - fn balanced_when_debit_equals_credit() { - let total_debit = dec!(1500); - let total_credit = dec!(1500); - assert!(total_debit == total_credit); + fn l_ouverture_s_equilibre_avec_le_resultat_reporte() { + let rows = [ + row(AccountType::Asset, dec!(1000)), + row(AccountType::Asset, dec!(-400)), + ]; + assert!(is_opening_balanced(&rows, dec!(600))); + assert!(!is_opening_balanced(&rows, dec!(500))); } + /// Un passif créditeur de 5000 (report d'un migrant) face à une banque de + /// 5000 : sans le re-signe en sens débit, la somme vaudrait 10000 et le + /// contrôle rougirait sur des livres exacts. #[test] - fn unbalanced_when_debit_diverges() { - let total_debit = dec!(1500); - let total_credit = dec!(1499); - assert!(total_debit != total_credit); + fn les_comptes_a_nature_creditrice_sont_re_signes() { + let rows = [ + row(AccountType::Asset, dec!(5000)), + row(AccountType::Liability, dec!(5000)), + ]; + assert!(is_opening_balanced(&rows, Decimal::ZERO)); } + /// Exercice en cours : un produit de 200 avant la période (janvier–février) + /// est porté par l'ouverture du compte de produits, PAS par le résultat + /// reporté — compté une fois. #[test] - fn zero_balance_for_inactive_accounts_without_entries() { - // L'invariant : si compte archivé sans écriture, exclus de la balance. - // Si compte actif sans écriture, balance = 0 (SUM NULL → COALESCE 0). - let active_no_entries_balance = rust_decimal::Decimal::ZERO; - assert_eq!(active_no_entries_balance, rust_decimal::Decimal::ZERO); + fn le_resultat_de_l_exercice_avant_la_periode_est_compte_une_fois() { + let rows = [ + row(AccountType::Asset, dec!(1200)), + row(AccountType::Asset, dec!(-400)), + row(AccountType::Revenue, dec!(200)), + ]; + assert!(is_opening_balanced(&rows, dec!(600))); } } diff --git a/crates/kesh-report/tests/general_ledger.rs b/crates/kesh-report/tests/general_ledger.rs index 8b0e1308c..29c051882 100644 --- a/crates/kesh-report/tests/general_ledger.rs +++ b/crates/kesh-report/tests/general_ledger.rs @@ -204,6 +204,47 @@ async fn concordance_compte_de_resultat(pool: MySqlPool) { section.closing, is_row.balance, "la clôture doit égaler le montant du compte de résultat" ); + + // Story 25-5-b — en cours d'exercice (à partir du 1er mars), la vente de + // février fait l'ouverture du compte de ventes ; le grand livre et la balance + // doivent la porter de la même façon, et clore au même nombre. Sur + // l'exercice entier, les deux ouvertures vaudraient 0 par construction. + let (from, to) = (ymd(2026, 3, 1), ymd(2026, 12, 31)); + let mid_ledger = generate( + &pool, + seeded.company_id, + &LedgerPeriod::new(from, to).expect("période"), + &LedgerOptions::default(), + ) + .await + .expect("grand livre en cours d'exercice"); + let mid_section = mid_ledger + .sections + .iter() + .find(|s| s.account_id == ventes) + .expect("le compte de ventes doit figurer"); + let mid_rp = ReportPeriod { + fiscal_year_id: seeded.fiscal_year_id, + start_date: from, + end_date: to, + }; + let mid_tb = generate_trial_balance(&pool, seeded.company_id, &mid_rp) + .await + .expect("balance en cours d'exercice"); + let mid_row = mid_tb + .rows + .iter() + .find(|r| r.account_id == ventes) + .expect("le compte doit figurer à la balance"); + assert_eq!(mid_section.opening, dec!(1000.00)); + assert_eq!( + mid_section.opening, mid_row.opening_balance, + "l'ouverture doit concorder entre grand livre et balance" + ); + assert_eq!( + mid_section.closing, mid_row.closing_balance, + "la clôture doit concorder entre grand livre et balance" + ); } /// La partie double, vue depuis le grand livre : c'est le test le moins cher du diff --git a/crates/kesh-report/tests/trial_balance_opening.rs b/crates/kesh-report/tests/trial_balance_opening.rs new file mode 100644 index 000000000..d1ee960c0 --- /dev/null +++ b/crates/kesh-report/tests/trial_balance_opening.rs @@ -0,0 +1,559 @@ +//! La balance des comptes porte un solde d'ouverture — Story 25-5-b (#385). +//! +//! ⚠️ **Les tests de CONCORDANCE sont la raison d'être de ce fichier.** Le défaut +//! redouté est muet : si l'ouverture est prise de travers, `clôture = ouverture + +//! mouvements` reste vrai et le rapport est intérieurement cohérent, extérieurement +//! faux. Ils appellent donc réellement le bilan, le compte de résultat et le grand +//! livre, et chacun nomme la mutation qu'il tue. +//! +//! Pré-requis : MariaDB démarré. + +use chrono::NaiveDate; +use kesh_db::entities::journal_entry::{Journal, NewJournalEntry, NewJournalEntryLine}; +use kesh_db::repositories::journal_entries; +use kesh_db::test_fixtures::{SeededCompany, seed_accounting_company}; +use kesh_report::general_ledger::{LedgerOptions, LedgerPeriod}; +use kesh_report::period::ReportPeriod; +use kesh_report::trial_balance::TrialBalance; +use kesh_report::{generate_balance_sheet, generate_general_ledger, generate_trial_balance}; +use rust_decimal::Decimal; +use rust_decimal_macros::dec; +use sqlx::MySqlPool; + +fn ymd(y: i32, m: u32, d: u32) -> NaiveDate { + NaiveDate::from_ymd_opt(y, m, d).expect("date valide") +} + +/// Poste une écriture équilibrée à deux lignes dans l'exercice `fy`. Renvoie +/// l'id de l'écriture. +#[allow(clippy::too_many_arguments)] +async fn post( + pool: &MySqlPool, + seeded: &SeededCompany, + fy: i64, + date: NaiveDate, + debit_account: &str, + credit_account: &str, + amount: Decimal, +) -> i64 { + journal_entries::create( + pool, + fy, + seeded.admin_user_id, + NewJournalEntry { + company_id: seeded.company_id, + entry_date: date, + journal: Journal::OD, + description: "écriture".into(), + project_id: None, + lines: vec![ + NewJournalEntryLine { + account_id: seeded.accounts[debit_account], + debit: amount, + credit: Decimal::ZERO, + project_id: None, + }, + NewJournalEntryLine { + account_id: seeded.accounts[credit_account], + debit: Decimal::ZERO, + credit: amount, + project_id: None, + }, + ], + }, + ) + .await + .expect("écriture postée") + .entry + .id +} + +/// Deux exercices, 2025 et 2026. ⚠️ La fixture seede UN exercice 2020-2030 : il +/// faut le rétrécir, sinon 2025 et 2026 sont le même exercice et le report ne +/// prouve plus rien. +async fn two_years(pool: &MySqlPool, seeded: &SeededCompany) -> (i64, i64) { + sqlx::query("UPDATE fiscal_years SET end_date = '2025-12-31' WHERE id = ?") + .bind(seeded.fiscal_year_id) + .execute(pool) + .await + .expect("exercice seedé rétréci à 2025"); + let fy2026: i64 = sqlx::query_scalar( + "INSERT INTO fiscal_years (company_id, name, start_date, end_date, status, created_at, updated_at) \ + VALUES (?, 'FY 2026', '2026-01-01', '2026-12-31', 'Open', NOW(3), NOW(3)) RETURNING id", + ) + .bind(seeded.company_id) + .fetch_one(pool) + .await + .expect("exercice 2026 créé"); + (seeded.fiscal_year_id, fy2026) +} + +/// Le jeu de référence : ventes 1000 et charge 400 en 2025 ; en 2026, une vente +/// de 200 en février, 50 la veille du 1er mars, 70 le 1er mars (la frontière), +/// 500 en avril, 30 en août. +async fn reference_books(pool: &MySqlPool, seeded: &SeededCompany) -> (i64, i64) { + let (fy25, fy26) = two_years(pool, seeded).await; + post( + pool, + seeded, + fy25, + ymd(2025, 3, 1), + "1100", + "3000", + dec!(1000), + ) + .await; + post( + pool, + seeded, + fy25, + ymd(2025, 6, 1), + "4000", + "1000", + dec!(400), + ) + .await; + post( + pool, + seeded, + fy26, + ymd(2026, 2, 1), + "1100", + "3000", + dec!(200), + ) + .await; + let veille = ymd(2026, 3, 1).pred_opt().expect("veille"); + post(pool, seeded, fy26, veille, "1000", "3000", dec!(50)).await; + post( + pool, + seeded, + fy26, + ymd(2026, 3, 1), + "1000", + "3000", + dec!(70), + ) + .await; + post( + pool, + seeded, + fy26, + ymd(2026, 4, 15), + "1100", + "3000", + dec!(500), + ) + .await; + post( + pool, + seeded, + fy26, + ymd(2026, 8, 1), + "1100", + "3000", + dec!(30), + ) + .await; + (fy25, fy26) +} + +fn period(fy: i64, start: NaiveDate, end: NaiveDate) -> ReportPeriod { + ReportPeriod { + fiscal_year_id: fy, + start_date: start, + end_date: end, + } +} + +async fn balance(pool: &MySqlPool, seeded: &SeededCompany, p: &ReportPeriod) -> TrialBalance { + generate_trial_balance(pool, seeded.company_id, p) + .await + .expect("balance") +} + +/// `(ouverture, débit, crédit, clôture)` d'un compte, par numéro. +fn row(tb: &TrialBalance, number: &str) -> (Decimal, Decimal, Decimal, Decimal) { + let r = tb + .rows + .iter() + .find(|r| r.account_number == number) + .unwrap_or_else(|| panic!("le compte {number} doit figurer à la balance")); + ( + r.opening_balance, + r.total_debit, + r.total_credit, + r.closing_balance, + ) +} + +/// ⛔ Toute la balance de l'exercice 2 concorde avec le bilan (comptes de bilan) +/// et le compte de résultat (comptes de résultat). Mutation tuée : « ouverture +/// bornée par l'exercice » (le défaut de #385 — la caisse et la banque perdraient +/// leur report) et « ouverture des comptes de résultat cumulée depuis l'origine » +/// (les ventes porteraient les 1000 de 2025). +#[sqlx::test(migrations = "../kesh-db/test-schema")] +async fn la_balance_de_l_exercice_concorde_avec_le_bilan_et_le_resultat(pool: MySqlPool) { + let seeded = seed_accounting_company(&pool).await.expect("seed"); + let (_, fy26) = reference_books(&pool, &seeded).await; + let p = period(fy26, ymd(2026, 1, 1), ymd(2026, 12, 31)); + let tb = balance(&pool, &seeded, &p).await; + + assert_eq!( + row(&tb, "3000").0, + Decimal::ZERO, + "les ventes repartent de zéro" + ); + assert_eq!(row(&tb, "1100").0, dec!(1000), "la banque reporte 2025"); + assert_eq!(row(&tb, "1000").0, dec!(-400), "la caisse reporte 2025"); + assert_eq!(tb.retained_earnings, dec!(600)); + assert!(tb.opening_balanced); + + let bs = generate_balance_sheet(&pool, seeded.company_id, &p) + .await + .expect("bilan"); + assert_eq!(tb.retained_earnings, bs.retained_earnings); + let bs_lines: Vec<_> = bs + .assets + .iter() + .chain(&bs.liabilities) + .chain(&bs.equity) + .collect(); + let is = kesh_report::generate_income_statement(&pool, seeded.company_id, &p) + .await + .expect("compte de résultat"); + let is_lines: Vec<_> = is.revenues.iter().chain(&is.expenses).collect(); + + for r in &tb.rows { + let expected = if kesh_report::opening::is_bilan(r.account_type) { + bs_lines + .iter() + .find(|b| b.account_id == r.account_id) + .map_or(Decimal::ZERO, |b| b.balance) + } else { + is_lines + .iter() + .find(|b| b.account_id == r.account_id) + .map_or(Decimal::ZERO, |b| b.balance) + }; + assert_eq!( + r.closing_balance, expected, + "compte {} : la clôture doit égaler le bilan / le compte de résultat", + r.account_number + ); + } +} + +/// ⛔ Période en cours d'exercice (mars–juin), avec une écriture LE 1er mars et une +/// la veille. Mutation tuée : `<` → `<=` sur la borne d'ouverture (la vente du 1er +/// mars serait comptée deux fois — en ouverture et en mouvement). +#[sqlx::test(migrations = "../kesh-db/test-schema")] +async fn en_cours_d_exercice_la_frontiere_n_est_comptee_qu_une_fois(pool: MySqlPool) { + let seeded = seed_accounting_company(&pool).await.expect("seed"); + let (_, fy26) = reference_books(&pool, &seeded).await; + let (start, end) = (ymd(2026, 3, 1), ymd(2026, 6, 30)); + let tb = balance(&pool, &seeded, &period(fy26, start, end)).await; + + // Ventes : 200 + 50 (la veille) en ouverture ; 70 (le 1er mars) + 500 en mouvement. + assert_eq!( + row(&tb, "3000"), + (dec!(250), Decimal::ZERO, dec!(570), dec!(820)) + ); + // Caisse : −400 + 50 en ouverture ; 70 en mouvement. + assert_eq!( + row(&tb, "1000"), + (dec!(-350), dec!(70), Decimal::ZERO, dec!(-280)) + ); + assert!(tb.opening_balanced); + + // La même clôture que le grand livre, compte par compte. + let ledger = generate_general_ledger( + &pool, + seeded.company_id, + &LedgerPeriod::new(start, end).expect("période"), + &LedgerOptions::default(), + ) + .await + .expect("grand livre"); + for s in &ledger.sections { + let r = tb + .rows + .iter() + .find(|r| r.account_id == s.account_id) + .expect("tout compte du grand livre figure à la balance"); + assert_eq!( + r.opening_balance, s.opening, + "ouverture {}", + s.account_number + ); + assert_eq!(r.closing_balance, s.closing, "clôture {}", s.account_number); + } + + // Et la clôture d'un compte de bilan, celle du bilan arrêté au 30 juin. + let bs = generate_balance_sheet(&pool, seeded.company_id, &period(fy26, start, end)) + .await + .expect("bilan"); + let caisse = bs + .assets + .iter() + .find(|a| a.account_number == "1000") + .expect("caisse au bilan"); + assert_eq!(row(&tb, "1000").3, caisse.balance); +} + +/// Une période d'un seul jour : l'écriture du jour en mouvement, celle de la +/// veille en ouverture. +#[sqlx::test(migrations = "../kesh-db/test-schema")] +async fn une_periode_d_un_jour(pool: MySqlPool) { + let seeded = seed_accounting_company(&pool).await.expect("seed"); + let (_, fy26) = reference_books(&pool, &seeded).await; + let day = ymd(2026, 3, 1); + let tb = balance(&pool, &seeded, &period(fy26, day, day)).await; + assert_eq!( + row(&tb, "1000"), + (dec!(-350), dec!(70), Decimal::ZERO, dec!(-280)) + ); + assert!(tb.opening_balanced); +} + +/// Le premier exercice d'une société : rien n'est reporté. +#[sqlx::test(migrations = "../kesh-db/test-schema")] +async fn le_premier_exercice_n_a_rien_a_reporter(pool: MySqlPool) { + let seeded = seed_accounting_company(&pool).await.expect("seed"); + let fy = seeded.fiscal_year_id; + post( + &pool, + &seeded, + fy, + ymd(2026, 3, 1), + "1100", + "3000", + dec!(1000), + ) + .await; + let tb = balance( + &pool, + &seeded, + &period(fy, ymd(2020, 1, 1), ymd(2030, 12, 31)), + ) + .await; + assert_eq!(tb.retained_earnings, Decimal::ZERO); + assert!(tb.rows.iter().all(|r| r.opening_balance.is_zero())); + assert_eq!(row(&tb, "1100").3, dec!(1000)); + assert!(tb.opening_balanced); +} + +/// Une société sans aucune écriture : tout à zéro, contrôle vrai. +#[sqlx::test(migrations = "../kesh-db/test-schema")] +async fn une_societe_sans_ecriture(pool: MySqlPool) { + let seeded = seed_accounting_company(&pool).await.expect("seed"); + let fy = seeded.fiscal_year_id; + let tb = balance( + &pool, + &seeded, + &period(fy, ymd(2020, 1, 1), ymd(2030, 12, 31)), + ) + .await; + assert!( + !tb.rows.is_empty(), + "les comptes actifs sont toujours rendus" + ); + assert!( + tb.rows + .iter() + .all(|r| r.opening_balance.is_zero() && r.closing_balance.is_zero()) + ); + assert_eq!(tb.retained_earnings, Decimal::ZERO); + assert!(tb.opening_balanced); +} + +/// L'exercice 2 ouvert et encore vide — la première vue d'une année neuve : +/// ouvertures = clôtures de 2025 pour le bilan, nulles pour le résultat. +#[sqlx::test(migrations = "../kesh-db/test-schema")] +async fn un_exercice_neuf_encore_vide_montre_le_report(pool: MySqlPool) { + let seeded = seed_accounting_company(&pool).await.expect("seed"); + let (fy25, fy26) = two_years(&pool, &seeded).await; + post( + &pool, + &seeded, + fy25, + ymd(2025, 3, 1), + "1100", + "3000", + dec!(1000), + ) + .await; + post( + &pool, + &seeded, + fy25, + ymd(2025, 6, 1), + "4000", + "1000", + dec!(400), + ) + .await; + let tb = balance( + &pool, + &seeded, + &period(fy26, ymd(2026, 1, 1), ymd(2026, 12, 31)), + ) + .await; + assert_eq!( + row(&tb, "1100"), + (dec!(1000), Decimal::ZERO, Decimal::ZERO, dec!(1000)) + ); + assert_eq!(row(&tb, "3000").0, Decimal::ZERO); + assert_eq!(row(&tb, "4000").0, Decimal::ZERO); + assert_eq!(tb.retained_earnings, dec!(600)); + assert!(tb.opening_balanced); +} + +/// Des pertes cumulées : résultat reporté négatif, contrôle vrai. +#[sqlx::test(migrations = "../kesh-db/test-schema")] +async fn des_pertes_cumulees(pool: MySqlPool) { + let seeded = seed_accounting_company(&pool).await.expect("seed"); + let (fy25, fy26) = two_years(&pool, &seeded).await; + post( + &pool, + &seeded, + fy25, + ymd(2025, 3, 1), + "1100", + "3000", + dec!(100), + ) + .await; + post( + &pool, + &seeded, + fy25, + ymd(2025, 6, 1), + "4000", + "1100", + dec!(900), + ) + .await; + let tb = balance( + &pool, + &seeded, + &period(fy26, ymd(2026, 1, 1), ymd(2026, 12, 31)), + ) + .await; + assert_eq!(tb.retained_earnings, dec!(-800)); + assert!(tb.opening_balanced); +} + +/// Insère une ligne **déséquilibrée** dans une écriture existante, hors +/// `create_in_tx` (qui garantit la partie double). +async fn break_entry(pool: &MySqlPool, entry_id: i64, account_id: i64) { + sqlx::query( + "INSERT INTO journal_entry_lines (entry_id, account_id, line_order, debit, credit) \ + VALUES (?, ?, 99, 10, 0)", + ) + .bind(entry_id) + .bind(account_id) + .execute(pool) + .await + .expect("ligne déséquilibrée insérée"); +} + +/// ⛔ Le contrôle d'ouverture TOMBE quand l'égalité est réellement cassée — sur un +/// compte de bilan, puis sur un compte de résultat (l'écart passe alors par le +/// résultat reporté). Le rapport est rendu : les mouvements de 2026 restent +/// équilibrés. Mutation tuée : un contrôle tautologique (toujours vrai). +#[sqlx::test(migrations = "../kesh-db/test-schema")] +async fn le_controle_d_ouverture_tombe_sur_un_compte_de_bilan(pool: MySqlPool) { + let seeded = seed_accounting_company(&pool).await.expect("seed"); + let (fy25, fy26) = two_years(&pool, &seeded).await; + let e = post( + &pool, + &seeded, + fy25, + ymd(2025, 3, 1), + "1100", + "3000", + dec!(1000), + ) + .await; + break_entry(&pool, e, seeded.accounts["1000"]).await; + let tb = balance( + &pool, + &seeded, + &period(fy26, ymd(2026, 1, 1), ymd(2026, 12, 31)), + ) + .await; + assert!(!tb.opening_balanced); +} + +#[sqlx::test(migrations = "../kesh-db/test-schema")] +async fn le_controle_d_ouverture_tombe_sur_un_compte_de_resultat(pool: MySqlPool) { + let seeded = seed_accounting_company(&pool).await.expect("seed"); + let (fy25, fy26) = two_years(&pool, &seeded).await; + let e = post( + &pool, + &seeded, + fy25, + ymd(2025, 3, 1), + "1100", + "3000", + dec!(1000), + ) + .await; + break_entry(&pool, e, seeded.accounts["4000"]).await; + let tb = balance( + &pool, + &seeded, + &period(fy26, ymd(2026, 1, 1), ymd(2026, 12, 31)), + ) + .await; + assert!(!tb.opening_balanced); +} + +async fn archive(pool: &MySqlPool, account_id: i64) { + sqlx::query("UPDATE accounts SET active = FALSE WHERE id = ?") + .bind(account_id) + .execute(pool) + .await + .expect("compte archivé"); +} + +/// ⛔ Un compte archivé qui porte encore un solde figure à la balance — comptes de +/// bilan et de résultat. Mutation tuée : la règle d'inclusion d'avant (archivé = +/// seulement s'il a un mouvement dans la période) — le compte disparaîtrait, sa +/// clôture ne concorderait plus avec le bilan et le contrôle rougirait. +#[sqlx::test(migrations = "../kesh-db/test-schema")] +async fn un_compte_archive_porteur_d_un_solde_figure(pool: MySqlPool) { + let seeded = seed_accounting_company(&pool).await.expect("seed"); + let (_, fy26) = reference_books(&pool, &seeded).await; + archive(&pool, seeded.accounts["1100"]).await; + archive(&pool, seeded.accounts["3000"]).await; + + // Exercice 2 entier : la banque archivée porte son report et ses mouvements. + let p = period(fy26, ymd(2026, 1, 1), ymd(2026, 12, 31)); + let tb = balance(&pool, &seeded, &p).await; + let banque = tb + .rows + .iter() + .find(|r| r.account_number == "1100") + .expect("banque archivée présente"); + assert!(!banque.active); + assert!(tb.opening_balanced); + + // Juillet : aucun mouvement de la banque ni des ventes ; les deux portent une + // ouverture non nulle et doivent figurer. + let july = period(fy26, ymd(2026, 7, 1), ymd(2026, 7, 31)); + let tb = balance(&pool, &seeded, &july).await; + assert_eq!(row(&tb, "1100").0, dec!(1700)); + assert_eq!(row(&tb, "3000").0, dec!(820)); + assert!(tb.opening_balanced); + let bs = generate_balance_sheet(&pool, seeded.company_id, &july) + .await + .expect("bilan"); + let b = bs + .assets + .iter() + .find(|a| a.account_number == "1100") + .expect("banque au bilan"); + assert_eq!(row(&tb, "1100").3, b.balance); +} diff --git a/docs/manual/fr/user-manual.pdf b/docs/manual/fr/user-manual.pdf index f68c17a33eef99c152198f4fadefa0357eb9ab58..04066d56443fd9d9c8f4131815e1d2bd38469f9c 100644 GIT binary patch delta 78186 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

&4=| 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

JOdE)xdU@W8U26 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 diff --git a/docs/manual/fr/user-manual.tex b/docs/manual/fr/user-manual.tex index fe5ed9fcb..b7e5b0828 100644 --- a/docs/manual/fr/user-manual.tex +++ b/docs/manual/fr/user-manual.tex @@ -603,7 +603,7 @@ \subsection{Réouverture d'un exercice clôturé} On rouvre les exercices \textbf{du plus récent vers l'ancien} : si un exercice postérieur est encore clôturé, Kesh refuse la réouverture et indique lequel rouvrir d'abord. Effectuer cette opération avec parcimonie pour préserver l'intégrité légale. \end{keshtip} -\subsection{Reprise de comptabilité — soldes de départ} +\subsection{Reprise de comptabilité — soldes de départ}\label{sec:soldes-depart} Si vous migrez vers Kesh depuis un autre logiciel comptable, l'écran \emph{Administration} → \emph{Soldes de départ} (réservé aux rôles Comptable et Administrateur) vous permet de reprendre le bilan de clôture de votre ancienne comptabilité en une seule saisie : @@ -1556,13 +1556,35 @@ \subsection{Compte de résultat} \subsection{Balance des comptes} -\emph{Mensuel} → \emph{Rapports} → \emph{Balance}. Liste tous les comptes avec leurs soldes débiteurs et créditeurs. Filtres : période, niveau de détail (comptes principaux uniquement, ou tous sous-comptes). +\emph{Mensuel} → \emph{Rapports} → \emph{Balance}. Pour chaque compte, sur la période choisie : le \textbf{solde +d'ouverture}, les \textbf{mouvements} débit et crédit, et le \textbf{solde de clôture} (ouverture + mouvements). + +\begin{itemize} + \item \textbf{L'ouverture suit la nature du compte.} Un compte de bilan (actif, passif) reporte son solde depuis + l'origine, tous exercices confondus ; un compte de produits ou de charges repart de zéro au début de + l'exercice — son ouverture ne compte que ce qui précède la période \emph{dans l'exercice}. + \item \textbf{La balance concorde avec le bilan et le grand livre.} La clôture d'un compte de bilan est son solde au + bilan à la fin de la période ; celle d'un compte de résultat, son montant au compte de résultat ; et les deux + colonnes sont celles du grand livre (\S\ref{sec:grand-livre}) sur la même période. + \item \textbf{La ligne « Résultat reporté (calculé) ».} Kesh ne passe aucune écriture de clôture : le résultat des + exercices antérieurs n'est porté par aucun compte. Une ligne calculée le montre, sous les comptes — la même + valeur qu'au bilan, « Perte reportée » à l'écran si elle est négative. + \item \textbf{Les montants sont signés selon la nature du compte} — positifs du côté naturel (débit pour un actif ou + une charge, crédit pour un passif ou un produit). La somme visible des colonnes d'ouverture et de clôture n'est + donc pas nulle ; le \textbf{contrôle} affiché sous l'ouverture (une coche) la vérifie en ramenant chaque solde au sens débit, résultat + reporté compris. Un avertissement à sa place signale une écriture déséquilibrée antérieure à la période. + \item \textbf{Premier exercice} : les soldes de départ saisis à la reprise sont une écriture datée du premier jour + de l'exercice (\S\ref{sec:soldes-depart}) — ils apparaissent donc en \emph{mouvements}, et l'ouverture vaut zéro. + \item Un compte \textbf{archivé} figure tant qu'il porte un solde ou un mouvement. +\end{itemize} + +Seul filtre : la période. \subsection{Grand livre}\label{sec:grand-livre} \emph{Rapports} → \emph{Grand livre}. C'est l'\textbf{extrait d'un compte} : ce qui a fait son -solde, ligne à ligne. Là où le bilan dit « clients : 84'320.15 » et où la balance dit la même -chose autrement, le grand livre répond à la question qui suit toujours — \emph{de quoi ce solde +solde, ligne à ligne. Là où le bilan dit « clients : 84'320.15 » et où la balance clôt ce compte au +même nombre, le grand livre répond à la question qui suit toujours — \emph{de quoi ce solde est-il fait ?} Chaque compte y est encadré par trois lignes sans lesquelles ce n'est pas un extrait : le @@ -2040,7 +2062,7 @@ \subsection{Glossaire comptable} \item[Actif] Ce que possède l'entreprise (liquidités, créances, stocks, immobilisations). Classe 1 du plan comptable. \item[Amortissement] Constatation comptable de la perte de valeur d'une immobilisation sur sa durée d'utilisation. \item[Avoir (note de crédit)] Document qui annule tout ou partie d'une facture émise (l'inverse d'une facture). - \item[Balance des comptes] Liste de tous les comptes avec leurs soldes débiteurs et créditeurs à une date donnée. + \item[Balance des comptes] Liste des comptes avec, sur une période, leur solde d'ouverture, leurs mouvements et leur solde de clôture. \item[Bilan] État du patrimoine d'une entreprise à une date donnée (actifs = passifs + capitaux propres). \item[Capitaux propres] Fonds appartenant aux propriétaires (capital, réserves, résultat reporté). Différence entre actifs et dettes. \item[Charge] Coût supporté par l'entreprise (achats, salaires, loyer). Classes 4 à 6. diff --git a/frontend/src/lib/features/reports/TrialBalanceView.svelte b/frontend/src/lib/features/reports/TrialBalanceView.svelte index f49146447..d4375d614 100644 --- a/frontend/src/lib/features/reports/TrialBalanceView.svelte +++ b/frontend/src/lib/features/reports/TrialBalanceView.svelte @@ -1,5 +1,8 @@ @@ -24,25 +39,24 @@ {i18nMsg('reports-error-no-entries-in-period', 'Aucune écriture dans la période sélectionnée.')}

{:else} -

- {i18nMsg( - 'reports-trial-balance-period-note', - 'La balance de vérification affiche le mouvement de la période (par exercice). Le total par compte n’est pas comparable au solde cumulé du même compte au bilan (report à-nouveau depuis l’origine).', - )} -

+ - + {#each dto.rows as r (r.accountId)} - + + - + {/each} + + + + + + + + + - diff --git a/frontend/src/lib/features/reports/TrialBalanceView.test.ts b/frontend/src/lib/features/reports/TrialBalanceView.test.ts new file mode 100644 index 000000000..29af85c96 --- /dev/null +++ b/frontend/src/lib/features/reports/TrialBalanceView.test.ts @@ -0,0 +1,101 @@ +// Story 25-5-b (#385) — tests Vitest pour TrialBalanceView.svelte : ouverture, +// mouvements, clôture, la ligne calculée du résultat reporté et le contrôle +// d'ouverture. i18nMsg est mocké pour renvoyer le fallback. + +import { describe, it, expect, vi } from "vitest"; +import { render, screen } from "@testing-library/svelte"; + +vi.mock("$lib/shared/utils/i18n.svelte", () => ({ + i18nMsg: (_key: string, fallback: string) => fallback, +})); + +import TrialBalanceView from "./TrialBalanceView.svelte"; +import type { TrialBalanceDto } from "./reports.types"; + +function makeDto(overrides: Partial = {}): TrialBalanceDto { + return { + period: { fiscalYearId: 1, startDate: "2026-01-01", endDate: "2026-12-31" }, + rows: [ + { + accountId: 1, + accountNumber: "1100", + accountName: "Banque", + accountType: "Asset", + active: true, + openingBalance: "1000", + totalDebit: "500", + totalCredit: "0", + balance: "500", + closingBalance: "1500", + }, + ], + totalDebit: "500", + totalCredit: "500", + balanced: true, + retainedEarnings: "600", + openingBalanced: true, + ...overrides, + }; +} + +describe("TrialBalanceView — Story 25-5-b", () => { + it("affiche les colonnes Ouverture et Clôture, sans « Solde » ni la note retirée", () => { + render(TrialBalanceView, { dto: makeDto() }); + const headers = screen + .getAllByRole("columnheader") + .map((h) => h.textContent?.trim()); + expect(headers).toEqual([ + "N°", + "Intitulé", + "Ouverture", + "Débit", + "Crédit", + "Clôture", + ]); + expect(screen.queryByText(/mouvement de la période/)).toBeNull(); + }); + + it("porte l’ouverture et la clôture de chaque compte, dans l’ordre des colonnes", () => { + render(TrialBalanceView, { dto: makeDto() }); + const cells = Array.from( + screen.getByTestId("tb-row").querySelectorAll("td"), + ).map((c) => c.textContent?.replace(/\s+/g, " ").trim()); + expect(cells[2]).toMatch(/1.?000\.00/); + expect(cells[5]).toMatch(/1.?500\.00/); + }); + + it("rend la ligne calculée du résultat reporté (ouverture = clôture)", () => { + render(TrialBalanceView, { dto: makeDto() }); + const row = screen.getByTestId("tb-retained"); + expect(row.textContent).toContain("Résultat reporté (calculé)"); + const cells = Array.from(row.querySelectorAll("td")).map((c) => + c.textContent?.trim(), + ); + expect(cells[2]).toBe(cells[5]); + expect(cells[3]).toBe(""); + }); + + it("bascule sur « Perte reportée » quand le résultat reporté est négatif", () => { + render(TrialBalanceView, { dto: makeDto({ retainedEarnings: "-800" }) }); + expect(screen.getByTestId("tb-retained").textContent).toContain( + "Perte reportée", + ); + }); + + it("signale une ouverture déséquilibrée, indépendamment des mouvements", () => { + render(TrialBalanceView, { dto: makeDto({ openingBalanced: false }) }); + expect(screen.getByTestId("tb-opening-check").textContent?.trim()).toBe( + "⚠️", + ); + expect(screen.getByTestId("tb-movements-check").textContent?.trim()).toBe( + "✓", + ); + }); + + it("affiche ✓ quand l’ouverture s’équilibre", () => { + render(TrialBalanceView, { dto: makeDto() }); + expect(screen.getByTestId("tb-opening-check").textContent?.trim()).toBe( + "✓", + ); + }); +}); diff --git a/frontend/src/lib/features/reports/reports.types.ts b/frontend/src/lib/features/reports/reports.types.ts index a32ef98c8..58635615b 100644 --- a/frontend/src/lib/features/reports/reports.types.ts +++ b/frontend/src/lib/features/reports/reports.types.ts @@ -56,9 +56,14 @@ export interface TrialBalanceRow { accountName: string; accountType: AccountType; active: boolean; + /** Story 25-5-b — solde au début de la période, signé selon la nature du compte. */ + openingBalance: string; totalDebit: string; totalCredit: string; + /** Net des mouvements de la période — pas un solde de compte. */ balance: string; + /** Story 25-5-b — ouverture + mouvements : pour un compte de bilan, son solde au bilan. */ + closingBalance: string; } export interface TrialBalanceDto { @@ -67,6 +72,10 @@ export interface TrialBalanceDto { totalDebit: string; totalCredit: string; balanced: boolean; + /** Story 25-5-b — résultat reporté calculé (exercices antérieurs), crédit-positif. */ + retainedEarnings: string; + /** Story 25-5-b — Σ des ouvertures en sens débit − résultat reporté = 0. */ + openingBalanced: boolean; } export type Journal = 'Achats' | 'Ventes' | 'Banque' | 'Caisse' | 'OD'; diff --git a/frontend/src/lib/shared/i18n-keys.test.ts b/frontend/src/lib/shared/i18n-keys.test.ts index 723844b02..eb8e271ef 100644 --- a/frontend/src/lib/shared/i18n-keys.test.ts +++ b/frontend/src/lib/shared/i18n-keys.test.ts @@ -349,7 +349,14 @@ const ATTENDU = { // (+4) — la section « Différences d'arrondi » : titre, aide, libellé du // sélecteur (trois clés neuves `settings-invoicing-rounding-*` dans les quatre // catalogues) et son option vide, qui réemploie `settings-invoicing-select-none`. - sitesTotal: 1756, + // + // Story 25-5-b (#385) : **1756 → 1758**, recompté par ce test et par + // `grep -c "i18nMsg("` aux deux bornes de `reports/TrialBalanceView.svelte` + // (11 → 13) — la note « mouvement de la période » retirée (−1, clé supprimée des + // quatre catalogues), l'en-tête « Solde » remplacé par « Ouverture » et + // « Clôture » (+1, deux clés neuves `reports-column-opening` / `-closing`), et + // le libellé de la ligne calculée, qui réemploie les deux clés du bilan (+2). + sitesTotal: 1758, sitesNonResolus: 34, relais: 7, sitesGabarit: 10, diff --git a/frontend/src/lib/shared/i18n-libelle-en-dur.test.ts b/frontend/src/lib/shared/i18n-libelle-en-dur.test.ts index 3b07559da..6bc62fdeb 100644 --- a/frontend/src/lib/shared/i18n-libelle-en-dur.test.ts +++ b/frontend/src/lib/shared/i18n-libelle-en-dur.test.ts @@ -140,8 +140,15 @@ const SUFFIXES = ['Label', 'Text', 'Display']; * à `i18nMsg` (`reconciliation-cancel-confirm-invoice` / `-entry`) : aucun littéral, donc * `conforme` **37 → 38** ; `ecartee` ne bouge pas. *Identifiée par la même recherche des * trois suffixes dans les fichiers de la story — la seule déclaration neuve qui en porte un.* + * + * ⚠️ **44 → 45, et la déclaration est NOMMÉE** : `retainedLabel` + * (`lib/features/reports/TrialBalanceView.svelte`), né avec la ligne calculée du résultat + * reporté de la Story 25-5-b (#385). Ses deux branches délèguent à `i18nMsg` + * (`reports-retained-earnings-loss` / `-calculated`) : aucun littéral, donc `conforme` + * **38 → 39** ; `ecartee` ne bouge pas. *Identifiée par la même recherche des trois + * suffixes dans les fichiers de la story — la seule déclaration neuve qui en porte un.* */ -const CANDIDATES_ATTENDUES = 44; +const CANDIDATES_ATTENDUES = 45; /** Les trois délimiteurs de littéral en JS/TS. */ const QUOTES = ["'", '"', '`']; @@ -658,7 +665,7 @@ describe('libellés en dur — l’angle mort #255', () => { else if (c.retours.length > 0) classes.ecartee += 1; else classes.conforme += 1; } - expect(classes).toEqual({ nonAnalysee: 0, enViolation: 0, ecartee: 6, conforme: 38 }); + expect(classes).toEqual({ nonAnalysee: 0, enViolation: 0, ecartee: 6, conforme: 39 }); // La somme est recalculée depuis les classes, jamais depuis le total qu'elle contrôle. const somme = Object.values(classes).reduce((a, b) => a + b, 0); expect(somme).toBe(CANDIDATES_ATTENDUES); diff --git a/frontend/tests/e2e/reports.spec.ts b/frontend/tests/e2e/reports.spec.ts index b4fbb95e8..ed27bb44d 100644 --- a/frontend/tests/e2e/reports.spec.ts +++ b/frontend/tests/e2e/reports.spec.ts @@ -399,6 +399,15 @@ test('grand livre : une écriture postée réapparaît dans l’extrait de son c await page.getByRole('button', { name: /générer/i }).click(); const lien = page.getByRole('link', { name: debitNumber }).first(); await expect(lien).toBeVisible({ timeout: 5000 }); + // Story 25-5-b (#385) — la balance porte l'ouverture et la clôture, et la + // ligne calculée du résultat reporté : les champs neufs traversent la frontière. + const panel = page.getByRole('tabpanel'); + await expect(page.getByTestId('tb-col-opening')).toBeVisible(); + await expect(page.getByTestId('tb-col-closing')).toBeVisible(); + // Six colonnes : la colonne « Solde » (net des mouvements) a disparu. + await expect(panel.getByRole('columnheader')).toHaveCount(6); + await expect(page.getByTestId('tb-retained')).toBeVisible(); + await expect(page.getByTestId('tb-opening-check')).toHaveText('✓'); await lien.click(); await expect(page).toHaveURL(/tab=general-ledger/); await expect(page).toHaveURL(new RegExp(`accountId=`)); From 2ebc8b6b1b5babe8b1c77c1328bfdd677b7fd98b Mon Sep 17 00:00:00 2001 From: Guy Corbaz Date: Sat, 3 Oct 2026 12:17:27 +0200 Subject: [PATCH 09/13] docs(25-5-b): prompt de revue de code P1 (refs #385) Co-Authored-By: Claude Opus 5.5 --- .../25-5-b-review-prompt-p1.md | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 _bmad-output/implementation-artifacts/25-5-b-review-prompt-p1.md diff --git a/_bmad-output/implementation-artifacts/25-5-b-review-prompt-p1.md b/_bmad-output/implementation-artifacts/25-5-b-review-prompt-p1.md new file mode 100644 index 000000000..7d9095475 --- /dev/null +++ b/_bmad-output/implementation-artifacts/25-5-b-review-prompt-p1.md @@ -0,0 +1,45 @@ +# Prompt — revue de code P1, Story 25-5-b (la balance porte un solde d'ouverture) + +*Versionné le 2026-10-03. Trois lentilles (Sonnet), contexte frais chacune.* + +Dépôt `/home/gcorbaz/devel/kesh`, branche `story/25-5-b-balance-solde-ouverture`. **Diff à revoir : `git diff 42623926 46edd970`** +— un seul commit d'implémentation. Fiche : `_bmad-output/implementation-artifacts/25-5-b-balance-solde-ouverture.md` +(AC 1–10, Dev Notes, Dev Agent Record). Issue : `gh issue view 385`. Règles : `CLAUDE.md`. + +## Lentilles + +- **A — Blind hunter** : le diff **seul**, sans la fiche. SQL de `trial_balance.rs` (liaisons dans le bon ordre — + compte-les une à une contre les `?` ; `SUM(CASE … END)` sans `ELSE` → `NULL` couvert par `COALESCE` ? ; `CAST … AS + SIGNED` lu en `i64` ; la jointure et le filtre de société), la requête d'ouverture réécrite du grand livre, `Decimal`, + rendus CSV/PDF, réactivité Svelte 5 et `big.js`. +- **B — Edge-case hunter** : le diff et le code environnant. + - Le grand livre : sa nouvelle requête d'ouverture rend-elle **exactement** l'ancien résultat dans tous les cas — compte + de bilan, compte de résultat, `from` hors de tout exercice, `from` = premier jour d'un exercice, exercices avec trous ? + - La balance : `fy_start` lu par `find_by_id_in_company` — exercice d'une autre société ? Une écriture d'une **autre + société** sur un compte de cette société (impossible ?) ; une écriture datée hors de son exercice ; un compte retypé. + - La règle d'inclusion : un compte archivé dont l'ouverture est nulle mais qui a des lignes **de montant net nul** dans + la période ; `period_lines` compte-t-il les bonnes lignes ? + - `opening_balanced` : faux positif possible sur des livres exacts (arrondi `Decimal`, échelle 4 décimales) ? + - L'écran : `retainedEarnings` absent ou illisible, rapport vide, ligne calculée quand il n'y a aucun compte. + - La performance : la sous-requête lit toutes les lignes jusqu'à `end` — index utilisés (`idx_jel_account`) ? +- **C — Acceptance auditor** : chaque AC tenu ? Recompte depuis la source (`sitesTotal`, relevé des libellés en dur, clés + dans les 4 locales, décomptes de tests du Dev Agent Record aux deux bornes `42623926` / `46edd970`). **Pars du + symptôme** : `grep -rn "\.balance\b\|totalDebit\|reports-column-balance" frontend/src crates/kesh-report crates/kesh-api/src` + — un consommateur de la balance qui suppose encore l'ancienne forme ? Les tests prouvent-ils ce qu'ils disent (chaque + mutation déclarée est-elle tuée par l'assertion écrite) ? Le **manuel** (`docs/manual/fr/user-manual.tex` et **PDF + aplati**, `pdftotext … | tr '\n' ' ' | tr -s ' '` vers `target/gate-logs/`) dit-il ce que fait le code — § *Balance des + comptes*, § *Grand livre*, glossaire, § *Reprise de comptabilité* ? Le CHANGELOG ? Le Dev Agent Record n'affirme-t-il que + ce qui a tourné ? + +## Ce que tu rends + +Findings avec sévérité (CRITICAL/HIGH/MEDIUM/LOW), `fichier:ligne`, **preuve** (commande et sortie, ou code cité relu), +scénario d'échec, correction. Pour tout finding affirmant qu'un code est absent ou présent : la sortie d'un `grep -nF`. +⛔ La liste des axes exercés ET non exercés. + +## Interdits + +⛔ N'écris aucun fichier du dépôt hors `target/gate-logs/` ; aucune commande qui écrit dans le dépôt ou dans une base : +`scripts/*` (dont `scripts/prepare-release.sh`), `make`, `latexmk`, `git commit`/`add`/`checkout`, `sqlx`, +`cargo test`/`nextest`, `npm run`, `npx`, `gh issue create`/`comment`/`edit`. Autorisés : lecture, `grep`, `sed`, +`git log`/`show`/`diff`, `gh issue view`, `pdftotext` vers `target/gate-logs/`. From 84505eeda525869ca79d98b01caf8e83eaf4c1c8 Mon Sep 17 00:00:00 2001 From: Guy Corbaz Date: Sat, 3 Oct 2026 12:24:10 +0200 Subject: [PATCH 10/13] =?UTF-8?q?fix(25-5-b):=20revue=20de=20code=20P1=20?= =?UTF-8?q?=E2=80=94=20concordance=20du=20r=C3=A9sultat=20born=C3=A9e=20?= =?UTF-8?q?=C3=A0=20l'exercice,=20contr=C3=B4le=20au=20PDF=20(refs=20#385)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- CHANGELOG.md | 2 +- .../25-5-b-balance-solde-ouverture.md | 29 +++++++++++-- crates/kesh-report/src/pdf.rs | 26 ++++++++++++ crates/kesh-report/src/trial_balance.rs | 12 +++++- .../tests/trial_balance_opening.rs | 40 ++++++++++++++++++ docs/manual/fr/user-manual.pdf | Bin 301695 -> 302018 bytes docs/manual/fr/user-manual.tex | 9 ++-- 7 files changed, 109 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 38791bc1e..25986289c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,7 +14,7 @@ Le contenu est rédigé en français à destination des **fiduciaires, PME, ind - **La balance des comptes ne concordait pas avec le bilan ([#385](https://github.com/guycorbaz/kesh/issues/385)).** Elle ne montrait que les **mouvements** de la période : le compte « clients » de la balance 2026 ne valait pas celui du bilan 2026, qui reporte les soldes depuis l'origine. Deux rapports, un compte, deux nombres — c'est ce qu'un réviseur trouve en dix minutes. - **Désormais**, chaque compte porte son **solde d'ouverture**, ses mouvements et son **solde de clôture**. Un compte de bilan reporte son solde depuis l'origine ; un compte de produits ou de charges repart de zéro au début de l'exercice. La clôture d'un compte de bilan **est** son solde au bilan, celle d'un compte de résultat son montant au compte de résultat, et les deux concordent avec le grand livre. Une ligne **« Résultat reporté (calculé) »** porte le résultat des exercices antérieurs, qu'aucun compte ne porte faute d'écriture de clôture, et un **contrôle ✓/⚠️** vérifie que les ouvertures s'équilibrent avec elle. Un compte archivé figure tant qu'il porte un solde. + **Désormais**, chaque compte porte son **solde d'ouverture**, ses mouvements et son **solde de clôture**. Un compte de bilan reporte son solde depuis l'origine ; un compte de produits ou de charges repart de zéro au début de l'exercice. La clôture d'un compte de bilan **est** son solde au bilan ; celle d'un compte de résultat est son montant **depuis le début de l'exercice** — égal au compte de résultat quand la période commence au premier jour de l'exercice —, et ouverture comme clôture concordent toujours avec le grand livre. Une ligne **« Résultat reporté (calculé) »** porte le résultat des exercices antérieurs, qu'aucun compte ne porte faute d'écriture de clôture, et un **contrôle ✓/⚠️** vérifie que les ouvertures s'équilibrent avec elle (écrit en toutes lettres dans le PDF quand il tombe). Un compte archivé figure tant qu'il porte un solde. - **L'export de souveraineté ne portait que la moitié de votre comptabilité.** Il promettait de quoi « migrer vers un autre logiciel » et laissait dehors **les factures fournisseurs, les avoirs, les règlements, les projets, les ordres de virement, les personnes de contact, les pièces reçues et le journal d'audit** — onze tables comptables sur les trente que compte désormais l'archive. diff --git a/_bmad-output/implementation-artifacts/25-5-b-balance-solde-ouverture.md b/_bmad-output/implementation-artifacts/25-5-b-balance-solde-ouverture.md index de29ac802..8ec2cf642 100644 --- a/_bmad-output/implementation-artifacts/25-5-b-balance-solde-ouverture.md +++ b/_bmad-output/implementation-artifacts/25-5-b-balance-solde-ouverture.md @@ -281,11 +281,14 @@ Claude Opus 5.5 (`claude-opus-5-5`). - `concordance_compte_de_resultat` étendu à une période en cours d'exercice ; - `reports_e2e.rs` : un test étendu à la forme de la réponse. - Vitest : **+6** dans `TrialBalanceView.test.ts`, neuf. E2E : `reports.spec.ts` étendu. -- **Gates** (T7) : base remise à zéro, `scripts/test-fast.sh` **2545/2545** (fmt, clippy, nextest) ; frontend `check` - 0 erreur, `lint-i18n-ownership` PASS, `test:unit` **845/845**, `build` OK ; **E2E complet** sur `kesh_e2e` migrée à - neuf (run de 10:07 UTC) : **224 passés, 12 échoués** — les 10 attendus d'un run matinal (KF-029 ×7, KF-045 ×2 +- **Gates** (T7) : base remise à zéro, `scripts/test-fast.sh` **2545/2545, 4 ignorés** (fmt, clippy, nextest) ; + frontend `check` 0 erreur, `lint-i18n-ownership` PASS, `test:unit` **845/845**, `build` OK ; **E2E complet** sur + `kesh_e2e` migrée à neuf (run de 10:07 UTC) : **224 passés, 12 échoués, 19 ignorés** — les 10 attendus d'un run matinal (KF-029 ×7, KF-045 ×2 `invoices.spec.ts:415/439`, KF-046 `sidebar-navigation:75`) et la paire de pollution documentée `products:166` + `product-revenue-account:133`, **vertes rejouées seules** ; `reports.spec.ts` vert, extension 25-5-b comprise. +- ⚠️ **Deux tests PDF prouvent moins que l'AC 9 ne le demande** (revue P1, C-2) : `…_is_not_the_empty_render` compare + des longueurs, et le test de troncature appelle la fonction et la constante, pas le site d'appel du rendu. Le PDF ne + s'inspecte pas sans extraire son texte ; limite assumée, aucune mutation PDF n'est déclarée tuée par eux. - **Mutations tuées** : - `<` → `<=` sur la borne d'ouverture : 2 tests tombent ; - ouverture de résultat cumulée depuis l'origine : 5 tests ; @@ -320,6 +323,26 @@ Claude Opus 5.5 (`claude-opus-5-5`). ## Change Log +- **2026-10-03** — Revue de code P1 (Sonnet ×3, prompt `25-5-b-review-prompt-p1.md`) : **0 CRITICAL/HIGH, 1 MED**, LOW. + - **C-1 (MED)** : le manuel et le CHANGELOG promettaient la concordance d'un compte de **résultat** avec le compte de + résultat **sur toute période**. C'est faux en cours d'exercice, où le compte de résultat ne porte que les mouvements + (mars–juin : 820 contre 570). Corrigé : clôture « depuis le début de l'exercice », égale au compte de résultat + quand la période commence au premier jour de l'exercice, au grand livre toujours. PDF régénéré, contrôlé aplati. + - **LOW corrigés** : + - le contrôle d'ouverture n'existait qu'à l'écran ; le PDF l'écrit désormais sous les totaux quand il tombe (A-1, + nouveau libellé `opening_unbalanced`, test, mutation tuée) ; + - un compte archivé aux mouvements compensés n'était exercé par aucun test (C-3, test neuf, mutation « inclusion + sur le montant net » tuée) ; + - les ignorés manquaient aux gates déclarés (C-4) ; + - la limite des deux tests PDF est écrite (C-2) ; + - deux limites sont écrites en doc de module : l'invariant date ↔ exercice, tenu par `create_in_tx` + (`DateOutsideFiscalYear`), vérifié (A-4, B-2) ; et la lecture de tout l'historique (A-5, B-1). + - **Écartés** : + - « Perte reportée » réservé à l'écran, voulu par l'AC 4 (A-2) ; + - la convention de signe, écrite au manuel (A-3) ; + - la ligne du résultat reporté affichée à 0, voulue (B-3). + - Gate ciblé : `fmt`, `clippy kesh-report`, `binary(trial_balance_opening)` + tests `trial_balance` 21/21. **Gate complet + au push.** - **2026-10-03** — Implémentée (dev-story) : T1–T7, gates ci-dessus. Statut → `review`. - **2026-10-03** — Validation P3 ciblée (Haiku, prompt `25-5-b-validate-prompt-p3.md`) : **0 finding**, sorties des quatre vérifications fournies. Le point 2 (compatibilité de l'AC 2 avec le grand livre), établi par raisonnement, **repris diff --git a/crates/kesh-report/src/pdf.rs b/crates/kesh-report/src/pdf.rs index 37158d353..f42abc32a 100644 --- a/crates/kesh-report/src/pdf.rs +++ b/crates/kesh-report/src/pdf.rs @@ -119,6 +119,9 @@ pub struct SectionLabels { pub col_opening: String, /// En-tête court de la colonne de clôture de la balance (Story 25-5-b). pub col_closing: String, + /// Avertissement de la balance quand les ouvertures ne s'équilibrent pas avec + /// le résultat reporté (Story 25-5-b) — l'écran a son ⚠️, le PDF ce texte. + pub opening_unbalanced: String, pub col_entry_date: String, pub col_description: String, // Header @@ -175,6 +178,10 @@ impl SectionLabels { col_balance: "Solde".into(), col_opening: "Ouverture".into(), col_closing: "Clôture".into(), + // ≈ 145 mm en Helvetica-Bold 10 pt : tient sur une ligne (`write_line` ne + // renvoie pas à la ligne). + opening_unbalanced: + "Contrôle d'ouverture en écart : une écriture antérieure est déséquilibrée.".into(), col_entry_date: "Date".into(), col_description: "Libellé".into(), header_period: "Période".into(), @@ -831,6 +838,14 @@ pub fn render_trial_balance_pdf( ); builder.cursor_y -= LINE_HEIGHT_MM; + // Le contrôle d'ouverture : silencieux s'il tient, écrit s'il tombe — un PDF + // remis à un réviseur ne doit pas perdre le signal que l'écran affiche. + if !tb.opening_balanced { + builder.cursor_y -= LINE_HEIGHT_MM * 0.5; + builder.ensure_space_for_row(); + builder.write_line(&l.opening_unbalanced, FONT_SIZE_PT, true, 0.0); + } + builder.finalize() } @@ -1990,6 +2005,17 @@ mod tests { assert!(full.len() > empty.len()); } + #[test] + fn trial_balance_pdf_writes_the_opening_warning_only_when_unbalanced() { + // Story 25-5-b, revue P1 — le contrôle d'ouverture n'existait qu'à l'écran. + let ctx = PdfContext::fr_ch_default("CI Test Company"); + let ok = render_trial_balance_pdf(&fixture_tb(false), &ctx).unwrap(); + let mut broken = fixture_tb(false); + broken.opening_balanced = false; + let warned = render_trial_balance_pdf(&broken, &ctx).unwrap(); + assert!(warned.len() > ok.len(), "l'avertissement doit être écrit"); + } + #[test] fn trial_balance_names_are_truncated_to_fit_six_columns() { // Story 25-5-b — 55 mm pour l'intitulé : 22 caractères, ellipsis comprise. diff --git a/crates/kesh-report/src/trial_balance.rs b/crates/kesh-report/src/trial_balance.rs index 6bad7abef..3c5d6d864 100644 --- a/crates/kesh-report/src/trial_balance.rs +++ b/crates/kesh-report/src/trial_balance.rs @@ -35,9 +35,17 @@ //! Il n'y a pas de contrôle de clôture : la clôture vaut l'ouverture plus des //! mouvements équilibrés, ce serait la copie du premier. //! -//! # Limite connue +//! # Limites connues //! -//! Le signe est lu sur le `account_type` **courant** du compte (issues #274, #382). +//! - Le signe est lu sur le `account_type` **courant** du compte (issues #274, #382). +//! - Les mouvements filtrent `fiscal_year_id` **et** la date, l'ouverture la date +//! seule : la concordance avec le bilan suppose que toute écriture tombe dans les +//! bornes de son exercice — ce que `journal_entries::create_in_tx` impose +//! (`DateOutsideFiscalYear`), et qu'aucune mise à jour ne peut plus défaire +//! (Story 24-4b). +//! - La requête lit **tout l'historique** de la société jusqu'à la fin de la +//! période — le prix d'une ouverture cumulée depuis l'origine, comme au bilan. +//! Sans mesure à ce jour ; à surveiller si l'historique grossit. use kesh_db::entities::AccountType; use kesh_db::repositories::fiscal_years; diff --git a/crates/kesh-report/tests/trial_balance_opening.rs b/crates/kesh-report/tests/trial_balance_opening.rs index d1ee960c0..d4cd0c565 100644 --- a/crates/kesh-report/tests/trial_balance_opening.rs +++ b/crates/kesh-report/tests/trial_balance_opening.rs @@ -518,6 +518,46 @@ async fn archive(pool: &MySqlPool, account_id: i64) { .expect("compte archivé"); } +/// Un compte archivé dont les lignes de la période **se compensent** (net nul), +/// sans ouverture : il figure — il a été mouvementé. Mutation tuée : une règle +/// d'inclusion sur le **montant** des mouvements plutôt que sur leur existence. +#[sqlx::test(migrations = "../kesh-db/test-schema")] +async fn un_compte_archive_aux_mouvements_compenses_figure(pool: MySqlPool) { + let seeded = seed_accounting_company(&pool).await.expect("seed"); + let fy = seeded.fiscal_year_id; + post( + &pool, + &seeded, + fy, + ymd(2026, 3, 1), + "1000", + "1100", + dec!(100), + ) + .await; + post( + &pool, + &seeded, + fy, + ymd(2026, 3, 2), + "1100", + "1000", + dec!(100), + ) + .await; + archive(&pool, seeded.accounts["1100"]).await; + let tb = balance( + &pool, + &seeded, + &period(fy, ymd(2026, 1, 1), ymd(2026, 12, 31)), + ) + .await; + assert_eq!( + row(&tb, "1100"), + (Decimal::ZERO, dec!(100), dec!(100), Decimal::ZERO) + ); +} + /// ⛔ Un compte archivé qui porte encore un solde figure à la balance — comptes de /// bilan et de résultat. Mutation tuée : la règle d'inclusion d'avant (archivé = /// seulement s'il a un mouvement dans la période) — le compte disparaîtrait, sa diff --git a/docs/manual/fr/user-manual.pdf b/docs/manual/fr/user-manual.pdf index 04066d56443fd9d9c8f4131815e1d2bd38469f9c..77a65c233f26c2f3e55e9786ea54be866eed30ca 100644 GIT binary patch delta 36457 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 AI{*Lx delta 36137 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 Date: Sat, 3 Oct 2026 12:24:26 +0200 Subject: [PATCH 11/13] =?UTF-8?q?docs(25-5-b):=20prompt=20de=20revue=20de?= =?UTF-8?q?=20code=20P2=20cibl=C3=A9e=20(refs=20#385)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- .../25-5-b-review-prompt-p2.md | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 _bmad-output/implementation-artifacts/25-5-b-review-prompt-p2.md diff --git a/_bmad-output/implementation-artifacts/25-5-b-review-prompt-p2.md b/_bmad-output/implementation-artifacts/25-5-b-review-prompt-p2.md new file mode 100644 index 000000000..c58358e2c --- /dev/null +++ b/_bmad-output/implementation-artifacts/25-5-b-review-prompt-p2.md @@ -0,0 +1,33 @@ +# Prompt — revue de code P2 ciblée, Story 25-5-b + +*Versionné le 2026-10-03. **Une lentille** (Haiku), contexte frais — passe ciblée sur la remédiation de P1 (P1 Sonnet ×3 : +1 MED, LOW).* + +Dépôt `/home/gcorbaz/devel/kesh`, branche `story/25-5-b-balance-solde-ouverture`. **Diff à relire, un seul commit, aplati : +`git show 84505eed`.** Fiche : `_bmad-output/implementation-artifacts/25-5-b-balance-solde-ouverture.md`. + +## Lentille — regression hunter + +1. `crates/kesh-report/src/pdf.rs` : le libellé `opening_unbalanced` est-il ajouté à `SectionLabels` **et** à ses défauts, + sans autre construction littérale de `SectionLabels` ailleurs (`grep -rn "SectionLabels {" crates`) ? L'avertissement + est-il dessiné après `ensure_space_for_row` ? Tient-il sur une ligne (Helvetica-Bold 10 pt, ~0,55 em par caractère, + largeur utile 180 mm) ? Le test l'exerce-t-il dans les deux sens ? +2. `crates/kesh-report/tests/trial_balance_opening.rs`, test `un_compte_archive_aux_mouvements_compenses_figure` : les deux + écritures tombent-elles dans l'exercice et la période ? L'assertion distingue-t-elle « présent à zéro » d'« absent » ? +3. Manuel et CHANGELOG : la phrase corrigée dit-elle juste ? Lis `crates/kesh-report/src/income_statement.rs` (filtre des + mouvements) et vérifie : sur une période qui ne commence pas au premier jour de l'exercice, compte de résultat ≠ clôture + de la balance ; sur l'exercice entier, égalité. Reste-t-il **ailleurs** la promesse fausse ? `grep -rn "montant au compte + de résultat" docs CHANGELOG.md _bmad-output/implementation-artifacts/25-5-b-*.md crates`. +4. La doc de module de `trial_balance.rs` : `DateOutsideFiscalYear` existe-t-il bien dans `journal_entries::create_in_tx` + (`grep -n "DateOutsideFiscalYear" crates/kesh-db/src/repositories/journal_entries.rs`) ? + +## Ce que tu rends + +Findings avec sévérité, endroit, **preuve obligatoire : la commande ET sa sortie** — un finding sans sortie sera rejeté, +un « 0 finding » sans les sorties des quatre vérifications aussi. ⛔ La liste des axes exercés ET non exercés. + +## Interdits + +⛔ N'écris aucun fichier du dépôt ; aucune commande qui écrit dans le dépôt ou dans une base (`scripts/*` dont +`scripts/prepare-release.sh`, `make`, `latexmk`, `git commit`/`add`/`checkout`, `sqlx`, `cargo test`/`nextest`, +`npm run`, `npx`, `gh issue create`/`comment`/`edit`). Autorisés : lecture, `grep`, `sed`, `git log`/`show`/`diff`. From 52e7f14fe3eb873b02e8cd61324120684e1be1b7 Mon Sep 17 00:00:00 2001 From: Guy Corbaz Date: Sat, 3 Oct 2026 12:41:48 +0200 Subject: [PATCH 12/13] =?UTF-8?q?docs(25-5-b):=20revue=20de=20code=20P2=20?= =?UTF-8?q?cibl=C3=A9e=20=E2=80=94=200=20finding,=20boucle=20close=20;=20s?= =?UTF-8?q?tory=20done=20(refs=20#385)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- .../25-5-b-balance-solde-ouverture.md | 8 +++++++- _bmad-output/implementation-artifacts/sprint-status.yaml | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/_bmad-output/implementation-artifacts/25-5-b-balance-solde-ouverture.md b/_bmad-output/implementation-artifacts/25-5-b-balance-solde-ouverture.md index 8ec2cf642..9510faccc 100644 --- a/_bmad-output/implementation-artifacts/25-5-b-balance-solde-ouverture.md +++ b/_bmad-output/implementation-artifacts/25-5-b-balance-solde-ouverture.md @@ -1,6 +1,6 @@ # Story 25.5-b : La balance des comptes porte un solde d'ouverture — et concorde enfin avec le bilan -Status: review +Status: done **Issue : [#385]**, que cette story **ferme** : la PR porte `closes #385` dans le **titre ET le corps**. Seconde moitié de la 25-5, découpée le 2026-09-23 (la 25-5-a a fermé #386). Branche `story/25-5-b-balance-solde-ouverture`, partie de @@ -323,6 +323,12 @@ Claude Opus 5.5 (`claude-opus-5-5`). ## Change Log +- **2026-10-03** — Revue de code P2 ciblée (Haiku, prompt `25-5-b-review-prompt-p2.md`) : **0 finding**, sorties des + quatre vérifications fournies. Le seul résidu cité (« montant au compte de résultat », AC 9) relu par + l'orchestrateur : il porte sur l'exercice entier, où l'égalité tient. **Boucle close** : P1 1 MED (Sonnet ×3) → P2 0 + (Haiku). Gate complet après la remédiation : base remise à zéro, `scripts/test-fast.sh` **2547/2547, 4 ignorés** ; + frontend inchangé depuis son gate ; E2E rejoué entre-temps : 227 passés, 9 échoués — les 9 attendus d'un run + matinal —, 19 ignorés. Statut → `done`. - **2026-10-03** — Revue de code P1 (Sonnet ×3, prompt `25-5-b-review-prompt-p1.md`) : **0 CRITICAL/HIGH, 1 MED**, LOW. - **C-1 (MED)** : le manuel et le CHANGELOG promettaient la concordance d'un compte de **résultat** avec le compte de résultat **sur toute période**. C'est faux en cours d'exercice, où le compte de résultat ne porte que les mouvements diff --git a/_bmad-output/implementation-artifacts/sprint-status.yaml b/_bmad-output/implementation-artifacts/sprint-status.yaml index a7423c77b..7f684779b 100644 --- a/_bmad-output/implementation-artifacts/sprint-status.yaml +++ b/_bmad-output/implementation-artifacts/sprint-status.yaml @@ -368,7 +368,7 @@ development_status: 25-4-d-solder-le-reste: backlog # [#384] ramenee de la 25-6 le 2026-09-27 (Guy) — perte sur debiteur, escompte, frais : clore une facture partiellement reglee ; la part TVA reduit la TVA due. 25-4-propager-le-residuel: split # 2026-09-26 DECOUPEE (six modules > 5) en 25-4-a / 25-4-b / 25-4-c, puis 25-4-d (#384, 2026-09-27) ; #455 et #456 rattachees. # [#416] [#420] — la 24-2 a introduit le reglement PARTIEL ; le residuel n'est propage ni a la balance agee ni a l'echeancier, et **le score de rapprochement compare au TTC et non au residuel** : le virement du solde d'une facture partiellement reglee **score 0**. 25-5-a-export-souverainete: done # 2026-09-26 **MERGEE (PR #452, #386 fermee) SANS REVUE DE CODE** ; revue a posteriori CLOSE en 3 passes sur `story/25-5-a-revue-code` (4 HIGH P1, 1 HIGH orchestrateur P2 — colonnes manquantes, garde de colonnes —, P3 ciblee test seul) ; gates 2466/2466, E2E 221/8/19 sans regression ; reports #465, #466. # 2026-09-23 **SPECIFIEE et VALIDEE en 1 passe** (0 CRITICAL, 4 HIGH, 3 MEDIUM, 3 LOW, tous corriges). [#386] — issue du DECOUPAGE de la 25-5 (arbitrage de Guy) : #385 (Balance vs Bilan) et #386 n'ont en commun que le mot « etat », et touchent des modules disjoints. ⛔ **Trois faits qui corrigent l'issue** : 21 tables absentes et non 10 ; le trou s'est CREUSE depuis son ecriture (`invoice_settlements`, Epic 24) ; et SIX repositories sur huit n'ont aucune lecture exhaustive — le vrai cout, absent de l'issue. Arbitrage de Guy : l'export CSV ne porte que les donnees COMPTABLES (11 dedans), les backups portent tout ; 10 exclusions AVEC motif ecrit, dont 3 tables de SECRETS. Les justificatifs (fichiers) partent en story separee sur la SAUVEGARDE. ⛔ La vraie livraison est la GARDE D'EXHAUSTIVITE (patron D4-ter + P7), non les 11 exports : trois epics ont rouvert le trou en silence. ⚠️ Pieges releves par la validation : le nombre 19 code en dur a SEPT endroits dont deux `debug_assert_eq!` qui PANIQUENT ; `audit_log` n'a AUCUN dump exhaustif (ses fonctions sont bornees, la route rejette au-dela de 10 000 lignes) ; `csv_sanitize` existe et n'est pas employee par cet export ; deux sites de manuel DEJA faux (un dossier `reports/` invente, un « 38 » pour 39). - 25-5-b-balance-solde-ouverture: review # 2026-10-03 CREEE, validee (3 passes), implementee, gates verts (partie de main). ouverture + mouvements + cloture, ligne calculee du resultat reporte, closes #385. [#385] — la **Balance des comptes ne concorde pas avec le Bilan**, il manque la colonne « solde d'ouverture » (III.4). Detachee de la 25-5 le 2026-09-23 : voir 25-5-a. + 25-5-b-balance-solde-ouverture: done # 2026-10-03 CREEE, validee (3 passes), implementee, revue close en 2 passes (P1 1 MED -> P2 0) (partie de main). ouverture + mouvements + cloture, ligne calculee du resultat reporte, closes #385. [#385] — la **Balance des comptes ne concorde pas avec le Bilan**, il manque la colonne « solde d'ouverture » (III.4). Detachee de la 25-5 le 2026-09-23 : voir 25-5-a. 25-5-etats-et-export: split # [#385] [#386] — la **Balance des comptes ne concorde pas avec le Bilan**, il manque la colonne « solde d'ouverture » (III.4) ; et **l'export de souverainete omet les achats, les avoirs, les projets et la piste d'audit** (V.6) — *il promet plus qu'il ne tient*. 25-6-tableau-de-bord-et-dettes: backlog # [#388] [#389] [#387] (#384 ramenee en 25-4-d le 2026-09-27) — la tuile « Dernieres ecritures » **n'appelle rien** et affiche toujours « Aucune ecriture » (*une tuile qui est un decor*) ; le « solde bancaire » est le solde COMPTABLE, donc faux ; imputer l'ECART d'un reglement partiel (escompte, frais bancaires, perte sur debiteur) ; et **le PDF d'une facture n'est jamais archive**, il est regenere a la volee (IV.9) — *un document reemis n'est pas le document envoye*. epic-16: done # 2026-08-11 (nuit) MERGÉ — dernière story 16-3b via PR #296 (squash `593bb845`). #152, #144 et #151 fermées. v0.9.0 prête à taguer. # 2026-07-26 KICKOFF (rétro Epic 14, décision D-D ; GO Guy après publication de v0.8.0). Branche `story/16-1-compte-produit-par-ligne` créée sur main `ef6cdf52`. Séquence : 16-1 (#152 socle par ligne) → 16-2 (#144 défaut catalogue) → 16-3 (#151 reliquat PDF). # ÉLU PROCHAIN EPIC — kickoff dès le merge de la PR groupée Epic 14. Reste #144 (compte produit au catalogue) + #152 (compte produit par ligne) ; #151 (récap TVA facture) déjà mergé hors-bande via PR #267 (bb3c8d64, partiel : coord. émetteur / n° client restants). Capte aussi les CR utilisateurs #265/#266/#268. # Facturation avancée (#144/#152/#151) — Epic dédié acté 2026-06-04 From 06f5c05956aebf8ef4814386099df503a7610eb7 Mon Sep 17 00:00:00 2001 From: Guy Corbaz Date: Sat, 3 Oct 2026 14:43:34 +0200 Subject: [PATCH 13/13] =?UTF-8?q?docs(25-5-b):=20arbitrage=20de=20Guy=20?= =?UTF-8?q?=E2=80=94=20colonne=20CSV=20Solde=20remplac=C3=A9e=20(refs=20#3?= =?UTF-8?q?85)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- .../implementation-artifacts/25-5-b-balance-solde-ouverture.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/_bmad-output/implementation-artifacts/25-5-b-balance-solde-ouverture.md b/_bmad-output/implementation-artifacts/25-5-b-balance-solde-ouverture.md index 9510faccc..ecd5c3264 100644 --- a/_bmad-output/implementation-artifacts/25-5-b-balance-solde-ouverture.md +++ b/_bmad-output/implementation-artifacts/25-5-b-balance-solde-ouverture.md @@ -113,7 +113,7 @@ donc (marqueur `active: false`), sinon la clôture ne concorderait pas avec le b La note `reports-trial-balance-period-note` est **retirée** (clé supprimée des 4 locales) — elle deviendrait fausse. - **CSV** : `NumeroCompte;NomCompte;SoldeOuverture;TotalDebit;TotalCredit;SoldeCloture` — la colonne `Solde` est **remplacée**, pas renommée en silence (CHANGELOG — ⚠️ changement incompatible pour qui lit ce CSV, dans une version - 0.x : arbitrage de Guy à recueillir, défaut retenu : remplacer). Ligne de résultat reporté sur le patron du bilan + 0.x : **arbitré par Guy le 2026-10-03 : remplacer**). Ligne de résultat reporté sur le patron du bilan (`csv.rs:139-145`) : numéro **vide**, libellé fixe « Résultat reporté (calculé) » dans la colonne du nom, ouverture et clôture remplies, débit et crédit vides ; puis ligne `Total` (débit, crédit ; ouverture et clôture vides). - **PDF** : six colonnes en A4 portrait. La mise en page à cinq colonnes occupe **déjà** toute la largeur @@ -323,6 +323,7 @@ Claude Opus 5.5 (`claude-opus-5-5`). ## Change Log +- **2026-10-03** — Arbitrage de Guy (« ok ») : la colonne CSV `Solde` est **remplacée**, comme livré. - **2026-10-03** — Revue de code P2 ciblée (Haiku, prompt `25-5-b-review-prompt-p2.md`) : **0 finding**, sorties des quatre vérifications fournies. Le seul résidu cité (« montant au compte de résultat », AC 9) relu par l'orchestrateur : il porte sur l'exercice entier, où l'égalité tient. **Boucle close** : P1 1 MED (Sonnet ×3) → P2 0
{i18nMsg('reports-column-account-number', 'N°')} {i18nMsg('reports-column-account-name', 'Intitulé')}{i18nMsg('reports-column-opening', 'Ouverture')} {i18nMsg('reports-column-debit', 'Débit')} {i18nMsg('reports-column-credit', 'Crédit')}{i18nMsg('reports-column-balance', 'Solde')}{i18nMsg('reports-column-closing', 'Clôture')}
{i18nMsg('reports-archived-label', 'archivé')}{/if} {fmt(r.openingBalance)} {fmt(r.totalDebit)} {fmt(r.totalCredit)}{fmt(r.balance)}{fmt(r.closingBalance)}
{retainedLabel}{fmt(dto.retainedEarnings)}{fmt(dto.retainedEarnings)}
{i18nMsg('reports-grand-total', 'Total général')} + {dto.openingBalanced ? '✓' : '⚠️'} + {fmt(dto.totalDebit)} {fmt(dto.totalCredit)} + {dto.balanced ? '✓' : '⚠️'}