From 9c4df1134e01032b3ca03a4fe83dd4b2a2edc278 Mon Sep 17 00:00:00 2001 From: Guy Corbaz Date: Sat, 26 Sep 2026 23:48:05 +0200 Subject: [PATCH 01/18] =?UTF-8?q?docs(25-4):=20d=C3=A9coup=C3=A9e=20en=20a?= =?UTF-8?q?/b/c=20;=2025-4-a=20sp=C3=A9cifi=C3=A9e=20=E2=80=94=20l'avoir?= =?UTF-8?q?=20compt=C3=A9=20TTC,=20refus=C3=A9=20sur=20une=20facture=20r?= =?UTF-8?q?=C3=A9gl=C3=A9e=20(refs=20#455,=20refs=20#456,=20refs=20#416,?= =?UTF-8?q?=20refs=20#420)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- .../25-4-a-residuel-juste.md | 308 ++++++++++++++++++ .../25-4-propager-le-residuel.md | 87 +++++ .../sprint-status.yaml | 6 +- 3 files changed, 400 insertions(+), 1 deletion(-) create mode 100644 _bmad-output/implementation-artifacts/25-4-a-residuel-juste.md create mode 100644 _bmad-output/implementation-artifacts/25-4-propager-le-residuel.md diff --git a/_bmad-output/implementation-artifacts/25-4-a-residuel-juste.md b/_bmad-output/implementation-artifacts/25-4-a-residuel-juste.md new file mode 100644 index 00000000..88396a44 --- /dev/null +++ b/_bmad-output/implementation-artifacts/25-4-a-residuel-juste.md @@ -0,0 +1,308 @@ +# Story 25.4-a : Le résiduel juste — l'avoir compté TTC, et refusé sur une facture réglée + +Status: ready-for-dev + +**Issues : [#455], [#456]**, qu'elle **ferme** : `closes #455, closes #456` dans le **titre ET le +corps** de la PR (squash ; un `refs` partout laisserait les issues ouvertes sans signal). + +**Mère : `25-4-propager-le-residuel.md`** (`split`) — l'inventaire vérifié et la raison du +découpage y sont. **Sœurs à venir** : 25-4-b (#416, agrégats et relances), 25-4-c (#420, +rapprochement), qui **réemploient la formule** que cette story corrige : ⛔ elle passe en premier. + +## Story + +En tant que comptable, +je veux que le montant « reste dû » d'une facture soit calculé dans la même unité que ce que le +grand livre a passé, et qu'un avoir ne puisse pas viser une facture déjà réglée en partie, +afin que le reste dû ne mente jamais — ni en gardant la TVA d'une facture créditée, ni en laissant +une créance devenir créditrice sans que rien ne le signale. + +## Le défaut, vérifié dans le code + +### #455 — l'avoir est soustrait HORS TAXES d'un total TTC + +`amount_due` (`crates/kesh-db/src/repositories/invoice_settlements.rs:131-145`) calcule +`TTC − avoir émis − Σ règlements` : + +- le TTC vient de `INVOICE_TTC_SUBQUERY_SQL` (`invoices.rs:161-162`) : + `Σ (line_total + ROUND(line_total × vat_rate / 100, 2))` sur `invoice_lines` ; +- l'avoir vient de `INVOICE_CREDITED_SUBQUERY_SQL` (`invoice_settlements.rs:51-52`) : + `SUM(cn.total_amount)` — et `credit_notes.total_amount` est **HT** + (`20260627000001_credit_notes.sql:19` : « `total_amount` stocké = HT (Σ line_total) » ; + `credit_notes.rs:514-529`, `.bind(total_ht)`). + +Or l'écriture de l'avoir crédite la créance du **TTC** (`generate_credit_note_journal_lines`, +`credit_notes.rs:217-223` : `credit: total_ht + total_vat`, la TVA arrondie **ligne par ligne** par +`kesh_core::accounting::vat::line_vat_amount`, `:194` et `:209`). Le grand livre et le résiduel ne parlent +donc pas la même unité : une facture de 100.— HT à 8,1 % entièrement créditée garde **8.10** de +reste dû. La forme jointe `INVOICE_CREDITED_DERIVED_JOIN_SQL` (`:56-58`) porte le même défaut. + +⚠️ **Pourquoi aucun test ne l'a vu** : les factures des tests de règlement sont à **0 % de TVA** +(`crates/kesh-db/tests/invoice_settlement.rs:32`), où HT = TTC ; et aucun test n'associe un avoir +et `amount_due`. + +⚠️ **Latent sur les données neuves, pas sur les anciennes.** L'avoir est **total** et fait passer +la facture à `cancelled` (`credit_notes.rs:560-575`) ; le terme « avoir » n'est donc non nul que sur +une facture annulée, où le reste dû ne s'affiche que si `amountSettled > 0` +(`invoices/[id]/+page.svelte:1022-1041`) — c'est-à-dire **exactement l'état que #456 permet de +produire**. Et un `.keshbackup` de la v0.12.0 **peut contenir** cet état : l'import le restaure. + +### #456 — un avoir peut viser une facture partiellement réglée + +`create_credit_note` (`credit_notes.rs:292-308`) refuse une facture `paid_at IS NOT NULL` (garde +AC2bis) — **et rien d'autre**. Depuis la 24-2, `paid_at` n'est posé qu'**au solde** : une facture +réglée en partie a une ligne `invoice_settlements`, un reste dû positif, `paid_at` à `NULL` — elle +passe. + +Conséquence comptable : la créance reçoit **D TTC** (vente), **C réglé** (règlement), **C TTC** +(avoir) — elle finit **créditrice du montant encaissé**, sans flux de remboursement. C'est le solde +contre nature que la garde AC2bis voulait empêcher, atteint par un chemin qu'elle ne voit pas. La +facture passe ensuite à `cancelled`, et la ligne de règlement **survit** sur une facture annulée — +où `settlement_cancel_blocker` la déclare inannulable (`SettlementCancelBlocker::InvoiceCredited`, +`invoice_settlements_write.rs:327-333`). + +⛔ **Le patron de la bonne garde existe déjà** : la dévalidation lit « **l'existence d'une ligne** +`invoice_settlements` **OU** `paid_at IS NOT NULL` — les deux, jamais l'une seule » +(`UnvalidationBlocker::Settled`, `errors.rs:134-160` ; `invoices.rs:1430-1448`). + +⚠️ **Un test encode le défaut comme attendu** : la fixture `monter` +(`crates/kesh-db/tests/invoice_settlement.rs:937-990`) règle 40 sur 100 puis appelle +`create_credit_note` avec `.expect("avoir après règlement partiel — le vrai chemin l'accepte")`. + +⚠️ **Le frontend affiche le bouton sur une facture partiellement réglée** : +`invoices/[id]/+page.svelte:836` n'exige que `!invoice.paidAt`. Le bouton voisin « Dévalider » a +retiré cette condition pour cette raison même (commentaire `:865`) ; « Créer un avoir » n'a pas été +aligné. + +## Acceptance Criteria + +### Volet 1 — l'avoir compté TTC (#455) + +**AC 1** — `INVOICE_CREDITED_SUBQUERY_SQL` rend le **TTC** de l'avoir émis : +`Σ (cl.line_total + ROUND(cl.line_total × cl.vat_rate / 100, 2))` sur `credit_note_lines cl` jointe à +`credit_notes cn` (`cn.id = cl.credit_note_id`), filtrée `cn.invoice_id = i.id AND +cn.status = 'issued'`, `COALESCE(…, 0)`. **Même formule d'arrondi, ligne par ligne**, que +`INVOICE_TTC_SUBQUERY_SQL` et que `line_vat_amount` : c'est ce qui la fait concorder avec +l'écriture. + +**AC 2** — `INVOICE_CREDITED_DERIVED_JOIN_SQL` rend la même grandeur sous forme de table dérivée +(alias `cnt`, colonne `credited`), agrégée par `invoice_id`. + +**AC 3** — Les doc-comments des deux constantes disent **l'unité** (TTC, arrondi ligne par ligne) et +**pourquoi pas `credit_notes.total_amount`** (HT, miroir de `invoices.total_amount`) ; celui +d'`amount_due` dit que ses trois termes sont TTC. + +**AC 4** — ⛔ **Aucune migration, aucune colonne.** Le résiduel se **calcule** (24-2, D3) ; stocker +un TTC d'avoir recréerait un chiffre qui peut diverger. `credit_notes.total_amount` reste HT et +n'est pas renommé. + +**AC 5** — **DRY** : la formule « TTC d'une ligne » (`line_total + ROUND(line_total * vat_rate / 100, +2)`) n'est pas recopiée une troisième et une quatrième fois à la main. Elle est factorisée en **une +seule source** dont dérivent les quatre constantes (TTC facture scalaire et jointe, avoir scalaire +et joint) — par exemple une macro `macro_rules!` rendant un littéral via `concat!`, les constantes +restant des `&'static str`. Si la factorisation s'avère impossible sans perdre le caractère +`const`, le test de parité de l'AC 6 en tient lieu **et le doc-comment le dit**. + +**AC 6** — **Le test de parité promis par `invoice_settlements.rs:27-29`, et qui n'existe pas**, +est écrit : pour un jeu de factures qui couvre **sans règlement / un règlement / deux règlements / +avoir émis / avoir brouillon / deux taux de TVA dont 8,1 %**, la forme scalaire et la forme jointe +rendent **la même valeur, facture par facture**, pour le réglé (`st.settled`) **et** pour l'avoir +(`cnt.credited`). ⚠️ Patron : `crates/kesh-db/tests/invoice_ttc_parity.rs`. + +**AC 7** — ⛔ **Concordance avec le grand livre** : pour une facture multi-taux (dont 8,1 %) +créditée, le montant d'avoir que rend la sous-requête est **égal au crédit porté au compte de +créance par l'écriture de l'avoir** (`credit_notes.journal_entry_id` → ligne de crédit du compte +`default_receivable_account_id`). C'est l'assertion qui aurait attrapé #455 ; une comparaison au +seul HT ou à une valeur recalculée en Rust **ne suffit pas**. + +**AC 8** — Une facture validée à 8,1 %, **non réglée, créditée** : `amount_due` = **0** (et non la +TVA). Une facture validée à 8,1 %, **réglée de 40 puis créditée** (état **monté en SQL**, cf. +AC 13) : `amount_due` = **−40**, le montant encaissé en trop sur une vente annulée — et non +`TVA − 40`. ⚠️ **Pas d'écrêtage à zéro** : le doc-comment d'`amount_due` l'interdit déjà. + +### Volet 2 — l'avoir refusé sur une facture réglée (#456) + +**AC 9** — `create_credit_note` refuse une facture qui porte **au moins une ligne +`invoice_settlements` OU** `paid_at IS NOT NULL` — **les deux, jamais l'une seule**, comme +`UnvalidationBlocker::Settled`. La lecture des règlements se fait **après** le verrou de la facture +(`FOR UPDATE`, première instruction de la transaction, `credit_notes.rs:280-289`) et **dans** la +transaction ; elle est **verrouillante** (`… FOR UPDATE` ou `LOCK IN SHARE MODE`). ⛔ Leçon de la +25-3-b : sous REPEATABLE READ, une lecture non verrouillante fige l'instantané — ne pas l'ajouter +**avant** le verrou. + +**AC 10** — Le refus est une **variante dédiée** de `DbError`, pas `IllegalStateTransition` — même +motif que `InvoiceNotUnvalidatable` (`errors.rs:119-130`) : le générique ne dit ni ce qui bloque ni +quoi faire. Elle porte l'identifiant du premier règlement trouvé (`Option`, `None` si seul +`paid_at` est posé) et le numéro de la facture. Code machine **`CREDIT_NOTE_INVOICE_SETTLED`**, +HTTP **409**, ajouté à `DbError::code()` (`errors.rs:640-670`) et à toute garde qui énumère les +codes. + +**AC 11** — **Le cas « payée » passe par la même variante** : AC2bis cesse de rendre le générique. +Une seule garde, un seul message, qu'elle soit déclenchée par `paid_at` ou par une ligne de +règlement. + +**AC 12** — Message, **quatre locales**, clé **`error-credit-note-blocked-settled`**, mapping dans +`kesh-api/src/errors.rs` sur le patron de `error-invoice-unvalidate-blocked-settled` +(`:2528-2530`) : fr « Cette facture porte un règlement, même partiel : annulez-le d'abord pour +pouvoir émettre un avoir. » Registre du glossaire (`docs/i18n-glossaire.md` § Registre : de = +*Sie*, it = 2ᵉ personne du singulier, en neutre). `details` : `{ "invoiceId", "settlementId" }`. + +**AC 13** — La fixture `monter` (`invoice_settlement.rs:937-990`) **cesse de passer par +`create_credit_note`** pour le motif `InvoiceCredited` : elle monte l'état **en SQL** (avoir +`issued` inséré, facture `cancelled`), avec un commentaire qui dit que l'état n'est **plus +atteignable par l'application** depuis 25-4-a, mais **reste possible** par l'import d'une +sauvegarde antérieure — ce qui justifie de garder `SettlementCancelBlocker::InvoiceCredited`. +⛔ Ne **pas** supprimer ce motif ni ses tests. + +**AC 14** — Frontend : le bouton « Créer un avoir » (`invoices/[id]/+page.svelte:836`) est masqué +quand `invoice.paidAt` est posé **ou** `Number(invoice.amountSettled) > 0` — même condition que le +serveur. Si la course fait arriver le refus malgré tout, le dialogue affiche le message du serveur +(`err.message`, `:98`), déjà traduit : rien à ajouter côté client. + +**AC 15** — Rien n'est **réparé** dans les données existantes : pas de migration, pas de rejeu +(`CLAUDE.md` : aucune donnée de production à protéger ; précédent 24-2 D7). Les états hérités +restent **visibles** — reste dû négatif sur la fiche, motif `InvoiceCredited` à l'annulation du +règlement — et c'est ce que les textes disent (AC 17). + +### Volet 3 — tests, textes + +**AC 16** — Tests, au minimum (noms indicatifs) : + +| Test | Prouve | +|---|---| +| `credited_amount_is_ttc_and_matches_the_ledger` | AC 1, AC 7 — facture multi-taux, avoir émis : sous-requête = crédit créance de l'écriture d'avoir | +| `amount_due_of_a_credited_invoice_is_zero` | AC 8 — 8,1 %, non réglée : `0`, pas `8.10` | +| `amount_due_after_settlement_then_legacy_credit_is_minus_settled` | AC 8 — état hérité monté en SQL : `−40` | +| `settled_and_credited_forms_are_at_parity` | AC 6 | +| `credit_note_refused_on_partially_settled_invoice` | AC 9, AC 10 — **rien écrit** : aucun avoir, facture `validated`, `version` inchangée, solde de créance inchangé, séquence d'avoir non consommée | +| `credit_note_refused_on_paid_invoice` (**existant, réécrit**) | AC 11 — nouvelle variante, plus `IllegalStateTransition` | +| `credit_note_accepted_after_the_settlement_is_cancelled` | la promesse du message : annuler le règlement rouvre l'avoir | +| `credit_note_waits_for_a_concurrent_settlement` | AC 9 — **entrelacement** : une transaction verrouille la facture et insère un règlement ; `create_credit_note` lancé en parallèle attend, puis **refuse** après le commit. Patron : la sonde d'entrelacement de la 25-3-b | +| e2e API `credit_note_on_partially_settled_invoice_is_409` | la frontière HTTP : 409, `CREDIT_NOTE_INVOICE_SETTLED`, message fr | +| Vitest fiche facture | AC 14 — bouton absent si `amountSettled > 0`, présent sinon | + +⛔ **Chaque test porte au moins une facture à TVA non nulle.** À 0 %, HT = TTC et #455 est +invisible — c'est ainsi qu'il est passé. + +⛔ **Mutations à exécuter et à consigner** (résultat **observé**, pas supposé) : (m1) revenir à +`SUM(cn.total_amount)` dans la forme scalaire ; (m2) idem dans la forme jointe seule ; (m3) retirer +la lecture des règlements de la garde (ne garder que `paid_at`) ; (m4) retirer `paid_at` (ne garder +que la ligne) ; (m5) lire les règlements **avant** le verrou, non verrouillant ; (m6) remettre +`!invoice.paidAt` seul sur le bouton. Chacune doit faire rougir au moins un test. + +**AC 17** — Manuels et notes (`docs/manual/fr/user-manual.tex`, PDF régénéré et **contrôlé +aplati**) : +- `:1040` et `:2073` : « un avoir est refusé sur une facture **marquée payée** » devient « … sur une + facture **réglée, même en partie** » — annuler d'abord le règlement ; +- § *Avoirs* (`\label{sec:avoirs}`, `:1007`) : le refus et son motif sont nommés à côté du refus + « compte de produit archivé » ; +- `:963` et `:1455` (« la facture a été créditée par un avoir… ») : précisés comme **cas hérité** + — une facture créditée avant la v0.12.1, ou restaurée d'une sauvegarde antérieure ; +- `CHANGELOG.md` `[0.12.1]` *Fixed* : les deux défauts, en langage d'utilisateur. + +⚠️ **Greper le symptôme, pas la phrase** : `grep -rniE "payée|encaissée|réglée" docs/manual/fr/*.tex` +restreint aux lignes qui parlent d'avoir, plus le PDF aplati. Les manuels DE/IT/EN sont des stubs. + +## Tasks / Subtasks + +- [ ] **T1 — formule de l'avoir TTC** (AC 1-5) : factoriser la formule de ligne, réécrire les deux + constantes d'avoir, doc-comments. +- [ ] **T2 — parité et concordance** (AC 6-8) : nouveau fichier de test sur le patron + `invoice_ttc_parity.rs` ; montage de l'état hérité en SQL. +- [ ] **T3 — garde de l'avoir** (AC 9-11) : lecture verrouillante des règlements après le verrou, + variante `DbError`, `code()`. +- [ ] **T4 — API et i18n** (AC 12) : mapping, clé ×4 ; e2e 409. +- [ ] **T5 — fixture `monter`** (AC 13) : état monté en SQL, commentaire ; les tests d'annulation de + règlement restent verts. +- [ ] **T6 — frontend** (AC 14) : condition du bouton, Vitest. +- [ ] **T7 — tests et mutations** (AC 16) : m1 à m6 exécutées, résultats consignés. +- [ ] **T8 — textes** (AC 17) : manuel, PDF aplati, CHANGELOG. +- [ ] **T9 — gates** : backend complet (base remise à zéro), frontend complet, E2E complet. + +## Dev Notes + +### Ce qu'il ne faut pas faire + +- ⛔ **Ne pas « corriger » `credit_notes.total_amount` en TTC.** Il est HT par construction, miroir + de `invoices.total_amount`, et d'autres lecteurs le consomment comme tel (export, PDF, liste des + avoirs). Le défaut est dans **le lecteur**, pas dans la colonne. +- ⛔ **Ne pas calculer le TTC de l'avoir en Rust** puis le soustraire : `amount_due` doit rester + **une requête**, et la forme jointe est destinée aux agrégats de 25-4-b (pas de N+1). +- ⛔ **Ne pas supprimer `SettlementCancelBlocker::InvoiceCredited`** : l'import d'un `.keshbackup` + v0.12.0 peut ramener l'état qu'il décrit. +- ⚠️ **Ne pas lire `cn.total_amount` × taux** : un avoir multi-taux n'a pas un taux, et l'arrondi + doit être **par ligne** pour concorder avec l'écriture (`line_vat_amount`). + +### Où regarder + +| Fichier | Rôle | +|---|---| +| `crates/kesh-db/src/repositories/invoice_settlements.rs:24-58, 122-145` | les quatre constantes, `amount_due` | +| `crates/kesh-db/src/repositories/invoices.rs:155-190` | `INVOICE_TTC_SUBQUERY_SQL` / `…_DERIVED_JOIN_SQL`, `total_ttc` | +| `crates/kesh-db/src/repositories/credit_notes.rs:187-257, 260-330, 510-575` | écriture de l'avoir, garde AC2bis, `total_amount`, bascule `cancelled` | +| `crates/kesh-db/src/errors.rs:119-175, 300-470, 640-670` | `UnvalidationBlocker`, `DbError`, `code()` | +| `crates/kesh-db/src/repositories/invoices.rs:1430-1448` | la garde de dévalidation, patron de l'AC 9 | +| `crates/kesh-api/src/errors.rs:2515-2560, 2741-2748` | mapping `InvoiceNotUnvalidatable`, générique | +| `crates/kesh-api/src/routes/credit_notes.rs:183-205` | handler de création | +| `crates/kesh-db/tests/invoice_ttc_parity.rs` | patron du test de parité | +| `crates/kesh-db/tests/credit_notes_repository.rs:267-299` | `credit_note_refused_on_paid_invoice` | +| `crates/kesh-db/tests/invoice_settlement.rs:32, 116-125, 937-990` | helper à 0 %, `solde()`, `monter` | +| `frontend/src/routes/(app)/invoices/[id]/+page.svelte:90-104, 836, 865, 1022-1041` | dialogue d'avoir, bouton, précédent « Dévalider », reste dû | + +### Courses + +Les trois chemins qui écrivent un règlement ou un avoir, et ce qui les sérialise : + +| Chemin | Verrou sur la facture | +|---|---| +| `settle_invoice` (`invoice_settlements_write.rs:60-72`) | `FOR UPDATE`, première lecture | +| `create_credit_note` (`credit_notes.rs:280-289`) | `FOR UPDATE`, première lecture | +| `accept_one_invoice` (`kesh-api/src/routes/reconciliation.rs`) | **aucun** — lecture simple, puis `UPDATE invoices … AND version = ? AND status = 'validated'` (`:1490-1500`) | + +Le troisième se sérialise par **verrou optimiste** : si l'avoir passe d'abord, la facture est +`cancelled` et l'`UPDATE` du rapprochement n'affecte aucune ligne. Si le rapprochement passe +d'abord, son `UPDATE` tient le verrou de ligne jusqu'au commit ; `create_credit_note` l'attend sur +son `FOR UPDATE`, puis **doit** voir le règlement — d'où l'exigence d'une lecture verrouillante +(AC 9). ⚠️ Vérifier en dev que l'échec de l'`UPDATE` du rapprochement sur 0 ligne rend bien un +`FailedProposal` et annule tout (test existant ou à ajouter). + +### Gardes-fous du dépôt + +- **Aucune migration** : P1-P8 ne s'arment pas. Si une migration devient nécessaire, arrêter et + demander. +- **Gate complet** : la story touche des repositories — § *Exception `kesh-db`* : gate complet même + en cours de boucle de revue. +- **i18n** : une clé ×4 côté serveur ; `sitesTotal` ne bouge que si le frontend ajoute un + `i18nMsg(` — AC 14 n'en ajoute pas. + +### References + +- 24-2, D3 (le résiduel se calcule) et D4 (`paid_at` projection du solde) — + `24-2-encaissement-client.md:120-160`. +- 25-3-b, leçon REPEATABLE READ — mémoire `etat-epic-25`. +- Glossaire et registre — `docs/i18n-glossaire.md`. + +## Questions pour Guy + +Aucune qui bloque cette story. La question de la QR-facture jointe aux relances (Q1 de la mère) +bloque **25-4-b**, pas celle-ci. + +## Dev Agent Record + +### Agent Model Used + +### Debug Log References + +### Completion Notes List + +### File List + +## Change Log + +- **2026-09-26** — Créée (Opus 5.5) après découpage de la 25-4. Inventaire vérifié par `grep -nF` + sur chaque site cité. Deux faits hors des issues : #455 est **latent** sur les données neuves et + ne se voit que dans l'état que #456 produit — d'où leur réunion ; et la fixture `monter` encode + #456 comme attendu. + +[#455]: https://github.com/guycorbaz/kesh/issues/455 +[#456]: https://github.com/guycorbaz/kesh/issues/456 diff --git a/_bmad-output/implementation-artifacts/25-4-propager-le-residuel.md b/_bmad-output/implementation-artifacts/25-4-propager-le-residuel.md new file mode 100644 index 00000000..6da60b67 --- /dev/null +++ b/_bmad-output/implementation-artifacts/25-4-propager-le-residuel.md @@ -0,0 +1,87 @@ +# Story 25.4 : Propager le résiduel — découpée en 25-4-a / 25-4-b / 25-4-c + +Status: split + +**Issues : [#416], [#420]** (fiche de l'epic) — **et [#455], [#456]**, rattachées ici le 2026-09-26 : +les quatre portent sur **la même grandeur**, le montant restant dû d'une facture client, et les +quatre sont au jalon « Vague 1 », que l'Epic 25 doit mettre à zéro. + +## Pourquoi découper — § *Règle de splitting préventif* + +Inventaire des sites fait le 2026-09-26 (trois lectures du code, chaque site revérifié par +`grep -nF`, cf. § ci-dessous). Les quatre issues réunies touchent **six modules** : `kesh-db` +(résiduel, avoirs, rapprochement, échéancier), `kesh-report` (balance âgée), +`kesh-reconciliation` (score), `kesh-api` (routes factures, rapprochement, relances, erreurs), +`kesh-i18n`, `frontend` (fiche facture, échéancier, dialogue de règlement). Le seuil est **5** : la +règle impose le découpage **avant** `bmad-create-story`. + +Le découpage suit l'ordre des dépendances, non celui des issues : + +| Story | Issues | Ce qu'elle fait | Pourquoi à ce rang | +|---|---|---|---| +| **25-4-a** — le résiduel juste | [#455], [#456] | la formule canonique soustrait l'avoir **TTC** ; un avoir est refusé sur une facture **réglée, même en partie** ; le test de parité promis et jamais écrit | ⛔ **les deux suivantes réemploient la formule** : la propager avant de la corriger propagerait le défaut | +| **25-4-b** — le résiduel aux agrégats | [#416] | balance âgée, totaux de l'échéancier, **colonne « reste dû »** de l'échéancier et de son CSV, dialogue de règlement pré-rempli, **montant réclamé par les relances** | le cœur de #416 ; première utilisatrice des formes jointes | +| **25-4-c** — le résiduel au rapprochement | [#420] | filtre des candidats, score, re-score à l'acceptation, montant affiché dans la proposition | indépendante de 25-4-b, mais du même socle | + +## Ce que l'inventaire a trouvé, et que les issues ne disaient pas + +1. ⛔ **#420 a DEUX sites, et le premier est avant le score.** `find_unpaid_invoices_for_window` + (`crates/kesh-db/src/repositories/reconciliation.rs:115`) filtre les candidats par + `HAVING total_ttc BETWEEN ? - ? AND ? + ?` : le virement du solde d'une facture partiellement + réglée n'est **même pas candidat**. Corriger `amount_score` seul (`matching.rs:68`) ne changerait + rien. Troisième site : le re-score à l'acceptation, `reconciliation.rs:1196` + (`invoices::total_ttc`). +2. ⛔ **Les relances réclament le TTC complet.** `render_reminder` + (`crates/kesh-api/src/routes/invoice_email.rs:334`) : `total_due = ttc + other_fees + level_fee`. + Une facture de 1 000.— réglée à 900.— est relancée pour 1 000.— plus les frais. Aucune issue ne le + nomme ; c'est **le même symptôme** que #416 — la grandeur agrégée n'est pas le résiduel —, sur un + document **envoyé au client**. Rattaché à 25-4-b. +3. ⚠️ **La relance joint le PDF de la facture**, dont la QR-facture porte le TTC + (`invoice_email.rs:1140-1166`, `invoice_pdf_service::render`). Un client qui paie la relance en + scannant la QR paie **à nouveau le montant entier**. Question posée à Guy (Q1 ci-dessous). +4. ⚠️ **#455 est latent sur les données neuves.** Un avoir est **total** et fait passer la facture à + `cancelled` (`credit_notes.rs:560-563`) ; or les agrégats, le rapprochement et le règlement ne + lisent que des factures `validated`. Le terme « avoir » du résiduel n'est donc **non nul que sur + une facture annulée**, et le reste dû ne s'y affiche que si `amountSettled > 0` — c'est-à-dire + **seulement dans l'état que #456 permet de produire**. Les deux issues se corrigent ensemble. +5. ⚠️ **Les formes jointes existent et ne servent nulle part.** `INVOICE_SETTLED_DERIVED_JOIN_SQL` + et `INVOICE_CREDITED_DERIVED_JOIN_SQL` (`invoice_settlements.rs:40`, `:56`) n'ont aucun appelant, + et le « test de parité » annoncé par le commentaire des lignes 27-29 **n'existe pas**. 25-4-b sera + leur premier appelant : la parité doit être prouvée **avant**. +6. **Pourquoi aucun test n'a vu #455** : toutes les factures des tests de règlement sont à **0 % de + TVA** (`crates/kesh-db/tests/invoice_settlement.rs:32`), où HT = TTC. +7. ⚠️ **Un test encode #456 comme attendu** : la fixture `monter` + (`crates/kesh-db/tests/invoice_settlement.rs:937-990`) règle 40 sur 100 puis crée un avoir avec + `.expect("avoir après règlement partiel — le vrai chemin l'accepte")`. Il **décrit le défaut** ; + il se corrige, il ne se contourne pas. + +## Hors périmètre des trois stories + +- **L'imputation de l'écart** d'un règlement partiel (escompte, frais, perte) — [#384], 25-6. +- **Le côté fournisseur** : une facture fournisseur se règle en une fois, il n'y a pas de résiduel + partiel (#416, § Portée). +- **L'avoir partiel et le remboursement** — v0.2, inchangé. + +## Questions pour Guy + +**Q1 — la QR-facture jointe à une relance.** Elle porte le TTC complet. Sur une facture +partiellement réglée, un client qui paie par la QR paie deux fois la part déjà réglée. Trois +issues : (a) la QR de la relance porte le **reste dû** — le PDF joint n'est alors plus tout à fait +la facture envoyée à l'origine, ce qui touche #387 (l'archivage du PDF) ; (b) la relance d'une +facture partiellement réglée joint un PDF **sans montant dans la QR** (la norme le permet : le +client saisit le montant) ; (c) laisser tel quel et le dire dans le texte de la relance. Ma +recommandation : (b), qui ne ment pas et ne modifie pas la facture. **À trancher avant 25-4-b.** + +## Change Log + +- **2026-09-26** — Découpée avant spécification (§ *Règle de splitting préventif*, six modules), + sur inventaire vérifié. #455 et #456 rattachées (même grandeur, même jalon). Deux sites trouvés + hors des issues : le filtre SQL des candidats du rapprochement (#420), et le montant des relances + (rattaché à 25-4-b). 25-4-a spécifiée dans la foulée. + +[#384]: https://github.com/guycorbaz/kesh/issues/384 +[#387]: https://github.com/guycorbaz/kesh/issues/387 +[#416]: https://github.com/guycorbaz/kesh/issues/416 +[#420]: https://github.com/guycorbaz/kesh/issues/420 +[#455]: https://github.com/guycorbaz/kesh/issues/455 +[#456]: https://github.com/guycorbaz/kesh/issues/456 diff --git a/_bmad-output/implementation-artifacts/sprint-status.yaml b/_bmad-output/implementation-artifacts/sprint-status.yaml index cfbc52ae..8d962656 100644 --- a/_bmad-output/implementation-artifacts/sprint-status.yaml +++ b/_bmad-output/implementation-artifacts/sprint-status.yaml @@ -1,3 +1,4 @@ +# last_updated: 2026-09-26 (5) (25-4 DECOUPEE en a/b/c ; 25-4-a creee, #455/#456 rattachees ; #467 #468 #470 mergees.) # last_updated: 2026-09-26 (4) (**25-3-c DONE** — revue close, gates verts, non poussee.) # last_updated: 2026-09-26 (3) (25-3-c IMPLEMENTEE, en review ; revue a posteriori de la 25-5-a close dans le worktree.) # last_updated: 2026-09-26 (2) (25-3-c : Q1 tranchee — reglement detache.) @@ -352,7 +353,10 @@ development_status: 25-3-c-annuler-facture-fournisseur: done # 2026-09-26 **REVUE CLOSE EN 1 PASSE** (0 > LOW, Sonnet x3) ; non poussee ; PR `closes #454` titre ET corps, sur accord. **IMPLEMENTEE** — gates 2472/2472, 783/783, E2E 220/10/19 sans regression ; prochaine etape : code review. # 2026-09-26 **SPEC VALIDEE en 2 passes** (3 MED -> 0 > LOW). **SPECIFIEE** — `25-3-c-annuler-facture-fournisseur.md`. Geste par le socle (autorite `SupplierPurchase`), queue commune sur l'ecriture d'ACHAT, lot en dernier rang ; payee : reglement DETACHE (colonnes remises a NULL, ecriture libre, lien garde par l'audit) — **Q1 tranchee par Guy le 2026-09-26**. Aucune migration. Prochaine etape : validate. # 2026-09-24 NEE d'un arbitrage de Guy — une facture fournisseur s'annule DANS TOUS LES CAS, sauf si l'exercice de son ecriture d'achat est CLOS (l'exercice, pas le verrou de periode) ; payee, son reglement RESTE au grand livre et REDEVIENT A LETTRER (Epic 15 : un paiement doit correspondre a une facture, au besoin creee pour lui — « comme bexio »). Reecrit `cancel`, donc absorbe [#454]. Derriere la 25-3-a. 25-3-b-annuler-rapprochement: done # 2026-09-25 DONE — revue close en passe 2 (Sonnet 1 MED -> Haiku 0) ; gates : backend 2463/2463, frontend 777/777, E2E sans regression. Non poussee. — SPEC VALIDEE (boucle close en passe 4 : 2 HIGH -> 2 MED -> 2 MED -> 3 LOW ; pas de decoupage, arbitrage de Guy). Q1-Q3 tranchees. — [#418] closes — annuler un rapprochement bancaire. ⛔ NE JAMAIS compter sur le `ON DELETE SET NULL` de `matched_entry_id` : supprimer l'ecriture ferait disparaitre le lien EN SILENCE, le mode d'echec meme que la story repare. ⚠️ Le chemin d'ACCEPTATION n'a aucune fonction `kesh-db` a mirer : il est ecrit en SQL brut a **cinq** sites de `routes/reconciliation.rs`, dont **un seul** porte le verrou optimiste. Quand la transaction porte une facture, appelle le geste de la 25-3-a. Derriere elle. 25-3-annuler-reglement-et-rapprochement: split # [#414] [#418] — **l'Epic 24 a livre l'encaissement et le rapprochement ; il n'a PAS livre le moyen de les DEFAIRE.** Une erreur d'imputation est aujourd'hui incorrigible autrement qu'a la main — et le gel de la 24-4b interdit precisement la main. ⚠️ *C'est le prix d'une story qui ferme une porte sans ouvrir la suivante : le gel est juste, et il rend cette story-ci necessaire.* - 25-4-propager-le-residuel: backlog # [#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-4-a-residuel-juste: ready-for-dev # 2026-09-26 [#455] [#456] — l'avoir soustrait HT d'un TTC (latent, visible seulement dans l'etat que #456 produit) ; un avoir accepte sur une facture partiellement reglee rend la creance creditrice. CREEE, validation a lancer. + 25-4-b-residuel-aux-agregats: backlog # [#416] + montant des RELANCES (TTC complet, trouve a l'inventaire) — Q1 a Guy : QR-facture jointe a la relance porte le TTC. + 25-4-c-residuel-au-rapprochement: backlog # [#420] — DEUX sites : filtre SQL des candidats (HAVING total_ttc) avant le score, puis score et re-score a l'acceptation. + 25-4-propager-le-residuel: split # 2026-09-26 DECOUPEE (six modules > 5) en 25-4-a / 25-4-b / 25-4-c ; #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-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*. From 0f4b9ec0caf993a11f4b7d86a9c69462326d06d9 Mon Sep 17 00:00:00 2001 From: Guy Corbaz Date: Sat, 26 Sep 2026 23:48:27 +0200 Subject: [PATCH 02/18] docs(25-4-a): prompt de validation P1 (refs #455, refs #456) Co-Authored-By: Claude Opus 5.5 --- .../25-4-a-validate-prompt-p1.md | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 _bmad-output/implementation-artifacts/25-4-a-validate-prompt-p1.md diff --git a/_bmad-output/implementation-artifacts/25-4-a-validate-prompt-p1.md b/_bmad-output/implementation-artifacts/25-4-a-validate-prompt-p1.md new file mode 100644 index 00000000..03a3ca11 --- /dev/null +++ b/_bmad-output/implementation-artifacts/25-4-a-validate-prompt-p1.md @@ -0,0 +1,50 @@ +# Prompt — validation P1, Story 25-4-a (le résiduel juste) + +*Versionné le 2026-09-26. **Une lentille** (Sonnet), contexte frais.* + +Dépôt `/home/gcorbaz/devel/kesh`, branche `story/25-4-propager-le-residuel`. Fiche à valider : +`_bmad-output/implementation-artifacts/25-4-a-residuel-juste.md`. Fiche mère (inventaire, +découpage) : `25-4-propager-le-residuel.md`. Issues : `gh issue view 455`, `gh issue view 456`. +Contexte de la grandeur : `24-2-encaissement-client.md` § D3, D4, AC 5-7. Règles du dépôt : +`CLAUDE.md`. Checklist BMAD : `.claude/skills/bmad-create-story/checklist.md`. + +## Axes — tous obligatoires + +1. **Chaque référence `fichier:ligne`** existe et dit ce que la fiche affirme — relis le code. +2. **La formule** : la sous-requête TTC de l'avoir prescrite (AC 1-2) concorde-t-elle réellement, + centime par centime, avec l'écriture de l'avoir (`generate_credit_note_journal_lines`, + `line_vat_amount` — lis `kesh-core`, sens d'arrondi de `ROUND` MariaDB vs Rust) ? Et avec + `INVOICE_TTC_SUBQUERY_SQL` ? Un écart d'un centime possible est un finding. +3. **La garde** (AC 9-11) : inventorie **tous** les chemins qui créent un règlement ou un avoir + (pars du symptôme : `grep -rn "INSERT INTO invoice_settlements\|create_credit_note\|INSERT INTO credit_notes" crates/`), + et leur sérialisation (verrou pessimiste ou optimiste). La fiche en nomme trois : en manque-t-il ? + Le raisonnement REPEATABLE READ de § Courses est-il juste ? +4. **L'état hérité** : l'import d'une sauvegarde ramène-t-il vraiment l'état (règlement + avoir) ? + Les textes prescrits (AC 17) le disent-ils juste ? D'autres lecteurs de `amount_due` ou des + constantes changent-ils de valeur avec l'AC 1 (inventaire : `grep -rn "INVOICE_CREDITED\|amount_due" crates/ frontend/src`) ? +5. **Les tests** (AC 16) : chacun prouve-t-il ce qu'il annonce, ou peut-il passer à vide ? Les + mutations m1-m6 sont-elles tuables par les tests listés ? Une mutation que rien ne tuerait est + un finding. +6. **Le manuel** : les lignes citées (AC 17) existent-elles et disent-elles ce qu'on veut changer ? + Contrôle aussi le **PDF aplati** (`pdftotext docs/manual/fr/user-manual.pdf - | tr '\n' ' ' | tr -s ' '`, + vers `/tmp/claude-1000/-home-gcorbaz-devel-kesh/ca5ce2e2-67a3-4eeb-817f-c2de35620a1c/scratchpad/`). + Une autre phrase du manuel ou de l'admin-manual, de l'api-external, du README, promet-elle + l'ancien comportement ? +7. **Le découpage** (fiche mère) : quelque chose de 25-4-a appartient-il en fait à b ou c, ou + l'inverse ? La latence de #455 est-elle correctement établie ? + +## Ce que tu rends + +- **Findings** : sévérité (CRITICAL / HIGH / MEDIUM / LOW), l'endroit exact de la fiche, **la + preuve** (commande et résultat, code lu), la correction proposée. +- ⛔ **La liste des axes réellement exercés ET de ceux qui ne l'ont pas été.** Un « 0 finding » + sans elle ne compte pas. + +## Interdits + +⛔ N'écris aucun fichier du dépôt ; n'exécute aucune commande qui écrit dans le dépôt ou dans une +base — `scripts/prepare-release.sh`, `scripts/regen-test-schema.sh`, `scripts/install-hooks.sh`, +`scripts/test-fast.sh`, `scripts/mem-guard.sh`, `make`, `latexmk`, tout `git commit`/`push`/`add`/ +`stash`/`reset`/`rebase`/`checkout`/`switch`/`worktree`, `sqlx migrate`, `cargo test`/`nextest`, +`npm run`, `npx playwright`. Autorisés : lecture, `grep`, `git log`/`show`/`diff`, `gh issue view`, +`pdftotext` vers le scratchpad, `cargo check`. From 73172c8f75ff9dbfc991b29f46799b12a0bb435a Mon Sep 17 00:00:00 2001 From: Guy Corbaz Date: Sat, 26 Sep 2026 23:53:51 +0200 Subject: [PATCH 03/18] =?UTF-8?q?docs(25-4):=20Q1=20tranch=C3=A9e=20?= =?UTF-8?q?=E2=80=94=20QR=20sans=20montant=20sur=20une=20relance=20partiel?= =?UTF-8?q?lement=20r=C3=A9gl=C3=A9e,=20reste=20=C3=A0=20payer=20dit=20au?= =?UTF-8?q?=20client=20(refs=20#416)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- .../25-4-propager-le-residuel.md | 17 +++++++++++++++++ .../implementation-artifacts/sprint-status.yaml | 2 +- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/_bmad-output/implementation-artifacts/25-4-propager-le-residuel.md b/_bmad-output/implementation-artifacts/25-4-propager-le-residuel.md index 6da60b67..f651c0ef 100644 --- a/_bmad-output/implementation-artifacts/25-4-propager-le-residuel.md +++ b/_bmad-output/implementation-artifacts/25-4-propager-le-residuel.md @@ -72,8 +72,25 @@ facture partiellement réglée joint un PDF **sans montant dans la QR** (la norm client saisit le montant) ; (c) laisser tel quel et le dire dans le texte de la relance. Ma recommandation : (b), qui ne ment pas et ne modifie pas la facture. **À trancher avant 25-4-b.** +### ✅ Arbitrage du 2026-09-26 (Q1) — (b), et le reste à payer dit au client + +*« b, mais indiquer au client ce qu'il reste à payer. »* (Guy) + +Pour 25-4-b, donc : + +- la relance d'une facture **partiellement réglée** joint un PDF dont la QR-facture **ne porte pas + de montant** — le client le saisit ; une facture sans règlement garde sa QR à montant ; +- la relance **dit ce qu'il reste à payer** : `{totalDue}` devient **reste dû + frais**, et non plus + TTC + frais (`invoice_email.rs:334`) — c'est le montant que le client doit saisir. + +⚠️ **Ne pas contester l'arbitrage en revue** : en contester la mise en œuvre. Reste à établir en +spécification de 25-4-b : si le reste dû figure **aussi sur le PDF** joint (mention à côté de la +QR sans montant), ou dans le seul texte de la relance — le PDF est aujourd'hui celui de la facture, +et #387 (l'archivage du PDF envoyé) le touche. + ## Change Log +- **2026-09-26** — Q1 tranchée par Guy : QR sans montant pour une facture partiellement réglée, et le reste à payer dit au client (rattaché à 25-4-b). - **2026-09-26** — Découpée avant spécification (§ *Règle de splitting préventif*, six modules), sur inventaire vérifié. #455 et #456 rattachées (même grandeur, même jalon). Deux sites trouvés hors des issues : le filtre SQL des candidats du rapprochement (#420), et le montant des relances diff --git a/_bmad-output/implementation-artifacts/sprint-status.yaml b/_bmad-output/implementation-artifacts/sprint-status.yaml index 8d962656..fdbcda3c 100644 --- a/_bmad-output/implementation-artifacts/sprint-status.yaml +++ b/_bmad-output/implementation-artifacts/sprint-status.yaml @@ -354,7 +354,7 @@ development_status: 25-3-b-annuler-rapprochement: done # 2026-09-25 DONE — revue close en passe 2 (Sonnet 1 MED -> Haiku 0) ; gates : backend 2463/2463, frontend 777/777, E2E sans regression. Non poussee. — SPEC VALIDEE (boucle close en passe 4 : 2 HIGH -> 2 MED -> 2 MED -> 3 LOW ; pas de decoupage, arbitrage de Guy). Q1-Q3 tranchees. — [#418] closes — annuler un rapprochement bancaire. ⛔ NE JAMAIS compter sur le `ON DELETE SET NULL` de `matched_entry_id` : supprimer l'ecriture ferait disparaitre le lien EN SILENCE, le mode d'echec meme que la story repare. ⚠️ Le chemin d'ACCEPTATION n'a aucune fonction `kesh-db` a mirer : il est ecrit en SQL brut a **cinq** sites de `routes/reconciliation.rs`, dont **un seul** porte le verrou optimiste. Quand la transaction porte une facture, appelle le geste de la 25-3-a. Derriere elle. 25-3-annuler-reglement-et-rapprochement: split # [#414] [#418] — **l'Epic 24 a livre l'encaissement et le rapprochement ; il n'a PAS livre le moyen de les DEFAIRE.** Une erreur d'imputation est aujourd'hui incorrigible autrement qu'a la main — et le gel de la 24-4b interdit precisement la main. ⚠️ *C'est le prix d'une story qui ferme une porte sans ouvrir la suivante : le gel est juste, et il rend cette story-ci necessaire.* 25-4-a-residuel-juste: ready-for-dev # 2026-09-26 [#455] [#456] — l'avoir soustrait HT d'un TTC (latent, visible seulement dans l'etat que #456 produit) ; un avoir accepte sur une facture partiellement reglee rend la creance creditrice. CREEE, validation a lancer. - 25-4-b-residuel-aux-agregats: backlog # [#416] + montant des RELANCES (TTC complet, trouve a l'inventaire) — Q1 a Guy : QR-facture jointe a la relance porte le TTC. + 25-4-b-residuel-aux-agregats: backlog # [#416] + montant des RELANCES (TTC complet, trouve a l'inventaire) — Q1 TRANCHEE 2026-09-26 : QR sans montant si partiellement reglee, et le reste a payer dit au client. 25-4-c-residuel-au-rapprochement: backlog # [#420] — DEUX sites : filtre SQL des candidats (HAVING total_ttc) avant le score, puis score et re-score a l'acceptation. 25-4-propager-le-residuel: split # 2026-09-26 DECOUPEE (six modules > 5) en 25-4-a / 25-4-b / 25-4-c ; #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). From 1ed731350eed377d07537833e1ff38a291065dce Mon Sep 17 00:00:00 2001 From: Guy Corbaz Date: Sat, 26 Sep 2026 23:57:36 +0200 Subject: [PATCH 04/18] =?UTF-8?q?docs(25-4):=20le=20reste=20=C3=A0=20payer?= =?UTF-8?q?=20figure=20aussi=20sur=20le=20PDF=20joint=20=C3=A0=20la=20rela?= =?UTF-8?q?nce=20(refs=20#416)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- .../25-4-propager-le-residuel.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/_bmad-output/implementation-artifacts/25-4-propager-le-residuel.md b/_bmad-output/implementation-artifacts/25-4-propager-le-residuel.md index f651c0ef..14bb15b1 100644 --- a/_bmad-output/implementation-artifacts/25-4-propager-le-residuel.md +++ b/_bmad-output/implementation-artifacts/25-4-propager-le-residuel.md @@ -83,13 +83,16 @@ Pour 25-4-b, donc : - la relance **dit ce qu'il reste à payer** : `{totalDue}` devient **reste dû + frais**, et non plus TTC + frais (`invoice_email.rs:334`) — c'est le montant que le client doit saisir. -⚠️ **Ne pas contester l'arbitrage en revue** : en contester la mise en œuvre. Reste à établir en -spécification de 25-4-b : si le reste dû figure **aussi sur le PDF** joint (mention à côté de la -QR sans montant), ou dans le seul texte de la relance — le PDF est aujourd'hui celui de la facture, -et #387 (l'archivage du PDF envoyé) le touche. +- ✅ **Le reste à payer figure AUSSI sur le PDF joint**, à côté de la QR sans montant (Guy, + 2026-09-26) : *« sinon le client ne peut pas savoir ce qu'il reste à payer sans recherche et + calculs »*. Le PDF d'une relance de facture partiellement réglée n'est donc plus exactement la + facture d'origine — à articuler en spécification avec #387 (l'archivage du PDF envoyé). + +⚠️ **Ne pas contester l'arbitrage en revue** : en contester la mise en œuvre. ## Change Log +- **2026-09-26** — Complément de Q1 : le reste à payer figure aussi sur le PDF joint. - **2026-09-26** — Q1 tranchée par Guy : QR sans montant pour une facture partiellement réglée, et le reste à payer dit au client (rattaché à 25-4-b). - **2026-09-26** — Découpée avant spécification (§ *Règle de splitting préventif*, six modules), sur inventaire vérifié. #455 et #456 rattachées (même grandeur, même jalon). Deux sites trouvés From f28811766faecd3b6f243fb535683be39c91c152 Mon Sep 17 00:00:00 2001 From: Guy Corbaz Date: Sun, 27 Sep 2026 00:00:19 +0200 Subject: [PATCH 05/18] =?UTF-8?q?docs(25-4):=20le=20document=20joint=20est?= =?UTF-8?q?=20un=20rappel=20pour=20un=20montant=20partiel,=20pas=20la=20fa?= =?UTF-8?q?cture=20r=C3=A9=C3=A9mise=20(refs=20#416)?= 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-4-propager-le-residuel.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/_bmad-output/implementation-artifacts/25-4-propager-le-residuel.md b/_bmad-output/implementation-artifacts/25-4-propager-le-residuel.md index 14bb15b1..b94e63cd 100644 --- a/_bmad-output/implementation-artifacts/25-4-propager-le-residuel.md +++ b/_bmad-output/implementation-artifacts/25-4-propager-le-residuel.md @@ -88,10 +88,18 @@ Pour 25-4-b, donc : calculs »*. Le PDF d'une relance de facture partiellement réglée n'est donc plus exactement la facture d'origine — à articuler en spécification avec #387 (l'archivage du PDF envoyé). +- ✅ **Recadrage (Guy, 2026-09-27)** : *« en fait c'est un rappel pour un montant partiel. »* Le + document joint à une relance de facture partiellement réglée n'est pas la facture réémise : c'est + **un rappel pour le montant restant**. L'objection « le PDF n'est plus la facture d'origine » + tombe donc — ce n'est pas la facture —, et #387 ne s'en trouve pas touché. ⚠️ **Point ouvert + posé à Guy** : dès lors, la QR du rappel doit-elle porter le **reste à payer** plutôt que rester + sans montant ? + ⚠️ **Ne pas contester l'arbitrage en revue** : en contester la mise en œuvre. ## Change Log +- **2026-09-27** — Recadrage de Q1 : le document est un rappel pour un montant partiel, pas la facture réémise. - **2026-09-26** — Complément de Q1 : le reste à payer figure aussi sur le PDF joint. - **2026-09-26** — Q1 tranchée par Guy : QR sans montant pour une facture partiellement réglée, et le reste à payer dit au client (rattaché à 25-4-b). - **2026-09-26** — Découpée avant spécification (§ *Règle de splitting préventif*, six modules), From b62db3c6548a0fe5dea578db5ec1e1b75ecb2812 Mon Sep 17 00:00:00 2001 From: Guy Corbaz Date: Sun, 27 Sep 2026 00:03:00 +0200 Subject: [PATCH 06/18] =?UTF-8?q?docs(25-4-a):=20validation=20P1=20?= =?UTF-8?q?=E2=80=94=20#455=20expos=C3=A9=20par=20l'API,=20bouton=20d'avoi?= =?UTF-8?q?r=20align=C3=A9=20sur=20=C2=AB=20D=C3=A9valider=20=C2=BB,=20mon?= =?UTF-8?q?tage=20de=20monter=20(refs=20#455,=20refs=20#456)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- .../25-4-a-residuel-juste.md | 101 +++++++++++++----- .../25-4-propager-le-residuel.md | 4 +- .../sprint-status.yaml | 2 +- 3 files changed, 77 insertions(+), 30 deletions(-) diff --git a/_bmad-output/implementation-artifacts/25-4-a-residuel-juste.md b/_bmad-output/implementation-artifacts/25-4-a-residuel-juste.md index 88396a44..90c7c586 100644 --- a/_bmad-output/implementation-artifacts/25-4-a-residuel-juste.md +++ b/_bmad-output/implementation-artifacts/25-4-a-residuel-juste.md @@ -41,11 +41,17 @@ reste dû. La forme jointe `INVOICE_CREDITED_DERIVED_JOIN_SQL` (`:56-58`) porte (`crates/kesh-db/tests/invoice_settlement.rs:32`), où HT = TTC ; et aucun test n'associe un avoir et `amount_due`. -⚠️ **Latent sur les données neuves, pas sur les anciennes.** L'avoir est **total** et fait passer -la facture à `cancelled` (`credit_notes.rs:560-575`) ; le terme « avoir » n'est donc non nul que sur -une facture annulée, où le reste dû ne s'affiche que si `amountSettled > 0` -(`invoices/[id]/+page.svelte:1022-1041`) — c'est-à-dire **exactement l'état que #456 permet de -produire**. Et un `.keshbackup` de la v0.12.0 **peut contenir** cet état : l'import le restaure. +⚠️ **Masqué à l'écran, exposé par l'API.** L'avoir est **total** et fait passer la facture à +`cancelled` (`credit_notes.rs:560-575`) ; le terme « avoir » n'est donc non nul que sur une facture +annulée. **L'écran** n'y affiche le reste dû que si `amountSettled > 0` +(`invoices/[id]/+page.svelte:1022-1041`) — l'état que #456 permet de produire. ⛔ **Mais l'API le +rend toujours** : `get_invoice` (`crates/kesh-api/src/routes/invoices.rs:621-642`) calcule +`amount_due` **sans condition de statut**, et `GET /invoices/{id}` est **ouverte aux clés API en +lecture** (`docs/api-external.md:211`). Un avoir émis sur une facture **jamais réglée** — geste +ordinaire, sans #456 — suffit donc, aujourd'hui, à faire rendre à une intégration tierce +`amountDue` = **la TVA** au lieu de `0`. *(Relevé en validation P1 ; la fiche mère disait « latent » +à tort.)* Et un `.keshbackup` de la v0.12.0 **peut contenir** l'état de #456 : l'import le +restaure. ### #456 — un avoir peut viser une facture partiellement réglée @@ -69,10 +75,10 @@ où `settlement_cancel_blocker` la déclare inannulable (`SettlementCancelBlocke (`crates/kesh-db/tests/invoice_settlement.rs:937-990`) règle 40 sur 100 puis appelle `create_credit_note` avec `.expect("avoir après règlement partiel — le vrai chemin l'accepte")`. -⚠️ **Le frontend affiche le bouton sur une facture partiellement réglée** : -`invoices/[id]/+page.svelte:836` n'exige que `!invoice.paidAt`. Le bouton voisin « Dévalider » a -retiré cette condition pour cette raison même (commentaire `:865`) ; « Créer un avoir » n'a pas été -aligné. +⚠️ **Le frontend masque le bouton sur une facture payée, pas sur une facture partiellement +réglée** : `invoices/[id]/+page.svelte:836` n'exige que `!invoice.paidAt`. Le bouton voisin +« Dévalider » a **retiré** cette condition pour cette raison même (commentaire `:864-871`) et laisse +le serveur refuser avec un motif nommé ; « Créer un avoir » n'a pas été aligné. ## Acceptance Criteria @@ -116,7 +122,8 @@ créance par l'écriture de l'avoir** (`credit_notes.journal_entry_id` → ligne seul HT ou à une valeur recalculée en Rust **ne suffit pas**. **AC 8** — Une facture validée à 8,1 %, **non réglée, créditée** : `amount_due` = **0** (et non la -TVA). Une facture validée à 8,1 %, **réglée de 40 puis créditée** (état **monté en SQL**, cf. +TVA) — **au repository ET à la frontière HTTP** : `GET /api/v1/invoices/{id}` rend `amountDue` = +`"0.00"` (ou sa forme décimale normalisée), `amountSettled` = `0`. Une facture validée à 8,1 %, **réglée de 40 puis créditée** (état **monté en SQL**, cf. AC 13) : `amount_due` = **−40**, le montant encaissé en trop sur une vente annulée — et non `TVA − 40`. ⚠️ **Pas d'écrêtage à zéro** : le doc-comment d'`amount_due` l'interdit déjà. @@ -134,7 +141,7 @@ transaction ; elle est **verrouillante** (`… FOR UPDATE` ou `LOCK IN SHARE MOD motif que `InvoiceNotUnvalidatable` (`errors.rs:119-130`) : le générique ne dit ni ce qui bloque ni quoi faire. Elle porte l'identifiant du premier règlement trouvé (`Option`, `None` si seul `paid_at` est posé) et le numéro de la facture. Code machine **`CREDIT_NOTE_INVOICE_SETTLED`**, -HTTP **409**, ajouté à `DbError::code()` (`errors.rs:640-670`) et à toute garde qui énumère les +HTTP **409**, ajouté à `DbError::error_code()` (`errors.rs:641`) et à toute garde qui énumère les codes. **AC 11** — **Le cas « payée » passe par la même variante** : AC2bis cesse de rendre le générique. @@ -147,17 +154,39 @@ règlement. pouvoir émettre un avoir. » Registre du glossaire (`docs/i18n-glossaire.md` § Registre : de = *Sie*, it = 2ᵉ personne du singulier, en neutre). `details` : `{ "invoiceId", "settlementId" }`. -**AC 13** — La fixture `monter` (`invoice_settlement.rs:937-990`) **cesse de passer par -`create_credit_note`** pour le motif `InvoiceCredited` : elle monte l'état **en SQL** (avoir -`issued` inséré, facture `cancelled`), avec un commentaire qui dit que l'état n'est **plus -atteignable par l'application** depuis 25-4-a, mais **reste possible** par l'import d'une +**AC 13** — La fixture `monter` (`invoice_settlement.rs:937-1019`) cesse d'obtenir l'état +`InvoiceCredited` **par le chemin que 25-4-a ferme**, avec un commentaire qui dit que l'état n'est +**plus atteignable par l'application** depuis 25-4-a, mais **reste possible** par l'import d'une sauvegarde antérieure — ce qui justifie de garder `SettlementCancelBlocker::InvoiceCredited`. ⛔ Ne **pas** supprimer ce motif ni ses tests. -**AC 14** — Frontend : le bouton « Créer un avoir » (`invoices/[id]/+page.svelte:836`) est masqué -quand `invoice.paidAt` est posé **ou** `Number(invoice.amountSettled) > 0` — même condition que le -serveur. Si la course fait arriver le refus malgré tout, le dialogue affiche le message du serveur -(`err.message`, `:98`), déjà traduit : rien à ajouter côté client. +⚠️ **`monter` sert quinze cas, pas un** : `la_precedence_de_l_annulation_lecture_et_ecriture` +(`:1056-1064`) l'appelle avec les cinq motifs seuls **et les dix paires** (`RANGS`, +`assert_eq!(cas.len(), 15)`), dont **quatre combinent `InvoiceCredited`** avec un motif monté +**après** lui dans le même corps (clôture d'exercice, rapprochement bancaire, compte archivé, +aucun exercice ouvert). Le montage doit rester compatible avec ces quatre suites, et **les quinze +cas restent verts**. + +⚠️ **Un avoir `issued` ne s'insère pas en SQL nu** : `chk_credit_notes_issued_has_je` exige +`credit_note_number` **et** `journal_entry_id` non nuls (`20260627000001_credit_notes.sql:53-54`), +donc une écriture de contre-passation valide. **Gabarit recommandé**, qui garde les **deux +écritures produites par les vrais chemins** et ne touche en SQL qu'un rattachement : régler (vrai +chemin) ; **détacher** la ligne de règlement vers une facture auxiliaire validée de la même société +(`UPDATE invoice_settlements SET invoice_id = ?`) ; créer l'avoir (vrai chemin, désormais accepté) ; +**rattacher** la ligne à la facture d'origine. Vérifier les contraintes d'unicité de +`invoice_settlements` avant de retenir ce gabarit ; à défaut, construire l'écriture d'avoir par +`journal_entries::create_in_tx` avec les lignes de `generate_credit_note_journal_lines`, jamais à la +main ligne par ligne. + +**AC 14** — Frontend : le bouton « Créer un avoir » (`invoices/[id]/+page.svelte:836`) **suit le +précédent du bouton « Dévalider »** (`:864-871`) : la condition `!invoice.paidAt` est **retirée**, le +bouton s'affiche pour `canManage`, et **le serveur refuse avec le motif nommé**, que le dialogue +affiche déjà (`creditNoteError = err.message`, `:101`). ⛔ L'écran **ne rejoue pas** la règle métier : +une condition client qui en couvre une moitié laisse l'utilisateur sans rien à lire, et une qui la +couvre entière se désynchronise à la première évolution du serveur. La garde étant désormais +**unique** côté serveur (AC 11), le motif « payée » et le motif « réglée en partie » s'affichent de +la même façon. Le commentaire de `:864-871` est étendu aux deux boutons, ou dupliqué au-dessus du +second. **AC 15** — Rien n'est **réparé** dans les données existantes : pas de migration, pas de rejeu (`CLAUDE.md` : aucune donnée de production à protéger ; précédent 24-2 D7). Les états hérités @@ -172,6 +201,7 @@ règlement — et c'est ce que les textes disent (AC 17). |---|---| | `credited_amount_is_ttc_and_matches_the_ledger` | AC 1, AC 7 — facture multi-taux, avoir émis : sous-requête = crédit créance de l'écriture d'avoir | | `amount_due_of_a_credited_invoice_is_zero` | AC 8 — 8,1 %, non réglée : `0`, pas `8.10` | +| e2e API `get_credited_invoice_reports_zero_amount_due` | AC 8 — la surface exposée aux clés API | | `amount_due_after_settlement_then_legacy_credit_is_minus_settled` | AC 8 — état hérité monté en SQL : `−40` | | `settled_and_credited_forms_are_at_parity` | AC 6 | | `credit_note_refused_on_partially_settled_invoice` | AC 9, AC 10 — **rien écrit** : aucun avoir, facture `validated`, `version` inchangée, solde de créance inchangé, séquence d'avoir non consommée | @@ -179,7 +209,7 @@ règlement — et c'est ce que les textes disent (AC 17). | `credit_note_accepted_after_the_settlement_is_cancelled` | la promesse du message : annuler le règlement rouvre l'avoir | | `credit_note_waits_for_a_concurrent_settlement` | AC 9 — **entrelacement** : une transaction verrouille la facture et insère un règlement ; `create_credit_note` lancé en parallèle attend, puis **refuse** après le commit. Patron : la sonde d'entrelacement de la 25-3-b | | e2e API `credit_note_on_partially_settled_invoice_is_409` | la frontière HTTP : 409, `CREDIT_NOTE_INVOICE_SETTLED`, message fr | -| Vitest fiche facture | AC 14 — bouton absent si `amountSettled > 0`, présent sinon | +| Vitest fiche facture | AC 14 — bouton **présent** sur une facture réglée (en partie **et** en totalité) ; au refus, le dialogue affiche le message du serveur | ⛔ **Chaque test porte au moins une facture à TVA non nulle.** À 0 %, HT = TTC et #455 est invisible — c'est ainsi qu'il est passé. @@ -188,7 +218,7 @@ invisible — c'est ainsi qu'il est passé. `SUM(cn.total_amount)` dans la forme scalaire ; (m2) idem dans la forme jointe seule ; (m3) retirer la lecture des règlements de la garde (ne garder que `paid_at`) ; (m4) retirer `paid_at` (ne garder que la ligne) ; (m5) lire les règlements **avant** le verrou, non verrouillant ; (m6) remettre -`!invoice.paidAt` seul sur le bouton. Chacune doit faire rougir au moins un test. +`!invoice.paidAt` sur le bouton ; (m7) mapper la variante sur le générique `ILLEGAL_STATE_TRANSITION`. Chacune doit faire rougir au moins un test. **AC 17** — Manuels et notes (`docs/manual/fr/user-manual.tex`, PDF régénéré et **contrôlé aplati**) : @@ -198,7 +228,9 @@ aplati**) : « compte de produit archivé » ; - `:963` et `:1455` (« la facture a été créditée par un avoir… ») : précisés comme **cas hérité** — une facture créditée avant la v0.12.1, ou restaurée d'une sauvegarde antérieure ; -- `CHANGELOG.md` `[0.12.1]` *Fixed* : les deux défauts, en langage d'utilisateur. +- `CHANGELOG.md` `[0.12.1]` *Fixed* : les deux défauts, en langage d'utilisateur — et, pour #455, + **nommer l'API** : `GET /invoices/{id}` rendait la TVA en `amountDue` sur une facture créditée ; + une intégration tierce qui le lisait doit le savoir. ⚠️ **Greper le symptôme, pas la phrase** : `grep -rniE "payée|encaissée|réglée" docs/manual/fr/*.tex` restreint aux lignes qui parlent d'avoir, plus le PDF aplati. Les manuels DE/IT/EN sont des stubs. @@ -210,11 +242,11 @@ restreint aux lignes qui parlent d'avoir, plus le PDF aplati. Les manuels DE/IT/ - [ ] **T2 — parité et concordance** (AC 6-8) : nouveau fichier de test sur le patron `invoice_ttc_parity.rs` ; montage de l'état hérité en SQL. - [ ] **T3 — garde de l'avoir** (AC 9-11) : lecture verrouillante des règlements après le verrou, - variante `DbError`, `code()`. + variante `DbError`, `error_code()`. - [ ] **T4 — API et i18n** (AC 12) : mapping, clé ×4 ; e2e 409. -- [ ] **T5 — fixture `monter`** (AC 13) : état monté en SQL, commentaire ; les tests d'annulation de - règlement restent verts. -- [ ] **T6 — frontend** (AC 14) : condition du bouton, Vitest. +- [ ] **T5 — fixture `monter`** (AC 13) : gabarit « détacher, créditer, rattacher », commentaire ; + **les quinze cas** de la précédence restent verts, dont les quatre paires avec `InvoiceCredited`. +- [ ] **T6 — frontend** (AC 14) : condition du bouton retirée, commentaire, Vitest. - [ ] **T7 — tests et mutations** (AC 16) : m1 à m6 exécutées, résultats consignés. - [ ] **T8 — textes** (AC 17) : manuel, PDF aplati, CHANGELOG. - [ ] **T9 — gates** : backend complet (base remise à zéro), frontend complet, E2E complet. @@ -240,14 +272,15 @@ restreint aux lignes qui parlent d'avoir, plus le PDF aplati. Les manuels DE/IT/ | `crates/kesh-db/src/repositories/invoice_settlements.rs:24-58, 122-145` | les quatre constantes, `amount_due` | | `crates/kesh-db/src/repositories/invoices.rs:155-190` | `INVOICE_TTC_SUBQUERY_SQL` / `…_DERIVED_JOIN_SQL`, `total_ttc` | | `crates/kesh-db/src/repositories/credit_notes.rs:187-257, 260-330, 510-575` | écriture de l'avoir, garde AC2bis, `total_amount`, bascule `cancelled` | -| `crates/kesh-db/src/errors.rs:119-175, 300-470, 640-670` | `UnvalidationBlocker`, `DbError`, `code()` | +| `crates/kesh-db/src/errors.rs:119-175, 300-470, 641-670` | `UnvalidationBlocker`, `DbError`, `error_code()` | | `crates/kesh-db/src/repositories/invoices.rs:1430-1448` | la garde de dévalidation, patron de l'AC 9 | | `crates/kesh-api/src/errors.rs:2515-2560, 2741-2748` | mapping `InvoiceNotUnvalidatable`, générique | | `crates/kesh-api/src/routes/credit_notes.rs:183-205` | handler de création | | `crates/kesh-db/tests/invoice_ttc_parity.rs` | patron du test de parité | | `crates/kesh-db/tests/credit_notes_repository.rs:267-299` | `credit_note_refused_on_paid_invoice` | | `crates/kesh-db/tests/invoice_settlement.rs:32, 116-125, 937-990` | helper à 0 %, `solde()`, `monter` | -| `frontend/src/routes/(app)/invoices/[id]/+page.svelte:90-104, 836, 865, 1022-1041` | dialogue d'avoir, bouton, précédent « Dévalider », reste dû | +| `frontend/src/routes/(app)/invoices/[id]/+page.svelte:90-104, 836, 864-871, 1022-1041` | dialogue d'avoir (`err.message` `:101`), bouton, précédent « Dévalider », reste dû | +| `crates/kesh-api/src/routes/invoices.rs:621-642` | `get_invoice` : `amountDue` rendu sans condition de statut | ### Courses @@ -299,6 +332,18 @@ bloque **25-4-b**, pas celle-ci. ## Change Log +- **2026-09-26** — **validation P1** (Sonnet, prompt `25-4-a-validate-prompt-p1.md`) — **1 HIGH, + 3 MEDIUM, 1 LOW**, tous confirmés dans le code avant correction. **HIGH** : le récit « #455 + latent » était faux — `get_invoice` rend `amountDue` sans condition de statut, et + `GET /invoices/{id}` est ouverte aux clés API : une facture créditée à TVA non nulle y rend la + TVA ; récit corrigé ici et dans la fiche mère, AC 8 étendu à la frontière HTTP, e2e ajouté, + CHANGELOG à nommer l'API. **MEDIUM** : `DbError::code()` n'existe pas — c'est `error_code()` ; + AC 14 contredisait le précédent « Dévalider » qu'il citait — il le suit désormais (bouton + visible, le serveur refuse avec le motif nommé, m7 ajoutée) ; AC 13 ignorait que `monter` sert + quinze cas dont quatre paires avec `InvoiceCredited`, et qu'un avoir `issued` exige une écriture + (`chk_credit_notes_issued_has_je`) — gabarit « détacher, créditer, rattacher » prescrit. **LOW** : + `err.message` est à `:101`, non `:98`. Axes tous exercés ; non exercé : la sémantique REPEATABLE + READ n'a pas été mesurée (raisonnée). - **2026-09-26** — Créée (Opus 5.5) après découpage de la 25-4. Inventaire vérifié par `grep -nF` sur chaque site cité. Deux faits hors des issues : #455 est **latent** sur les données neuves et ne se voit que dans l'état que #456 produit — d'où leur réunion ; et la fixture `monter` encode diff --git a/_bmad-output/implementation-artifacts/25-4-propager-le-residuel.md b/_bmad-output/implementation-artifacts/25-4-propager-le-residuel.md index b94e63cd..6d9372fd 100644 --- a/_bmad-output/implementation-artifacts/25-4-propager-le-residuel.md +++ b/_bmad-output/implementation-artifacts/25-4-propager-le-residuel.md @@ -39,7 +39,9 @@ Le découpage suit l'ordre des dépendances, non celui des issues : 3. ⚠️ **La relance joint le PDF de la facture**, dont la QR-facture porte le TTC (`invoice_email.rs:1140-1166`, `invoice_pdf_service::render`). Un client qui paie la relance en scannant la QR paie **à nouveau le montant entier**. Question posée à Guy (Q1 ci-dessous). -4. ⚠️ **#455 est latent sur les données neuves.** Un avoir est **total** et fait passer la facture à +4. ⚠️ **#455 est masqué à l'écran — mais pas à l'API.** *(Corrigé en validation P1 de 25-4-a : la + première rédaction disait « latent ». `GET /invoices/{id}`, ouverte aux clés API, rend + `amountDue` sans condition de statut : sur une facture créditée à TVA non nulle, il vaut la TVA.)* Un avoir est **total** et fait passer la facture à `cancelled` (`credit_notes.rs:560-563`) ; or les agrégats, le rapprochement et le règlement ne lisent que des factures `validated`. Le terme « avoir » du résiduel n'est donc **non nul que sur une facture annulée**, et le reste dû ne s'y affiche que si `amountSettled > 0` — c'est-à-dire diff --git a/_bmad-output/implementation-artifacts/sprint-status.yaml b/_bmad-output/implementation-artifacts/sprint-status.yaml index fdbcda3c..84c42644 100644 --- a/_bmad-output/implementation-artifacts/sprint-status.yaml +++ b/_bmad-output/implementation-artifacts/sprint-status.yaml @@ -353,7 +353,7 @@ development_status: 25-3-c-annuler-facture-fournisseur: done # 2026-09-26 **REVUE CLOSE EN 1 PASSE** (0 > LOW, Sonnet x3) ; non poussee ; PR `closes #454` titre ET corps, sur accord. **IMPLEMENTEE** — gates 2472/2472, 783/783, E2E 220/10/19 sans regression ; prochaine etape : code review. # 2026-09-26 **SPEC VALIDEE en 2 passes** (3 MED -> 0 > LOW). **SPECIFIEE** — `25-3-c-annuler-facture-fournisseur.md`. Geste par le socle (autorite `SupplierPurchase`), queue commune sur l'ecriture d'ACHAT, lot en dernier rang ; payee : reglement DETACHE (colonnes remises a NULL, ecriture libre, lien garde par l'audit) — **Q1 tranchee par Guy le 2026-09-26**. Aucune migration. Prochaine etape : validate. # 2026-09-24 NEE d'un arbitrage de Guy — une facture fournisseur s'annule DANS TOUS LES CAS, sauf si l'exercice de son ecriture d'achat est CLOS (l'exercice, pas le verrou de periode) ; payee, son reglement RESTE au grand livre et REDEVIENT A LETTRER (Epic 15 : un paiement doit correspondre a une facture, au besoin creee pour lui — « comme bexio »). Reecrit `cancel`, donc absorbe [#454]. Derriere la 25-3-a. 25-3-b-annuler-rapprochement: done # 2026-09-25 DONE — revue close en passe 2 (Sonnet 1 MED -> Haiku 0) ; gates : backend 2463/2463, frontend 777/777, E2E sans regression. Non poussee. — SPEC VALIDEE (boucle close en passe 4 : 2 HIGH -> 2 MED -> 2 MED -> 3 LOW ; pas de decoupage, arbitrage de Guy). Q1-Q3 tranchees. — [#418] closes — annuler un rapprochement bancaire. ⛔ NE JAMAIS compter sur le `ON DELETE SET NULL` de `matched_entry_id` : supprimer l'ecriture ferait disparaitre le lien EN SILENCE, le mode d'echec meme que la story repare. ⚠️ Le chemin d'ACCEPTATION n'a aucune fonction `kesh-db` a mirer : il est ecrit en SQL brut a **cinq** sites de `routes/reconciliation.rs`, dont **un seul** porte le verrou optimiste. Quand la transaction porte une facture, appelle le geste de la 25-3-a. Derriere elle. 25-3-annuler-reglement-et-rapprochement: split # [#414] [#418] — **l'Epic 24 a livre l'encaissement et le rapprochement ; il n'a PAS livre le moyen de les DEFAIRE.** Une erreur d'imputation est aujourd'hui incorrigible autrement qu'a la main — et le gel de la 24-4b interdit precisement la main. ⚠️ *C'est le prix d'une story qui ferme une porte sans ouvrir la suivante : le gel est juste, et il rend cette story-ci necessaire.* - 25-4-a-residuel-juste: ready-for-dev # 2026-09-26 [#455] [#456] — l'avoir soustrait HT d'un TTC (latent, visible seulement dans l'etat que #456 produit) ; un avoir accepte sur une facture partiellement reglee rend la creance creditrice. CREEE, validation a lancer. + 25-4-a-residuel-juste: ready-for-dev # 2026-09-26 [#455] [#456] — l'avoir soustrait HT d'un TTC (masque a l'ecran, mais rendu par GET /invoices/{id} aux cles API : amountDue = la TVA sur une facture creditee) ; un avoir accepte sur une facture partiellement reglee rend la creance creditrice. CREEE ; validation P1 (Sonnet) 1 HIGH / 3 MED / 1 LOW, corriges. 25-4-b-residuel-aux-agregats: backlog # [#416] + montant des RELANCES (TTC complet, trouve a l'inventaire) — Q1 TRANCHEE 2026-09-26 : QR sans montant si partiellement reglee, et le reste a payer dit au client. 25-4-c-residuel-au-rapprochement: backlog # [#420] — DEUX sites : filtre SQL des candidats (HAVING total_ttc) avant le score, puis score et re-score a l'acceptation. 25-4-propager-le-residuel: split # 2026-09-26 DECOUPEE (six modules > 5) en 25-4-a / 25-4-b / 25-4-c ; #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**. From ca66c118b7156bb0e7028036a34357361fb3645c Mon Sep 17 00:00:00 2001 From: Guy Corbaz Date: Sun, 27 Sep 2026 00:03:17 +0200 Subject: [PATCH 07/18] =?UTF-8?q?docs(25-4-a):=20prompt=20de=20validation?= =?UTF-8?q?=20P2=20;=20plages=20de=20monter=20harmonis=C3=A9es=20(refs=20#?= =?UTF-8?q?455,=20refs=20#456)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- .../25-4-a-residuel-juste.md | 4 +- .../25-4-a-validate-prompt-p2.md | 66 +++++++++++++++++++ .../25-4-propager-le-residuel.md | 2 +- 3 files changed, 69 insertions(+), 3 deletions(-) create mode 100644 _bmad-output/implementation-artifacts/25-4-a-validate-prompt-p2.md diff --git a/_bmad-output/implementation-artifacts/25-4-a-residuel-juste.md b/_bmad-output/implementation-artifacts/25-4-a-residuel-juste.md index 90c7c586..3c1658d5 100644 --- a/_bmad-output/implementation-artifacts/25-4-a-residuel-juste.md +++ b/_bmad-output/implementation-artifacts/25-4-a-residuel-juste.md @@ -72,7 +72,7 @@ où `settlement_cancel_blocker` la déclare inannulable (`SettlementCancelBlocke (`UnvalidationBlocker::Settled`, `errors.rs:134-160` ; `invoices.rs:1430-1448`). ⚠️ **Un test encode le défaut comme attendu** : la fixture `monter` -(`crates/kesh-db/tests/invoice_settlement.rs:937-990`) règle 40 sur 100 puis appelle +(`crates/kesh-db/tests/invoice_settlement.rs:937-1019`) règle 40 sur 100 puis appelle `create_credit_note` avec `.expect("avoir après règlement partiel — le vrai chemin l'accepte")`. ⚠️ **Le frontend masque le bouton sur une facture payée, pas sur une facture partiellement @@ -278,7 +278,7 @@ restreint aux lignes qui parlent d'avoir, plus le PDF aplati. Les manuels DE/IT/ | `crates/kesh-api/src/routes/credit_notes.rs:183-205` | handler de création | | `crates/kesh-db/tests/invoice_ttc_parity.rs` | patron du test de parité | | `crates/kesh-db/tests/credit_notes_repository.rs:267-299` | `credit_note_refused_on_paid_invoice` | -| `crates/kesh-db/tests/invoice_settlement.rs:32, 116-125, 937-990` | helper à 0 %, `solde()`, `monter` | +| `crates/kesh-db/tests/invoice_settlement.rs:32, 116-125, 937-1019` | helper à 0 %, `solde()`, `monter` | | `frontend/src/routes/(app)/invoices/[id]/+page.svelte:90-104, 836, 864-871, 1022-1041` | dialogue d'avoir (`err.message` `:101`), bouton, précédent « Dévalider », reste dû | | `crates/kesh-api/src/routes/invoices.rs:621-642` | `get_invoice` : `amountDue` rendu sans condition de statut | diff --git a/_bmad-output/implementation-artifacts/25-4-a-validate-prompt-p2.md b/_bmad-output/implementation-artifacts/25-4-a-validate-prompt-p2.md new file mode 100644 index 00000000..f6a2662a --- /dev/null +++ b/_bmad-output/implementation-artifacts/25-4-a-validate-prompt-p2.md @@ -0,0 +1,66 @@ +# Prompt — validation P2, Story 25-4-a (le résiduel juste) + +*Versionné le 2026-09-26. **Une lentille** (Haiku), contexte frais.* + +Dépôt `/home/gcorbaz/devel/kesh`, branche `story/25-4-propager-le-residuel`. Fiche à valider : +`_bmad-output/implementation-artifacts/25-4-a-residuel-juste.md`. Fiche mère (inventaire, +découpage) : `25-4-propager-le-residuel.md`. Issues : `gh issue view 455`, `gh issue view 456`. +Contexte de la grandeur : `24-2-encaissement-client.md` § D3, D4, AC 5-7. Règles du dépôt : +`CLAUDE.md`. Checklist BMAD : `.claude/skills/bmad-create-story/checklist.md`. + +## Axes — tous obligatoires + +1. **Chaque référence `fichier:ligne`** existe et dit ce que la fiche affirme — relis le code. +2. **La formule** : la sous-requête TTC de l'avoir prescrite (AC 1-2) concorde-t-elle réellement, + centime par centime, avec l'écriture de l'avoir (`generate_credit_note_journal_lines`, + `line_vat_amount` — lis `kesh-core`, sens d'arrondi de `ROUND` MariaDB vs Rust) ? Et avec + `INVOICE_TTC_SUBQUERY_SQL` ? Un écart d'un centime possible est un finding. +3. **La garde** (AC 9-11) : inventorie **tous** les chemins qui créent un règlement ou un avoir + (pars du symptôme : `grep -rn "INSERT INTO invoice_settlements\|create_credit_note\|INSERT INTO credit_notes" crates/`), + et leur sérialisation (verrou pessimiste ou optimiste). La fiche en nomme trois : en manque-t-il ? + Le raisonnement REPEATABLE READ de § Courses est-il juste ? +4. **L'état hérité** : l'import d'une sauvegarde ramène-t-il vraiment l'état (règlement + avoir) ? + Les textes prescrits (AC 17) le disent-ils juste ? D'autres lecteurs de `amount_due` ou des + constantes changent-ils de valeur avec l'AC 1 (inventaire : `grep -rn "INVOICE_CREDITED\|amount_due" crates/ frontend/src`) ? +5. **Les tests** (AC 16) : chacun prouve-t-il ce qu'il annonce, ou peut-il passer à vide ? Les + mutations m1-m6 sont-elles tuables par les tests listés ? Une mutation que rien ne tuerait est + un finding. +6. **Le manuel** : les lignes citées (AC 17) existent-elles et disent-elles ce qu'on veut changer ? + Contrôle aussi le **PDF aplati** (`pdftotext docs/manual/fr/user-manual.pdf - | tr '\n' ' ' | tr -s ' '`, + vers `/tmp/claude-1000/-home-gcorbaz-devel-kesh/ca5ce2e2-67a3-4eeb-817f-c2de35620a1c/scratchpad/`). + Une autre phrase du manuel ou de l'admin-manual, de l'api-external, du README, promet-elle + l'ancien comportement ? +7. **Le découpage** (fiche mère) : quelque chose de 25-4-a appartient-il en fait à b ou c, ou + l'inverse ? La latence de #455 est-elle correctement établie ? + +## Ce que tu rends + +- **Findings** : sévérité (CRITICAL / HIGH / MEDIUM / LOW), l'endroit exact de la fiche, **la + preuve** (commande et résultat, code lu), la correction proposée. +- ⛔ **La liste des axes réellement exercés ET de ceux qui ne l'ont pas été.** Un « 0 finding » + sans elle ne compte pas. + +## Interdits + +⛔ N'écris aucun fichier du dépôt ; n'exécute aucune commande qui écrit dans le dépôt ou dans une +base — `scripts/prepare-release.sh`, `scripts/regen-test-schema.sh`, `scripts/install-hooks.sh`, +`scripts/test-fast.sh`, `scripts/mem-guard.sh`, `make`, `latexmk`, tout `git commit`/`push`/`add`/ +`stash`/`reset`/`rebase`/`checkout`/`switch`/`worktree`, `sqlx migrate`, `cargo test`/`nextest`, +`npm run`, `npx playwright`. Autorisés : lecture, `grep`, `git log`/`show`/`diff`, `gh issue view`, +`pdftotext` vers le scratchpad, `cargo check`. + +## Contexte P1 — à vérifier, pas à re-signaler + +La passe 1 a trouvé 1 HIGH (récit « #455 latent » faux : `get_invoice` rend `amountDue` aux clés +API), 3 MEDIUM (`error_code()` et non `code()` ; AC 14 aligné sur le précédent « Dévalider » ; +AC 13, `monter` sert quinze cas et un avoir `issued` exige une écriture) et 1 LOW. Les corrections +sont dans `git diff 0f4b9ec0 HEAD -- _bmad-output/implementation-artifacts/25-4-*.md` — **diff unique +aplati, à lire en priorité** : une correction introduit souvent le défaut suivant. + +**Tes priorités** : (1) le **gabarit « détacher, créditer, rattacher »** de l'AC 13 — est-il +réalisable (contraintes d'unicité et FK d'`invoice_settlements`, `company_id`, écriture de +règlement qui pointe l'ancienne facture ?) et compatible avec les quatre paires combinées ? +(2) l'AC 14 révisé — le dialogue d'avoir affiche-t-il vraiment le message du serveur, et que +voit l'utilisateur sur une facture **entièrement** payée, où le bouton était masqué jusqu'ici ? +(3) l'e2e `get_credited_invoice_reports_zero_amount_due` — la forme JSON exacte de `amountDue` +(sérialisation de `Decimal`) pour que l'assertion ne soit pas fragile. diff --git a/_bmad-output/implementation-artifacts/25-4-propager-le-residuel.md b/_bmad-output/implementation-artifacts/25-4-propager-le-residuel.md index 6d9372fd..4070392c 100644 --- a/_bmad-output/implementation-artifacts/25-4-propager-le-residuel.md +++ b/_bmad-output/implementation-artifacts/25-4-propager-le-residuel.md @@ -53,7 +53,7 @@ Le découpage suit l'ordre des dépendances, non celui des issues : 6. **Pourquoi aucun test n'a vu #455** : toutes les factures des tests de règlement sont à **0 % de TVA** (`crates/kesh-db/tests/invoice_settlement.rs:32`), où HT = TTC. 7. ⚠️ **Un test encode #456 comme attendu** : la fixture `monter` - (`crates/kesh-db/tests/invoice_settlement.rs:937-990`) règle 40 sur 100 puis crée un avoir avec + (`crates/kesh-db/tests/invoice_settlement.rs:937-1019`) règle 40 sur 100 puis crée un avoir avec `.expect("avoir après règlement partiel — le vrai chemin l'accepte")`. Il **décrit le défaut** ; il se corrige, il ne se contourne pas. From 7503be9b81dff44556711bc0d6b19389d6cdb56c Mon Sep 17 00:00:00 2001 From: Guy Corbaz Date: Sun, 27 Sep 2026 00:09:34 +0200 Subject: [PATCH 08/18] =?UTF-8?q?docs(25-4):=20la=20QR=20du=20rappel=20por?= =?UTF-8?q?te=20le=20reste=20d=C3=BB,=20une=20r=C3=A8gle=20pour=20les=20de?= =?UTF-8?q?ux=20rappels=20(refs=20#416)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- .../25-4-propager-le-residuel.md | 16 ++++++++++++++++ .../implementation-artifacts/sprint-status.yaml | 2 +- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/_bmad-output/implementation-artifacts/25-4-propager-le-residuel.md b/_bmad-output/implementation-artifacts/25-4-propager-le-residuel.md index 4070392c..3f404e51 100644 --- a/_bmad-output/implementation-artifacts/25-4-propager-le-residuel.md +++ b/_bmad-output/implementation-artifacts/25-4-propager-le-residuel.md @@ -96,11 +96,27 @@ Pour 25-4-b, donc : tombe donc — ce n'est pas la facture —, et #387 ne s'en trouve pas touché. ⚠️ **Point ouvert posé à Guy** : dès lors, la QR du rappel doit-elle porter le **reste à payer** plutôt que rester sans montant ? +- ✅ **Tranché (Guy, 2026-09-27)** : *« si c'est un rappel pour facture impayée, on met le montant + total de la facture, et si c'est un montant partiel, on met le montant à payer : on traite les + deux types de rappel de la même manière. »* Une seule règle : **la QR du rappel porte le reste + dû** — qui vaut le TTC sur une facture sans règlement. L'option (b) « sans montant » est + **abandonnée**. Guy suggère aussi de rappeler **le numéro de la facture d'origine** : la QR le + porte déjà (référence QRR dérivée de la facture, ou message non structuré = numéro, + `invoice_pdf_service.rs:218-250`) et **doit le garder à l'identique** — c'est ce que lit le + rapprochement ; le PDF du rappel le nomme en toutes lettres, avec montant initial, déjà réglé, + reste à payer. +- ⚠️ **Point ouvert pour la spécification de 25-4-b — les frais de rappel.** Le texte réclame + aujourd'hui `TTC + frais` (`invoice_email.rs:334`), mais les frais ne semblent **pas + comptabilisés** (aucune écriture dans `invoice_reminders.rs`, à vérifier). Si la QR portait + « reste dû + frais », le virement du client **dépasserait le reste dû** et serait refusé comme + trop-perçu, au rapprochement comme au règlement manuel (`RECONCILIATION_OVERPAYMENT`). À établir + avant de fixer le montant de la QR. ⚠️ **Ne pas contester l'arbitrage en revue** : en contester la mise en œuvre. ## Change Log +- **2026-09-27** — Q1 retranchée : la QR du rappel porte le reste dû (le TTC s'il n'y a aucun règlement) ; numéro de facture conservé ; point ouvert sur les frais. - **2026-09-27** — Recadrage de Q1 : le document est un rappel pour un montant partiel, pas la facture réémise. - **2026-09-26** — Complément de Q1 : le reste à payer figure aussi sur le PDF joint. - **2026-09-26** — Q1 tranchée par Guy : QR sans montant pour une facture partiellement réglée, et le reste à payer dit au client (rattaché à 25-4-b). diff --git a/_bmad-output/implementation-artifacts/sprint-status.yaml b/_bmad-output/implementation-artifacts/sprint-status.yaml index 84c42644..cd62a835 100644 --- a/_bmad-output/implementation-artifacts/sprint-status.yaml +++ b/_bmad-output/implementation-artifacts/sprint-status.yaml @@ -354,7 +354,7 @@ development_status: 25-3-b-annuler-rapprochement: done # 2026-09-25 DONE — revue close en passe 2 (Sonnet 1 MED -> Haiku 0) ; gates : backend 2463/2463, frontend 777/777, E2E sans regression. Non poussee. — SPEC VALIDEE (boucle close en passe 4 : 2 HIGH -> 2 MED -> 2 MED -> 3 LOW ; pas de decoupage, arbitrage de Guy). Q1-Q3 tranchees. — [#418] closes — annuler un rapprochement bancaire. ⛔ NE JAMAIS compter sur le `ON DELETE SET NULL` de `matched_entry_id` : supprimer l'ecriture ferait disparaitre le lien EN SILENCE, le mode d'echec meme que la story repare. ⚠️ Le chemin d'ACCEPTATION n'a aucune fonction `kesh-db` a mirer : il est ecrit en SQL brut a **cinq** sites de `routes/reconciliation.rs`, dont **un seul** porte le verrou optimiste. Quand la transaction porte une facture, appelle le geste de la 25-3-a. Derriere elle. 25-3-annuler-reglement-et-rapprochement: split # [#414] [#418] — **l'Epic 24 a livre l'encaissement et le rapprochement ; il n'a PAS livre le moyen de les DEFAIRE.** Une erreur d'imputation est aujourd'hui incorrigible autrement qu'a la main — et le gel de la 24-4b interdit precisement la main. ⚠️ *C'est le prix d'une story qui ferme une porte sans ouvrir la suivante : le gel est juste, et il rend cette story-ci necessaire.* 25-4-a-residuel-juste: ready-for-dev # 2026-09-26 [#455] [#456] — l'avoir soustrait HT d'un TTC (masque a l'ecran, mais rendu par GET /invoices/{id} aux cles API : amountDue = la TVA sur une facture creditee) ; un avoir accepte sur une facture partiellement reglee rend la creance creditrice. CREEE ; validation P1 (Sonnet) 1 HIGH / 3 MED / 1 LOW, corriges. - 25-4-b-residuel-aux-agregats: backlog # [#416] + montant des RELANCES (TTC complet, trouve a l'inventaire) — Q1 TRANCHEE 2026-09-26 : QR sans montant si partiellement reglee, et le reste a payer dit au client. + 25-4-b-residuel-aux-agregats: backlog # [#416] + montant des RELANCES (TTC complet, trouve a l'inventaire) — Q1 TRANCHEE 2026-09-27 : la QR du rappel porte le RESTE DU (= TTC sans reglement), reste a payer sur le PDF et dans le texte ; point ouvert : frais de rappel non comptabilises vs trop-percu. 25-4-c-residuel-au-rapprochement: backlog # [#420] — DEUX sites : filtre SQL des candidats (HAVING total_ttc) avant le score, puis score et re-score a l'acceptation. 25-4-propager-le-residuel: split # 2026-09-26 DECOUPEE (six modules > 5) en 25-4-a / 25-4-b / 25-4-c ; #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). From 5c337d679a612c94379e93ffe72687f0204dff34 Mon Sep 17 00:00:00 2001 From: Guy Corbaz Date: Sun, 27 Sep 2026 00:09:55 +0200 Subject: [PATCH 09/18] =?UTF-8?q?docs(25-4):=20frais=20de=20rappel=20confi?= =?UTF-8?q?gurables,=20rien=20d'affich=C3=A9=20=C3=A0=20z=C3=A9ro=20(refs?= =?UTF-8?q?=20#416)?= 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-4-propager-le-residuel.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/_bmad-output/implementation-artifacts/25-4-propager-le-residuel.md b/_bmad-output/implementation-artifacts/25-4-propager-le-residuel.md index 3f404e51..471fbefd 100644 --- a/_bmad-output/implementation-artifacts/25-4-propager-le-residuel.md +++ b/_bmad-output/implementation-artifacts/25-4-propager-le-residuel.md @@ -105,6 +105,10 @@ Pour 25-4-b, donc : `invoice_pdf_service.rs:218-250`) et **doit le garder à l'identique** — c'est ce que lit le rapprochement ; le PDF du rappel le nomme en toutes lettres, avec montant initial, déjà réglé, reste à payer. +- ✅ **Les frais restent configurables, et à zéro rien ne s'affiche** (Guy, 2026-09-27) : *« ils + doivent être configurables : si 0, ne rien afficher. »* Les frais sont déjà réglés par niveau de + rappel (`dunning_levels.fee_amount`) ; le PDF et le texte du rappel n'ont **aucune ligne de + frais** quand le montant est nul — ni « frais : 0.00 », ni total qui les mentionne. - ⚠️ **Point ouvert pour la spécification de 25-4-b — les frais de rappel.** Le texte réclame aujourd'hui `TTC + frais` (`invoice_email.rs:334`), mais les frais ne semblent **pas comptabilisés** (aucune écriture dans `invoice_reminders.rs`, à vérifier). Si la QR portait @@ -116,6 +120,7 @@ Pour 25-4-b, donc : ## Change Log +- **2026-09-27** — Frais de rappel : configurables, aucune ligne affichée à zéro. - **2026-09-27** — Q1 retranchée : la QR du rappel porte le reste dû (le TTC s'il n'y a aucun règlement) ; numéro de facture conservé ; point ouvert sur les frais. - **2026-09-27** — Recadrage de Q1 : le document est un rappel pour un montant partiel, pas la facture réémise. - **2026-09-26** — Complément de Q1 : le reste à payer figure aussi sur le PDF joint. From 451e2cc2b88a3a88ed6fd12076e64f8122c74941 Mon Sep 17 00:00:00 2001 From: Guy Corbaz Date: Sun, 27 Sep 2026 00:10:33 +0200 Subject: [PATCH 10/18] =?UTF-8?q?docs(25-4-a):=20validation=20P2=20?= =?UTF-8?q?=E2=80=94=20facture=20auxiliaire=20du=20gabarit,=20amountDue=20?= =?UTF-8?q?compar=C3=A9=20en=20Decimal,=20bouton=20sur=20facture=20pay?= =?UTF-8?q?=C3=A9e=20(refs=20#455,=20refs=20#456)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- .../25-4-a-residuel-juste.md | 35 ++++++++++++++++--- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/_bmad-output/implementation-artifacts/25-4-a-residuel-juste.md b/_bmad-output/implementation-artifacts/25-4-a-residuel-juste.md index 3c1658d5..bfa466fa 100644 --- a/_bmad-output/implementation-artifacts/25-4-a-residuel-juste.md +++ b/_bmad-output/implementation-artifacts/25-4-a-residuel-juste.md @@ -122,8 +122,12 @@ créance par l'écriture de l'avoir** (`credit_notes.journal_entry_id` → ligne seul HT ou à une valeur recalculée en Rust **ne suffit pas**. **AC 8** — Une facture validée à 8,1 %, **non réglée, créditée** : `amount_due` = **0** (et non la -TVA) — **au repository ET à la frontière HTTP** : `GET /api/v1/invoices/{id}` rend `amountDue` = -`"0.00"` (ou sa forme décimale normalisée), `amountSettled` = `0`. Une facture validée à 8,1 %, **réglée de 40 puis créditée** (état **monté en SQL**, cf. +TVA) — **au repository ET à la frontière HTTP** : `GET /api/v1/invoices/{id}` rend `amountDue` **non nul +(pas `null` : `null` veut dire « non calculé »,** `routes/invoices.rs:242-248`**)**, sérialisé en +**chaîne** (`rust_decimal` avec `serde-str`, `kesh-api/Cargo.toml:39`), dont la valeur **parsée en +`Decimal`** vaut zéro — ⛔ ni comparaison de chaîne (l'échelle suit le calcul SQL : `"0.0000"` et +`"0.00"` sont également justes), ni `f64` (patron à ne pas suivre : +`invoice_echeancier_e2e.rs:636-641`). Même règle pour `amountSettled`. Une facture validée à 8,1 %, **réglée de 40 puis créditée** (état **monté en SQL**, cf. AC 13) : `amount_due` = **−40**, le montant encaissé en trop sur une vente annulée — et non `TVA − 40`. ⚠️ **Pas d'écrêtage à zéro** : le doc-comment d'`amount_due` l'interdit déjà. @@ -173,8 +177,14 @@ donc une écriture de contre-passation valide. **Gabarit recommandé**, qui gard écritures produites par les vrais chemins** et ne touche en SQL qu'un rattachement : régler (vrai chemin) ; **détacher** la ligne de règlement vers une facture auxiliaire validée de la même société (`UPDATE invoice_settlements SET invoice_id = ?`) ; créer l'avoir (vrai chemin, désormais accepté) ; -**rattacher** la ligne à la facture d'origine. Vérifier les contraintes d'unicité de -`invoice_settlements` avant de retenir ce gabarit ; à défaut, construire l'écriture d'avoir par +**rattacher** la ligne à la facture d'origine. La facture auxiliaire est créée **dans `monter`** +par le helper existant `validated_invoice` (même société, montant quelconque), n'est jamais réglée +ni créditée, et ne sert qu'à porter la ligne le temps de l'avoir ; aucun motif ne la lit. Seule +contrainte d'unicité d'`invoice_settlements` : `uq_invoice_settlements_entry (journal_entry_id)` +(`20260827000001_invoice_settlements.sql:65`) — le détachement ne la touche pas. ⚠️ Pendant le +détachement, l'écriture de règlement reste celle de la facture d'origine : aucun motif de +`settlement_cancel_blocker` ne doit être évalué entre les deux `UPDATE`. Si le gabarit se révèle +impraticable, à défaut, construire l'écriture d'avoir par `journal_entries::create_in_tx` avec les lignes de `generate_credit_note_journal_lines`, jamais à la main ligne par ligne. @@ -188,6 +198,11 @@ couvre entière se désynchronise à la première évolution du serveur. La gard la même façon. Le commentaire de `:864-871` est étendu aux deux boutons, ou dupliqué au-dessus du second. +⚠️ **Changement visible sur une facture entièrement payée** : le bouton, masqué jusqu'ici, apparaît ; +l'utilisateur ouvre le dialogue, confirme, et lit le refus nommé (AC 12) dans le dialogue même +(`creditNoteError`, `:77`, `:101`). C'est le comportement de « Dévalider » depuis le 2026-09-19, et +le CHANGELOG le dit. + **AC 15** — Rien n'est **réparé** dans les données existantes : pas de migration, pas de rejeu (`CLAUDE.md` : aucune donnée de production à protéger ; précédent 24-2 D7). Les états hérités restent **visibles** — reste dû négatif sur la fiche, motif `InvoiceCredited` à l'annulation du @@ -332,6 +347,18 @@ bloque **25-4-b**, pas celle-ci. ## Change Log +- **2026-09-27** — **validation P2** (Haiku, diff aplati, prompt `25-4-a-validate-prompt-p2.md`) — + rapporte 3 MEDIUM et 1 LOW ; **un seul MEDIUM retenu** : l'AC 13 ne disait pas d'où vient la facture + auxiliaire du gabarit — précisé (helper `validated_invoice`, contrainte d'unicité vérifiée, aucun + motif évalué pendant le détachement). **Écartés** : deux « MEDIUM » reprochent au **code** de ne pas + encore faire ce que la fiche prescrit (bouton `:836`, test e2e absent) — ce n'est pas un défaut de + spec ; le LOW (deux formulations du manuel) est déjà couvert par l'AC 17. ⚠️ **Repris par + l'orchestrateur**, la priorité 3 que la passe n'a pas tranchée : `amountDue` est une **chaîne** à + l'échelle du calcul SQL — l'AC 8 impose désormais `null` interdit et une comparaison **en + `Decimal`**, ni chaîne ni `f64` ; et la priorité 2 : ce que voit l'utilisateur sur une facture + entièrement payée (bouton désormais visible, refus nommé dans le dialogue) est écrit à l'AC 14. + La passe affirmait aussi que `accept_one_invoice` ne crée pas de règlement — faux + (`invoice_settlements::create_in_tx`, `reconciliation.rs:1416-1428`) ; sans conséquence sur la fiche. - **2026-09-26** — **validation P1** (Sonnet, prompt `25-4-a-validate-prompt-p1.md`) — **1 HIGH, 3 MEDIUM, 1 LOW**, tous confirmés dans le code avant correction. **HIGH** : le récit « #455 latent » était faux — `get_invoice` rend `amountDue` sans condition de statut, et From 1344b3a2945e183eb1e94123ae507c3c2bddb09b Mon Sep 17 00:00:00 2001 From: Guy Corbaz Date: Sun, 27 Sep 2026 00:10:59 +0200 Subject: [PATCH 11/18] =?UTF-8?q?docs(25-4-a):=20prompt=20de=20validation?= =?UTF-8?q?=20P3=20cibl=C3=A9e=20(refs=20#455,=20refs=20#456)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- .../25-4-a-validate-prompt-p3.md | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 _bmad-output/implementation-artifacts/25-4-a-validate-prompt-p3.md diff --git a/_bmad-output/implementation-artifacts/25-4-a-validate-prompt-p3.md b/_bmad-output/implementation-artifacts/25-4-a-validate-prompt-p3.md new file mode 100644 index 00000000..d26bf42e --- /dev/null +++ b/_bmad-output/implementation-artifacts/25-4-a-validate-prompt-p3.md @@ -0,0 +1,49 @@ +# Prompt — validation P3 CIBLÉE, Story 25-4-a (le résiduel juste) + +*Versionné le 2026-09-27. **Une lentille** (Opus), contexte frais. Passe **ciblée** (CLAUDE.md +§ « La passe ciblée ») : la P2 n'a trouvé qu'un défaut **né de la correction P1** ; ce qu'il reste à +relire est la remédiation, pas la story entière.* + +Dépôt `/home/gcorbaz/devel/kesh`, branche `story/25-4-propager-le-residuel`. Fiche : +`_bmad-output/implementation-artifacts/25-4-a-residuel-juste.md`. **Périmètre** : le diff aplati +des remédiations P1 et P2 — +`git diff 0f4b9ec0 451e2cc2 -- _bmad-output/implementation-artifacts/25-4-a-residuel-juste.md _bmad-output/implementation-artifacts/25-4-propager-le-residuel.md` +(ignore les paragraphes de la fiche mère qui consignent les arbitrages de Guy sur 25-4-b : **ne pas +les contester**). + +## La lentille : chasseur de régressions de remédiation + +Pour chaque passage réécrit en P1 ou P2 : + +1. **Est-il vrai contre le code ?** Relis les sites qu'il cite : `get_invoice` + (`crates/kesh-api/src/routes/invoices.rs`), `docs/api-external.md`, le bouton et le dialogue + d'avoir (`frontend/src/routes/(app)/invoices/[id]/+page.svelte`), la fixture `monter` et + `la_precedence_de_l_annulation_lecture_et_ecriture` (`crates/kesh-db/tests/invoice_settlement.rs`), + la migration `20260827000001_invoice_settlements.sql`, `settlement_cancel_blocker` + (`invoice_settlements_write.rs`), `kesh-api/Cargo.toml` (`serde-str`). +2. **Le gabarit « détacher, créditer, rattacher » (AC 13)** : exécute-le **mentalement** pas à pas + contre `create_credit_note` et les quatre paires combinées de `monter`. Une FK, une contrainte, + un audit, un contrôle de `company_id`, une lecture de `invoice_settlements` par + `create_credit_note` ou par les motifs montés **après** peut-il le casser ? L'écriture de + règlement porte-t-elle quelque part l'identifiant de la facture d'origine (description, audit, + `invoice_settlements` seul) ? +3. **AC 14 révisé** : cohérent avec l'AC 11, l'AC 12, les tests de l'AC 16 et la mutation m6/m7 ? + Un test de l'AC 16 porte-t-il encore l'ancienne logique (« bouton absent ») ? +4. **AC 8 révisé** : l'assertion prescrite peut-elle passer à vide (`null`, champ absent) ? +5. **Propagation** : chaque correction a-t-elle été reportée partout où le symptôme était écrit + (tâches T1-T9, tableau « Où regarder », Change Log, fiche mère, `sprint-status.yaml`) ? Greppe la + **valeur** (`code()`, `:98`, `937-990`, `latent`, `masqué`), pas la formulation. + +## Ce que tu rends + +- **Findings** : sévérité, endroit exact, **preuve** (commande et résultat, code lu), correction. +- ⛔ **La liste des axes réellement exercés ET de ceux qui ne l'ont pas été.** + +## Interdits + +⛔ N'écris aucun fichier du dépôt ; aucune commande qui écrit dans le dépôt ou dans une base — +`scripts/prepare-release.sh`, `scripts/regen-test-schema.sh`, `scripts/install-hooks.sh`, +`scripts/test-fast.sh`, `scripts/mem-guard.sh`, `make`, `latexmk`, tout `git commit`/`push`/`add`/ +`stash`/`reset`/`rebase`/`checkout`/`switch`/`worktree`, `sqlx migrate`, `cargo test`/`nextest`, +`npm run`, `npx playwright`. Autorisés : lecture, `grep`, `git log`/`show`/`diff`, `gh issue view`, +`cargo check`. From 9df357e7c22e07f47478a7d950818e19cadd72b5 Mon Sep 17 00:00:00 2001 From: Guy Corbaz Date: Sun, 27 Sep 2026 00:15:42 +0200 Subject: [PATCH 12/18] =?UTF-8?q?docs(25-4-a):=20validation=20P3=20cibl?= =?UTF-8?q?=C3=A9e=20=E2=80=94=20propagation=20des=20corrections=20P1=20(r?= =?UTF-8?q?efs=20#455,=20refs=20#456)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- .../25-4-a-residuel-juste.md | 44 ++++++++++++++----- .../25-4-propager-le-residuel.md | 12 ++--- .../sprint-status.yaml | 2 +- 3 files changed, 40 insertions(+), 18 deletions(-) diff --git a/_bmad-output/implementation-artifacts/25-4-a-residuel-juste.md b/_bmad-output/implementation-artifacts/25-4-a-residuel-juste.md index bfa466fa..8e1d17a4 100644 --- a/_bmad-output/implementation-artifacts/25-4-a-residuel-juste.md +++ b/_bmad-output/implementation-artifacts/25-4-a-residuel-juste.md @@ -122,9 +122,10 @@ créance par l'écriture de l'avoir** (`credit_notes.journal_entry_id` → ligne seul HT ou à une valeur recalculée en Rust **ne suffit pas**. **AC 8** — Une facture validée à 8,1 %, **non réglée, créditée** : `amount_due` = **0** (et non la -TVA) — **au repository ET à la frontière HTTP** : `GET /api/v1/invoices/{id}` rend `amountDue` **non nul -(pas `null` : `null` veut dire « non calculé »,** `routes/invoices.rs:242-248`**)**, sérialisé en -**chaîne** (`rust_decimal` avec `serde-str`, `kesh-api/Cargo.toml:39`), dont la valeur **parsée en +TVA) — **au repository ET à la frontière HTTP** : `GET /api/v1/invoices/{id}` rend `amountDue` **présent et non `null`** +(`null` veut dire « non calculé », `routes/invoices.rs:242-248` ; ⛔ aucun `unwrap_or`, aucune +valeur par défaut dans l'assertion), sérialisé en +**chaîne** (sérialisation par défaut de `rust_decimal`, `kesh-api/Cargo.toml:39`), dont la valeur **parsée en `Decimal`** vaut zéro — ⛔ ni comparaison de chaîne (l'échelle suit le calcul SQL : `"0.0000"` et `"0.00"` sont également justes), ni `f64` (patron à ne pas suivre : `invoice_echeancier_e2e.rs:636-641`). Même règle pour `amountSettled`. Une facture validée à 8,1 %, **réglée de 40 puis créditée** (état **monté en SQL**, cf. @@ -142,7 +143,7 @@ transaction ; elle est **verrouillante** (`… FOR UPDATE` ou `LOCK IN SHARE MOD **avant** le verrou. **AC 10** — Le refus est une **variante dédiée** de `DbError`, pas `IllegalStateTransition` — même -motif que `InvoiceNotUnvalidatable` (`errors.rs:119-130`) : le générique ne dit ni ce qui bloque ni +motif que `InvoiceNotUnvalidatable` (`errors.rs:453`, raison écrite `:119-130`) : le générique ne dit ni ce qui bloque ni quoi faire. Elle porte l'identifiant du premier règlement trouvé (`Option`, `None` si seul `paid_at` est posé) et le numéro de la facture. Code machine **`CREDIT_NOTE_INVOICE_SETTLED`**, HTTP **409**, ajouté à `DbError::error_code()` (`errors.rs:641`) et à toute garde qui énumère les @@ -162,7 +163,10 @@ pouvoir émettre un avoir. » Registre du glossaire (`docs/i18n-glossaire.md` § `InvoiceCredited` **par le chemin que 25-4-a ferme**, avec un commentaire qui dit que l'état n'est **plus atteignable par l'application** depuis 25-4-a, mais **reste possible** par l'import d'une sauvegarde antérieure — ce qui justifie de garder `SettlementCancelBlocker::InvoiceCredited`. -⛔ Ne **pas** supprimer ce motif ni ses tests. +⛔ Ne **pas** supprimer ce motif ni ses tests. Ses doc-comments, qui le présentent comme un chemin +normal (« créditée par un avoir après ce règlement … pas une anomalie », `errors.rs:214-217` ; +« Hors `validated`, c'est donc un avoir », `invoice_settlements_write.rs:326-329`), disent +désormais que l'état n'est plus atteignable **que par l'import d'une sauvegarde antérieure**. ⚠️ **`monter` sert quinze cas, pas un** : `la_precedence_de_l_annulation_lecture_et_ecriture` (`:1056-1064`) l'appelle avec les cinq motifs seuls **et les dix paires** (`RANGS`, @@ -184,9 +188,11 @@ contrainte d'unicité d'`invoice_settlements` : `uq_invoice_settlements_entry (j (`20260827000001_invoice_settlements.sql:65`) — le détachement ne la touche pas. ⚠️ Pendant le détachement, l'écriture de règlement reste celle de la facture d'origine : aucun motif de `settlement_cancel_blocker` ne doit être évalué entre les deux `UPDATE`. Si le gabarit se révèle -impraticable, à défaut, construire l'écriture d'avoir par -`journal_entries::create_in_tx` avec les lignes de `generate_credit_note_journal_lines`, jamais à la -main ligne par ligne. +impraticable, **arrêter et demander** : le repli naturel — construire l'écriture d'avoir avec les +lignes de `generate_credit_note_journal_lines` — est **impossible depuis un test**, la fonction +étant privée (`credit_notes.rs:187`), et la rendre publique pour un test n'est pas un choix du +développeur. La facture auxiliaire est datée **dans l'exercice seedé** (`d − 20` suffit) : c'est ce +qui garde valide le cas « aucun exercice ouvert ». **AC 14** — Frontend : le bouton « Créer un avoir » (`invoices/[id]/+page.svelte:836`) **suit le précédent du bouton « Dévalider »** (`:864-871`) : la condition `!invoice.paidAt` est **retirée**, le @@ -196,7 +202,9 @@ une condition client qui en couvre une moitié laisse l'utilisateur sans rien à couvre entière se désynchronise à la première évolution du serveur. La garde étant désormais **unique** côté serveur (AC 11), le motif « payée » et le motif « réglée en partie » s'affichent de la même façon. Le commentaire de `:864-871` est étendu aux deux boutons, ou dupliqué au-dessus du -second. +second. ⚠️ Le commentaire du bloc Suspendre/Reprendre (`:842-844`) affirme que ces boutons sont +« masqués, comme les boutons voisins « Créer un avoir » / « Supprimer » » : il devient faux et se +corrige dans le même patch. ⚠️ **Changement visible sur une facture entièrement payée** : le bouton, masqué jusqu'ici, apparaît ; l'utilisateur ouvre le dialogue, confirme, et lit le refus nommé (AC 12) dans le dialogue même @@ -255,14 +263,17 @@ restreint aux lignes qui parlent d'avoir, plus le PDF aplati. Les manuels DE/IT/ - [ ] **T1 — formule de l'avoir TTC** (AC 1-5) : factoriser la formule de ligne, réécrire les deux constantes d'avoir, doc-comments. - [ ] **T2 — parité et concordance** (AC 6-8) : nouveau fichier de test sur le patron - `invoice_ttc_parity.rs` ; montage de l'état hérité en SQL. + `invoice_ttc_parity.rs`. ⚠️ Le helper `validated_invoice` (`invoice_settlement.rs:32`) est + **à 0 % en dur** et passe une écriture de vente **au HT** : les factures à 8,1 % se montent + par le **vrai chemin de validation**, sur le patron de `create_and_validate` + (`credit_notes_repository.rs`) ; l'état hérité, par le gabarit de l'AC 13. - [ ] **T3 — garde de l'avoir** (AC 9-11) : lecture verrouillante des règlements après le verrou, variante `DbError`, `error_code()`. - [ ] **T4 — API et i18n** (AC 12) : mapping, clé ×4 ; e2e 409. - [ ] **T5 — fixture `monter`** (AC 13) : gabarit « détacher, créditer, rattacher », commentaire ; **les quinze cas** de la précédence restent verts, dont les quatre paires avec `InvoiceCredited`. - [ ] **T6 — frontend** (AC 14) : condition du bouton retirée, commentaire, Vitest. -- [ ] **T7 — tests et mutations** (AC 16) : m1 à m6 exécutées, résultats consignés. +- [ ] **T7 — tests et mutations** (AC 16) : m1 à **m7** exécutées, résultats consignés. - [ ] **T8 — textes** (AC 17) : manuel, PDF aplati, CHANGELOG. - [ ] **T9 — gates** : backend complet (base remise à zéro), frontend complet, E2E complet. @@ -347,6 +358,17 @@ bloque **25-4-b**, pas celle-ci. ## Change Log +- **2026-09-27** — **validation P3 ciblée** (Opus, diff aplati des remédiations P1-P2, prompt + `25-4-a-validate-prompt-p3.md`) — **2 MEDIUM, 5 LOW**, tous de **propagation** des corrections P1, + tous confirmés par grep. MEDIUM : T7 disait encore « m1 à m6 » (m7 ajoutée) ; l'AC 14 rendait faux + le commentaire Suspendre/Reprendre (`:842-844`) et l'AC 13 laissait deux doc-comments décrire + l'avoir après règlement comme un chemin normal. LOW : « non nul » pour « non `null` » (et + `unwrap_or` interdit) ; repli de l'AC 13 impossible (fonction privée) — remplacé par « arrêter et + demander », date de la facture auxiliaire précisée ; cause de la sérialisation en chaîne ; + fiche mère et `sprint-status` en retard d'une passe ; T2 ne disait pas comment monter une facture + à 8,1 %. Le gabarit « détacher, créditer, rattacher » a été **exécuté mentalement** contre + `create_credit_note`, le rang 1 et les quatre paires : il tient. Référence de l'AC 10 corrigée + (`errors.rs:453`). Aucun code de production touché par ces corrections. - **2026-09-27** — **validation P2** (Haiku, diff aplati, prompt `25-4-a-validate-prompt-p2.md`) — rapporte 3 MEDIUM et 1 LOW ; **un seul MEDIUM retenu** : l'AC 13 ne disait pas d'où vient la facture auxiliaire du gabarit — précisé (helper `validated_invoice`, contrainte d'unicité vérifiée, aucun diff --git a/_bmad-output/implementation-artifacts/25-4-propager-le-residuel.md b/_bmad-output/implementation-artifacts/25-4-propager-le-residuel.md index 471fbefd..b836637b 100644 --- a/_bmad-output/implementation-artifacts/25-4-propager-le-residuel.md +++ b/_bmad-output/implementation-artifacts/25-4-propager-le-residuel.md @@ -40,12 +40,12 @@ Le découpage suit l'ordre des dépendances, non celui des issues : (`invoice_email.rs:1140-1166`, `invoice_pdf_service::render`). Un client qui paie la relance en scannant la QR paie **à nouveau le montant entier**. Question posée à Guy (Q1 ci-dessous). 4. ⚠️ **#455 est masqué à l'écran — mais pas à l'API.** *(Corrigé en validation P1 de 25-4-a : la - première rédaction disait « latent ». `GET /invoices/{id}`, ouverte aux clés API, rend - `amountDue` sans condition de statut : sur une facture créditée à TVA non nulle, il vaut la TVA.)* Un avoir est **total** et fait passer la facture à - `cancelled` (`credit_notes.rs:560-563`) ; or les agrégats, le rapprochement et le règlement ne - lisent que des factures `validated`. Le terme « avoir » du résiduel n'est donc **non nul que sur - une facture annulée**, et le reste dû ne s'y affiche que si `amountSettled > 0` — c'est-à-dire - **seulement dans l'état que #456 permet de produire**. Les deux issues se corrigent ensemble. + première rédaction disait « latent ».)* L'avoir est **total** et fait passer la facture à + `cancelled` (`credit_notes.rs:560-575`) ; le terme « avoir » n'est donc non nul que sur une + facture annulée. L'**écran** n'y affiche le reste dû que si `amountSettled > 0` — l'état que #456 + permet de produire. Mais `GET /invoices/{id}`, ouverte aux clés API, rend `amountDue` **sans + condition de statut** : sur toute facture créditée à TVA non nulle, il vaut la TVA. Les deux + issues se corrigent ensemble parce qu'elles portent sur la même grandeur et le même état. 5. ⚠️ **Les formes jointes existent et ne servent nulle part.** `INVOICE_SETTLED_DERIVED_JOIN_SQL` et `INVOICE_CREDITED_DERIVED_JOIN_SQL` (`invoice_settlements.rs:40`, `:56`) n'ont aucun appelant, et le « test de parité » annoncé par le commentaire des lignes 27-29 **n'existe pas**. 25-4-b sera diff --git a/_bmad-output/implementation-artifacts/sprint-status.yaml b/_bmad-output/implementation-artifacts/sprint-status.yaml index cd62a835..04f53ab6 100644 --- a/_bmad-output/implementation-artifacts/sprint-status.yaml +++ b/_bmad-output/implementation-artifacts/sprint-status.yaml @@ -353,7 +353,7 @@ development_status: 25-3-c-annuler-facture-fournisseur: done # 2026-09-26 **REVUE CLOSE EN 1 PASSE** (0 > LOW, Sonnet x3) ; non poussee ; PR `closes #454` titre ET corps, sur accord. **IMPLEMENTEE** — gates 2472/2472, 783/783, E2E 220/10/19 sans regression ; prochaine etape : code review. # 2026-09-26 **SPEC VALIDEE en 2 passes** (3 MED -> 0 > LOW). **SPECIFIEE** — `25-3-c-annuler-facture-fournisseur.md`. Geste par le socle (autorite `SupplierPurchase`), queue commune sur l'ecriture d'ACHAT, lot en dernier rang ; payee : reglement DETACHE (colonnes remises a NULL, ecriture libre, lien garde par l'audit) — **Q1 tranchee par Guy le 2026-09-26**. Aucune migration. Prochaine etape : validate. # 2026-09-24 NEE d'un arbitrage de Guy — une facture fournisseur s'annule DANS TOUS LES CAS, sauf si l'exercice de son ecriture d'achat est CLOS (l'exercice, pas le verrou de periode) ; payee, son reglement RESTE au grand livre et REDEVIENT A LETTRER (Epic 15 : un paiement doit correspondre a une facture, au besoin creee pour lui — « comme bexio »). Reecrit `cancel`, donc absorbe [#454]. Derriere la 25-3-a. 25-3-b-annuler-rapprochement: done # 2026-09-25 DONE — revue close en passe 2 (Sonnet 1 MED -> Haiku 0) ; gates : backend 2463/2463, frontend 777/777, E2E sans regression. Non poussee. — SPEC VALIDEE (boucle close en passe 4 : 2 HIGH -> 2 MED -> 2 MED -> 3 LOW ; pas de decoupage, arbitrage de Guy). Q1-Q3 tranchees. — [#418] closes — annuler un rapprochement bancaire. ⛔ NE JAMAIS compter sur le `ON DELETE SET NULL` de `matched_entry_id` : supprimer l'ecriture ferait disparaitre le lien EN SILENCE, le mode d'echec meme que la story repare. ⚠️ Le chemin d'ACCEPTATION n'a aucune fonction `kesh-db` a mirer : il est ecrit en SQL brut a **cinq** sites de `routes/reconciliation.rs`, dont **un seul** porte le verrou optimiste. Quand la transaction porte une facture, appelle le geste de la 25-3-a. Derriere elle. 25-3-annuler-reglement-et-rapprochement: split # [#414] [#418] — **l'Epic 24 a livre l'encaissement et le rapprochement ; il n'a PAS livre le moyen de les DEFAIRE.** Une erreur d'imputation est aujourd'hui incorrigible autrement qu'a la main — et le gel de la 24-4b interdit precisement la main. ⚠️ *C'est le prix d'une story qui ferme une porte sans ouvrir la suivante : le gel est juste, et il rend cette story-ci necessaire.* - 25-4-a-residuel-juste: ready-for-dev # 2026-09-26 [#455] [#456] — l'avoir soustrait HT d'un TTC (masque a l'ecran, mais rendu par GET /invoices/{id} aux cles API : amountDue = la TVA sur une facture creditee) ; un avoir accepte sur une facture partiellement reglee rend la creance creditrice. CREEE ; validation P1 (Sonnet) 1 HIGH / 3 MED / 1 LOW, corriges. + 25-4-a-residuel-juste: ready-for-dev # 2026-09-26 [#455] [#456] — l'avoir soustrait HT d'un TTC (masque a l'ecran, mais rendu par GET /invoices/{id} aux cles API : amountDue = la TVA sur une facture creditee) ; un avoir accepte sur une facture partiellement reglee rend la creance creditrice. CREEE ; validation : P1 Sonnet 1H/3M/1L -> P2 Haiku 1M retenu -> P3 ciblee Opus 2M/5L (propagation), tous corriges. EN ATTENTE de l'arbitrage de Guy sur l'avoir (volet 2). 25-4-b-residuel-aux-agregats: backlog # [#416] + montant des RELANCES (TTC complet, trouve a l'inventaire) — Q1 TRANCHEE 2026-09-27 : la QR du rappel porte le RESTE DU (= TTC sans reglement), reste a payer sur le PDF et dans le texte ; point ouvert : frais de rappel non comptabilises vs trop-percu. 25-4-c-residuel-au-rapprochement: backlog # [#420] — DEUX sites : filtre SQL des candidats (HAVING total_ttc) avant le score, puis score et re-score a l'acceptation. 25-4-propager-le-residuel: split # 2026-09-26 DECOUPEE (six modules > 5) en 25-4-a / 25-4-b / 25-4-c ; #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**. From b18f7cd2ad1d5958e97005dd6a2893b963dadf4f Mon Sep 17 00:00:00 2001 From: Guy Corbaz Date: Sun, 27 Sep 2026 00:18:50 +0200 Subject: [PATCH 13/18] =?UTF-8?q?docs(25-4):=20avoir=20sur=20facture=20r?= =?UTF-8?q?=C3=A9gl=C3=A9e=20refus=C3=A9=20en=2025-4-a=20(lev=C3=A9e=20:?= =?UTF-8?q?=20#471)=20;=20#384=20ramen=C3=A9e=20en=2025-4-d=20(refs=20#384?= =?UTF-8?q?,=20refs=20#456)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- .../25-4-a-residuel-juste.md | 13 ++++++++++++ .../25-4-propager-le-residuel.md | 21 +++++++++++++++---- .../sprint-status.yaml | 6 ++++-- .../epic-25-vague1-suite.md | 8 +++++-- 4 files changed, 40 insertions(+), 8 deletions(-) diff --git a/_bmad-output/implementation-artifacts/25-4-a-residuel-juste.md b/_bmad-output/implementation-artifacts/25-4-a-residuel-juste.md index 8e1d17a4..ff0af122 100644 --- a/_bmad-output/implementation-artifacts/25-4-a-residuel-juste.md +++ b/_bmad-output/implementation-artifacts/25-4-a-residuel-juste.md @@ -80,6 +80,17 @@ réglée** : `invoices/[id]/+page.svelte:836` n'exige que `!invoice.paidAt`. Le « Dévalider » a **retiré** cette condition pour cette raison même (commentaire `:864-871`) et laisse le serveur refuser avec un motif nommé ; « Créer un avoir » n'a pas été aligné. +## ✅ Arbitrage du 2026-09-27 — le refus reste, sa levée a son issue + +Guy : *« si on peut créer un avoir pour une facture payée, il faudrait aussi pouvoir le faire pour +une facture partiellement payée »* — les deux cas sont traités **de la même façon**, et, pour +l'instant, **dans le sens du refus** : un avoir sur facture encaissée laisse au client un crédit que +Kesh ne sait ni montrer, ni rembourser, ni imputer. La levée du refus est tracée par **[#471]**. Le +message de refus (AC 12) n'a donc pas à promettre une fonction à venir ; le manuel (AC 17) renvoie à +la limite existante « avoir sur facture déjà encaissée ». + +⚠️ **Ne pas contester l'arbitrage en revue** : en contester la mise en œuvre. + ## Acceptance Criteria ### Volet 1 — l'avoir compté TTC (#455) @@ -358,6 +369,7 @@ bloque **25-4-b**, pas celle-ci. ## Change Log +- **2026-09-27** — Arbitrage de Guy : le refus de l'avoir sur facture réglée (en tout ou en partie) reste ; sa levée est tracée par #471. - **2026-09-27** — **validation P3 ciblée** (Opus, diff aplati des remédiations P1-P2, prompt `25-4-a-validate-prompt-p3.md`) — **2 MEDIUM, 5 LOW**, tous de **propagation** des corrections P1, tous confirmés par grep. MEDIUM : T7 disait encore « m1 à m6 » (m7 ajoutée) ; l'AC 14 rendait faux @@ -398,5 +410,6 @@ bloque **25-4-b**, pas celle-ci. ne se voit que dans l'état que #456 produit — d'où leur réunion ; et la fixture `monter` encode #456 comme attendu. +[#471]: https://github.com/guycorbaz/kesh/issues/471 [#455]: https://github.com/guycorbaz/kesh/issues/455 [#456]: https://github.com/guycorbaz/kesh/issues/456 diff --git a/_bmad-output/implementation-artifacts/25-4-propager-le-residuel.md b/_bmad-output/implementation-artifacts/25-4-propager-le-residuel.md index b836637b..65f7e7dc 100644 --- a/_bmad-output/implementation-artifacts/25-4-propager-le-residuel.md +++ b/_bmad-output/implementation-artifacts/25-4-propager-le-residuel.md @@ -1,4 +1,4 @@ -# Story 25.4 : Propager le résiduel — découpée en 25-4-a / 25-4-b / 25-4-c +# Story 25.4 : Propager le résiduel — découpée en 25-4-a / 25-4-b / 25-4-c / 25-4-d Status: split @@ -15,13 +15,14 @@ Inventaire des sites fait le 2026-09-26 (trois lectures du code, chaque site rev `kesh-i18n`, `frontend` (fiche facture, échéancier, dialogue de règlement). Le seuil est **5** : la règle impose le découpage **avant** `bmad-create-story`. -Le découpage suit l'ordre des dépendances, non celui des issues : +Le découpage (quatre stories depuis le 2026-09-27) suit l'ordre des dépendances, non celui des issues : | Story | Issues | Ce qu'elle fait | Pourquoi à ce rang | |---|---|---|---| | **25-4-a** — le résiduel juste | [#455], [#456] | la formule canonique soustrait l'avoir **TTC** ; un avoir est refusé sur une facture **réglée, même en partie** ; le test de parité promis et jamais écrit | ⛔ **les deux suivantes réemploient la formule** : la propager avant de la corriger propagerait le défaut | | **25-4-b** — le résiduel aux agrégats | [#416] | balance âgée, totaux de l'échéancier, **colonne « reste dû »** de l'échéancier et de son CSV, dialogue de règlement pré-rempli, **montant réclamé par les relances** | le cœur de #416 ; première utilisatrice des formes jointes | | **25-4-c** — le résiduel au rapprochement | [#420] | filtre des candidats, score, re-score à l'acceptation, montant affiché dans la proposition | indépendante de 25-4-b, mais du même socle | +| **25-4-d** — solder le reste | [#384] | imputer l'écart d'un règlement partiel — **perte sur débiteur**, escompte, frais bancaires — pour clore une facture partiellement réglée ; la part de **TVA** réduit la TVA due, seule la part HT va au compte de perte | ramenée de la 25-6 le 2026-09-27 (Guy) : elle repose entièrement sur le résiduel juste de 25-4-a | ## Ce que l'inventaire a trouvé, et que les issues ne disaient pas @@ -57,9 +58,19 @@ Le découpage suit l'ordre des dépendances, non celui des issues : `.expect("avoir après règlement partiel — le vrai chemin l'accepte")`. Il **décrit le défaut** ; il se corrige, il ne se contourne pas. -## Hors périmètre des trois stories +## ✅ Arbitrages du 2026-09-27 — l'avoir sur facture encaissée, et la perte + +- **L'avoir reste refusé sur une facture réglée, en tout ou en partie** (25-4-a). Un avoir sur + facture encaissée est un geste normal, mais il laisse au client un **crédit** que Kesh ne sait ni + montrer, ni rembourser, ni imputer : l'autoriser seul reproduirait la dette muette de #456. Sa + levée est tracée par **[#471]** (crédit visible, remboursement ou imputation). +- **#384 est ramenée dans la 25-4, en 25-4-d**, pour clore une facture partiellement réglée dont le + solde ne sera pas payé (perte sur débiteur), ou réglée sous déduction d'un escompte ou de frais. + ⚠️ La part de TVA du montant passé en perte **réduit la TVA due** (correction de l'impôt sur + créance irrécouvrable) ; seule la part HT va au compte de perte. + +## Hors périmètre des quatre stories -- **L'imputation de l'écart** d'un règlement partiel (escompte, frais, perte) — [#384], 25-6. - **Le côté fournisseur** : une facture fournisseur se règle en une fois, il n'y a pas de résiduel partiel (#416, § Portée). - **L'avoir partiel et le remboursement** — v0.2, inchangé. @@ -120,6 +131,7 @@ Pour 25-4-b, donc : ## Change Log +- **2026-09-27** — Arbitrages : avoir sur facture réglée refusé en 25-4-a, levée tracée par #471 ; #384 ramenée en 25-4-d. - **2026-09-27** — Frais de rappel : configurables, aucune ligne affichée à zéro. - **2026-09-27** — Q1 retranchée : la QR du rappel porte le reste dû (le TTC s'il n'y a aucun règlement) ; numéro de facture conservé ; point ouvert sur les frais. - **2026-09-27** — Recadrage de Q1 : le document est un rappel pour un montant partiel, pas la facture réémise. @@ -131,6 +143,7 @@ Pour 25-4-b, donc : (rattaché à 25-4-b). 25-4-a spécifiée dans la foulée. [#384]: https://github.com/guycorbaz/kesh/issues/384 +[#471]: https://github.com/guycorbaz/kesh/issues/471 [#387]: https://github.com/guycorbaz/kesh/issues/387 [#416]: https://github.com/guycorbaz/kesh/issues/416 [#420]: https://github.com/guycorbaz/kesh/issues/420 diff --git a/_bmad-output/implementation-artifacts/sprint-status.yaml b/_bmad-output/implementation-artifacts/sprint-status.yaml index 04f53ab6..dbcd648d 100644 --- a/_bmad-output/implementation-artifacts/sprint-status.yaml +++ b/_bmad-output/implementation-artifacts/sprint-status.yaml @@ -1,3 +1,4 @@ +# last_updated: 2026-09-27 (1) (25-4-d creee depuis #384 ; #471 ouverte ; 25-4-a validee P3.) # last_updated: 2026-09-26 (5) (25-4 DECOUPEE en a/b/c ; 25-4-a creee, #455/#456 rattachees ; #467 #468 #470 mergees.) # last_updated: 2026-09-26 (4) (**25-3-c DONE** — revue close, gates verts, non poussee.) # last_updated: 2026-09-26 (3) (25-3-c IMPLEMENTEE, en review ; revue a posteriori de la 25-5-a close dans le worktree.) @@ -353,14 +354,15 @@ development_status: 25-3-c-annuler-facture-fournisseur: done # 2026-09-26 **REVUE CLOSE EN 1 PASSE** (0 > LOW, Sonnet x3) ; non poussee ; PR `closes #454` titre ET corps, sur accord. **IMPLEMENTEE** — gates 2472/2472, 783/783, E2E 220/10/19 sans regression ; prochaine etape : code review. # 2026-09-26 **SPEC VALIDEE en 2 passes** (3 MED -> 0 > LOW). **SPECIFIEE** — `25-3-c-annuler-facture-fournisseur.md`. Geste par le socle (autorite `SupplierPurchase`), queue commune sur l'ecriture d'ACHAT, lot en dernier rang ; payee : reglement DETACHE (colonnes remises a NULL, ecriture libre, lien garde par l'audit) — **Q1 tranchee par Guy le 2026-09-26**. Aucune migration. Prochaine etape : validate. # 2026-09-24 NEE d'un arbitrage de Guy — une facture fournisseur s'annule DANS TOUS LES CAS, sauf si l'exercice de son ecriture d'achat est CLOS (l'exercice, pas le verrou de periode) ; payee, son reglement RESTE au grand livre et REDEVIENT A LETTRER (Epic 15 : un paiement doit correspondre a une facture, au besoin creee pour lui — « comme bexio »). Reecrit `cancel`, donc absorbe [#454]. Derriere la 25-3-a. 25-3-b-annuler-rapprochement: done # 2026-09-25 DONE — revue close en passe 2 (Sonnet 1 MED -> Haiku 0) ; gates : backend 2463/2463, frontend 777/777, E2E sans regression. Non poussee. — SPEC VALIDEE (boucle close en passe 4 : 2 HIGH -> 2 MED -> 2 MED -> 3 LOW ; pas de decoupage, arbitrage de Guy). Q1-Q3 tranchees. — [#418] closes — annuler un rapprochement bancaire. ⛔ NE JAMAIS compter sur le `ON DELETE SET NULL` de `matched_entry_id` : supprimer l'ecriture ferait disparaitre le lien EN SILENCE, le mode d'echec meme que la story repare. ⚠️ Le chemin d'ACCEPTATION n'a aucune fonction `kesh-db` a mirer : il est ecrit en SQL brut a **cinq** sites de `routes/reconciliation.rs`, dont **un seul** porte le verrou optimiste. Quand la transaction porte une facture, appelle le geste de la 25-3-a. Derriere elle. 25-3-annuler-reglement-et-rapprochement: split # [#414] [#418] — **l'Epic 24 a livre l'encaissement et le rapprochement ; il n'a PAS livre le moyen de les DEFAIRE.** Une erreur d'imputation est aujourd'hui incorrigible autrement qu'a la main — et le gel de la 24-4b interdit precisement la main. ⚠️ *C'est le prix d'une story qui ferme une porte sans ouvrir la suivante : le gel est juste, et il rend cette story-ci necessaire.* - 25-4-a-residuel-juste: ready-for-dev # 2026-09-26 [#455] [#456] — l'avoir soustrait HT d'un TTC (masque a l'ecran, mais rendu par GET /invoices/{id} aux cles API : amountDue = la TVA sur une facture creditee) ; un avoir accepte sur une facture partiellement reglee rend la creance creditrice. CREEE ; validation : P1 Sonnet 1H/3M/1L -> P2 Haiku 1M retenu -> P3 ciblee Opus 2M/5L (propagation), tous corriges. EN ATTENTE de l'arbitrage de Guy sur l'avoir (volet 2). + 25-4-a-residuel-juste: ready-for-dev # 2026-09-26 [#455] [#456] — l'avoir soustrait HT d'un TTC (masque a l'ecran, mais rendu par GET /invoices/{id} aux cles API : amountDue = la TVA sur une facture creditee) ; un avoir accepte sur une facture partiellement reglee rend la creance creditrice. CREEE ; validation : P1 Sonnet 1H/3M/1L -> P2 Haiku 1M retenu -> P3 ciblee Opus 2M/5L (propagation), tous corriges. Arbitrage 2026-09-27 : refus maintenu, levee tracee par #471. 25-4-b-residuel-aux-agregats: backlog # [#416] + montant des RELANCES (TTC complet, trouve a l'inventaire) — Q1 TRANCHEE 2026-09-27 : la QR du rappel porte le RESTE DU (= TTC sans reglement), reste a payer sur le PDF et dans le texte ; point ouvert : frais de rappel non comptabilises vs trop-percu. 25-4-c-residuel-au-rapprochement: backlog # [#420] — DEUX sites : filtre SQL des candidats (HAVING total_ttc) avant le score, puis score et re-score a l'acceptation. + 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 ; #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-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] [#384] [#387] — 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*. + 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 # 2026-07-26 ORDRE INVERSÉ au kickoff (#152 avant #144). Motif ground-truth : `invoice_lines` ne # porte AUCUNE colonne de compte ET ne référence même pas `products` (pas de `product_id` — cf. diff --git a/_bmad-output/planning-artifacts/epic-25-vague1-suite.md b/_bmad-output/planning-artifacts/epic-25-vague1-suite.md index 95bded48..1802055b 100644 --- a/_bmad-output/planning-artifacts/epic-25-vague1-suite.md +++ b/_bmad-output/planning-artifacts/epic-25-vague1-suite.md @@ -211,6 +211,11 @@ propagé aux rapports agrégés — balance âgée et échéancier ([#416]) — rapprochement compare au TTC et non au résiduel**, si bien que le virement du solde d'une facture partiellement réglée **score 0** ([#420]). +⚠️ **Découpée le 2026-09-26 en 25-4-a / b / c, puis d** : [#455] et [#456] (même grandeur, même +jalon) y sont rattachées, et **[#384] y est ramenée de la 25-6 le 2026-09-27** (arbitrage de Guy) +en 25-4-d — solder le reste d'une facture partiellement réglée repose sur le résiduel juste. Détail : +`25-4-propager-le-residuel.md`. + ### 25-5 — Les états et l'export **Issues : [#385], [#386].** La **Balance des comptes ne concorde pas avec le Bilan** — il @@ -225,8 +230,7 @@ tient*. - La tuile **« Dernières écritures » n'appelle rien** et affiche toujours « Aucune écriture » ([#388]) — *une tuile qui est un décor*. - Le **« solde bancaire » est le solde comptable**, donc faux ([#389]). -- **Imputer l'écart d'un règlement partiel** — escompte, frais bancaires, perte sur débiteur - ([#384]). +- ~~**Imputer l'écart d'un règlement partiel**~~ — [#384] **ramenée en 25-4-d** le 2026-09-27. - **Le PDF d'une facture n'est jamais archivé**, il est régénéré à la volée ([#387], IV.9) — un document réémis n'est pas le document envoyé. From 5ed4a704b7bdf349c70ecb1a882428862c18c64c Mon Sep 17 00:00:00 2001 From: Guy Corbaz Date: Sun, 27 Sep 2026 00:19:05 +0200 Subject: [PATCH 14/18] =?UTF-8?q?docs(25-4-a):=20prompt=20de=20validation?= =?UTF-8?q?=20P4=20cibl=C3=A9e=20(refs=20#455,=20refs=20#456)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- .../25-4-a-validate-prompt-p4.md | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 _bmad-output/implementation-artifacts/25-4-a-validate-prompt-p4.md diff --git a/_bmad-output/implementation-artifacts/25-4-a-validate-prompt-p4.md b/_bmad-output/implementation-artifacts/25-4-a-validate-prompt-p4.md new file mode 100644 index 00000000..047bdafb --- /dev/null +++ b/_bmad-output/implementation-artifacts/25-4-a-validate-prompt-p4.md @@ -0,0 +1,58 @@ +# Prompt — validation P4 CIBLÉE, Story 25-4-a (le résiduel juste) + +*Versionné le 2026-09-27. **Une lentille** (Sonnet), contexte frais. Passe **ciblée** (CLAUDE.md +§ « La passe ciblée ») : la P3 n'a trouvé que des défauts **de propagation des corrections P1** ; ce qu'il reste à +relire est la remédiation, pas la story entière.* + +Dépôt `/home/gcorbaz/devel/kesh`, branche `story/25-4-propager-le-residuel`. Fiche : +`_bmad-output/implementation-artifacts/25-4-a-residuel-juste.md`. **Périmètre** : le diff aplati +des remédiations P3 et de l'arbitrage du 2026-09-27 — +`git diff 1344b3a2 b18f7cd2 -- _bmad-output/implementation-artifacts/25-4-a-residuel-juste.md _bmad-output/implementation-artifacts/25-4-propager-le-residuel.md` +(ignore les paragraphes de la fiche mère qui consignent les arbitrages de Guy sur 25-4-b : **ne pas +les contester**). + +## La lentille : chasseur de régressions de remédiation + +Pour chaque passage réécrit en P1 ou P2 : + +1. **Est-il vrai contre le code ?** Relis les sites qu'il cite : `get_invoice` + (`crates/kesh-api/src/routes/invoices.rs`), `docs/api-external.md`, le bouton et le dialogue + d'avoir (`frontend/src/routes/(app)/invoices/[id]/+page.svelte`), la fixture `monter` et + `la_precedence_de_l_annulation_lecture_et_ecriture` (`crates/kesh-db/tests/invoice_settlement.rs`), + la migration `20260827000001_invoice_settlements.sql`, `settlement_cancel_blocker` + (`invoice_settlements_write.rs`), `kesh-api/Cargo.toml` (`serde-str`). +2. **Le gabarit « détacher, créditer, rattacher » (AC 13)** : exécute-le **mentalement** pas à pas + contre `create_credit_note` et les quatre paires combinées de `monter`. Une FK, une contrainte, + un audit, un contrôle de `company_id`, une lecture de `invoice_settlements` par + `create_credit_note` ou par les motifs montés **après** peut-il le casser ? L'écriture de + règlement porte-t-elle quelque part l'identifiant de la facture d'origine (description, audit, + `invoice_settlements` seul) ? +3. **AC 14 révisé** : cohérent avec l'AC 11, l'AC 12, les tests de l'AC 16 et la mutation m6/m7 ? + Un test de l'AC 16 porte-t-il encore l'ancienne logique (« bouton absent ») ? +4. **AC 8 révisé** : l'assertion prescrite peut-elle passer à vide (`null`, champ absent) ? +5. **Propagation** : chaque correction a-t-elle été reportée partout où le symptôme était écrit + (tâches T1-T9, tableau « Où regarder », Change Log, fiche mère, `sprint-status.yaml`) ? Greppe la + **valeur** (`code()`, `:98`, `937-990`, `latent`, `masqué`), pas la formulation. + +## Ce que tu rends + +- **Findings** : sévérité, endroit exact, **preuve** (commande et résultat, code lu), correction. +- ⛔ **La liste des axes réellement exercés ET de ceux qui ne l'ont pas été.** + +## Interdits + +⛔ N'écris aucun fichier du dépôt ; aucune commande qui écrit dans le dépôt ou dans une base — +`scripts/prepare-release.sh`, `scripts/regen-test-schema.sh`, `scripts/install-hooks.sh`, +`scripts/test-fast.sh`, `scripts/mem-guard.sh`, `make`, `latexmk`, tout `git commit`/`push`/`add`/ +`stash`/`reset`/`rebase`/`checkout`/`switch`/`worktree`, `sqlx migrate`, `cargo test`/`nextest`, +`npm run`, `npx playwright`. Autorisés : lecture, `grep`, `git log`/`show`/`diff`, `gh issue view`, +`cargo check`. + +## Complément P4 + +Le diff contient aussi l'**arbitrage du 2026-09-27** (refus de l'avoir maintenu, levée tracée par +#471 ; #384 ramenée en 25-4-d) : **ne pas le contester**, mais vérifier que la fiche 25-4-a reste +cohérente avec lui (AC 12, AC 17, hors périmètre de la fiche mère), et que la fiche de l'epic et +`sprint-status.yaml` disent la même chose. Vérifie aussi que les corrections P3 (T7 « m1 à m7 », +commentaire `:842-844`, doc-comments `errors.rs:214-217` et `invoice_settlements_write.rs:326-329`, +repli « arrêter et demander », T2 et le montage à 8,1 %) sont justes et ne contredisent rien. From ab9b9448ae0320602aec32a7f3cceeb930e29106 Mon Sep 17 00:00:00 2001 From: Guy Corbaz Date: Sun, 27 Sep 2026 00:31:45 +0200 Subject: [PATCH 15/18] =?UTF-8?q?docs(25-4-a):=20validation=20P4=20?= =?UTF-8?q?=E2=80=94=20close=20en=204=20passes,=20pr=C3=AAte=20pour=20le?= =?UTF-8?q?=20dev=20(refs=20#455,=20refs=20#456)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- .../25-4-a-residuel-juste.md | 16 ++++++++++++++-- .../implementation-artifacts/sprint-status.yaml | 4 ++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/_bmad-output/implementation-artifacts/25-4-a-residuel-juste.md b/_bmad-output/implementation-artifacts/25-4-a-residuel-juste.md index ff0af122..5cacd437 100644 --- a/_bmad-output/implementation-artifacts/25-4-a-residuel-juste.md +++ b/_bmad-output/implementation-artifacts/25-4-a-residuel-juste.md @@ -176,7 +176,7 @@ pouvoir émettre un avoir. » Registre du glossaire (`docs/i18n-glossaire.md` § sauvegarde antérieure — ce qui justifie de garder `SettlementCancelBlocker::InvoiceCredited`. ⛔ Ne **pas** supprimer ce motif ni ses tests. Ses doc-comments, qui le présentent comme un chemin normal (« créditée par un avoir après ce règlement … pas une anomalie », `errors.rs:214-217` ; -« Hors `validated`, c'est donc un avoir », `invoice_settlements_write.rs:326-329`), disent +« Hors `validated`, c'est donc un avoir », `invoice_settlements_write.rs:327-330`), disent désormais que l'état n'est plus atteignable **que par l'import d'une sauvegarde antérieure**. ⚠️ **`monter` sert quinze cas, pas un** : `la_precedence_de_l_annulation_lecture_et_ecriture` @@ -309,7 +309,8 @@ restreint aux lignes qui parlent d'avoir, plus le PDF aplati. Les manuels DE/IT/ | `crates/kesh-db/src/repositories/invoice_settlements.rs:24-58, 122-145` | les quatre constantes, `amount_due` | | `crates/kesh-db/src/repositories/invoices.rs:155-190` | `INVOICE_TTC_SUBQUERY_SQL` / `…_DERIVED_JOIN_SQL`, `total_ttc` | | `crates/kesh-db/src/repositories/credit_notes.rs:187-257, 260-330, 510-575` | écriture de l'avoir, garde AC2bis, `total_amount`, bascule `cancelled` | -| `crates/kesh-db/src/errors.rs:119-175, 300-470, 641-670` | `UnvalidationBlocker`, `DbError`, `error_code()` | +| `crates/kesh-db/src/errors.rs:119-175, 205-260, 300-470, 641-670` | `UnvalidationBlocker`, `SettlementCancelBlocker` (doc d'`InvoiceCredited` `:214-217`), `DbError`, `error_code()` | +| `crates/kesh-db/src/repositories/invoice_settlements_write.rs:60-72, 320-333` | verrou de `settle_invoice` ; rang 1 de `settlement_cancel_blocker` (commentaire `:327-330`, AC 13) | | `crates/kesh-db/src/repositories/invoices.rs:1430-1448` | la garde de dévalidation, patron de l'AC 9 | | `crates/kesh-api/src/errors.rs:2515-2560, 2741-2748` | mapping `InvoiceNotUnvalidatable`, générique | | `crates/kesh-api/src/routes/credit_notes.rs:183-205` | handler de création | @@ -369,6 +370,17 @@ bloque **25-4-b**, pas celle-ci. ## Change Log +- **2026-09-27** — **validation P4 ciblée** (Sonnet, diff `1344b3a2..b18f7cd2`, prompt + `25-4-a-validate-prompt-p4.md`) — rapporte **1 MEDIUM, 3 LOW**, tous confirmés et corrigés. + **MEDIUM reclassé LOW** par l'orchestrateur : `invoice_settlements_write.rs` manquait au tableau + « Où regarder », mais l'AC 13 qui prescrit son édition le **nomme avec sa ligne** — une omission de + navigation, sans risque qu'une prescription soit perdue. LOW : citation `:326-329` → `:327-330` ; + plage d'`errors.rs` étendue à `SettlementCancelBlocker` ; commentaire de la ligne mère de + `sprint-status` resté à trois stories. Le gabarit de l'AC 13, l'AC 8 et l'AC 14 revérifiés sans + défaut ; la sérialisation en chaîne confirmée dans la source de `rust_decimal` 1.42.1. + ⛔ **Boucle de validation CLOSE en 4 passes** : `1H/3M/1L → 1M → 2M/5L → 0 > LOW` (après + reclassement), rotation Sonnet → Haiku → Opus → Sonnet, deux passes ciblées ; toutes les + corrections ont porté sur la spec, aucune sur du code. **Prête pour le dev.** - **2026-09-27** — Arbitrage de Guy : le refus de l'avoir sur facture réglée (en tout ou en partie) reste ; sa levée est tracée par #471. - **2026-09-27** — **validation P3 ciblée** (Opus, diff aplati des remédiations P1-P2, prompt `25-4-a-validate-prompt-p3.md`) — **2 MEDIUM, 5 LOW**, tous de **propagation** des corrections P1, diff --git a/_bmad-output/implementation-artifacts/sprint-status.yaml b/_bmad-output/implementation-artifacts/sprint-status.yaml index dbcd648d..daf27cdd 100644 --- a/_bmad-output/implementation-artifacts/sprint-status.yaml +++ b/_bmad-output/implementation-artifacts/sprint-status.yaml @@ -354,11 +354,11 @@ development_status: 25-3-c-annuler-facture-fournisseur: done # 2026-09-26 **REVUE CLOSE EN 1 PASSE** (0 > LOW, Sonnet x3) ; non poussee ; PR `closes #454` titre ET corps, sur accord. **IMPLEMENTEE** — gates 2472/2472, 783/783, E2E 220/10/19 sans regression ; prochaine etape : code review. # 2026-09-26 **SPEC VALIDEE en 2 passes** (3 MED -> 0 > LOW). **SPECIFIEE** — `25-3-c-annuler-facture-fournisseur.md`. Geste par le socle (autorite `SupplierPurchase`), queue commune sur l'ecriture d'ACHAT, lot en dernier rang ; payee : reglement DETACHE (colonnes remises a NULL, ecriture libre, lien garde par l'audit) — **Q1 tranchee par Guy le 2026-09-26**. Aucune migration. Prochaine etape : validate. # 2026-09-24 NEE d'un arbitrage de Guy — une facture fournisseur s'annule DANS TOUS LES CAS, sauf si l'exercice de son ecriture d'achat est CLOS (l'exercice, pas le verrou de periode) ; payee, son reglement RESTE au grand livre et REDEVIENT A LETTRER (Epic 15 : un paiement doit correspondre a une facture, au besoin creee pour lui — « comme bexio »). Reecrit `cancel`, donc absorbe [#454]. Derriere la 25-3-a. 25-3-b-annuler-rapprochement: done # 2026-09-25 DONE — revue close en passe 2 (Sonnet 1 MED -> Haiku 0) ; gates : backend 2463/2463, frontend 777/777, E2E sans regression. Non poussee. — SPEC VALIDEE (boucle close en passe 4 : 2 HIGH -> 2 MED -> 2 MED -> 3 LOW ; pas de decoupage, arbitrage de Guy). Q1-Q3 tranchees. — [#418] closes — annuler un rapprochement bancaire. ⛔ NE JAMAIS compter sur le `ON DELETE SET NULL` de `matched_entry_id` : supprimer l'ecriture ferait disparaitre le lien EN SILENCE, le mode d'echec meme que la story repare. ⚠️ Le chemin d'ACCEPTATION n'a aucune fonction `kesh-db` a mirer : il est ecrit en SQL brut a **cinq** sites de `routes/reconciliation.rs`, dont **un seul** porte le verrou optimiste. Quand la transaction porte une facture, appelle le geste de la 25-3-a. Derriere elle. 25-3-annuler-reglement-et-rapprochement: split # [#414] [#418] — **l'Epic 24 a livre l'encaissement et le rapprochement ; il n'a PAS livre le moyen de les DEFAIRE.** Une erreur d'imputation est aujourd'hui incorrigible autrement qu'a la main — et le gel de la 24-4b interdit precisement la main. ⚠️ *C'est le prix d'une story qui ferme une porte sans ouvrir la suivante : le gel est juste, et il rend cette story-ci necessaire.* - 25-4-a-residuel-juste: ready-for-dev # 2026-09-26 [#455] [#456] — l'avoir soustrait HT d'un TTC (masque a l'ecran, mais rendu par GET /invoices/{id} aux cles API : amountDue = la TVA sur une facture creditee) ; un avoir accepte sur une facture partiellement reglee rend la creance creditrice. CREEE ; validation : P1 Sonnet 1H/3M/1L -> P2 Haiku 1M retenu -> P3 ciblee Opus 2M/5L (propagation), tous corriges. Arbitrage 2026-09-27 : refus maintenu, levee tracee par #471. + 25-4-a-residuel-juste: ready-for-dev # 2026-09-26 [#455] [#456] — l'avoir soustrait HT d'un TTC (masque a l'ecran, mais rendu par GET /invoices/{id} aux cles API : amountDue = la TVA sur une facture creditee) ; un avoir accepte sur une facture partiellement reglee rend la creance creditrice. CREEE ; validation : P1 Sonnet 1H/3M/1L -> P2 Haiku 1M retenu -> P3 ciblee Opus 2M/5L -> P4 ciblee Sonnet 0 > LOW (1M reclasse LOW) : VALIDEE en 4 passes. Arbitrage 2026-09-27 : refus maintenu, levee tracee par #471. 25-4-b-residuel-aux-agregats: backlog # [#416] + montant des RELANCES (TTC complet, trouve a l'inventaire) — Q1 TRANCHEE 2026-09-27 : la QR du rappel porte le RESTE DU (= TTC sans reglement), reste a payer sur le PDF et dans le texte ; point ouvert : frais de rappel non comptabilises vs trop-percu. 25-4-c-residuel-au-rapprochement: backlog # [#420] — DEUX sites : filtre SQL des candidats (HAVING total_ttc) avant le score, puis score et re-score a l'acceptation. 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 ; #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-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-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*. From 9bf2bb2b5c992e107e70d39849daa76408358ef9 Mon Sep 17 00:00:00 2001 From: Guy Corbaz Date: Sun, 27 Sep 2026 01:02:05 +0200 Subject: [PATCH 16/18] =?UTF-8?q?feat(25-4-a):=20le=20reste=20d=C3=BB=20co?= =?UTF-8?q?mpte=20l'avoir=20TTC,=20et=20l'avoir=20est=20refus=C3=A9=20sur?= =?UTF-8?q?=20une=20facture=20r=C3=A9gl=C3=A9e=20(refs=20#455,=20refs=20#4?= =?UTF-8?q?56)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- CHANGELOG.md | 6 + .../25-4-a-residuel-juste.md | 111 ++++++- .../sprint-status.yaml | 2 +- crates/kesh-api/src/errors.rs | 30 ++ .../kesh-api/tests/invoice_echeancier_e2e.rs | 113 +++++++ crates/kesh-db/src/errors.rs | 34 +- .../kesh-db/src/repositories/credit_notes.rs | 37 ++- .../src/repositories/invoice_settlements.rs | 47 ++- .../repositories/invoice_settlements_write.rs | 3 + crates/kesh-db/src/repositories/invoices.rs | 43 ++- .../kesh-db/tests/credit_notes_repository.rs | 239 +++++++++++++- .../tests/invoice_amount_due_parity.rs | 311 ++++++++++++++++++ crates/kesh-db/tests/invoice_settlement.rs | 27 +- crates/kesh-i18n/locales/de-CH/messages.ftl | 1 + crates/kesh-i18n/locales/en-CH/messages.ftl | 1 + crates/kesh-i18n/locales/fr-CH/messages.ftl | 1 + crates/kesh-i18n/locales/it-CH/messages.ftl | 1 + docs/manual/fr/user-manual.pdf | Bin 290281 -> 292120 bytes docs/manual/fr/user-manual.tex | 21 +- .../routes/(app)/invoices/[id]/+page.svelte | 30 +- .../[id]/invoice-settlements-page.test.ts | 38 ++- 21 files changed, 1042 insertions(+), 54 deletions(-) create mode 100644 crates/kesh-db/tests/invoice_amount_due_parity.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index ca07fcb0..1d6e160a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,8 +28,14 @@ Le contenu est rédigé en français à destination des **fiduciaires, PME, ind - **Sur la fiche d'une facture fournisseur, le bouton *Annuler la facture* s'affichait aussi pour un utilisateur en consultation**, qui ne pouvait que se heurter à un refus ; et un refus effaçait toute la fiche au profit d'un message d'erreur. Le bouton est désormais réservé aux comptables et administrateurs, et un refus s'affiche sous le bouton, la fiche restant visible. +- **Une facture créditée gardait sa TVA en « reste dû » ([#455](https://github.com/guycorbaz/kesh/issues/455)).** Le reste dû retranchait l'avoir **hors taxes** d'un total **TTC** : une facture de 100.— HT à 8,1 %, entièrement créditée, restait due de 8.10, alors que le grand livre, lui, avait soldé la créance. L'écran ne l'affichait que dans un cas rare, mais ⚠️ **l'API le rendait toujours** : `GET /api/v1/invoices/{id}`, ouverte aux clés API en lecture, renvoyait la TVA dans `amountDue` pour toute facture créditée. **Si une intégration lisait ce champ, ses chiffres étaient faux.** Le reste dû compte désormais l'avoir TTC, arrondi ligne par ligne comme son écriture. + +- **Un avoir pouvait viser une facture déjà réglée en partie ([#456](https://github.com/guycorbaz/kesh/issues/456)), et le compte du client devenait créditeur sans que rien ne le signale.** L'avoir était refusé sur une facture **payée**, mais pas sur une facture réglée **en partie** — qui, depuis le règlement partiel, n'est « payée » qu'une fois soldée. Comme l'avoir annule tout le montant de la facture, le client se retrouvait avec un crédit du montant déjà encaissé, que Kesh ne sait ni montrer, ni rembourser. L'avoir est désormais refusé dès qu'un règlement existe, avec un message qui dit quoi faire : annuler d'abord le règlement. *(Autoriser l'avoir sur une facture encaissée, avec le remboursement ou l'imputation qu'il appelle, est suivi par [#471](https://github.com/guycorbaz/kesh/issues/471).)* + ### Changed +- **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. Le geste existe toujours, mais il porte désormais son nom. Le bouton **« Dévalider »** repasse la facture en **brouillon**, supprime son écriture, et **conserve son numéro** — en la revalidant, elle reprend *le même*, sans consommer le compteur. Deux sorties s'ouvrent alors : corriger puis revalider, ou supprimer le brouillon. diff --git a/_bmad-output/implementation-artifacts/25-4-a-residuel-juste.md b/_bmad-output/implementation-artifacts/25-4-a-residuel-juste.md index 5cacd437..82b5e340 100644 --- a/_bmad-output/implementation-artifacts/25-4-a-residuel-juste.md +++ b/_bmad-output/implementation-artifacts/25-4-a-residuel-juste.md @@ -1,6 +1,6 @@ # Story 25.4-a : Le résiduel juste — l'avoir compté TTC, et refusé sur une facture réglée -Status: ready-for-dev +Status: review **Issues : [#455], [#456]**, qu'elle **ferme** : `closes #455, closes #456` dans le **titre ET le corps** de la PR (squash ; un `refs` partout laisserait les issues ouvertes sans signal). @@ -271,22 +271,22 @@ restreint aux lignes qui parlent d'avoir, plus le PDF aplati. Les manuels DE/IT/ ## Tasks / Subtasks -- [ ] **T1 — formule de l'avoir TTC** (AC 1-5) : factoriser la formule de ligne, réécrire les deux +- [x] **T1 — formule de l'avoir TTC** (AC 1-5) : factoriser la formule de ligne, réécrire les deux constantes d'avoir, doc-comments. -- [ ] **T2 — parité et concordance** (AC 6-8) : nouveau fichier de test sur le patron +- [x] **T2 — parité et concordance** (AC 6-8) : nouveau fichier de test sur le patron `invoice_ttc_parity.rs`. ⚠️ Le helper `validated_invoice` (`invoice_settlement.rs:32`) est **à 0 % en dur** et passe une écriture de vente **au HT** : les factures à 8,1 % se montent par le **vrai chemin de validation**, sur le patron de `create_and_validate` (`credit_notes_repository.rs`) ; l'état hérité, par le gabarit de l'AC 13. -- [ ] **T3 — garde de l'avoir** (AC 9-11) : lecture verrouillante des règlements après le verrou, +- [x] **T3 — garde de l'avoir** (AC 9-11) : lecture verrouillante des règlements après le verrou, variante `DbError`, `error_code()`. -- [ ] **T4 — API et i18n** (AC 12) : mapping, clé ×4 ; e2e 409. -- [ ] **T5 — fixture `monter`** (AC 13) : gabarit « détacher, créditer, rattacher », commentaire ; +- [x] **T4 — API et i18n** (AC 12) : mapping, clé ×4 ; e2e 409. +- [x] **T5 — fixture `monter`** (AC 13) : gabarit « détacher, créditer, rattacher », commentaire ; **les quinze cas** de la précédence restent verts, dont les quatre paires avec `InvoiceCredited`. -- [ ] **T6 — frontend** (AC 14) : condition du bouton retirée, commentaire, Vitest. -- [ ] **T7 — tests et mutations** (AC 16) : m1 à **m7** exécutées, résultats consignés. -- [ ] **T8 — textes** (AC 17) : manuel, PDF aplati, CHANGELOG. -- [ ] **T9 — gates** : backend complet (base remise à zéro), frontend complet, E2E complet. +- [x] **T6 — frontend** (AC 14) : condition du bouton retirée, commentaire, Vitest. +- [x] **T7 — tests et mutations** (AC 16) : m1 à **m7** exécutées, résultats consignés. +- [x] **T8 — textes** (AC 17) : manuel, PDF aplati, CHANGELOG. +- [x] **T9 — gates** : backend complet (base remise à zéro), frontend complet, E2E complet. ## Dev Notes @@ -362,14 +362,105 @@ bloque **25-4-b**, pas celle-ci. ### Agent Model Used +Claude Opus 5.5. + ### Debug Log References +- `settled_and_credited_forms_are_at_parity` attendait **huit** factures ; il y en a **sept** (six du jeu + + la facture auxiliaire du gabarit). Erreur de décompte dans le test, pas dans le code — corrigée, + et l'assertion anti-vacuité garde des valeurs **non nulles** des deux côtés. +- `SUM(next_number)` rend un `DECIMAL` sous MariaDB : `CAST(… AS SIGNED)` dans l'empreinte du test de + refus. +- Un doc-comment égaré (« Paramètres de recherche, tri et pagination. ») précédait + `INVOICE_TTC_SUBQUERY_SQL` : remis au-dessus d'`InvoiceListQuery`, qu'il décrit. +- ⚠️ **Relevé hors périmètre, non corrigé** : la clé voisine `error-invoice-unvalidate-blocked-settled` + (it-CH) dit « annullatelo » — forme *voi*, contraire au registre du glossaire (2ᵉ personne du + singulier). La nouvelle clé suit le glossaire (« annullalo »). + ### Completion Notes List +- **Formule du TTC d'une ligne, source unique** (AC 5) : macro `line_ttc_sql!` (`invoices.rs`), + rendant un littéral via `concat!` ; les **quatre** constantes en dérivent (TTC facture scalaire et + jointe, avoir scalaire et joint), qui restent des `&'static str`. +- **Avoir compté TTC** (AC 1-4) : les deux constantes d'avoir somment `credit_note_lines` (jointes à + `credit_notes`, `status = 'issued'`), arrondi ligne par ligne ; doc-comments sur l'unité et sur + `total_amount` HT. Aucune migration. +- **Parité et concordance** (AC 6-8) : `tests/invoice_amount_due_parity.rs`, 4 tests, **tous à TVA + non nulle** ; factures par le vrai chemin de validation ; l'état hérité par le gabarit « détacher, + créditer, rattacher ». Le crédit de l'avoir égale le crédit de la créance **dans l'écriture** + (1207.71 sur trois lignes à 8,1 % / 2,6 % / sous-centime), et le test s'assure que `total_amount` + (HT) en diffère — sans quoi il ne distinguerait rien. +- **Garde de l'avoir** (AC 9-11) : lecture **verrouillante** des règlements, **après** le verrou de la + facture ; une ligne **ou** `paid_at` ; variante `DbError::CreditNoteBlockedBySettlement` + (`CREDIT_NOTE_INVOICE_SETTLED`), qui remplace aussi le générique du cas « payée ». +- **API et i18n** (AC 12) : 409, message suffixé du numéro de facture, `details { invoiceId, + settlementId }` ; clé `error-credit-note-blocked-settled` ×4. +- **Fixture `monter`** (AC 13) : gabarit « détacher, créditer, rattacher » ; les **quinze cas** de la + précédence restent verts. Doc-comments de `InvoiceCredited` et du rang 1 : état hérité. +- **Frontend** (AC 14) : condition `!invoice.paidAt` retirée du bouton, commentaires Suspendre/Reprendre + corrigé ; `data-testid` sur le bouton, la confirmation et l'erreur du dialogue. +- **Textes** (AC 17) : manuel (refus nommé en § Avoirs, deux limites reformulées « réglée, même en + partie », deux motifs d'annulation de règlement précisés comme **cas hérité**), PDF régénéré et + contrôlé aplati (0 `??`, six phrases de contrôle présentes, « marquée payée » absente) ; CHANGELOG + *Fixed* ×2 (dont **l'API** pour #455) et *Changed* (le bouton). Grep large du symptôme « avoir sur + facture payée » : aucun autre site. + +**Tests ajoutés** (recomptés depuis la source, `main` → arbre de travail) : `invoice_amount_due_parity` +0 → 4, `credit_notes_repository` 6 → 9 (dont `credit_note_refused_on_paid_invoice` réécrit), +`invoice_echeancier_e2e` 11 → 13, Vitest `invoice-settlements-page.test.ts` 2 → 5. + +**Mutations** (résultat **observé**, chaque fichier restauré après) : + +| Mutation | Tests rouges | +|---|---| +| m1 — avoir scalaire en `SUM(cn.total_amount)` | 4 / 4 de `invoice_amount_due_parity` | +| m2 — avoir joint seul en `SUM(total_amount)` | `settled_and_credited_forms_are_at_parity` | +| m3 — garde sur `paid_at` seul | `…refused_on_partially_settled_invoice`, `…accepted_after_the_settlement_is_cancelled`, `…waits_for_a_concurrent_settlement` | +| m4 — garde sur la ligne seule | `credit_note_refused_on_paid_invoice` | +| m5 — lecture avant le verrou, non verrouillante | `credit_note_waits_for_a_concurrent_settlement` | +| m6 — `!invoice.paidAt` remis sur le bouton | 2 Vitest (bouton sur facture payée ; refus dans le dialogue) | +| m7 — code mappé sur `ILLEGAL_STATE_TRANSITION` | `credit_note_on_partially_settled_invoice_is_409` | + +### Gates + +- Backend : base remise à zéro (tmpfs + migrations + seed), `scripts/test-fast.sh` (fmt + clippy + + nextest) **2484 / 2484** (2475 + 9). +- Frontend : `check` 0 erreur (27 avertissements), `lint-i18n-ownership` PASS, `test:unit` + **826 / 826** (823 + 3), build OK. +- E2E : `kesh_e2e` reconstruite, montage complet (`smtpConfigured:true`, répertoires inbox et documents), + frontend buildé après le dernier patch, run à **22:52 UTC** : **222 passés / 10 échoués / 19 + ignorés**. Les 7 de KF-029 ; les 3 autres — `product-revenue-account:133`, `sidebar-navigation:75` + (pollution documentée) et ⚠️ `accounts.spec.ts:185` (**non documenté**) — passent **rejoués seuls, + 18 / 18**, et ne touchent aucun écran de la story. Trois pollutions dans un même run, là où la + baseline en compte une : à surveiller, sans régression de la story. + ### File List +| Fichier | Nature | +|---|---| +| `crates/kesh-db/src/repositories/invoices.rs` | macro `line_ttc_sql!`, constantes TTC recomposées, doc-comment replacé | +| `crates/kesh-db/src/repositories/invoice_settlements.rs` | avoir TTC (scalaire, joint), doc d'`amount_due` | +| `crates/kesh-db/src/repositories/credit_notes.rs` | garde : lecture verrouillante des règlements | +| `crates/kesh-db/src/errors.rs` | `CreditNoteBlockedBySettlement`, `error_code()`, doc d'`InvoiceCredited` | +| `crates/kesh-db/src/repositories/invoice_settlements_write.rs` | commentaire du rang 1 | +| `crates/kesh-api/src/errors.rs` | mapping 409 | +| `crates/kesh-i18n/locales/{fr,de,it,en}-CH/messages.ftl` | `error-credit-note-blocked-settled` | +| `crates/kesh-db/tests/invoice_amount_due_parity.rs` | **nouveau** — parité, concordance, zéro, hérité | +| `crates/kesh-db/tests/credit_notes_repository.rs` | refus payée réécrit ; partiel, réouverture, entrelacement | +| `crates/kesh-db/tests/invoice_settlement.rs` | `monter` : gabarit « détacher, créditer, rattacher » | +| `crates/kesh-api/tests/invoice_echeancier_e2e.rs` | e2e : `amountDue` nul, 409 | +| `frontend/src/routes/(app)/invoices/[id]/+page.svelte` | bouton, commentaires, `data-testid` | +| `frontend/src/routes/(app)/invoices/[id]/invoice-settlements-page.test.ts` | 3 Vitest | +| `docs/manual/fr/user-manual.tex` / `.pdf` | avoirs, limites, cas hérité | +| `CHANGELOG.md` | *Fixed* ×2, *Changed* | + ## Change Log +- **2026-09-27** — **dev** (Opus 5.5) : formule du TTC de ligne factorisée (`line_ttc_sql!`), avoir + compté TTC, garde verrouillante de l'avoir sur facture réglée, variante `CREDIT_NOTE_INVOICE_SETTLED` + ×4 locales, fixture `monter` par le gabarit, bouton aligné sur « Dévalider », manuel et CHANGELOG. + +9 tests Rust, +3 Vitest ; **7 / 7 mutations tuées**. Gates : backend 2484/2484, frontend 826/826, + E2E 222/10/19 sans régression (7 KF-029 + 3 pollutions, rejouées seules vertes). - **2026-09-27** — **validation P4 ciblée** (Sonnet, diff `1344b3a2..b18f7cd2`, prompt `25-4-a-validate-prompt-p4.md`) — rapporte **1 MEDIUM, 3 LOW**, tous confirmés et corrigés. **MEDIUM reclassé LOW** par l'orchestrateur : `invoice_settlements_write.rs` manquait au tableau diff --git a/_bmad-output/implementation-artifacts/sprint-status.yaml b/_bmad-output/implementation-artifacts/sprint-status.yaml index daf27cdd..7502347b 100644 --- a/_bmad-output/implementation-artifacts/sprint-status.yaml +++ b/_bmad-output/implementation-artifacts/sprint-status.yaml @@ -354,7 +354,7 @@ development_status: 25-3-c-annuler-facture-fournisseur: done # 2026-09-26 **REVUE CLOSE EN 1 PASSE** (0 > LOW, Sonnet x3) ; non poussee ; PR `closes #454` titre ET corps, sur accord. **IMPLEMENTEE** — gates 2472/2472, 783/783, E2E 220/10/19 sans regression ; prochaine etape : code review. # 2026-09-26 **SPEC VALIDEE en 2 passes** (3 MED -> 0 > LOW). **SPECIFIEE** — `25-3-c-annuler-facture-fournisseur.md`. Geste par le socle (autorite `SupplierPurchase`), queue commune sur l'ecriture d'ACHAT, lot en dernier rang ; payee : reglement DETACHE (colonnes remises a NULL, ecriture libre, lien garde par l'audit) — **Q1 tranchee par Guy le 2026-09-26**. Aucune migration. Prochaine etape : validate. # 2026-09-24 NEE d'un arbitrage de Guy — une facture fournisseur s'annule DANS TOUS LES CAS, sauf si l'exercice de son ecriture d'achat est CLOS (l'exercice, pas le verrou de periode) ; payee, son reglement RESTE au grand livre et REDEVIENT A LETTRER (Epic 15 : un paiement doit correspondre a une facture, au besoin creee pour lui — « comme bexio »). Reecrit `cancel`, donc absorbe [#454]. Derriere la 25-3-a. 25-3-b-annuler-rapprochement: done # 2026-09-25 DONE — revue close en passe 2 (Sonnet 1 MED -> Haiku 0) ; gates : backend 2463/2463, frontend 777/777, E2E sans regression. Non poussee. — SPEC VALIDEE (boucle close en passe 4 : 2 HIGH -> 2 MED -> 2 MED -> 3 LOW ; pas de decoupage, arbitrage de Guy). Q1-Q3 tranchees. — [#418] closes — annuler un rapprochement bancaire. ⛔ NE JAMAIS compter sur le `ON DELETE SET NULL` de `matched_entry_id` : supprimer l'ecriture ferait disparaitre le lien EN SILENCE, le mode d'echec meme que la story repare. ⚠️ Le chemin d'ACCEPTATION n'a aucune fonction `kesh-db` a mirer : il est ecrit en SQL brut a **cinq** sites de `routes/reconciliation.rs`, dont **un seul** porte le verrou optimiste. Quand la transaction porte une facture, appelle le geste de la 25-3-a. Derriere elle. 25-3-annuler-reglement-et-rapprochement: split # [#414] [#418] — **l'Epic 24 a livre l'encaissement et le rapprochement ; il n'a PAS livre le moyen de les DEFAIRE.** Une erreur d'imputation est aujourd'hui incorrigible autrement qu'a la main — et le gel de la 24-4b interdit precisement la main. ⚠️ *C'est le prix d'une story qui ferme une porte sans ouvrir la suivante : le gel est juste, et il rend cette story-ci necessaire.* - 25-4-a-residuel-juste: ready-for-dev # 2026-09-26 [#455] [#456] — l'avoir soustrait HT d'un TTC (masque a l'ecran, mais rendu par GET /invoices/{id} aux cles API : amountDue = la TVA sur une facture creditee) ; un avoir accepte sur une facture partiellement reglee rend la creance creditrice. CREEE ; validation : P1 Sonnet 1H/3M/1L -> P2 Haiku 1M retenu -> P3 ciblee Opus 2M/5L -> P4 ciblee Sonnet 0 > LOW (1M reclasse LOW) : VALIDEE en 4 passes. Arbitrage 2026-09-27 : refus maintenu, levee tracee par #471. + 25-4-a-residuel-juste: review # 2026-09-27 IMPLEMENTEE — 7/7 mutations tuees ; gates 2484/2484, 826/826, E2E 222/10/19 sans regression ; prochaine etape : code review. # 2026-09-26 [#455] [#456] — l'avoir soustrait HT d'un TTC (masque a l'ecran, mais rendu par GET /invoices/{id} aux cles API : amountDue = la TVA sur une facture creditee) ; un avoir accepte sur une facture partiellement reglee rend la creance creditrice. CREEE ; validation : P1 Sonnet 1H/3M/1L -> P2 Haiku 1M retenu -> P3 ciblee Opus 2M/5L -> P4 ciblee Sonnet 0 > LOW (1M reclasse LOW) : VALIDEE en 4 passes. Arbitrage 2026-09-27 : refus maintenu, levee tracee par #471. 25-4-b-residuel-aux-agregats: backlog # [#416] + montant des RELANCES (TTC complet, trouve a l'inventaire) — Q1 TRANCHEE 2026-09-27 : la QR du rappel porte le RESTE DU (= TTC sans reglement), reste a payer sur le PDF et dans le texte ; point ouvert : frais de rappel non comptabilises vs trop-percu. 25-4-c-residuel-au-rapprochement: backlog # [#420] — DEUX sites : filtre SQL des candidats (HAVING total_ttc) avant le score, puis score et re-score a l'acceptation. 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. diff --git a/crates/kesh-api/src/errors.rs b/crates/kesh-api/src/errors.rs index 61ecab28..a93bc1f6 100644 --- a/crates/kesh-api/src/errors.rs +++ b/crates/kesh-api/src/errors.rs @@ -2563,6 +2563,36 @@ impl IntoResponse for AppError { }); (StatusCode::CONFLICT, Json(body)).into_response() } + // Story 25-4-a (#456) — l'avoir refusé sur une facture réglée, + // même en partie. ⛔ Un code et un message propres : l'ancienne + // garde rendait `ILLEGAL_STATE_TRANSITION`, dont le texte n'était + // que journalisé. Le numéro de la facture est suffixé, comme + // pour la dévalidation. + DbError::CreditNoteBlockedBySettlement { + invoice_id, + settlement_id, + invoice_number, + } => { + let base = t( + "error-credit-note-blocked-settled", + "Cette facture porte un règlement, même partiel : annulez-le d'abord pour pouvoir émettre un avoir.", + ); + let message = match invoice_number.as_deref() { + Some(numero) => format!("{base} ({numero})"), + None => base, + }; + let body = serde_json::json!({ + "error": { + "code": "CREDIT_NOTE_INVOICE_SETTLED", + "message": message, + "details": { + "invoiceId": invoice_id, + "settlementId": settlement_id, + }, + } + }); + (StatusCode::CONFLICT, Json(body)).into_response() + } // Story 25-3-a-1 (#414) — l'annulation d'un règlement refusée. // // ⚠️ Seuls les rangs que le GESTE refuse lui-même arrivent ici diff --git a/crates/kesh-api/tests/invoice_echeancier_e2e.rs b/crates/kesh-api/tests/invoice_echeancier_e2e.rs index ab43533a..99cffc3f 100644 --- a/crates/kesh-api/tests/invoice_echeancier_e2e.rs +++ b/crates/kesh-api/tests/invoice_echeancier_e2e.rs @@ -728,3 +728,116 @@ async fn a_closed_fiscal_year_is_announced_and_refused(pool: MySqlPool) { let body: serde_json::Value = resp.json().await.unwrap(); assert_eq!(body["error"]["code"], "FISCAL_YEAR_CLOSED", "got {body:?}"); } + +// --- Story 25-4-a (#455, #456) — le résiduel juste, à la frontière HTTP -------- + +async fn post_credit_note(app: &TestApp, token: &str, invoice_id: i64) -> reqwest::Response { + app.client + .post(app.url("/api/v1/credit-notes")) + .header("Authorization", format!("Bearer {token}")) + .json(&json!({ "invoiceId": invoice_id, "date": "2026-05-10" })) + .send() + .await + .unwrap() +} + +/// Lit un montant rendu par l'API : **présent, non `null`, chaîne**, parsé en +/// `Decimal` — ni comparaison de chaîne (l'échelle suit le calcul SQL), ni +/// `f64`. `null` voudrait dire « non calculé » : ce serait un échec. +fn montant(v: &serde_json::Value, champ: &str) -> rust_decimal::Decimal { + let s = v[champ] + .as_str() + .unwrap_or_else(|| panic!("`{champ}` doit être une chaîne présente, reçu {v:?}")); + s.parse::() + .unwrap_or_else(|e| panic!("`{champ}` = {s:?} n'est pas un décimal : {e}")) +} + +/// #455 — une facture à 8,1 % créditée, jamais réglée : `GET /invoices/{id}` +/// — ouverte aux clés API en lecture — rend un reste dû **nul**, et non la TVA. +#[sqlx::test(migrations = "../kesh-db/test-schema")] +async fn get_credited_invoice_reports_zero_amount_due(pool: MySqlPool) { + let (company_id, admin_id) = seed_base(&pool).await; + let contact_id = seed_contact(&pool, company_id, admin_id).await; + let (id, _v) = create_validated_invoice( + &pool, + company_id, + contact_id, + admin_id, + NaiveDate::from_ymd_opt(2026, 4, 1).unwrap(), + NaiveDate::from_ymd_opt(2026, 4, 30).unwrap(), + dec!(100.00), + ) + .await; + + let app = spawn_app(pool.clone()).await; + let token = login(&app).await; + assert_eq!(post_credit_note(&app, &token, id).await.status(), 201); + + let resp = app + .client + .get(app.url(&format!("/api/v1/invoices/{id}"))) + .header("Authorization", format!("Bearer {token}")) + .send() + .await + .unwrap(); + assert_eq!(resp.status(), 200); + let v: serde_json::Value = resp.json().await.unwrap(); + assert_eq!(v["status"], "cancelled", "anti-vacuité : l'avoir est passé"); + assert_eq!( + montant(&v, "amountDue"), + rust_decimal::Decimal::ZERO, + "et non 8.10 — got {v:?}" + ); + assert_eq!(montant(&v, "amountSettled"), rust_decimal::Decimal::ZERO); +} + +/// #456 — un avoir sur une facture réglée en partie est refusé en **409**, avec +/// son code et un message qui dit quoi faire. +#[sqlx::test(migrations = "../kesh-db/test-schema")] +async fn credit_note_on_partially_settled_invoice_is_409(pool: MySqlPool) { + let (company_id, admin_id) = seed_base(&pool).await; + let contact_id = seed_contact(&pool, company_id, admin_id).await; + let (id, _v) = create_validated_invoice( + &pool, + company_id, + contact_id, + admin_id, + NaiveDate::from_ymd_opt(2026, 4, 1).unwrap(), + NaiveDate::from_ymd_opt(2026, 4, 30).unwrap(), + dec!(100.00), + ) + .await; + let caisse = caisse_id(&pool, company_id).await; + + let app = spawn_app(pool.clone()).await; + let token = login(&app).await; + let settle = app + .client + .post(app.url(&format!("/api/v1/invoices/{id}/settlements"))) + .header("Authorization", format!("Bearer {token}")) + .json(&json!({ + "settlementType": "internal_account", + "accountId": caisse, + "amount": "40.00", + "settledOn": "2026-04-15" + })) + .send() + .await + .unwrap(); + assert_eq!(settle.status(), 200); + + let resp = post_credit_note(&app, &token, id).await; + assert_eq!(resp.status(), 409); + let v: serde_json::Value = resp.json().await.unwrap(); + assert_eq!( + v["error"]["code"], "CREDIT_NOTE_INVOICE_SETTLED", + "got {v:?}" + ); + let message = v["error"]["message"].as_str().unwrap(); + assert!( + message.starts_with("Cette facture porte un règlement, même partiel"), + "message fr attendu, reçu {message:?}" + ); + assert_eq!(v["error"]["details"]["invoiceId"], id); + assert!(v["error"]["details"]["settlementId"].as_i64().is_some()); +} diff --git a/crates/kesh-db/src/errors.rs b/crates/kesh-db/src/errors.rs index 48326cf1..402d0b39 100644 --- a/crates/kesh-db/src/errors.rs +++ b/crates/kesh-db/src/errors.rs @@ -212,9 +212,13 @@ pub enum SettlementCancelBlocker { /// rapprochement à annuler. Coupe court : aucun rang suivant ne s'évalue. BankTransactionNotReconciled, /// La facture a été **créditée** par un avoir après ce règlement. Le - /// règlement est alors un paiement **à lettrer** (Epic 15), pas une - /// anomalie — il ne s'annule pas. ⚠️ Le statut `cancelled` d'une facture - /// ne naît en production que de l'avoir (`credit_notes.rs`). + /// règlement est alors un paiement **à lettrer** (Epic 15) — il ne + /// s'annule pas. ⚠️ Le statut `cancelled` d'une facture ne naît en + /// production que de l'avoir (`credit_notes.rs`). + /// + /// ⚠️ **État hérité depuis la Story 25-4-a (#456)** : un avoir est refusé + /// sur une facture réglée, même en partie. L'état n'est plus atteignable + /// que par l'**import d'une sauvegarde antérieure** — d'où ce motif, gardé. InvoiceCredited, /// Tête **fournisseur** (Story 25-3-a-2) : la facture n'est pas `paid` — il /// n'y a pas de règlement à annuler. ⚠️ Coupe court par construction : une @@ -460,6 +464,29 @@ pub enum DbError { document_label: Option, }, + /// Un avoir ne peut pas viser cette facture : elle porte un **règlement**, + /// même partiel (Story 25-4-a, #456). + /// + /// ⛔ **Les deux conditions, jamais l'une seule** : une ligne + /// `invoice_settlements` **ou** `paid_at` posé — comme + /// [`UnvalidationBlocker::Settled`]. Depuis la 24-2, `paid_at` n'est posé + /// qu'au solde : la garde qui ne lisait que lui laissait passer une facture + /// réglée en partie, et l'avoir, qui contre-passe tout le TTC, rendait la + /// créance **créditrice** du montant encaissé. + /// + /// Conflit d'état → HTTP **409** `CREDIT_NOTE_INVOICE_SETTLED`. Remplace le + /// générique `IllegalStateTransition` de l'ancienne garde AC2bis, qui ne + /// disait ni ce qui bloquait ni quoi faire. + #[error("Avoir refusé : la facture porte un règlement")] + CreditNoteBlockedBySettlement { + invoice_id: i64, + /// Le premier règlement trouvé ; `None` quand seul `paid_at` est posé + /// (facture soldée avant la 24-2, sans ligne de règlement). + settlement_id: Option, + /// Le **numéro** de la facture, que l'utilisateur connaît. + invoice_number: Option, + }, + /// Le règlement ne peut pas être annulé (Story 25-3-a-1, #414). /// /// Conflit d'état → HTTP **409**, avec le code canonique du @@ -655,6 +682,7 @@ impl DbError { Self::AccountRoleInvalidForType { .. } => "ACCOUNT_ROLE_INVALID_FOR_TYPE", Self::InvalidRevenueAccounts(_) => "INVOICE_LINE_REVENUE_ACCOUNT_INVALID", Self::CreditNoteRevenueAccountsArchived(_) => "CREDIT_NOTE_REVENUE_ACCOUNT_ARCHIVED", + Self::CreditNoteBlockedBySettlement { .. } => "CREDIT_NOTE_INVOICE_SETTLED", // ⚠️ Le code EXPOSÉ est celui du `ReversalBlocker` (huit valeurs) ; // celui-ci n'est que le repli générique du mapping structuré. Self::EntryNotReversable { .. } => "ENTRY_NOT_REVERSABLE", diff --git a/crates/kesh-db/src/repositories/credit_notes.rs b/crates/kesh-db/src/repositories/credit_notes.rs index a0b164a7..cf79168e 100644 --- a/crates/kesh-db/src/repositories/credit_notes.rs +++ b/crates/kesh-db/src/repositories/credit_notes.rs @@ -297,16 +297,39 @@ pub async fn create_credit_note( inv.status ))); } - // AC2bis : pas d'avoir sur une facture déjà encaissée (v0.2, pas de - // flux de remboursement — éviterait un solde débiteur négatif). - Some(inv) if inv.paid_at.is_some() => { - return Err(DbError::IllegalStateTransition( - "impossible de créer un avoir sur une facture déjà payée".into(), - )); - } Some(inv) => inv, }; + // AC2bis, refondue par la Story 25-4-a (#456) : pas d'avoir sur une + // facture ENCAISSÉE, même en partie. L'avoir contre-passe tout le TTC ; + // sur une facture réglée, la créance deviendrait créditrice du montant + // encaissé, et Kesh n'a ni remboursement ni imputation pour le dénouer + // (#471 porte la levée de ce refus). + // + // ⛔ **Une ligne de règlement OU `paid_at`** — jamais `paid_at` seul : + // depuis la 24-2, il n'est posé qu'au solde. + // + // ⛔ **Lecture VERROUILLANTE, et après le verrou de la facture** (leçon + // de la 25-3-b) : sous REPEATABLE READ, une lecture simple fige + // l'instantané. Un règlement commité pendant que nous attendions le + // verrou de la facture doit être vu ici. + let settlement: Option = sqlx::query_scalar( + "SELECT id FROM invoice_settlements \ + WHERE invoice_id = ? AND company_id = ? ORDER BY id LIMIT 1 FOR UPDATE", + ) + .bind(invoice_id) + .bind(company_id) + .fetch_optional(&mut *tx) + .await + .map_err(map_db_error)?; + if settlement.is_some() || invoice.paid_at.is_some() { + return Err(DbError::CreditNoteBlockedBySettlement { + invoice_id, + settlement_id: settlement, + invoice_number: invoice.invoice_number.clone(), + }); + } + // AC3 : une seule note de crédit par facture (UNIQUE(invoice_id) en DB ; // check explicite pour une erreur métier claire plutôt qu'une FK 1062). let existing: Option = diff --git a/crates/kesh-db/src/repositories/invoice_settlements.rs b/crates/kesh-db/src/repositories/invoice_settlements.rs index d2c85887..0261a9b0 100644 --- a/crates/kesh-db/src/repositories/invoice_settlements.rs +++ b/crates/kesh-db/src/repositories/invoice_settlements.rs @@ -20,13 +20,14 @@ const COLUMNS: &str = "id, company_id, invoice_id, journal_entry_id, amount, set use crate::entities::{InvoiceSettlement, NewInvoiceSettlement}; use crate::errors::{DbError, map_db_error}; +use crate::repositories::invoices::line_ttc_sql; /// Forme **scalaire par facture** du total réglé — sous-requête corrélée. /// **Prérequis : alias `i` sur `invoices`.** /// /// Miroir exact de [`INVOICE_SETTLED_DERIVED_JOIN_SQL`], qui en est la forme /// agrégée. Les deux doivent rester d'accord : c'est ce que vérifie le test de -/// parité. +/// parité (`tests/invoice_amount_due_parity.rs`). /// /// ⚠️ Même discipline que `INVOICE_TTC_SUBQUERY_SQL` (`invoices.rs`) : la forme /// corrélée sert **une** facture, jamais une liste — elle y serait ré-évaluée @@ -40,22 +41,44 @@ pub const INVOICE_SETTLED_SUBQUERY_SQL: &str = pub const INVOICE_SETTLED_DERIVED_JOIN_SQL: &str = "LEFT JOIN (SELECT invoice_id, SUM(amount) AS settled \ FROM invoice_settlements GROUP BY invoice_id) st ON st.invoice_id = i.id"; -/// Forme **scalaire par facture** de l'avoir émis — `0` s'il n'y en a pas. -/// **Prérequis : alias `i` sur `invoices`.** +/// Forme **scalaire par facture** de l'avoir émis, **TTC** — `0` s'il n'y en +/// a pas. **Prérequis : alias `i` sur `invoices`.** +/// +/// ⛔ **TTC, arrondi ligne par ligne** — la grandeur que l'écriture de l'avoir +/// porte au crédit de la créance (`credit_notes::generate_credit_note_journal_lines` : +/// `total_ht + total_vat`, TVA par [`kesh_core::accounting::vat::line_vat_amount`]). +/// Le résiduel soustrait ce terme d'un TTC : il doit être dans la même unité. +/// **Jamais `credit_notes.total_amount`**, qui est **HT** (miroir de +/// `invoices.total_amount`) : le lire ici laissait la TVA d'une facture +/// créditée en reste dû (#455, Story 25-4-a). /// /// ⚠️ **Seul un avoir `issued` compte.** Les statuts sont /// `draft / issued / cancelled` (`chk_credit_notes_status`) : un brouillon n'a /// pas d'écriture et n'éteint donc rien. `credit_notes.invoice_id` est /// `NOT NULL UNIQUE` — au plus un avoir par facture, la somme est donc une /// commodité de forme, pas un cumul réel. -pub const INVOICE_CREDITED_SUBQUERY_SQL: &str = "(SELECT COALESCE(SUM(cn.total_amount), 0) FROM credit_notes cn \ - WHERE cn.invoice_id = i.id AND cn.status = 'issued')"; +pub const INVOICE_CREDITED_SUBQUERY_SQL: &str = concat!( + "(SELECT COALESCE(SUM(", + line_ttc_sql!("cl."), + "), 0) FROM credit_note_lines cl \ + INNER JOIN credit_notes cn ON cn.id = cl.credit_note_id \ + WHERE cn.invoice_id = i.id AND cn.status = 'issued')" +); -/// Forme **agrégat multi-factures** de l'avoir émis — alias `cnt`. -/// **Prérequis : alias `i` sur `invoices`.** -pub const INVOICE_CREDITED_DERIVED_JOIN_SQL: &str = "LEFT JOIN (SELECT invoice_id, SUM(total_amount) AS credited \ - FROM credit_notes WHERE status = 'issued' GROUP BY invoice_id) cnt \ - ON cnt.invoice_id = i.id"; +/// Forme **agrégat multi-factures** de l'avoir émis, **TTC** — table dérivée +/// à joindre (alias `cnt`), puis `COALESCE(cnt.credited, 0)` côté requête +/// externe. **Prérequis : alias `i` sur `invoices`.** +/// +/// Miroir exact de [`INVOICE_CREDITED_SUBQUERY_SQL`] — même unité, même +/// arrondi ; les deux sont tenues d'accord par le test de parité +/// (`tests/invoice_amount_due_parity.rs`). +pub const INVOICE_CREDITED_DERIVED_JOIN_SQL: &str = concat!( + "LEFT JOIN (SELECT cn.invoice_id, SUM(", + line_ttc_sql!("cl."), + ") AS credited FROM credit_note_lines cl \ + INNER JOIN credit_notes cn ON cn.id = cl.credit_note_id \ + WHERE cn.status = 'issued' GROUP BY cn.invoice_id) cnt ON cnt.invoice_id = i.id" +); /// Enregistre un règlement dans la transaction courante. /// @@ -121,6 +144,10 @@ where /// Ce qui reste dû sur une facture : `TTC − avoir émis − Σ règlements`. /// +/// ⛔ **Trois termes TTC.** Le TTC de la facture et celui de l'avoir sont +/// arrondis ligne par ligne par la même formule (`line_ttc_sql!`) ; les +/// règlements sont des montants encaissés, donc TTC par nature. +/// /// ⚠️ **Calculé, jamais stocké.** C'est la seule source de vérité du « combien /// reste-t-il ? », et elle est adossée aux mêmes données que le grand livre. /// diff --git a/crates/kesh-db/src/repositories/invoice_settlements_write.rs b/crates/kesh-db/src/repositories/invoice_settlements_write.rs index 6b7d692f..415b747c 100644 --- a/crates/kesh-db/src/repositories/invoice_settlements_write.rs +++ b/crates/kesh-db/src/repositories/invoice_settlements_write.rs @@ -328,6 +328,9 @@ pub async fn settlement_cancel_blocker_unlinking( // `validated` ou `cancelled` : l'encaissement exige `validated`, la // dévalidation refuse une facture réglée, et `cancelled` ne naît en // production que de l'avoir. Hors `validated`, c'est donc un avoir. + // ⚠️ Depuis la Story 25-4-a (#456), l'avoir est refusé sur une facture + // réglée : ce rang ne se déclenche plus que sur un état HÉRITÉ, ramené par + // l'import d'une sauvegarde antérieure. if status != "validated" { return Ok(Some((SettlementCancelBlocker::InvoiceCredited, None, None))); } diff --git a/crates/kesh-db/src/repositories/invoices.rs b/crates/kesh-db/src/repositories/invoices.rs index b62c4e68..526bbc5a 100644 --- a/crates/kesh-db/src/repositories/invoices.rs +++ b/crates/kesh-db/src/repositories/invoices.rs @@ -147,7 +147,32 @@ pub enum PausedFilter { NotPaused, } -/// Paramètres de recherche, tri et pagination. +/// **Source unique** de la formule SQL du TTC d'UNE ligne (facture ou avoir) — +/// Story 25-4-a (#455). Rend un **littéral** (`concat!`), pour que les +/// constantes qui la composent restent des `&'static str`. +/// +/// `$p` est le préfixe de colonne : `"l."`, `"cl."`, ou `""` dans une table +/// dérivée sans alias. Arrondi **par ligne**, half-away-from-zero : miroir de +/// `kesh_core::accounting::vat::line_vat_amount`, que l'écriture de vente +/// comme celle de l'avoir emploient. +/// +/// ⛔ Ne pas recopier la formule à la main : la TVA de l'avoir a été soustraite +/// au HT pendant des mois (#455) parce que la grandeur « avoir » n'empruntait +/// pas cette formule-ci. +macro_rules! line_ttc_sql { + ($p:literal) => { + concat!( + $p, + "line_total + ROUND(", + $p, + "line_total * ", + $p, + "vat_rate / 100, 2)" + ) + }; +} +pub(crate) use line_ttc_sql; + /// Forme **scalaire par facture** du TTC canonique (#246, Story 21-2a) — /// sous-requête corrélée. **Prérequis : alias `i` sur `invoices`.** /// @@ -158,8 +183,11 @@ pub enum PausedFilter { /// /// `pub` (PAS `pub(crate)`) : consommée par `repositories::reconciliation` /// (21-2b) et par `kesh-report` (balance âgée 21-7) — source de vérité unique. -pub const INVOICE_TTC_SUBQUERY_SQL: &str = "(SELECT COALESCE(SUM(l.line_total + ROUND(l.line_total * l.vat_rate / 100, 2)), 0) \ - FROM invoice_lines l WHERE l.invoice_id = i.id)"; +pub const INVOICE_TTC_SUBQUERY_SQL: &str = concat!( + "(SELECT COALESCE(SUM(", + line_ttc_sql!("l."), + "), 0) FROM invoice_lines l WHERE l.invoice_id = i.id)" +); /// Forme **agrégat multi-factures** du TTC canonique (#246) — table dérivée à /// joindre (alias `lt`), puis `SUM(COALESCE(lt.ttc, 0))` côté requête externe. @@ -168,9 +196,11 @@ pub const INVOICE_TTC_SUBQUERY_SQL: &str = "(SELECT COALESCE(SUM(l.line_total + /// Choix de la table dérivée plutôt que la corrélée dans le `SUM()` externe : /// raison de **performance** (la corrélée serait ré-évaluée par ligne et par /// `CASE`), pas un interdit SQL (MariaDB accepte les deux — vérifié). -pub const INVOICE_TTC_DERIVED_JOIN_SQL: &str = "LEFT JOIN (SELECT invoice_id, \ - SUM(line_total + ROUND(line_total * vat_rate / 100, 2)) AS ttc \ - FROM invoice_lines GROUP BY invoice_id) lt ON lt.invoice_id = i.id"; +pub const INVOICE_TTC_DERIVED_JOIN_SQL: &str = concat!( + "LEFT JOIN (SELECT invoice_id, SUM(", + line_ttc_sql!(""), + ") AS ttc FROM invoice_lines GROUP BY invoice_id) lt ON lt.invoice_id = i.id" +); /// TTC canonique d'UNE facture (#246) via la forme scalaire — pour les /// call-sites qui n'ont pas les lignes chargées (ex. re-score de la @@ -189,6 +219,7 @@ where .map_err(map_db_error) } +/// Paramètres de recherche, tri et pagination. #[derive(Debug, Clone, Default)] pub struct InvoiceListQuery { pub search: Option, diff --git a/crates/kesh-db/tests/credit_notes_repository.rs b/crates/kesh-db/tests/credit_notes_repository.rs index f5f97df7..0c16ade8 100644 --- a/crates/kesh-db/tests/credit_notes_repository.rs +++ b/crates/kesh-db/tests/credit_notes_repository.rs @@ -10,9 +10,9 @@ use chrono::NaiveDate; use kesh_db::entities::contact::{ContactType, NewContact}; -use kesh_db::entities::{NewCreditNote, NewInvoice, NewInvoiceLine}; +use kesh_db::entities::{NewCreditNote, NewInvoice, NewInvoiceLine, SettlementChoice}; use kesh_db::errors::DbError; -use kesh_db::repositories::{contacts, credit_notes, invoices}; +use kesh_db::repositories::{contacts, credit_notes, invoice_settlements_write, invoices}; use kesh_db::test_fixtures::{SeededCompany, seed_accounting_company}; use rust_decimal::Decimal; use rust_decimal_macros::dec; @@ -290,9 +290,18 @@ async fn credit_note_refused_on_paid_invoice(pool: MySqlPool) { ) .await .unwrap_err(); + // Story 25-4-a (#456) : la garde AC2bis rend sa variante dédiée, plus le + // générique — le motif « payée » et le motif « réglée en partie » sont un + // seul et même refus. `settlement_id` est `None` : seul `paid_at` est posé. assert!( - matches!(err, DbError::IllegalStateTransition(_)), - "refus facture payée" + matches!( + err, + DbError::CreditNoteBlockedBySettlement { + settlement_id: None, + .. + } + ), + "refus facture payée — reçu {err:?}" ); } @@ -423,3 +432,225 @@ async fn credit_note_inherits_project_and_nets_to_zero(pool: MySqlPool) { .unwrap(); assert_eq!(debit, credit, "net par projet doit être 0 après l'avoir"); } + +// ─── Story 25-4-a (#456) — pas d'avoir sur une facture réglée, même en partie ─── + +async fn settle(pool: &MySqlPool, seeded: &SeededCompany, invoice_id: i64, amount: Decimal) -> i64 { + let out = invoice_settlements_write::settle_invoice( + pool, + seeded.admin_user_id, + seeded.company_id, + invoice_id, + SettlementChoice::InternalAccount { + account_id: seeded.accounts["1000"], + }, + amount, + d(2026, 6, 20), + ) + .await + .expect("règlement"); + sqlx::query_scalar("SELECT id FROM invoice_settlements WHERE journal_entry_id = ?") + .bind(out.journal_entry_id) + .fetch_one(pool) + .await + .expect("ligne de règlement") +} + +async fn try_credit( + pool: &MySqlPool, + seeded: &SeededCompany, + invoice_id: i64, +) -> Result { + credit_notes::create_credit_note( + pool, + NewCreditNote { + company_id: seeded.company_id, + invoice_id, + date: d(2026, 7, 1), + }, + seeded.admin_user_id, + ) + .await +} + +/// Ce qu'un refus ne doit pas avoir touché : avoirs, statut et version de la +/// facture, solde de la créance, séquence de numéros d'avoir. +async fn empreinte( + pool: &MySqlPool, + seeded: &SeededCompany, + invoice_id: i64, +) -> (i64, String, i32, Decimal, i64) { + let avoirs: i64 = sqlx::query_scalar("SELECT COUNT(*) FROM credit_notes WHERE company_id = ?") + .bind(seeded.company_id) + .fetch_one(pool) + .await + .unwrap(); + let (status, version): (String, i32) = + sqlx::query_as("SELECT status, version FROM invoices WHERE id = ?") + .bind(invoice_id) + .fetch_one(pool) + .await + .unwrap(); + let creance: Decimal = sqlx::query_scalar( + "SELECT COALESCE(SUM(debit) - SUM(credit), 0) FROM journal_entry_lines WHERE account_id = ?", + ) + .bind(seeded.accounts["1100"]) + .fetch_one(pool) + .await + .unwrap(); + let sequence: i64 = sqlx::query_scalar( + "SELECT CAST(COALESCE(SUM(next_number), 0) AS SIGNED) FROM credit_note_number_sequences WHERE company_id = ?", + ) + .bind(seeded.company_id) + .fetch_one(pool) + .await + .unwrap(); + (avoirs, status, version, creance, sequence) +} + +/// AC 9, AC 10 — une facture réglée en partie (`paid_at` NULL) est refusée, et +/// **rien n'est écrit**. C'est le chemin que l'ancienne garde laissait passer. +#[sqlx::test(migrations = "./test-schema")] +async fn credit_note_refused_on_partially_settled_invoice(pool: MySqlPool) { + let seeded = seed_accounting_company(&pool).await.unwrap(); + let contact = make_contact(&pool, seeded.company_id, seeded.admin_user_id).await; + let invoice_id = + create_and_validate(&pool, &seeded, contact, &[(dec!(8.10), dec!(100.00))]).await; + let sid = settle(&pool, &seeded, invoice_id, dec!(40.00)).await; + + let paid_at: Option = + sqlx::query_scalar("SELECT paid_at FROM invoices WHERE id = ?") + .bind(invoice_id) + .fetch_one(&pool) + .await + .unwrap(); + assert!( + paid_at.is_none(), + "anti-vacuité : la facture n'est PAS soldée" + ); + + let avant = empreinte(&pool, &seeded, invoice_id).await; + let err = try_credit(&pool, &seeded, invoice_id).await.unwrap_err(); + match &err { + DbError::CreditNoteBlockedBySettlement { + invoice_id: iid, + settlement_id, + invoice_number, + } => { + assert_eq!(*iid, invoice_id); + assert_eq!(*settlement_id, Some(sid)); + assert!(invoice_number.is_some(), "le numéro est rendu"); + } + other => panic!("attendu CreditNoteBlockedBySettlement, reçu {other:?}"), + } + assert_eq!(err.error_code(), "CREDIT_NOTE_INVOICE_SETTLED"); + assert_eq!( + empreinte(&pool, &seeded, invoice_id).await, + avant, + "rien n'est écrit" + ); +} + +/// La promesse du message : annuler le règlement rouvre l'avoir. +#[sqlx::test(migrations = "./test-schema")] +async fn credit_note_accepted_after_the_settlement_is_cancelled(pool: MySqlPool) { + let seeded = seed_accounting_company(&pool).await.unwrap(); + let contact = make_contact(&pool, seeded.company_id, seeded.admin_user_id).await; + let invoice_id = + create_and_validate(&pool, &seeded, contact, &[(dec!(8.10), dec!(100.00))]).await; + let sid = settle(&pool, &seeded, invoice_id, dec!(40.00)).await; + assert!(try_credit(&pool, &seeded, invoice_id).await.is_err()); + + invoice_settlements_write::cancel_settlement( + &pool, + seeded.admin_user_id, + seeded.company_id, + invoice_id, + sid, + ) + .await + .expect("annulation du règlement"); + + try_credit(&pool, &seeded, invoice_id) + .await + .expect("l'avoir passe une fois le règlement annulé"); +} + +/// AC 9 — ⛔ **entrelacement** : un règlement commité PENDANT que l'avoir +/// attend le verrou de la facture doit être vu. +/// +/// 1. une transaction verrouille la facture et y rattache un règlement, sans +/// valider ; +/// 2. l'avoir démarre et attend au `FOR UPDATE` de la facture ; +/// 3. la transaction est validée. +/// +/// La garde lit les règlements **après** le verrou, par une lecture +/// verrouillante : elle voit la ligne et refuse. Lue **avant** le verrou, en +/// lecture simple, elle aurait vu « aucun règlement » et laissé passer. +/// +/// Le règlement vient du vrai chemin, sur une facture auxiliaire : seul son +/// rattachement se fait dans la transaction concurrente. +#[sqlx::test(migrations = "./test-schema")] +async fn credit_note_waits_for_a_concurrent_settlement(pool: MySqlPool) { + let seeded = seed_accounting_company(&pool).await.unwrap(); + let contact = make_contact(&pool, seeded.company_id, seeded.admin_user_id).await; + let invoice_id = + create_and_validate(&pool, &seeded, contact, &[(dec!(8.10), dec!(100.00))]).await; + let parking = create_and_validate(&pool, &seeded, contact, &[(dec!(8.10), dec!(100.00))]).await; + let sid = settle(&pool, &seeded, parking, dec!(40.00)).await; + + // (1) Le règlement concurrent, non validé, qui tient la facture. + let mut concurrent = pool.begin().await.unwrap(); + sqlx::query("SELECT id FROM invoices WHERE id = ? FOR UPDATE") + .bind(invoice_id) + .execute(&mut *concurrent) + .await + .unwrap(); + sqlx::query("UPDATE invoice_settlements SET invoice_id = ? WHERE id = ?") + .bind(invoice_id) + .bind(sid) + .execute(&mut *concurrent) + .await + .unwrap(); + + // (2) L'avoir démarre et attend. + let p = pool.clone(); + let (company_id, user_id) = (seeded.company_id, seeded.admin_user_id); + let avoir = tokio::spawn(async move { + credit_notes::create_credit_note( + &p, + NewCreditNote { + company_id, + invoice_id, + date: d(2026, 7, 1), + }, + user_id, + ) + .await + }); + let vue = kesh_db::test_fixtures::attendre_une_requete_en_cours( + &pool, + &["FROM invoices", "FOR UPDATE"], + || avoir.is_finished(), + ) + .await; + if !vue { + panic!("l'avoir a fini sans attendre le verrou : {:?}", avoir.await); + } + + // (3) Le règlement concurrent est validé. + concurrent.commit().await.unwrap(); + + let result = avoir.await.expect("tâche d'avoir"); + assert!( + matches!( + result, + Err(DbError::CreditNoteBlockedBySettlement { + settlement_id: Some(s), + .. + }) if s == sid + ), + "l'avoir devait attendre le règlement puis refuser — reçu {:?}", + result.map(|c| c.credit_note.id) + ); +} diff --git a/crates/kesh-db/tests/invoice_amount_due_parity.rs b/crates/kesh-db/tests/invoice_amount_due_parity.rs new file mode 100644 index 00000000..79523b62 --- /dev/null +++ b/crates/kesh-db/tests/invoice_amount_due_parity.rs @@ -0,0 +1,311 @@ +//! Le résiduel juste — Story 25-4-a (#455, #456). +//! +//! `amount_due` = `TTC − avoir émis − Σ règlements`. Trois garanties : +//! +//! 1. **L'avoir est compté TTC** (#455) : le terme « avoir » vaut le crédit que +//! l'écriture de l'avoir porte à la créance — pas `credit_notes.total_amount`, +//! qui est HT. C'est l'assertion de **concordance avec le grand livre** qui le +//! prouve, pas une valeur recalculée à côté. +//! 2. **Les formes scalaire et jointe sont d'accord**, facture par facture, pour le +//! réglé comme pour l'avoir — le test de parité que le doc-comment +//! d'`INVOICE_SETTLED_SUBQUERY_SQL` annonçait sans qu'il existe. Les formes +//! jointes n'avaient aucun appelant ; 25-4-b en sera le premier. +//! 3. **Chaque facture porte une TVA non nulle.** À 0 %, HT = TTC et #455 est +//! invisible — c'est ainsi qu'il a passé tous les tests de la 24-2. +//! +//! Pré-requis : MariaDB démarré (`sqlx::test` crée une DB éphémère par test). + +use chrono::NaiveDate; +use kesh_db::entities::contact::{ContactType, NewContact}; +use kesh_db::entities::{NewCreditNote, NewInvoice, NewInvoiceLine, SettlementChoice}; +use kesh_db::repositories::invoice_settlements::{ + INVOICE_CREDITED_DERIVED_JOIN_SQL, INVOICE_CREDITED_SUBQUERY_SQL, + INVOICE_SETTLED_DERIVED_JOIN_SQL, INVOICE_SETTLED_SUBQUERY_SQL, amount_due, +}; +use kesh_db::repositories::{contacts, credit_notes, invoice_settlements_write, invoices}; +use kesh_db::test_fixtures::{SeededCompany, seed_accounting_company}; +use rust_decimal::Decimal; +use rust_decimal_macros::dec; +use sqlx::MySqlPool; + +fn d(y: i32, m: u32, day: u32) -> NaiveDate { + NaiveDate::from_ymd_opt(y, m, day).unwrap() +} + +async fn make_contact(pool: &MySqlPool, seeded: &SeededCompany) -> i64 { + contacts::create( + pool, + seeded.admin_user_id, + NewContact { + company_id: seeded.company_id, + contact_type: ContactType::Entreprise, + name: "Client Résiduel".into(), + first_name: None, + last_name: None, + is_client: true, + is_supplier: false, + address: Some("Rue 1\n1000 Lausanne".into()), + address_street: None, + address_building: None, + address_postal_code: None, + address_city: None, + address_country: None, + email: None, + phone: None, + ide_number: None, + client_number: None, + default_payment_terms: Some("30".into()), + default_payment_terms_days: None, + language: None, + salutation: kesh_db::entities::contact::Salutation::Neutre, + }, + ) + .await + .expect("contact") + .id +} + +/// Une facture validée par le **vrai chemin** (écriture de vente TVA comprise) — +/// jamais par le helper à 0 % de `invoice_settlement.rs`. +async fn validated( + pool: &MySqlPool, + seeded: &SeededCompany, + contact_id: i64, + lines: &[(Decimal, Decimal)], +) -> i64 { + let new = NewInvoice { + company_id: seeded.company_id, + contact_id, + date: d(2026, 6, 15), + due_date: None, + payment_terms: None, + lines: lines + .iter() + .map(|(rate, price)| NewInvoiceLine { + revenue_account_id: None, + description: "Ligne".into(), + quantity: dec!(1), + unit_price: *price, + vat_rate: *rate, + }) + .collect(), + project_id: None, + }; + let (inv, _) = invoices::create(pool, seeded.admin_user_id, new) + .await + .expect("create invoice"); + invoices::validate_invoice(pool, seeded.company_id, inv.id, seeded.admin_user_id) + .await + .expect("validate invoice"); + inv.id +} + +async fn settle(pool: &MySqlPool, seeded: &SeededCompany, invoice_id: i64, amount: Decimal) -> i64 { + invoice_settlements_write::settle_invoice( + pool, + seeded.admin_user_id, + seeded.company_id, + invoice_id, + SettlementChoice::InternalAccount { + account_id: seeded.accounts["1000"], + }, + amount, + d(2026, 6, 20), + ) + .await + .expect("règlement") + .journal_entry_id +} + +async fn credit( + pool: &MySqlPool, + seeded: &SeededCompany, + invoice_id: i64, +) -> credit_notes::IssuedCreditNote { + credit_notes::create_credit_note( + pool, + NewCreditNote { + company_id: seeded.company_id, + invoice_id, + date: d(2026, 7, 1), + }, + seeded.admin_user_id, + ) + .await + .expect("avoir") +} + +/// L'état hérité **règlement puis avoir**, que 25-4-a rend inatteignable par +/// l'application mais qu'un `.keshbackup` v0.12.0 peut ramener. +/// +/// Gabarit « détacher, créditer, rattacher » (AC 13) : les deux écritures +/// viennent des **vrais chemins** ; seul le rattachement de la ligne de +/// règlement passe en SQL, le temps de l'avoir. +async fn settle_then_legacy_credit( + pool: &MySqlPool, + seeded: &SeededCompany, + contact_id: i64, + invoice_id: i64, + amount: Decimal, +) { + settle(pool, seeded, invoice_id, amount).await; + let parking = validated(pool, seeded, contact_id, &[(dec!(8.10), dec!(10.00))]).await; + sqlx::query("UPDATE invoice_settlements SET invoice_id = ? WHERE invoice_id = ?") + .bind(parking) + .bind(invoice_id) + .execute(pool) + .await + .expect("détacher"); + credit(pool, seeded, invoice_id).await; + sqlx::query("UPDATE invoice_settlements SET invoice_id = ? WHERE invoice_id = ?") + .bind(invoice_id) + .bind(parking) + .execute(pool) + .await + .expect("rattacher"); +} + +async fn credited_scalar(pool: &MySqlPool, invoice_id: i64) -> Decimal { + sqlx::query_scalar(&format!( + "SELECT {INVOICE_CREDITED_SUBQUERY_SQL} FROM invoices i WHERE i.id = ?" + )) + .bind(invoice_id) + .fetch_one(pool) + .await + .expect("avoir scalaire") +} + +/// AC 1, AC 7 — ⛔ le terme « avoir » du résiduel égale le crédit que +/// l'écriture de l'avoir porte à la créance. Multi-taux, arrondis limites. +#[sqlx::test(migrations = "./test-schema")] +async fn credited_amount_is_ttc_and_matches_the_ledger(pool: MySqlPool) { + let seeded = seed_accounting_company(&pool).await.unwrap(); + let contact = make_contact(&pool, &seeded).await; + let invoice_id = validated( + &pool, + &seeded, + contact, + &[ + (dec!(8.10), dec!(1000.00)), + (dec!(2.60), dec!(123.45)), + (dec!(8.10), dec!(0.05)), + ], + ) + .await; + + let issued = credit(&pool, &seeded, invoice_id).await; + let receivable = seeded.accounts["1100"]; + let ledger_credit: Decimal = issued + .journal_entry + .lines + .iter() + .filter(|l| l.account_id == receivable) + .map(|l| l.credit) + .sum(); + + // 1000 + 81.00 ; 123.45 + 3.21 ; 0.05 + 0.00 (sous-centime) = 1207.71 + assert_eq!(ledger_credit, dec!(1207.71), "crédit créance de l'avoir"); + assert_eq!(credited_scalar(&pool, invoice_id).await, ledger_credit); + assert_ne!( + issued.credit_note.total_amount, ledger_credit, + "le montant stocké est HT : s'il égalait le crédit, le test ne distinguerait rien" + ); +} + +/// AC 8 — facture créditée, jamais réglée : il ne reste rien à payer. +#[sqlx::test(migrations = "./test-schema")] +async fn amount_due_of_a_credited_invoice_is_zero(pool: MySqlPool) { + let seeded = seed_accounting_company(&pool).await.unwrap(); + let contact = make_contact(&pool, &seeded).await; + let invoice_id = validated(&pool, &seeded, contact, &[(dec!(8.10), dec!(100.00))]).await; + credit(&pool, &seeded, invoice_id).await; + + assert_eq!( + amount_due(&pool, invoice_id).await.unwrap(), + Decimal::ZERO, + "et non 8.10, la TVA (#455)" + ); +} + +/// AC 8 — l'état hérité : 40 encaissés sur une vente annulée. Le reste dû vaut +/// −40, un trop-perçu visible — pas `TVA − 40`, et pas écrêté à zéro. +#[sqlx::test(migrations = "./test-schema")] +async fn amount_due_after_settlement_then_legacy_credit_is_minus_settled(pool: MySqlPool) { + let seeded = seed_accounting_company(&pool).await.unwrap(); + let contact = make_contact(&pool, &seeded).await; + let invoice_id = validated(&pool, &seeded, contact, &[(dec!(8.10), dec!(100.00))]).await; + settle_then_legacy_credit(&pool, &seeded, contact, invoice_id, dec!(40.00)).await; + + assert_eq!(amount_due(&pool, invoice_id).await.unwrap(), dec!(-40.00)); +} + +/// AC 6 — la forme scalaire et la forme jointe rendent la même valeur, facture +/// par facture, pour le réglé et pour l'avoir. Jeu : sans règlement, un +/// règlement, deux règlements, avoir émis, avoir brouillon, état hérité. +#[sqlx::test(migrations = "./test-schema")] +async fn settled_and_credited_forms_are_at_parity(pool: MySqlPool) { + let seeded = seed_accounting_company(&pool).await.unwrap(); + let contact = make_contact(&pool, &seeded).await; + let lines = [(dec!(8.10), dec!(100.00)), (dec!(2.60), dec!(33.33))]; + + let _plain = validated(&pool, &seeded, contact, &lines).await; + let one = validated(&pool, &seeded, contact, &lines).await; + settle(&pool, &seeded, one, dec!(40.00)).await; + let two = validated(&pool, &seeded, contact, &lines).await; + settle(&pool, &seeded, two, dec!(40.00)).await; + settle(&pool, &seeded, two, dec!(12.34)).await; + let credited = validated(&pool, &seeded, contact, &lines).await; + credit(&pool, &seeded, credited).await; + let legacy = validated(&pool, &seeded, contact, &lines).await; + settle_then_legacy_credit(&pool, &seeded, contact, legacy, dec!(25.00)).await; + + // Avoir BROUILLON : sans écriture, il n'éteint rien — monté en SQL, aucun + // chemin applicatif ne produisant de brouillon (single-step, DC5). + let draft_target = validated(&pool, &seeded, contact, &lines).await; + let cn = sqlx::query( + "INSERT INTO credit_notes (company_id, invoice_id, contact_id, date, status, total_amount) \ + VALUES (?, ?, ?, ?, 'draft', 133.33)", + ) + .bind(seeded.company_id) + .bind(draft_target) + .bind(contact) + .bind(d(2026, 7, 1)) + .execute(&pool) + .await + .expect("avoir brouillon"); + sqlx::query( + "INSERT INTO credit_note_lines (credit_note_id, position, description, quantity, unit_price, vat_rate, line_total) \ + VALUES (?, 1, 'Ligne', 1, 100.00, 8.10, 100.00)", + ) + .bind(cn.last_insert_id()) + .execute(&pool) + .await + .expect("ligne d'avoir brouillon"); + + let rows: Vec<(i64, Decimal, Decimal, Decimal, Decimal)> = sqlx::query_as(&format!( + "SELECT i.id, {INVOICE_SETTLED_SUBQUERY_SQL}, COALESCE(st.settled, 0), \ + {INVOICE_CREDITED_SUBQUERY_SQL}, COALESCE(cnt.credited, 0) \ + FROM invoices i {INVOICE_SETTLED_DERIVED_JOIN_SQL} {INVOICE_CREDITED_DERIVED_JOIN_SQL} \ + WHERE i.company_id = ? ORDER BY i.id" + )) + .bind(seeded.company_id) + .fetch_all(&pool) + .await + .expect("parité"); + + // Six factures du jeu, plus la facture auxiliaire du gabarit. + assert_eq!(rows.len(), 7, "le jeu entier est lu : {rows:?}"); + for (id, settled_s, settled_j, credited_s, credited_j) in &rows { + assert_eq!(settled_s, settled_j, "réglé, facture {id}"); + assert_eq!(credited_s, credited_j, "avoir, facture {id}"); + } + // Anti-vacuité : la parité porte sur des valeurs NON nulles des deux côtés. + let by_id = |x: i64| rows.iter().find(|r| r.0 == x).unwrap(); + assert_eq!(by_id(two).1, dec!(52.34)); + assert_eq!(by_id(credited).3, dec!(142.30)); // 100 + 8.10 ; 33.33 + 0.87 + assert_eq!( + by_id(draft_target).3, + Decimal::ZERO, + "un brouillon n'éteint rien" + ); +} diff --git a/crates/kesh-db/tests/invoice_settlement.rs b/crates/kesh-db/tests/invoice_settlement.rs index 0f01479f..f14d1f21 100644 --- a/crates/kesh-db/tests/invoice_settlement.rs +++ b/crates/kesh-db/tests/invoice_settlement.rs @@ -974,6 +974,25 @@ async fn monter(pool: &MySqlPool, motifs: &[SettlementCancelBlocker]) -> (Seeded .await; if motifs.contains(&SettlementCancelBlocker::InvoiceCredited) { + // ⚠️ **État HÉRITÉ, plus atteignable par l'application** depuis la + // Story 25-4-a (#456) : un avoir est désormais refusé sur une facture + // réglée, même en partie. Il reste possible par l'**import d'une + // sauvegarde antérieure** — c'est ce qui justifie de garder le motif + // `InvoiceCredited` et ce montage. + // + // Gabarit « détacher, créditer, rattacher » : les deux écritures + // (règlement, avoir) viennent des VRAIS chemins ; seul le rattachement + // de la ligne de règlement passe en SQL, le temps de l'avoir. La + // facture auxiliaire est datée dans l'exercice seedé, que le cas + // « aucun exercice ouvert » garde valide. + let parking = + validated_invoice(pool, &seeded, dec!(10.00), d - chrono::Duration::days(20)).await; + sqlx::query("UPDATE invoice_settlements SET invoice_id = ? WHERE id = ?") + .bind(parking) + .bind(sid) + .execute(pool) + .await + .expect("détacher le règlement"); credit_notes::create_credit_note( pool, kesh_db::entities::NewCreditNote { @@ -984,7 +1003,13 @@ async fn monter(pool: &MySqlPool, motifs: &[SettlementCancelBlocker]) -> (Seeded seeded.admin_user_id, ) .await - .expect("avoir après règlement partiel — le vrai chemin l'accepte"); + .expect("avoir sur la facture détachée de son règlement"); + sqlx::query("UPDATE invoice_settlements SET invoice_id = ? WHERE id = ?") + .bind(inv_id) + .bind(sid) + .execute(pool) + .await + .expect("rattacher le règlement"); } if motifs.contains(&SettlementCancelBlocker::MatchedBankTransaction) { match_to_bank( diff --git a/crates/kesh-i18n/locales/de-CH/messages.ftl b/crates/kesh-i18n/locales/de-CH/messages.ftl index 3a86241f..6c151ed9 100644 --- a/crates/kesh-i18n/locales/de-CH/messages.ftl +++ b/crates/kesh-i18n/locales/de-CH/messages.ftl @@ -24,6 +24,7 @@ error-journal-entry-linked-to-invoice = Diese Buchung stammt aus einer freigegeb error-check-constraint = Ungültiger Wert error-illegal-state = Unzulässiger Statusübergang error-invoice-unvalidate-blocked-settled = Diese Rechnung weist eine — auch nur teilweise — Zahlung auf: stornieren Sie diese zuerst. +error-credit-note-blocked-settled = Diese Rechnung weist eine — auch nur teilweise — Zahlung auf: Stornieren Sie diese zuerst, um eine Gutschrift ausstellen zu können. error-invoice-unvalidate-blocked-credited = Diese Rechnung wurde durch eine Gutschrift korrigiert, die bereits die Korrektur darstellt. error-invoice-unvalidate-blocked-reminders = Diese Rechnung hat eine Mahnhistorie: deren Löschung würde den Nachweis des Inkassos vernichten. error-invoice-unvalidate-blocked-emailed = Diese Rechnung wurde dem Kunden zugestellt: korrigieren Sie sie mit einer Gutschrift. diff --git a/crates/kesh-i18n/locales/en-CH/messages.ftl b/crates/kesh-i18n/locales/en-CH/messages.ftl index fe0684f0..92ffd928 100644 --- a/crates/kesh-i18n/locales/en-CH/messages.ftl +++ b/crates/kesh-i18n/locales/en-CH/messages.ftl @@ -24,6 +24,7 @@ error-journal-entry-linked-to-invoice = This journal entry was generated by a va error-check-constraint = Invalid value error-illegal-state = Illegal state transition error-invoice-unvalidate-blocked-settled = This invoice carries a payment, even a partial one: cancel it first. +error-credit-note-blocked-settled = This invoice carries a payment, even a partial one: cancel it first to be able to issue a credit note. error-invoice-unvalidate-blocked-credited = This invoice is credited by a credit note, which is already the correction. error-invoice-unvalidate-blocked-reminders = This invoice has a reminder history: removing it would erase the proof of collection. error-invoice-unvalidate-blocked-emailed = This invoice was sent to the client: correct it with a credit note. diff --git a/crates/kesh-i18n/locales/fr-CH/messages.ftl b/crates/kesh-i18n/locales/fr-CH/messages.ftl index 8aaa3a6a..9e47a49e 100644 --- a/crates/kesh-i18n/locales/fr-CH/messages.ftl +++ b/crates/kesh-i18n/locales/fr-CH/messages.ftl @@ -24,6 +24,7 @@ error-journal-entry-linked-to-invoice = Cette écriture comptable a été géné error-check-constraint = Valeur invalide error-illegal-state = Transition d'état interdite error-invoice-unvalidate-blocked-settled = Cette facture porte un règlement, même partiel : annulez-le d'abord. +error-credit-note-blocked-settled = Cette facture porte un règlement, même partiel : annulez-le d'abord pour pouvoir émettre un avoir. error-invoice-unvalidate-blocked-credited = Cette facture est créditée par un avoir, qui en est déjà la correction. error-invoice-unvalidate-blocked-reminders = Cette facture a un historique de rappels : le dévalider effacerait la preuve du recouvrement. error-invoice-unvalidate-blocked-emailed = Cette facture a été envoyée au client : corrigez-la par un avoir. diff --git a/crates/kesh-i18n/locales/it-CH/messages.ftl b/crates/kesh-i18n/locales/it-CH/messages.ftl index 5cb50f27..06f35274 100644 --- a/crates/kesh-i18n/locales/it-CH/messages.ftl +++ b/crates/kesh-i18n/locales/it-CH/messages.ftl @@ -24,6 +24,7 @@ error-journal-entry-linked-to-invoice = Questa registrazione contabile è stata error-check-constraint = Valore non valido error-illegal-state = Transizione di stato non consentita error-invoice-unvalidate-blocked-settled = Questa fattura presenta un pagamento, anche parziale: annullatelo prima. +error-credit-note-blocked-settled = Questa fattura presenta un pagamento, anche parziale: annullalo prima per poter emettere una nota di credito. error-invoice-unvalidate-blocked-credited = Questa fattura è stornata da una nota di credito, che ne è già la correzione. error-invoice-unvalidate-blocked-reminders = Questa fattura ha uno storico di solleciti: eliminarlo cancellerebbe la prova del recupero. error-invoice-unvalidate-blocked-emailed = Questa fattura è stata inviata al cliente: correggetela con una nota di credito. diff --git a/docs/manual/fr/user-manual.pdf b/docs/manual/fr/user-manual.pdf index f402efacd064c0d629e774329a4ebf85ccc8cddd..139a7469d9ca487daeca37fabb66e16230e00dcb 100644 GIT binary patch delta 127804 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�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

@lfr;_q5P-uG6MwJ0xI7Uu~vT0>mc46S0rA!T3 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 U3j{X`l4$a zQmq&;wsu+@P-oMY+?Hb?7;w4~Y$xMm6QlslEyHDHr4oi`A1^ZJjd;%ZSu|8`-}X2n zHO6=M!ZSmB{hjg8fq(b|9JC|VnSi9U#remKElY5gcll^me^$c=iNuHDpysPLftzT~ z%)eN$rM`n+YHX6-D}S`{SDToG zR}lfuv>&^(()O6`u>4gs=)0|ZCNVl7)J0cX6SKJ0zgRZ$NM@{B{n?eBSX*<|^95bU zRQn1`Sg=mY-Ks9vY!(*F?^Z=aximJJ10;u8tV@EEBC3wh*0wLAGB_Z-bAtBz&onuH z_edn<&6cHmSuzITZ|xYxwTd)^f_;pp@~>;l4BL1g4KNi zlO#}VrC}iw^e~Legf(s|M^KsLj8iejLybJ&CCD&Y z7o$#?Dz16vGBTNh5H|}iI!6~kOnHr}VhZ#|M`KX|@_-Zu%y0p-BA!5%4?^Y0O|>lL zam9D$G-YQ-nd+_D)G+pEmEE73MK1Eo)*=IrDuSh1kxt2f7?&g6-!kUlJ~1f|25GiV zjS~}Y$1@E7=B(*I8dcPD$88?RSv)~n{LIB3E1uih_a{--?@sUM7H^(_AP+!5UK~pL z=sH^mc(y1CJN@etJa+NpS{!8$&EJ%nZ>HB@xaKoi4j=We0`Sn)Iz`Y?3RNvHEmVpT zdwAukZ0+SB-Y(dxd><}T9)G{y_UAr>A)I}`FcQRq5gfV={0k)FYA{#Vk5#-@fkq*~ z+Q^Cy4rVTD;&l8P;IF0t2iXQ~bfin-0{fPlOX%0D;yF>5=t)s_z)ZuOrs2Gkmwt)C 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 L$;rhPC1C$QKSLtR delta 125782 zcmY(K)0QR*kgUtLZQHhO+jf`rmu=g&ZFbqV)n)tanX5C8kQbS;zKn>BOXQ0!jSi`}} zp}*tC^v;by7X9i2^JoVyXn1o#7I4WbPe9!&AYmhb2zK53v_|0wzh;K8Q!a!??M7^G zU}T1r2xDfO#L+Lvttki6D$h;bpqZt#>z@zXZs*Vi6!gDnz*5X2%^-XN#1cWGX0rGR zc;r%AaY>QS7j{eMpu3ZafnIq;91tV~OJj)mwc=gd1SOd>Qw^AoppP}m&6O7BRLEl# z++lA3yy}!NY9{zG##?87oyai8`6~RDBu6x+b*^LZg=B}wPe?FMl4NW$q$Qye%LWfq z2$^)1EH7HpKk-CCTCi_&_?wLnAY?EtJl_0Do>46tZ5<%yd-IjE1*I{puzUXTI z6io1gTs^n2M?lvZgP{_2Y2}X9L_H=;ClEUW;PGRC|6wFX=kvSZSdJjiqZP~Wv9_PN z*IF{~oxV3|qfg6mH|L*oz13Ji90U7OuKPj?kljef!%CG(h1(EN7%!&aQq9Dp;bUFq zn5cBdeOotE(Vbl=VIHo63{JaOpH{T0ydUbws0CqdQ^9E&1-G^%=SJIGaUkbj0i7iP z3U7$*F>`<$?I{(+<>A%B{?Y*7X__(3z;Au@kQ$YaW$JjhG3*3ZN*^I%yq54r z%tc4k5n*V*HR?H^6wds)ncr$7&@y`)y^NKRykn5h=}riWDEovG3j)b|2+_+Vh$RfhS;w|zU0gKu|E=rvidqBc&9s;K& zV`*=~y6Rm7dC;!K0@&RkByDXe<1>yNNmMS+_C?z9U%jxf%}(PR=T>0F+pDd*}Q?_ z1)Y2%r%$H{L(I{a8R(h7d@#|dCpkv+xIPB|(mQy^LmYbr4QeH;(PKD-W5};kU6RLa z;z2<{JOl=UQmlxrO=4~NGL-8|vmz?ynm?c84D3s4@%9Ui?=TdA)OgFCxhgZY$>RdL zR6s&UdZU)VorO$PLUWXh0?^_BIje9R>M`a8K7-L5qT? zjt*Pkc0fL&i^r=Y7@`Nn;-1HUih}u0Btp;#AVI7sYPf^~Q`(K414Sj{Rziq63Ao4j z&94rHdu8x&cAV^%UF?XWM4^{Qy6@kvON?zV)jfK5kLi1)9*usPQQLwPuN;Ej5 zYc#J&d%wFQV?PNSwL7(s)^FOT1aU1z{vFic8?r*1d>*Y0j{g3<#D6ey{7w!?|7u!a z|Jsx;p?-`1jwsdxw&ME-PBT~+?dlCV2I(-fTYgUce0qPp4!1i*oo@CbXwOuf>+sHi z#-m$S4Z#Tn-nR}@cMO%&kHn8ZXR?3c^dg!8j0hf8oWIW>TQ84SG2Fqd{;NWkwbQ^o zGLQLQnGMgXi~i6&zJH`|ueZK^1*)F;2nREd6hfeTr7Yk8c8otLT8WfsBZ0Dq)1^^U zq%!ytY~6z7UAWJMQ*%{+n>Uy3NiBxA@|PUBc-luwldETwAgiv@C8LD}1xTd&9tXPF(y%r>_> z6+9Rk-h7IbAnBI%Dv zDVBVHKdgFfgQTImSotF&63^@w+tJd3{975ia;!H1V*hx^Z{nmGAZ2p01tPF&#Bz8J z&v0ko%gPY1MAIMUtk~73%nXF5{??aMSWmehNZ1I>%`9pmNHC zlDI?IFUfgQ{}WWu+@USCTomp!!hHqteCbFI$lySeYS>+Zb@Ua9ipiOTejh$mca6j%DoJvbADN0ZH?oeY%7se1FmTf8I-Q2ZiC7#<&G%P;g64!tk?AV1};-z9-q(wsBXF?rmT+Cy*BhHVS z4T-r~bGq7u`xOFZhS-*X)oUK=HsapzlMR+kx*a6DMUkHMU$XwRMF&k;+ZCC2m&?ZQ$TmJ(DM>6B~Z$QM*8a5KZX{rOC zJfpJ(B-OMIIR}H~pQSP_;)-vOR3uyi>8kLss^o^|708!VlgpH~nz=cZvJ0t!SY({}`Z`Wfwqp>!*_E7jc^ zN;0$Z4Talqe2rEELbLh9*qhkz=meDv7KXjHdjy#bELbeOPg0E$B#+95_)da_b3|Wp z6hHc%=U*3>G509ZWt#mK;%q1BYPJSmQZYZSxEzT-`9Z()$y1Dkyz4wbci2zKlkuuH z@<}T={`!D=6WDzwhw4j$i^^n=ViCdh4O!8k3j_39L%j52w-i;P#CGzfcvo#|xbOhR zIHlH{0)2scC1Ka8h?BN1BaIi>5gdJC;-EE-#163*&$>9`yPv|$z~oVF?t-FQ;T`1~vW+x8Ynu~J~>=wCd9mctgyF%2>1+L7Bn~k0B-W%|RX;sL+QcBLb z&_EzvK2H0se6WP=ldoT8If@Bd*;m7dC%7N0B?lR2#tS( zUC7#9$bd8MK{&Y6jjcc^0BlVEzucbSP9@=XUgy=H6Dl$8#nYtHq{dTUr*MSNg)Bj|6- z|IxGOxT}98_<5dX1o*LHf1M0JgnLXO0@`E^{xBH>yk7kmY}LG(ktxbgcBT|1OYQiy z5f#6SQn+c+^Y;G)#tYmtA#hixaNhOgAW%rWjS3Dk!tAm1VC$ZB#N;o{-TLeOJl(J> zwQ&P2x{GawCaXvA{ljw79MuvZSOG;K0C2C}-dl&y#g&fPh=Kf01kP{8xcy^;tEl0X zM0C;sjG?-mo!M@so_K2^7#8EMk-|PtT^PZ$td?DD;Q100TA)j$yuZoQ`ngEvM2&|# zf|b|QUm2mT((pAb39n7Wh1(1J%aS`8k{1-3obyA6oqi+JqQ!>Aj`0@@SdTPA#g)FJ zAp-04n4}v$qJ`wt19FJCQ5}zVABusrpt}aJX!D~w#>38p%}4^ zbF^dUVo?<{U&(n~N5cff`fFPhv96vjDF$e=uGtU}(!{dUgAuBm>8MVi82o6(NtD?K z%T)8Op1G89WA_#l(K~weJvFNMMp)Uiyd5n^lg*}xHa&%Q=5UsWW zKtNDbA;8EGnW(*d9g{+4aomV_8dCnINYrcTX6>^UQ!A2k`o(kZ<>wV+thI7PF-9C( z{z{cl>9D$&VkAn-xfN8Blx~)$*WlM7qUG(CF6Clwg-mhih~v;_iQl_*IPsZIW0~fo z;4|WW9-1qoW$Kn&7*nQ?&+A>f6=?PVMTM;=doY&u74z2sAq1c-b+uDku_>Bv`o^^qKXQK;A>-}NCO<~a4J?Z#|KF_A1nY|&+LDn>qB%SPrp2O%NG zpQ8C8jwq{Z2?jQbj4c&u)>Da?95vusmZgkycRoJ$SB(u1qp;YttPI~#0=pQ1Q<_El zfq~_jsp-K8uQmE=LAU6_*%jF=hv*(tVdJ~gABHD`LNgrBl9h3XLNX&5A}Izp&agy1 zW7cRh6j%rHqRdP~v9atSqBM3S1b1m*kmdBUWdWJh|e0JeW-B)jfU3_vg|FVCMS;i{qb zXE5pip#ReSv4pd{BKowI1@kgt7NSw=tDLsN*HoH@46z6qu;^+?CcPH|r1kA$^&xp~ zB*d{8RlG#mXU+PeQwe1U!)QZU$Fq%))0lbPS5Z}BAa)w#$THJE3<)WgPJ6bWZi^Bu ztq$>kqA9jJ@~nXR_B|GiJQH<)Zsf+pvpYG$+014Z2Pa5~w01x&)|X|?fT}in*tr%L zxapP(75{ZvLd7F9lIUg!WJPGl&B-E{lPHq2Cwx8Jf`?!I+NeJ%?CmZxhQlNifG=)Q zWVXBabSlvcG6;Ud#bauHLI@C2FvT{~hS*UEso`*|t5TmQ)G_WAKc)SU9`-3_eP`9a zpNidD{rYPeA!}&)LLi*4oeG%-d0>MgV-hO>nHJHnt1_)lA^v^=?tWg+DB=IT-PKQG zz-L+a7z(Ut))RHiD=-pX=+_%uge=HUN{o);&NIT6kHc;acxmT6+R6}T3w$6;4ri&h zRL1jQ9>C=4|HF+S9xD_&R^GiW#xtw=z=&{_1lnDZP94Ed1K5ghvoL@|$* z0yBQhbtiv3il_h#hTGKp9+zVekC5%O%QWrj&r^+X5v5mKnd70EKnq8!k`SRQA)3LH zaeh6GjEE3PR*?H_BNVoX65t`!!x4Jpr7#`}%!uQe1@L){x19@s2W1FAgklLOQu4O$ zX=`?zp4;02O@l@4(F=;z$=45+Q4OJoAx-YjFP->b=H5Pd3uP%*-G;m!WT`k3Q?E{W z2eRe@-hENeZ@hk9a7SRR_@G71=(STZQB z(H8k+FmVzSa8Ian`%|sXkub^HWC(aW$>B5}nbZ#edUX(eS&2@v2Ue2rSRk7?p%fV$ z6#RQv$6i&Hs7`WBg`ywn6-Ulm|8a@%Tk-`RXur$O%741 zu5@5U2+qjHa&#GR3Dc?ucLb-GA`Lh(7pA>b+?b@WJkrK9jiZP0LNOYauwJ9KT3?J^ ztm`SjA(l^6;}PZL82$L#(rSAZte#TpGkYEM>QAJ9n}8v!&E81Fb?sYkwI@!v{WFARM=M7tAP44-CArQFXZ0(ROoDeBg$Np=lusg<2FR~`AObXkz; z;PcQq)?4b((@^fgWj*DcG$uMnkM$O5ehzWKdYxuW#=)TCDEjcf+T%+K-uM)B#^qP6f&7t z*OpSgIxdy0$UN)(m+!ydC?=n@SC!Fqk(5B8%mGN2%7k*@iXCmA496jn$jABkTK{m_N zPq4H=9v4eEWy)f0cRlI3@$4=FbZL(3KLlSqHwWKXL0qMr{K|E!Wv^qxjy1vSa*9J( zx-^8m6jBgZiGx^ai1s?!ECAWxZV6&QeivPt8ghQF@np5L_6z6}1vzZl)U{ZtN17e` za&h~U7cS{X&maw=R^g#~88)N66R%T&@JAOE#1aT=gzbB^mTid;_uU5v3rwCE%V zHK>@xjXe}%+ddkYUq)qHD=@=r@6hqiM5PAuwM|s1y^;xB;>9|7??4&^F= zl#9qnM=*MB=(ovLJZL!r339VA_ZYebd@qK(l}3y5CXRBGf1{ccVU`r&(p7r}(Yq%d zJ(8}OY4V7LbC1=Yw;Dxs+#slY7P#r96pi5VT$YBWR+e$G!)lJ?4S&m3)z@0$0+cid zl4h|7wD!_D)yBk$eMA`TT--+_)|$MmW@=KERkUEXH}9RxdC!rV)QzNBKGgKp=AXb7 zm0|3K{SsT7a|srl#{3pQ;>eTW^DHV$lWdlML#4xeV|2RZz4l8f)NR=8DNmWAol>d| zI3vD#wn#beN216e=?Q=5%N+Ff{1VeL%5`6#gF(}RSEp+E)s!MAo0{Q3GdI-?lPEtW zLf(_xBU7(E))aNA1{h0s*UwUU*>pVK4(bM=61fJs@;Hn>cfhFSc)UAxUfXZPd`jG{MIMr0Gy38{ zVluvhDi;tAe2gqsWLklGD%joBhGlN|Fz!db4aZ9HyjWBvWC?Qsz^Yj6;R>rC4*9J% zmhdt^>#$HaUXug#!aE%tBYN+bPwlcRc_d6}IULEZd&_-Pas|QEUArlN6urB|0PU)U zJB7o}cJ{89vv)J<(SF0fZnUynddC=Rx^mgiPTqWc4%u*ipSoTU{Zp(X+5Ul+?~PJ} zAVUwBIFO-m)9$e24zyYIys~;BK{H8@M05Q1uHpsXM*#V(^noX^%(hsF8(a8j*z8@G z9qcJeyk<7hZbq}7T|xw$VvW>C3_-i4w)zS14)VH%e{TaEK>5UK7b708d1UC|A2y>8 z*S9~Q%A?}t>BgiWXuzzA$YcOE4o;5$LpFF*4rKpBHWh?UwYxOH5~NTGbPes$r)}%O zI}VBn&B7Lc{@#WsNkyqtZrLmHvSyFUb$6?wh%2K>C#Iw^MjN1gEy=RU!ddSux_qI- z_=n7uzw~aJ?f(zkc%P;B|8h}(T`xVP$N$GR&9VR;RVL&2x1Mu$n%)ek)b^93rMXv` z4bKLGzUO3>(+V}ufH`P^524Y{zuKiefwxNsvm;QBU!*bQTt~25i0Dx zAvD zBt=m(88@XLIRTyBdc#JXZ1hJ1ek2tRq{}cU7;8Sld7mnOarhH$Lbug3*I)`nS&{+t zoYPP$Q!oVG^*PFwqM{RgN@x8Dr2A_7bev|1U?3AJA|Y2B-P1bQ&gSW&rCymr>~gsU zLI=N-{xKpaL6IS!GSDbbHm#isA?uk?7eFE9O>wY+ZF~-=wr0~5H_EQACKKZ;7XwAX zSjB8@U@YloT}zuKsed0^kOy-yqAvh7J&Si+=j-0~kK-nlP2QmnFym~yfA_{BpEW)h z7YEqwu9bUMZtk7RtBtbb?%`fqV`oc4TWKQm&oPaRS^&nG7+-UPj=itbFf}-9M%M$) zr5pE7XC(odNu^QzsO_J9OMGT@;twn3uqXsG0i6ZbGmO*5p-!zlCamHyr}zL35-dC- zwLTMacOXylG-AcotVUWV)IWY@rWl+Bh^pl2w0Y-52EZOzzHvd_v3I{c1B%O^AQa2##ASSr+_IdE)pE%eHA4ciO0$WY8(RhN5QVd*OS^y~X5bdy0>wW|2^cxlrWB{<`UlofTfUJ6@+p|$ zrM`|at~3x;&^h1Sz~C3(PkmAZ_66~_U%4$1WygCy3p`AJ{iXJwx<)(1`X%~zvOwfQ z=&>3K2F2tkJ~iO9iZ<4@{omHg&jbkx*%FSH`f0U|_OfDLr|JRE$Z)bJ`VUi~`@`(0$kh^YH+ zLVvv=6rJfgOV}eZxwi4$#EaCr*0|=A)q zzCV-3iYzl`wBuximJ|>2)H~L!R*i@b)V>FX!qz}--NFH`EFdL9KNTJ>;e~ZQ62W4J z!zsZQbtDf8P@{mBG7nJzjf3JuYY;+8iULAH3?GZ!GkwP%LSXxTap4FcaJG%{$C)+h zhp(yWs+$Bp>g$p;RUF_*~$>U zS({>-i?K9kugBg)x>zT-H+5;+fi1D$_}U_(oq;LQ+^tuVv3zHhzmxS!EKk$Cr@TuG zA_wfmR8d*B26KEGS$$p@igu z&%OYJy^ft_ofQevOBdTiikMAAOq*B`z*KpTJQI)^>_~*eB0XimyBez7*Y#SBA2s!h9Ic7 z+1+eT&?}kZSUR;m1-d!nA}w2NWAjd1@(%{a*)6p&P=De?vLNKQb-Lrq1$drVsu;z@ zn4F7U#c6UhvBIq7paJg!XkHJygUNZwj>wm=UzV((RAQfUC- zY<#n!Vm9zC&=wDhqbr=Kl_9nArIgghM4w=8ux0H9gIt`HVh@FLsKGW7D$tRVht`(j zxy2b%D#hX*891SR_|qH-#z0jO%{BdDP8ohpcn!Rw9!*mx&vOu~ajjKh5dG325Y_S|#Fl zEB#2Z3;JG~Co(Eoaz6K%btXTmqDNk$n|2t19arVmebutq%&|QGv#K$RVAfM1xsPC~ zEDyZ;1PikKhcs?-I9FqWSY@Y{aa%^v!xdtne0>~>?7wmxu3_7cN{&eyD;OT)S zxXx6iJ!}m0s1E7shz_Q^80yy_cu++-9%L_<32ahQMWVKXqzJy1ptu0F3`u(-FuRF% z5iEBM-9lkU@}v+oD(U}Df?{wS7AIcc?fg<~>A%#%@+x*b23pNt2~)s9={{;{eR0Vnb<`K0%p<)=+R|it8zLrlqbdZb6t_6*2APNiG*+ z%)&_nZYNpHdC#0eZ7E=|?L1gLCMT_~C0UMe+$w2DNG+3Dg4r`s?o8YUk|?S@H~+ya zc9XDsrkfOQsCMzemC^G#;xq+E&YvJks^PZ^e+5frPgjB6D>yEe6kps?Ms-*;&;upl z3aBOG&|QE`83ECb#L6itE^PxD%wlfMmMqyudkf3b4O~L2DE!tT z$V?=p=FFK|4*a6i_imMYX_dn;*6)qN>Qzhj_Tmy?;a)Yi$==1_WN@sp`-Rcu;Sz1u z!)K;sQf;8m*8vD<)k7gE>a?o#d+E#LjkM@Vi{8l>45n+s=@}|Wc0FYsbW%W_AWZkB zxJd$4kXK+rLx!aZY6eO`a7j1d>N-tL&}NItav_c+`I^$2G+DHD21|N?+?K|ZTwbDj zcS@62pu*QUO~zgO?389U1qxMwh;-|_JS1fnrwOH3lLoM3SS+}^$)ngH%Nc1I$yac% z+krI|K1lm|(d=!l9ktv}I2x6O7Sb}c)o$fl+fu0UW|~MoYr4+e{MCmAeWnmeRB@Zo zl$CTI8k=%Kw@xui#-$v08y{BOS!nYn%2{ug?81+Q*9jZDgswCl$AUNJ!K*!k6$v;u zX&uzFh6X@JH7m(Sf$2CO!aDn+tk2S=el(3o1!Y?Q_0NQCREwq#^@UMn9j_V=Z7w)Fo`6F zv~e#4tLY?~3six^9{lI%BPCttf1jzGcITV576pK6ME6bP*d#7t+gQHq{0i3>(6Ekr z{KMoh|DXZ&PlvbH_pnFE)!jQHFaBypr-ZLUu=A)Y3TiGntAVCp^Mzg4@;qwjz_&i&nQ4?9&9!Gy-^YKYj}DkZ&OXO5m_?rg{mP^d1f(2J z+W(X|X>~55i7cIfK0SPZ{Vy!h?h&SzFaZqoY6p4eK=F)HrXZfRi&dE%Vn(Tql%j zr8KTnEtoX`rHV_j;gXXNn1OS~B_`zR1_gkP&oczU_MaJ1WH<%GpL`lzl#ZrLObTF^ zNW%!j5gUWpz4+B?AeNU9-G+)7iT$)*I)KQ&HCyeH$!RNhA6`=ZyhJZV4KuSHeEOmPU`hR0emRf%%YRG zXkXv;p4z{Y6L06lNq%ZQ!jnCF-LanfFN?G zuFzd;V{SLujDf2<%gk4=Od6xI%{&$+#t&+IOy@oZ+N^NfR~ih&K#B3Rk?p0yWt#Xh zdM%Lcc`C!SO(57i>?aS+*7{oon@t9mB0ZgJKA5FIg#ElYtAwaQcS>aAbp_CUTy}vR zf_2ByLrE)em!6P_Oi_^eyxMzsS#oWft5`dJD}#Lv*f$zA=W3QWJc|h?^MMq9=x!eb>^e@mH z8y}W2XD)y6;~$O9y!D@k5dT3aOgpjsc>4w(&@2+1NZ@Q6iS((tM9$zkKacR?KK5x} z1Ll_XBHYQg&hm9=*S<1((K>~-ZUn8q8t-ke6x6)9xtChs^mMwJrUCH(VLhLou@`dm zqGbc4Biro*qvpyHyL80sVDy~9)2z8#8#sjgi$;Q1ORuXx z&0U~s%`?R8v1@C0y9=;o*IebI$-`CCOcLD7j_;1$?b+EJTu6^9dU;Z!(8Zg(Wy>i; zE`*5$fF|2&k0%zMQ@#OCwQTJvj9_d{fS^B5KUlXj?bY_!)qI(PLxF&5GPHupAVkjEmRUBJVT z-v4MQ`2GG=pHDEzavUHx{I>qL=F>`P0YU*>Pbv;1pLf#4zk#I86lUA;lk}&JV z9}wnBHUqJX@+w%s`>cOT6;)M9C_Jeq`ZfB~5fB zke?4nR;KMu4WLUK{I#ivaH;e$4hqil-OT~8i#WXowk)~r`=M)*9gQphY^tsxP1Ulc zLW;Kr(X`jQ^)5rrdISZ30L<_ULOf4g~7PBjLKIyt<%Irl1O(He_G=3to9}6wB|HGYLNL>Ti zh-Ck*iGU-8Xwc?`%jiykV|%HN8?+bb-aTiXIHIo<+g3^}@YgV@p4DIPiOoeq2aea;ulnBi+4%efRmw8(|xoaW1-e^_B`B|Bi^FUv{ z3J_Q7`W!?W;`W-oQ|!)^_~v`CUijZ~+(8ekDU|~Cwkxx0*ZQjUXOKE4ZXl~?nG_`F zZO}(lRK~9GVY&?6&c^`D*J2BuoT@*!p)DzB*-H>5o^L(fL69{ z&IU02tB2$1S{&*B$l17_ZgVC)ODM**DZI#$m63yUV*0iJ^+9r+IW^s2{`ppG$8(!g zZr7g3|M^sly7=|mde7|t9_k11Axr;Mr1)ERr*rc7Ht+a=I?TGW81W>4VoA%!?>xXf_%98(mZ{=N zO;XbiDTY=}QH@`J9>0sO8R$%!e1Ra^d<@KiR0n4RO2-!O zPP2I@TGam-&Z;HJsS7ZIHH27fc95P*2a+m@p#de2lcn}8sj%n*2dRvp{MO%cE>v?C z(lYw>Yclih{^0-j=nxs%uOBtub{oR{hynq#y>9Gg)?^N0F7&`G;X9%n+nREY%lPhC z?bO1~T#6KgfqwT*1LHevoP+ILqBC7;FEkBJMzFt%<)9RqF$d7L>vbtBn)k6N@m1}g zR<%^ZD9ThlZ3h8FnW~bFX1&tN5)Vt))t|gGzZR-iB9R`Nn)Op+q*Q>0QAt1ho8reP z))+knlk3g6K$9X;D?*Lx&*OI5@i+i3ld2LDlEp_t1(-eIRO7XNMtp53tWnX8@sE7w z2uQ+!8*iRO76EW$!iaGLVOBt*;;#8Nz`CFe7AuB|URs}qB89Ru+!2IL=EvWW{|*`w zh(31Xy5`WF2Ra`)I)z{q`{Ri=12YULLf|=c#DJVS49dkptjxc6#ZZw6)QND%n84dk zVn4R}Zb9~(yF3D>8}TA2A@&KK=~gKgr~@DyC+opgNfKmDrF|!J2cg4p zws`9>t9nHE^>~Bi@m91#h=kU*a;B*B^KQ6Kl3i#kmerQ;oFoDy+rS`q+qInbf#a4g z-8lym;Bd14-avWH(Ueo~O&-C_LT+!8bi*U{F~`Xn@3&QtAs-LY zv6+!Kg9C6W2c&P9Zb6o{*F&A%VH>x^h2J6|F`+u~4#l1^!SAahnQ6L>;d5->9%9mxkVjg)Oesl)ctDP!H{H>jiI$k=+7l~NOJV29Kx>40ejay(auC?G z#@J{dJj~IMHfwe$mlym`-y8PSdcL@ZOkh>vtN@Wdh+ydWmJ7}rg$bBhGC?Emf*mmo z2eX4Oo|1uKAc{W76tQXZ>0?0p%FEY4uM18dHP&hNV_<~SMu04W@__>?jHJP z34qSNWKfqriAHSV(c$LcUVAvHH>NUNRjFc_@>IaRq8#5_Snb3$m*M>k$XUZYs?!ZhktQazuS&e$lMNf;WkC=Wlm zQGX-{Cd|njRhu!tP@P`2K01#Vq_LiICT zb7;EbMlKu^c(jq0jMeJq2Bm3WID13r|K_v3vPm+RTFCE8qU&%Zw^?`q zjM+0+m>O$L#3Fp8Ra-*H1Q!_TZ%ExOquNF4u>GQ4l9K60n>}=vSyQU!z8#fmU}4AsKntuM`=_T(Bo( zEFzj66*Xq5&sTo|Msk$ z{~o0)7m$v~^FxRcaZ%9+MM_$~#O5dR$02HW;x(VNZ_csu5yLlFr8=`(Xj)YDgXkPQ zcK1uDcH7wW8&o)l5z!!jW11U>uMU|tif}IJ1t#T6DJcUz;A#-`O!yCQ)@a-H)f(8y z#VWF#d(?oM$gA15s_LzzRYLDBkkv{QGsh<9mw)Yxgk2f$?`pFaf^^DP>ee+*4qw3G4pH3QsLj6!JW1Lc?o;RV%>nE^142$)$oBYb zR>+ezPwYPi0A4=&iyF4u%LlQk9hfovfV-8CEd>TASgwoghA-VX1(T>Ce)b~%G#5Tu z>tEiwIQum|B8tRv*}YIk5Z)i4%w`R@4)Xy;uI)u+g6n!yZ-VK`9`8igoK!XEup#UV z>>EQPYlD(KOx%ov$}Z>9>$~a}Ti%Hb!JcHj<2l_NKqv2$snWtgfSZ-g7fWz`Ha^(u>GXFkO8ye^K%kV@BtZlfoKf0AxG5**Sr_57boA^a4a`fnG<5!>c{ zc_Rf6O$j|`z{`u_&qJQrG1HxxBl`^h0GvCwA2#gEw`G^{V>K#nte&E#Wl0Ur`k#J| z4E|iesIBQus;gNF>HiUK1orFcI$e})fhkq0R+F( zY?q_qJ7><|OUp%P2P?5yFreA@6dzve8?a93(|FSM`i9S`t0C6kAJ}*0Fm;J80}#lB z7gCZ#^4z+flf+x)pA&YdqPIc(n^NskJp*Q8IpOi4%vy0S) zGWOL+C??mhd5^_JC&5&v%23L`=^Oxg_nfO9V|Mk-FjN;wo!W}h^Z&IMuf~Kq_!~A4 zs=;%-c$3F2=2b~_m%R}z@dw<`1aPw~>$n|thFDsO9kZ$ z<>uv+zN(q!Y`))%aDqJhkJ=+%y1HN%?IrIH>fmaC@unPpSpGvnbtS`tzAQY&La5%g1-EE zAYEW^=!qLlAeN@VsNM#~2QvSRBQ3+K;ml*{9j@;iNBpmhz(|`t*C345r2ZMf;*EdQ z99Q4c^Ulo?RL+~~x+#J`50KO|@!-dQSe=>&u^ap}rqR&jo!IARARiN%$mg>QuDaZB zKUV6R84jE*cG~}Zl^lmZ?CpBL>!w^v10^8*oF&^Vf@@r_w!TH(K8pwe8^cZT?*Y7Z zaGL5wFI62Q#(t%`{@n+72=iacpH(@R~anvqk$K%`ZMee--dWBh#* z1U=sCNe7k%Moqtw0HFkBXW~vL{shJXSZUuvGGT!Mqk#5tuV)_&+bP!nX$W5DKK`FT zPNYhug{_Bo&wrt8JCDk0s;D9=0fCj5t1=Gg-%9#(`ZqF@&ANU&6)N-t!|Q#^gW|oP zpX;Lg_WXV2H{)PMUnCk}xb5>Mep(Htxw-6PHwlOr z?E7^1yz2I8uuFn0oB7-F94}#US}2+ClRBafWPpFS|G>ER!OwU7&Q1Tcs_H5}SV`CNrbM~AcMjvPa+eQx zsjRIQ$WmMOHI_F6GRMEJ>tH`OUdo}bm)YP&8pwh) zOhFmXhA6dn9w=8;BQYxN8kC6?J0yr6GzrYxr?&An5!0@bv2spWnNC zvds1s8<%G9E;)dueDLrfr~+1ZiOz*2#AS({2hjqV`fH)F`>4Zqg~xCv@MI+un3>iu z!jd(e#u!2tZTRyvuo;#t(#&pBD)=C=U;UEcc~f;rn+~jF_8WQVqikEaoIKAfw z*!XR=+!A#Jc2@U=A;SJkX_D3b&_jn_r0~4B3_Im77z`p-ha^QD`=u(9F}z4?ePX}l zgvz2rAtQ2~KmJ%ymiu|~UKkP9lL#q>MJhModIJ(A*SHv|$<^zz?eS)Uny(~Hu-Ufo zDE!0x+*wBT>E2kRHW;4MS_Of#ooR`{ekw2W*E{Gc|78qRB)NM3KTH-%)E-<-B8WtK!sNQOF;CnZZ5GVPt27#~Cp6<41W;g(r7b9$NYkHrHnM+e4+1mlR^^F6hUmNdQynG(IgkNGyF0hQ>3ei~iuH23y#)ZxyDxPi%zg15zFZ*z1$7XkrrdAV8m&?$}2x(xdvH77ur#Jr2 zzXByaz%t^Z=fNd$K>uk4}_bs)7OTRb;^t|q~t%497AU)m(BV{ySJo5PQoL# z@UcAUsvP^e(H0(WU%~!aHy6P-~gT!RZ7v)5c2$Uhx&okotTt zlu~^`t_ai5Jo{5!JkKo9xOadmY%LR$Lq2@iywW_^%=W;q=I=3#@g`zDKqzB?GV*-k zTrnbOQ!R(iuJX7Fe{#JiK!xC>dI30Pyf@O#rtz=9)yYde?=W7W zkj=Foy5%~$C$MwZ(`I@bU(5Om7VQQ^uZ3EnLgLsM6?$0h-;Ed^ONQo~zK2wEGg&cs zADoVd3Lov-lKDxSZ|&w*K>7Ed9aqKjVd1ZJkmNX`apX#w#q6U%1{yh&j3IRHPNfe??L?gyjPZwTY>M{f z{T)X-aBjK%wn`u*M@3CIF=#Vb3!2CQiBWRsFf6`Y?}O>59DG?wY^|4li9p14if}oB zxhz)PdivkD159seKuKsc__yOh{AfE=xkrnzY{`kG3$NW4RAKqPe2w*!Yc2t`ti#_} zmcOqdFtkjXC)^k}F-#Czp0i_G>+`gp*`fy7x6bmuxYn3I%SZhd5tm>78r4Q5geR=a z5ab)GY}zw|1m;nbMVs{Y2ySd)g6~?_u?B-+GSflB)wHjJ15v2ak2mETyFf_P+4)vu zI_@8ayDD++kz~Burw~eEpSbS-g3iHv9DJsO5Yaje2TGxTV{WgCi#wt}QPprDDBR?oxm|_^s5Qkqc~EF5%9kM2lFxWUS**c|0Vf79}%n#d1rvfr?MxsVXcD zB!YtJOS{#+0YnTMfs&vx{1rO>rw2wp;GP@Cd{1<{8bf;R#nah23>& z`xBy*Z9YpNyR)&pq&P7>u`h;1F!=D7BgCnP7M^}sz=2yL%HlO~h26X*2>%3ep>iX& zDOJG6tcTJ~5~A}+nKE^BzN;-44JTO_JwH4#{O4SL;8yxfqZOgHL+-kXyyS)M3Ys>4 zLHkTMN0RB4m>}Z;KXjMySqFO8^J|nFH5gB-W_5`h*Py7PH}0$-SK)dTRt1{EtS0FgM5?{flhHA3R`usmhJ{s zvgpR<2@L#nA`4zTh5~jU8+Cu?Hh-&5rNX_LwijB-71GD?1d#Okd2wg=Q!TS^nk_dv z)rhVeLF9DCq~_;xE5V~LafLhjA7zPGc;#dWfY!qvn~hnlmepb~vxwC}aS+i7r7(jf zOkGles~jAkT%|7!T`jF|J$A-T*T@>nKe5`iPp)MJ(c1>Uj$(iBVxZ(4578gBy1_PR z*}CbQ%z_4!p1rxBG^!CM%rFN4Kl7uOI@e469bAO5mqt$Vgw%6DWjSG)!#DE^G#qpR zXK8|?YcqMOj6Sz5fyyTGx?+&h5Hkp1kqhi}c=U~tm4#N(*xgh{^fYTG

z~ZAKfN z@|o;PFTr@tEUM(4=-5{Hrf$4&2JjgZ;As~P zt(uyq{kbDYx0Y>zRD}9FrA>&}SpQ|d4+9&it8~RMj_g0`=kScozU*26r(NnZ2V%EK zOnD>nq+B*)O8!jH1vyVu<1MGUZdfG$2>$dC?8RYl;yadTHKd1qHxC}(R)4?r_XtRT z+N*~1e(#zHir0-xxVQ#hrSVM0)dqwS0}b*5lvho3J;juoY54i~kb|!1Fg`2ZL>f*` zE;EWXag@_X_ov&zg5exc2RE~DjgLBgC`hCm!80U+pSvJe&I@bKqR#8cg-rq}Q?oqw!OtG` zeuNCsZy3eQp8swndfQ^b?s7UlPKM5I6H)dZtriTy^NP_;?->U`vx3l0=^W=<06%V( z_1^uFfI}1PhC&!H6iZ}W2zI|fjb?WDrP$QGO(=h8CXkY@|HmAQI*aPAdLrv*01Gtv zwZQU4TZ$GXl47J(Gm;v zmJZW@>2ipPtv4TFhpWkpb@HL(xb#6$g&ym&J1t|IBlu9QleiAXX>g?)*1JFH_xk!# zBO7K2xT}KZj@|two>tCG`!dy)4f*l0&TeM!YQjibP{A_bkIaeuY~d0lz8=`!Oo_uI zLCSK$(+^d`8GdE+wU4C;W-`tx!R>i4xp$zU8T@D9ZQwt^h0is3QI3R$mxu(?dtM=I zed{9c74&2-%}vwGDSIt{oLq|e=w^`QgfSE6l$sNN;BWT=Z19i~`A77(vl^%GLVH;? zi}TgSvI7%9+8QQv3bQvPJPwr>TOcKZRNcpeMS2(R&*m7TK=r!cy%J|Mq)kYRpml?6 zou~v3twC-RHcyRFABhf;F~_TuR}La6=7APVx>tz#=8JakZs%B4aq~SA_)xI9XL0>4 zq$3ywB-?^0D_-w3_nd!yv?}`e&sR(kOH9 z>D3_Yy-gTCa{H2tgECfxYU})!K9Rrd?H$K+sCR=(rY{G91bUin@$IKYsN~_et{B9B z^zBx5g-eXHlj0X-QPQkFwLFqmkWL(#YKSjQfr3hepcrrXgE_j?c%+jLlgxPD8V8hD zt30rPo7=TtPThog;r)P2-N;jarQKG#d++Z@Fpo;qVj;_8x~xw@!n>wJWB#R1uV+m; zDkIr)>VSv?vZ?en4$n))zdb7pPU-bE-d;bSa&*leecQS%q9{THR3k~7U4P8h`jt9? zPhPx5h6lC`G|%ly5Ts9l62zJu1tgbb9cF#B zt2a;QZm)8WuRe=@>2K56A~piRJ0r5W*2Vw6;(&7fuS9|z;Nal;|FKrOlQFuj$Xh40 z*GN6ZW#Xp`*=0e_-TTtsYfvpOt5EU1+T51juAQEbZ9Bko zwm<0VYgFz=>16iIu}8*b<7A88e)a2B^W zh+9*X6sVvo|Xjr3(!YP#RJ@EfL%$n;KiNoEAyDN+!_bkt22r7a*l)3dJh6 z^5b{foTtg0m4`W6Fh}O@q#);7sAV6wEdH3T?^$Z;aybd$R4;V7%f*wrRsq&)WrUO1 z0rQ3HEMj3iK|X`H1ffLP^&f7{*3wWrzP04kneK`BkD`-*+@NO&i#5s0FRn@IQ)6)I zjdc4j5m%T|1BJ1!YIoTfA=>vGVb`n;vPPaZBCE~dMvUS8!^UL(AP0;@f+99?0BTE`05o3cDkSJ$CnNbbv8Aa)KJ=D%2(7&)Cr&) zm3A_kMKtU9CDTOh{(`h%u>9qY_|bWmEuqI_ZJmvdkx8VxXVbX?I{r^0Li5Z3W}6fw zk#MCg8lMEJQTK@68)d^=dU^n?I3DZGdUCvDSc=UzuL z?pn)wn#8~xc;{nAER%HbO_7Pf(WqaWQW2*?V2n**Hs0v zO=vOX7CN)t`TAHK{U0!>?qCt1(GfZ`1M8ogePRY@VjVBNV&yN@8?Nd@4u)RzrwIwb zYi_AFqUEy7boNuA77ALH7Br#$DK}rDe=ej}@~@MEA4WnTnm<=TezygLcxC7@6ZqeLa@3k9}rOC|O%ck*5 zP@8v=E)%i(yFd7uQb(nyj@oxgrxRi1y3#d2)n3R)zFSx*H9-F}KZbmichK1|`7XrD zY|Xrrd8~r?kCk%NOTBZ;cx=E61CPcQtxes_U7)k>41nf8`MYt)YPVUJ`NOI#d9Nlg zn}EcIhQWE4JF`soU-Z#tUo;&2ch+2K#KdxF^7~}l^21ql&v#_JLI|tFhv$6S&HKZpK+0$V~Ry?4)SHq zVNzA5XVHI3PI2Vf%fz22=p0`v3sj-igb?}I3fL^!Fuv-$fWF4-;xa8iw7#5Cz%rOU zp-(1NB&9)Kcb$XlWz! zndqt^#26uzXWslNcVtNQGafs2@878n zw%>AbSNEx8qNKa4-f5rjALPA4zEXNdGIri}nqX9FJ%LVjbDd0XFY%)suz<`xda-3mbY*!tyci%K z7$3ugP5!GU2ervAG5iXv#DIrC6v<+P-dcxsToy@Vm&H+I{r73WA6*LUF*Cj_`7Fr4 zp|`fAx|}PGt0cPAOTKDu=pec#>nJ;*96w|>^QhaYnz$~ch&M$Q%Dhn>fQyEG4~VvA zlJ~3eR~iRvQ1WVN+@WpD*7)F(*wBO}y<0clC8h@x8>{JO+32CAEj^&jXad z%)c4^NG0FF(>0I8(Bymp>WE|FRtjj5LqwEa6a2tb6Db%U`gAgl5L>?bVU4^NGW^lC z#+37)*dG<5hja-kL_QrsfE9VG_iAFqR5(n`=kKM`o<@Fy@DQd_6Xo}b?4zy>OENL& z2ZOS}99|Fe2FAA>(pOM?HGPkGFI`mclP+Q0N0Rvw$cH1b%mM$rYwQ=aaV-)BUJj>J zX98D;PPmeK40TbhjY#sIxXAHN1(wwd^>n?7gFQGV?tq6He0Rf9V9M6MR_d-1D+iN!T5~W1vXN;Y&qB!~6X!@Ew z9*1UrUWdzM6XT<`xybZJR2whMw(t-a96?F+g#(uqQ8SzFykWNnM%Mux-e|e=^a%>_ zLZW&-{m%T56tTiUKDux-?7eL$+NdL!xr{1py`|D!2Q7LnfYRAyOH3Y{aM)mD-Hc=w zr<;OX+|x7ieC*w$w^%~YLLJL8+fo*UzBGH~4ck#Pj}$SM!>Jv$-+KJ^{VQ^;hsM0| z-tOx5OZ8-`Vh6ghcRQ5-Wv7;1a+Xb(-JV?BGX;Txbd8aADuqd{mgr`y<`df&xAQu4 zWiuP~B$nq5I5D_4ev*j(2q}k{Z95Fc(i;b(awx7@%$e{LPd}K(?EYF|i1L5A_6;)J zG|GVsvaCLRcy>p7dn)kVXX<=Dy0oh=va1!#T!zf3U~>ozX{gI>`tbkm{QEASmNy{o zQ#t>fim|1u=~G2Qr<|^vD`2UoC!H}z?G#(Oo>uj83?3SCV!p6 z@@ucG>k?;8>+(<(%*dpir#BqJF%L#fv|qIkm{BApzU+rCgW2TY+_AN6(AZn5J%Tu$ zc^W7wXjG+m7pv!tE!Hzqp0!^1FJvvT8uKjG$Q_i|3R8YEWS~6sx;lH}I#;9QHlf-( z+RtwRNSwH0m|ybRxaz2`y|YYuRVwDfYTX!Qwbkuwu~%2|45_NDulabEk)73+Z62&! z$p^nxHbS||2rhDTy8dXDs?FIXZOWaygMC5Gc$V!6H#SLcOedF~gZ_SZ6?uF6eY6fK zHHxoGN4Z3H3sVp|-{YwHY|Km9;h2VXb>jCExK8Uno3oq9F^C@Gm88vFVOrE0I zeuNX<&t7-ss7>X>^4elV@Z+jUNp1Pr6E<0Jx6AMfrhQDBZg}zeH0IxdnQI8D_o+{; z3cF%HF^gO4%_rOA{;*5#$BOuJf0(igevtE7XX~G3U2nP5LkG{1OoKdYu=Z@j9zLL%2S%Z+UKkt#Qi;ylRz`nhu z7{f7DA@`?uo$L2Zc8(51C+AO8_4PC491k-+&C6BC|AYhMfQTl#UNNOzH)aYc{=$ql zvJ6yq@kq_Afgz2#AxM3Wyp%^sDm{Y(CjVx~lu_Q0J&i2v;CN`_pvwiyi&PbgX2V!# zZF5kuH6Yiv+MIofb0@#jJMo?&z%&FXBKIb!EyIA?fI9&Gz|4C(n))zlrWD#nM5V5e>=VUlz$z{PH7EDx1KP%3@QD!W{$;Z`d7=(H(n1#RgP-&{QQ(+s|`2xTUAerWajS24SW6 zU&57MRLPJfAL52SaaYnT*rsWCUJo5(y@iiGZ7wf2Fv_?49re$t(IUPgb-X& zF^X~bl2k-DT&2n$eLdT?)v;bc)h?9Pt?m5c-6zV27Wu+RXZ@9=){0o1U>tIAec+_=l0iG@9~z}FIclwFc}bJP;Qq0 zF&h3GMp>Ev?=X7x54WJJMA$x|euoJ8ajaqWM52sB-6_@>*^qX2=B=T1*z+HWKQmTD zMU8+t8o9E|l6grbEYrwZuAqwZ+p4&p>3b=IzCl+c$=|IYXuB8C`FS-P z8QJ@lkx_!1`luqbbOxB%zV84yk@Uxr<->&AjJ^M%>K25_cTRpVVv={MVqPr{?%jVs zy;LUc+8Zt^Jdqdo$eal*oWu*9mzee-ow>JlTi!ghIz*m@EZ)X>dw;!^IrUu;-T>{NcVE26i1WqkgIwMRc5{GvdXG%Pbm^U~ zY_88qOz%f*HD9U5&10gtldF=xg_)c|ORLMUhX(nqQ?kO;J$t*KF&e7aqZ`ghF}n1B zWMt@Rn&`z+zdW-ajD-yj>ez~nVLr<>rYsgb^|Z*vla!dhgwS>~b~})2^e{LgYkTfn z=>mYfI0^OsiDfnm_Y6 zwnIsq`KvkkA*JSb@R6PFB1N@Bj*xnq=UjFL+XNjD_5&=kYcH6R-(T+0S*c zWGjY8Zdg39*nkP)yMAXECRj5@t7b-CNi`%7oZw*Fjw?`aM0ReINY}op*CC4vGXzt2S`}SO?+SbrWNE$4SudkL~T#; zpUw*CvC6hiucWYF@Gdq9&FxPN+%EWWmUw$#P|(*e)AtufEo511YsFP!EfRRQNUYoj zx5RA9;q(57xO}0hpkQYHXRsuzeswe=Fu}%0$Sp-xi*N- zFKojx`X%RH-1IXtLY;hweL*(D}i1B_~a05*pPHUi% z>tFqwrtXj0Bml*ACye%3BG)gq3><75*btJtMm+Gh;cuv%^{yv|SZ`9g4lAWRFsc5c zbqT}8Y;`Ghy$^}Z%d{u{6Cs(v#pS6xxMv%muKf4<#_K%(3rmCi<(KWo;JXk)A{INf zDu8%fV5E#4#PZNkm`5e;l&;+%*6$Li*9Lw&z>=0?u76@i)RvS??-c1a~VAnhJC?Kmw0ADFDkUZh%L1*yD`4lo0%Ms+g0)mH6-Yz{VX zy0!`rsN$CAs!$A14$Tvm^r@A?LOm(L^Z7;lMyZ!*c2oyO2sqnzAuT?C`92wQ;!Kd7 zokUR3eSZWxLE6m{R_0_st~ z=I$eGnjx5CM{@^m|b2uN4kDCFxX#E@Yt^a^kwAEDQp;Pv{;QtXsWeEzeT^^01E zf9VlIvXufe%K(|VB7LJ-_qf>(J%Mn0urF(;S|ZjqEZDKs~2lC&9IWs+0pK9pP= z4J^=vLaD5G`vSpHR?`X9>W*!B12U8;2(vIX?Cu~C3E13cLDbPS+qFNH$^r!%O^t)< zys>zAAP_F9WSmRqxeXuq2`4EVZgsUOhiE!4Hb}{){i?#KtMSSk{zcCQ`TMNIix?fy z?7ZArqsreUhb4cv&=~X{6O7f@pnR?v3P_`1W?&Wl`Z3}n$GQX90^P;9fB_T3yEqgY zNeg&Bu$bae5Y}W`bEfK`)eHFSIPi6+=??Yj&OR|P_Hx)7#@=L(`hQl7Xu1RQcqb`j ztHhoHpE-zdZT11Y$rz5e7B`g*p&#i3kWKHw2Y#J(Y$q=!_)4X+!mVC|Cy;JMmr3p6 zM`-FwMJ*c|HSu1`HJ0ZL0IJzqnK_ijBxGuShpx>k+9MVy%0xk{4jSWq-#Sa80IlyR zJUY}Nw$-AvWAkftC<7KIl;om8BTwRI+u)VITe~MJTqpwVhJiSUba`&cnY!Il{cSzB z!Vh*PrgBNw(CEl%8tt|x8@UzXz*5+A|j>2o>=b!#I~+=HmkIOalhRwNNj1j5e6tcYfJ1 z7WbnOXV<^yh~o$&fGB~ebx&F;7oKhfYw76=H4;fK#@<54k&OQ+X%}`Ai?y0*egS^w zzr65Hpglj`SN?Ivi`Ywla+k~<SFq*tIgu#ZFfn?PIdH8IdRs#7LwKnkxIz8DWi50sIgo0-zv%M$t^f0W42o zU8%XMbfhgq@G3pJAM3OGSXaDnFGNxIV8klSUV(=ElY69hx8WM?Cv0#8GrE{>QmYD% zrt1e!vW>sltR^hf!5dykm|_}KbEIcKsO*L5ak4+d1;hjFQDz!)zhs-p!IqpxL9dV* zEwkPod2|t7fVNKxo-QYCtKLI;_i?9o;>jcGhOCgl7VOxtyQOO5ATY#9ROAihB>yUR z2rkMbnAtTKwCKGH_=r98j%ZT4AM=?Qp>}?z7 zhMLQoiw@zII6D>{rk&}kbq1obXfQ$U|IzHnDa-WiKrO3j8VBhveqJRmS+&!L7Q_h= zKgS9!1s>Nf+VSj?^(FT_rn}|JHk4!Q!hFN1YkAsVD6*=~-c3~B`aSD(D-amup$0Aw z&7n&BUh?#o+#{M?TH?wsE9i`mc_6g43Z-KI&5`ZmJG9oI=>tun^u##`1M-kc_*?o; za08O;LE*+HeR3$h+2x)A=~gIxh}%nZobOydzytw7l+p(5w92jd2|@q;p+Dlq9G$8U z_HoF{^ek_aAXth|2ec3it>O?`7(q8Ywhm&lH(zi)I>V zfcPs~v!t1fZ$xb_(v$87_1OpYuO81Om!BZsneKOX!c}DW^S8dZ^fH9-t3G_qh1PAu z@|X_o+eSg0h`gyk&mHDwCDmiss7fr_0&vhw&6K<>3ctp>|2tr2ztw?qahm$2X|jjs&Dr#4YE~r1?+0l;USi`xKTE!a zJ2za8qnlU$BYWJGe`nJte^y}AUGRM?!m1~U`NR%~1I6lyC`GGv+4qVl@TBC)tMc4m zS>6+EFR)lY22LhEk>3hsp`hMJ+?#5A4BM6sC5?$dF; zQ()U6WQW=-(B(5KP#3+sGUz07TTiqayaDAzSFECv^EVoHUcWE0c(=};SFfkD<>6D4 zRBH(5pZm`0t1#b-CM3seU~3tpraLnwvcCX*Ln@I27BX>5RP<<>F5!0fG9A~$aOdQg zN5+KAhK}EYtKuaL-oX{KqfuBa)=J8c=uDx zB_>z;ZUcHVzQ<15k{cfkH*BtR->1)#NpyfGPaj=38&z=FcKPum0xT6D| zN^<{q`fq2%)n?Z;{^eQc;6c8x`|HuG+MDZo_e8$S?WoSy@9{^yfkv`5(5f4h&m&IC zPl6oxTeh~Oh}&8>hoAw;ScmE8-rf@P^*0fdlA)Xm>pwao!iO5LoqoJ z)==gzaE=%QELaw3W?^xta6p(OdCit=-drriS8;fxE8$vTFW;V^RmWq8BC-H4rA}!( zzJ&L8zYgRC1YnwugV|$Zu1UD@1UHdfpZ{M zOk3IBuRJFD4h>S*MpN+njoEh)*G-agadl;_ZPgC^*>Ci{X*v6Mg3w zc=5@J>G5N;*HxoVyd(-Im`tK+fjV!Fptaq|5~{JwnqNiV*36a}Jii|_J2_ICDWfm| zGz`9r@#<90ZAOSlm1~{}FKw1_EE8%OG+85DjYbY@wP3ELa+^2a#(j`*S5V6&(b_BboyxFr`N73I#MB#Lm#gq5VlU|O={^TCdT(+1J_m5$E_%3VPJ|Cn7g{R(*c6;Ypd9uRZ*AjQ{|J)SwY|x0wU#DgUx|M`9!{ke< zfa({4!!7Fr*_HlH_kaHU*S*#uA%Z zQ#o9c&su~l`cm7*m2fQnvBd39YEJKXW2Sg^9b7T^@sJ*9dlI3EqU!dVw{T7r3*UHn z&rmYKe+2tK_2kxG*=HE=mEJK>)@#2R8ncO3IJHQLDFoMFs?H2lJHUP7XYZdDW5n#O zNPL9iLk!1*^PvX309a(GUFiS*7CtpOWl-g?G8Dhm-q@hz5saYBUw0M+VO{*#1nk*9 zdrxy2J5D&q^}%{%B3j1vk!F;CK>vD_F#6*y)CYT!0D=7@Z!>R`%L{U4z8@COPQxNP z*NN0a686!~8e9gDea#ce?QESpH*49DqV+Yl1((a^2ipWG3-Aw77Zxg%+PF7A=Lz@s zL1f)X`oRndKcXlphv!?3P?W0S0IEH9D)ijnL)vwY+7yBuzNF1@9Tcgbv7OO+E&F4( zg>fzuQcU^6|Cbe6*%}JE1EjRA5mj*sALdmghwMS=GZ-46u=e%tv>K@Y2d3MA;y>*r zkf*R}y5G#_Oq>jbi2l%ddysC?)33*2*>3uaXr|bKEXyig{SQ?j8T*89&Pei0oQ~M$ z-VU){8qrnMQC0DnURj@NComGPpHx%@rAoXal31LN``SWHytv%$%I} z0Us@9MbX-wMAbV#I@RIonALYYj zv@(9AO+_|-2{TWQka;Hrd3jykA@7#((jUz=%zBwvW|owU_vlYIzTPgpgB!@V5uALeBEyw#k~+cB-%BVk2ryDrW*~ zCgcAi+Dh6N=%KdrPVT&W$df)?5B{YE$=DuGbJ}XhSbU;;HF!d`teUjV*x+>*=Cfjj z8tFJbi>yIK2OC%mQAgq9#R)+!tjNlvA*0IlA|zf0G6m^QdQ{dTxe8)xTm=+e7ndi1 zKqtRkIou_4--_bo#ETbkxYhf@r0<=9HN#q1BGLXwXTV2+g8Z%Oi^>XtmJ$jZ(NbN z8pxYsnbSlg8U@WDli9m9PI3`{L=+V8GzL9Fm*{)`{cgPH?~5FIQHf!R!xZ2Y=B`wn zeyvr^U1Rd3NghmP5_5 zDVU^gfAtbl_Z^+fIx8BmrEsU!HIz;ZU02V7w5tGaK}T3^Sj}4{LYqq-POEZRQ^~(C zac9|m&p0)~#2Ul>>EydEbJT#+2KCJey4Gs~j=st8mBck2|E|Z5i@@uuGgK=gJE1fc zToXdd?L{&L(1+Vv=~QEGb_KKB10i24`VBMx)>Weo)uh{G9$C_Yb9oc=WxM^car3IR z2S1Eu)$FpELs-<+N@dyd;BYq=P3^ON)8A7@8B_(<0+kInAm_@;(i_0&R)MeduxX(> z5N$aDJCU>ES}cKuD;Y=#$D;uW&^}f5NZIZ+C-G7)h03&IO>EKyWQpT02TkCzgsq7X z`-Nft*u?B~B^#buo+%dFDWUnW@)$%dRAtAXdKThrr;)@|(Q@eT>8)V41@1EnvpP zNb0owR{cq@2*}j-G}y)Zmx@(5^;7g3`?LB`)Qp;0uPqu56Tk>ebyQ8udul*+ft2!w z_oHd);X4`ba=iA%e9JL%x*uGS?65YyW|OXhk>kVd{Zm=}k3?wI#5f0+Tl%V_%uGy= zq#u)1%ZG?Lh>XWq#8cF*k8Qo7EFpN<<2SWvZ1jAUTf=hn*;DM{Q*uX=gcch!@B56IGfv~{W( ztyf2g;FyI$jlsr4gme6!$xN*3XOOuNJ*%;~pbx7#lm(!G5_k5Yy9pI%)sJdOy1i>A z3@#Z+g(a}J8zK1cp&|S7f!pw65WX`nsYZ`{mc=Y7;+IYP)f6r zuve$;DvIx%wT$n<;JA{!^5FCMJHIOO;z&`yqLrz7f(xPPxvv#7!N-@wzM$8 zOodd(L~ynu<{+`psDt2o`5x!l-_;s2D&r12cT{#KJwmHTmK1dbv4|7dbgdWSoosS1 zCgJq^i81N)lv;htS3pTTGNh4MQ*z~cWZ()4yx)q`ar<-lfNeLImgqiL zhZ?0}Z|+{@{TW2iL2@@u`fR;*)J7|~uZJi$b5Ao$s5kB3b>}vTIr(jnG?lt}r*nAW z6FCT*C|BVYIS5xw?%{rgA^DTR0Musr^9kT?q%V`}GEv%OSrsStb*O1x&k}`Keo6O+ ztIj?tkZ^m*bFZpv8i)ID@!a4F5=vCh+IfJY(zSO!?t+%z#p<;%1mw?Bnmhh(Crb_I zKnsGD$By|8JzntAn39w&xI%8X$X}z^7JIwDS`){axP58;dOhvLaP@BmK?xO{oX5b3 zOfROep~gvhJK=M2NRiiB$_32h$F?_D3PG;pjS>4ovf>r5Ps;0 z2VK!h$Xh$Lg!vMyyA<5&(_Wxr7@!U#PdqDt6~Hg0(Lsd4%ST%*`0a!lApG5( zQp7YEd!$>lJ1Q_rTqNGaxRiiQ4i@>?{P&-pK6xh<7sCjoc2XR-5=*`-y6Z++(4%wr zJ?g!`^;_2rTxRQmW#C6jj&+f9Nh1{9Y;m*d%Sv6Rg23OKQMP8tXo!$C@LX!GZ^Hv~ z!+vSffbG3Mej0V3C3&M_EZ?Apax}Rs4u8G;1i#J%g>qhc|G(!85PV92jrIQ~h+jN==b4ia(iLWhGqc&%9~Mkx5*Q_Gt<#z z%pFGC0}ucLpQa&P6188qpO*g2R9G&3IbelNEkQSgK)Y0Q&5EteS{~0 zh{!ihYOwka=MXouZ`-%3Uba)8Cl9rQv9mh?GN}CbmKZ+o=~^sJ)cWek)*zAt$n7y6 z^O?>aNTck6?vroV=~LVji!ny9iV!}1$hNk}e0TqB=$>G%=8{J#hvI+i@`d!!x%4!M z7;V|O7lXdw29c%MW9oCl1r>*Q=mN%YH6$K5Uc5u0#YOjB8HyB zUDL=AZ(?|fsN?FV9y|8@6_e+ zW8ey}6f-$bgs9p$pHGzWc@N=rqmzH;@($*7J5F5l9AioR zKnj{Tdx%!adMWi-%S=CdqfI^cH>hs&I@LiKS%*4I)=8Q1SRjW>^#xqQnte32cvBi9 z@6_Lrbs^s1$roYSv`zD1j^hVQ&SZe3lYY9FE;CO(aV11J7skmUn+O|~`Ne@`@Ih9n1y87n<7w;1v| zUyHTqGopEnOPi*TgC~e3R7bJhF^5i0IS5p)$|$`ZG3)+6s=hHevuN8kcE`4DJ007$ zZL5{nJvTbl>A*sI`4w|SY z<~TtN&Lqg#hlZ8S>3YGe0a|9oV6if$Rfyl?;SKwn=P9@8Wrxmn2}#vId0G6Cy;w3V6gIJj&_Exiv;J%>6|FK(c!$ zWCgi9D#v?^VGSqXwScl9wKdq#b$4zKpTZ|xL69;?W{+>72{hxj1FmB%E!z=<+GMZm zFV)8IBENQdkBb&Sa06uD6`{7n4(!=jN=oK%JOb07P&HUngNlqWV$*g0TCKQ}n=93u zIfXILE*WqUr!(Iuwb-$U*OwuhcA12qg|CvlTO%tTRtHF;T#<6^c3F`kcO-Klzz{zu z?xPB5>dOSR;g1k$mOJjWOY-3DkMuMEdSn^*={Rvy`}Q~o$lSr35br-O73VIY2SKD*(}(( znfA>^rFfpFj?Lg|wm-fsMu=r+P^&&P>>p^doy9Tzdl9BUTTercwK$5ksSM7(V4{Gf zjgBwKC#nCjM>@K5W7*nP=B3EcK}R^Nwr*70mWM~w%g1xxeju%WXb@Hwrul}~mRCEn z3_-&m1!E<$0SK6CvEF!fbRrYx7tnZ&Z1$#ZKeXX#%lU2i3_SdcYV(Qs_ceN_8#pk)t6)4UTMnV?QPNzop5i}R#ZJH4E3&Z3WQyEDw)=d~>O7~06wLyc#CX^yA| zeK=F5X0_rRc{oMPCZm-fzr%@&$GGSTU73AQ#HSVC31HQr4dI_2J1$TtsmqNCW{YiW zfsvdK@+#|tb4zA$T>Eu3IZP8eTvhQ+DUNmjmw^b~FV(Fgh!b*19XAE5o>sGB9L5sR zPg_(PkhL`_b;WjQqtYUx#@Nb`rKfHObSA^a{DA`+x;(Oc`7YcmnnQ`D+AlL;sU{>x z=?H|cU|AkRx2p|+WI%HPjpe6kC06oAW6*(egU|Z?bql~q>CoN{ck%skMRk19Oq^PX3o3 z!ZtWlh~ZldM)&TC_9r?b>lerJKEV3&_9ZnrvSuelUq3`X*AR-B^C~2@CJ!AHeVvEbqD*{_3$6d>xFE)FPL$etN4hpnh6Pzw8ifgR!R+@U zn6xsVE%czF{?q#iCYY>AoH(sgQNaB;5TXNf{XF$6LNj1C;}PK2UWHuHI$!>)*DvPh-#J1Nb4{D9o;+CcUVI+U(u_1 z4GBK2gq}Tf3ANwL<6r?f&f3dKINN2RY&#Ku5iL5JyJ2%CckapQ4;4OXQjqHbZI9{g zn%%meuRj-~45L&v7^^B^kqcLWU{4A*xtI56k=O(HctB?1`RAI2lgSn<1fV8wzMMLF zR6()8rZDD&k}J4b*(LFdnMqQ)(nyvt3~`v%HSx~Wk>T>4BIAjAxz0KXpa`p-lr>7n zO%U5)YgxRPHtj-S_C3X{;fw1b<>|O_Afl)&Y$m((;$joYq zF+uGd3NXbn8B-w)t4abM76^8ZZZ`k}`Wtpo_(3i@nZRn+=7CNk229=DQwluXf%G(V z;n)lTznsg#;I%22pyLcJf81vrCev&b*D?UV6M+gtE;|}G#r;mS?M&f((+W8Zy8M-K z(MtHmGb>r<71uYCXk?;w>!7C{AC$nmdYW+_M0% z!i?B$!)$m+GG#|}m$MufGG~#%-zY|6nAAs!bYPDg4RjiYTInEdnXP^tYBm}Tb6zlD zsc7#;CsP{o;-TWKUD@j_TpUSOYUhBxC_RSQHF!FkTo(3&HqAxTbuS*`Z^xV2(|eGl zrwb&L_EW8z>ROMhxh{mS2-R>M&ujoxhOC}G+jN^SK+VsT^hL?0JvMYNn$Q7ciPafl z!Ln6Xt-{u+Dcctd<{KF;@LHT0h;5e=nlx@b$1>dwv0=DtVP~I~$AWs$K+2qrq81CR zPHjpNYzr-o>Hj=wM!UM`HQt_2!S5}FR-|ZcK}ACU)*Hc@$%)FYIhf27QltYO!_M^R zRhPudbwcOoNKDB~EM<$*XamY3ZRj(ma9G7`PPqTLrlSBxIuA!r2#?E@{uPIu(x}D`smZCvsQ4d=sEjCj?y0Eq z-i*Wyx{y9ERIMNJ!7PYboM2rp1Y%#5WmymOj*&sQuZ3(KSy)(yEk9w-zF?{vFA1)P z1Q#4Q_p)GT!Vhc>+zH-tQy(v4Y}Xpy=&rdno!XgMMvFVlAGa>KEN}t(6dFP0IP;dp z*Ji38H`}uxlTCPr9a8Q!sm!eyuYyNCc{nbXp7y+)zu>@m$ZBs*z(XO?HWl0E;n8)t zgGWNHz4{teSVXBYj1(d`hr2gi%9p8HwN!705z0QS+s*P+OIKtGFd>~3LGj@~ApBnx z4nsGWK1`ycEq!7{t_1{L`c{H0Af5ywhpF&~7}UKgvPu99ODpX&}cB{C4Wezm@x%vc(7 z|F&7MPXA$b@zZBS|3g(PB9c|e-1DmYOP^k%Wvyp%WX7mi$JPiyiPd~+$ooR5Wj7bJ z(0MSRNvyIJJ4yoTP19IM$ERIc@v%1LnxfhA*^*LQ`9oRpIwW2yXc?V{G+xp19$z~b zvk1eDfY&m3e!-b|(TWBfh`hHJ9Wg-9B;jFR!d#I5AiX@>?S4LBPBq1TKeU8?w7wY5 zm$Vb`M8laN06Aer7`$|2L*zwF;AcV=WzTNN0YIL2vDU%Wzo5DP=o_UBa4z;~zuwpgtO)`OqmzdoVWk|q*Pn-I5+E4st4Ni||eEwVfM>}IQ#D3ovCZ>TzN2xPN3$LIPP`-_MR6w)a%fctZe(sAsQPC9e zVI%Ah`zp-DQQ?&{IA8SdAfH6`USJRIx$|c3sB=L6GV~WV!rzHFp=5hWlYfn~QfA4n zp^l&^CcNS1fJnp>2r@KrbYKWame||blD;Euv@n)a)urW9r}brhU?;CgRtiQ@Q@P{j zW0cPH>>xG)aAgP|10&0guDfJtii>IYm{K@xw^|m{sk3awjXsNtviSExgs)ThM<0`sOzeaKoM}+)izXDu zBT~5SU<|SJyE=h1$t(R4O4wQ`FehL|bs~C^B;_Ic*IMbh6ESmTjD#iwtvP-agRC|v z?jC1jRJuj1L3nW8gx0Cq{$x6eZsv0S?);k!z?|Q(-A3PU!MlHpK&{3$FhFQ##<}Tu zGl>E$^6PVjZdCgzWVXzB@4c7hnx&e#RxEI6@*9-BjG5H~eXskM)OG&9_iQ551|LwD z%(!gmuyXt6oNFfA9VdMBUvrKt0_P@empnS`Pxlrz9_xpej(?BM1U$bxH)9d~yTgS- z0OZ;++Id9TjrEZ&T0X)o5l_(OYX7cL1L<-dtx+`E_mfgtRD{-HpIUw?80(5;mg6}i zvgp=HPaohV2}EeJEJ5P~)vT~}M>u9AcHp8a3E_{EzsTWbdr@&N>5hwF8hq(PmI1s6%XjhMN875eMf2=%igM?^S{nUK=5q zK@}L>5a&c*)Q^U4a?jC_`nzuGbgj#Cg)&t0)y;RHzGqHm>^~EbF*BsnrkITn^&-e* zPO78}y~-Q-CveEdhic2S(-X@Qf-2(j>plUeif)QYaQewXTYg#gR#E=5sgjFy0C#G* zP^cpX*piijgYLi+=7?N{cwz0GtlJ`{j9*k@lCU9*e)EX*Fb>=vREfKW7TrI=DjT^X zm!9?$5P?D@E);H`31IFLg{@4cXD0Fbw)Q8sDScI!SC?OOFK!Erg0jWO8(Bl5^d^gC zvUFM7n&xMLDccT&2q8xC&Ko~xoZ|NMHQ0EK8Pa1WGy6>0~E(~kTeZuDP4h3=|#SoQKzgM~1 z>{GdXx4I`}X>^QkQzoeFWx955(OORC-NC)hwejKwRi$#de>5wIOK1JtH7C9umHu*W zquTG>04Mw@nyS^HGTW>Q-I#$6*lFFAg56ds6w`5{IBEXGba^f>)uQH_|ahALIN^Tg3kQ#|4C^9jw~iQjVFJGOi&J`1*K#yc^a4fMZI&)inGR*|#GZ zp4h-feG}yOS-L2ozqGhKB-cvAg8rj$HzvUia+sb-5@OGO!z$}#70u*cYJ=i5!FUG( z4L7C4O&>q-!4IcZB>)M&HrmB;*z11PLape!ZSsBN;Rx3IM%u3n%#a;@RXq%~H=m*| zt*uO4A*mL@$}a6H zYu^onFkFdJmkfUs>>cx|`i58hMB7Y`)m1FXS*fS$cn&G^3*^X z)8GJ>*KCN%hO!CpM`Z<_5Q9@AM;vpz&K4RVVJW_o$c6k-9WA7#*7go0!tTZ#g0v#= ztlnj3(xVH=^&_*=H8qXJ_A*%l)x>u;a)(mFO-3M;Wu=N=DM7|Tqxi{)6dBHEgKuIZ zH9{2iWwKpoGXsq_BK@MQ&bLpD!d{4gsX*>>FLh>g(#jCHCLmz zX_;)Uq3H_HO;%Nf<%C^7mxc8MCbSh=SmwE{7A80q@s#I@9XOxLV%f3Z=kzTOFBGWO3 zR~5E(Q>9F2w9NjBHy1YJ6JtZVK)7skSr~J8d) zpWXya)#dH0E9N;zn|tJ@-@?PJ*g~OtJFWPoUTXN@+&tdaS$5&}7X5X&IjN?*n1ex zEUI;dGwc>TDYU?1{}>&6 zv_(c4Az;$v(mYtbEPkTVdkw2$ILh`{-tqGBeS02m-iqFmV7v|-jkl#_RY<>90_oFtMw;t*!{iQR91&hcAs52z z6nA`>>zEGi-&*?})s*ZM=jP%&0y#j$0$m{`Y5KiqJ18rqCt9J`XsBo$0JHiugv>lQ z&QV;D4Mz18i3u>WWd2nL+#RJPe}P#z0fsJZMDO~5Yb1*Hp*wv~Dpz<4t%q8#hj6~K zKfGtF0@tFv9FHYLOGIWnIzp@fnQ2Z9XK7d~$HAm<&taD2EJqV@8TVZ{-V^4069DRDYgfB{&B zIV0K(f-D*#Q7R%vbY?vUzTw~KsL+xA>%fT2hdN3vUs zc}%8rRILn5Rk;GPl^KLgV!u**7f-V4#$ONdvcFp=Upi(5-<>;h+u41)TfdOF8*LOi z_TO8e!5HZ{)rj;PvWG8=6=YeAhXc=mAkq>7s9!_%d+yIl0F;SW#}o#=fV)~ooMWJU zLtyKzhW*;N^<*H?Vu<902j_W58WqDP5#%|^rE*CouDCpr4RUojhtHfK&k`0TMxn(^ zm4+zFryLnfVuv(nW2@78Lq3Zd#CSY?-^dmB&)O@ox0 z4dD_C0@u3SsF(9M26Z&n`IJ^zhVDI`S$s7`sSvJnlX{>698ZMzKB*>ys3bC2XF%dRW3Bs1Ox z*P5pqWj;2VvMXU|Q{hWDQ79pVr5-lvgy}4r3647VYNNG&1gs)P>v!T2gDZo<-(i_l z&j~E*Zl|&j-}U9ngnyI(gM*U|Fcj_n>-7oiK3?r&Q<&qY&Qv31-*T{B@d_{Ka~=PC z)cgu=D9M}1Dzgk_03<=*zbtug`#4of_^^e%Xv)*7^@v&}{iCuZew%4yaGv{l&{c|z zh@%qPKh!~_1W&kbZ!()l@I_%H?{a!m1)kB!*pLUH?H;$JkqAIvK($Wwa z;HP=v&xgaUt;wF_fy4%-(Os*7TYY|x0~U&xObwTz1Xu#Up2}=0lH&2^YSDP7jA_6i z$vaq_fdiySTN>MKVjiMM}ON^~CD;5{00C1*9N+sBHK({gm>f||J7 zw9$p_VYVAXs1$|j%d#Q!vO52H`J?+SD*RrKK_+l`|2W9cPNT!O_B;Y!-Y#xxf zrM!TviM5qy^Sxe_p>dkaNBwiO=)?*+A1Gw^2#DhKt;X(&k}hSu2$9V-5;NtKq`fEIiQ((*LoR8I*Z3ndaNxArDH7`?>g-w4f)nG7H3p#I` zqak%9#@AhpEgf8!TI}PQq_eLwEMe=qp{~{lz~2h)!w%{ZZz`1@D!SZE@`w$o8`1uv zsq`7e$@pYU9BCUny|hovtbLM4e`=gZn&)QGamy?l( z!|m_47-75c&G2BqHw>5`|NFPvs?XcwS?D)V1DPD^e~K$gJOVj@iHn8p|6aK_X-V7v zPia-3B)F}_PhtfL;6k;f+|; z10Z?GjUiJV?pb+zK0ZxkSR2^nhxcP0&5Z+CAn1>v&VS9{P%)-5nJ8 zlT|;W8uGJQd@nygW*!IzYGw!=fpbap`WR|}9G^qve>R1Qa6TEXB&9#iKlKG`rY{>oOD>ac%pX230lW#4 zAGM3$+Ff|{Sh1X6yK$-09NZ1K+#dp-0c8xbwn*$ zdC6`MOZb8hd)LK`g7yeGFTFG$}!5Jl2EYa`?Zcz|V z7w}Pm0t1l0>(I@PJF-AD{J?P-_lj@76|u?Mb9FyHwb&|(&J><6!(bsEfV27bd}gWN z&Qef6yI3Zx5y*X{^}M5PLeW*pmCQM&4bQQm=P&> zg{QYnN8^J^cRQ-V4$K6@ERdiP9!Hh6u1QHYLpE3W$yMXkJN8FCu$8f@O}X2s_40=X z3|22GPizt4GrAc&gsu@E@bPsNlWqgcqwyO8F`M1&w!o2FY*?AXz1b2w2IUfn=E`&^f^@?upD1pi6Wq+w2gxN z)bBc{1i$|R8=`GifT9Xpe&lpv`eTyQzi7gc#OsEWj9DKgY&p5ygy{r%$J4rN7t8kM z#FICv#erRzM+{j)ysdxNGCePRtk zuL4mUifAff7SmH*wr7QqGezqz85gl}cDSIfzkKv-*BI3kOrdF98sRP=BXxSW62(mn z4A*AJr$N3Cl!WL8@BX;X?I>IgonwNVz})DoUFNUG??e4<$9gc+*B9c zQ>N=rC!QjI$`R2GIc9Au)6LwXsSZY^U!e8b?RgD>@%Gdi`nn4X5EZ)BH8q3*MOy_8 z2O(64XtiI@V;wTXcuUhoGK=bH1mPKW@$6yPU^<^VBG|9Eo_X*79l+!7IM93K9cAa( z9}tU1$V3GLXmd>$B#4noTPiVV>`s*7^rD3#DWCXq)E)jYfKH5cXa+z+6{6 z4lL8(LoO{Bq%9(P;frXolsHDRPL~`UAem9=>H~?XX%JPcvQ#WiKV* z=*x4vm*&L>dK`RVa-d6;tuvgKoFA+%IYpIaqROfc5IS#73s+vGZ9<@a`}mUCYp@g< z3fzyebJAc9#(Tq^d4X-@gCMU=>wn9#z_*3y(HGLIIPU(n?UmGbSmx5p;zgj~O(u81 z>iIa5B__XhE8hcP8({c`2k4z_QqFN|mI3)Ne}PxDKeUmG*cX?}dL+Ad`Lu4K?prA9 z+)&#BB06|1oesP6lYT#)`LIi{JP-#zCqe54!!Oa@ihY!HF7PF^Z@dp#tQ95$Rag`| z`I{JK&y4RIchteZiw59@5I4dafCWI zKhtdXdhGXXGEZDaNuU=zO%6_VST0q9G$A7aR0;PiNcC_G+ayO24a@!&T9dmuZ8s&x z2ffhcTt4}1u0HSU)T|6|$ah=#{6Cbqr<)I=3{>ZI>%ug`2MdOdR_#d%}Ir-n? z>rJqTbANL}eSe$@q)IF6r+(#vr~#Nb{);1brK@d^*Mj7GQ?okqOvHQWU?zd?@2_*d z_}d@C5eAjg=)(r*6i&6hwu0nKqsx<1Ny#4lzhr^_K^V9((=bB}urIn_iu z;`Nm{Uz%EaxxKL6BA%>i2%CPeh*X!9j!k{DnRP^c*!kBEz(c|z^lx-!>qKgy9y*mp zdy(j@7BG^f1=~e}V7;a;GyPCT3atgxFqt_kbU>@9is_ig;ASe^o1X2MMZFj=WWuj# zlTB<{j3GnnT@2YHN>~C_#yl)p4V_-4=YaQBk%?0bd{O(RJw=!B7E+zYAaOiAF%+y9 zEIYPMab$R6fSJdlBYy0%7W#mG(C#dnw2i}G8J7^P#bQe2UZn%%@c>grwN@RQd_l1Q4@ zq<)&efOzaOXDx}z&VUqBENwcKr+)K0>Bb#MY)m&jLFxTNsaL8)7-#VIKHoZkf_yU! zHdkG$&$z@MSbrB62Y%ksPq2W<*fjd;iB8hXtvXrBA#KaB_id7!FOz8k47u1%m)QxQ zg=AjspBNODrxOR2RS@r*C66HXCbVr(?BVoi0J;O|A?{mzB@TpzP`w?_Xso93d)9ey z);)YhC*%U`LeFgX?m^u>hefNxa2f@))R?mc-uVzomU~5^q_YZVOd7%%4Oj3ndC8VU zCkDp5^A8A!gZYPwD7l!=TFT@yX@y2~+>K8(xtdNfm9cc%qEMGr-JBlvMi4ucc!{Zmj6{d)y9A!qmXMM@ zHg~<&uGl|lw(>dtuv*viSeHa9sg1~GCIJtXQSd98VWs?mEC=)e0;lm5qXNsTMiLVW zsr9mZ=#K!WJk==3)*?%`d)w`3h8P$#0C!bHtuy1`mWF#`9?G;iY;NhL_*ob8Hl&`@Ve$_ySi^eHIo^p-49$1slibCT#!0lZ#)Pi)&&G77 z0~h;Uo}M+KV3?nx`S&`tO}f0HWuK5AQjkrSc#?29@4lAUntQkfXc2+{@5kOdU}6Se z4__CbA!6u0a)82oW_p^So72mwQ$xV<`|=U_Wh>NE4=@mOzq{QvaB^ZUkB_g<&;94? z>~kP>WJez#pI6}R?KSb~;^66`PyOZk6XMsOe=`^(p|NuEGxP`u`uyF8H3HdLzTU5| zmmg<${|ds`57a{i@bPi^J(HFR00et=p`kO=-Ml=1-Zml+C{iiRONc(MhRDXKY0uCewWEk>skTxI{{SgrR`oJ`DfF|WIJpO)YwV|570C*;Mk5z zrIrDrQw2VXtM$~&O_RGIRF8N@X@cHq+PAHsc)Xlg!2BEs!wL`6^UEFcA@5k&flxF= zRdd$0YVPFLpqiL<*Y09zt>c92fhEuZ&5m&mzt_$P1JM4Qz|9QJ1K{QlFvWi%^&xF; z&r0yH)f(|gZ`?@XY}-*rIm^~rAd&eeqEpCF5fuHh$A>qJJqbXnEn~DWg*X#j4?`9* z3=6-uj%DnRa-APpf-#hoNY*n9;meyK5Y8sKHEa^{K#%fQ?zdn(3}_#q$rbe0s79;} zS&r|b;fYYi!Jmg)2Mjm`_u}n@8?KaJYBXiaV%;#$hA=GmOj z*nHIenPo%fKyfYY9Kj8--XLYEj)R%kt-m))&mpJlKyySfs>PVx)R{bXD6Ro)mN-nT ze|kqnV3MNxLiz|E=8FkUY*7jp0{$jIMz#OL?meX z?oG-7Ci{*g0mh?ejgpX&5Ufj#O?0S1BA53KWRJ232MWZN6<~RxQhd9E>CpwYqr8BjWVdK8stbp%gN{icp?R@iuyrN< zN&L=2ujJ23K5{Ne&5;@!^#k*4HU8j$9*10|2TwSg#acpYRn0j<(OQv^KjR?Hs)HaL zb4lS$9%z_8Bv7Z^72oxmqJvTin|(eh;@XlAqxQ*&d$iK~;bMK^L_F)~sCTgvgmb_~ zB=JlIbYd<)_y$4UZ;=C0uVltQ7?3QC-pw4_S;P70aE;9s$f7~C>rk9C8v4zEaxDWh zne z6yau2Xc$v;c1XDt5T{n49Cg=q4z*70HV2M%l^JO3b+<{iUDj zJFZpT`_s7fhsw8Oo&Kmz*tFG6jGMQKM4_bKsm#TB?J=jAFB*)%ee>NRo{U2cE7AQl z%V~}cFktqZoxfYmrFe^QiqvV#E39XNgMX*Eig$p=TTpN_ z3+6hT=YVB@fe$3hCc2F7XE}gJkgq_@p{{5(KdS$Cwf8pX-w;bd@O{tWt4DdiXRqu z4Am9oy~50r=VOfq>z72*@D9O@1slsax+!OB{rRg7FTL(csQEl5?CQ~|qH}o*aVs6T zY)ow~*6IBx3Din_fu&LJ&yA``2&palo7pzr7YGG!Lk7ToIE8v`bl%DR7mK;5w!3 z#xw4(BlZC)V7}Kx-@(vtd<@^NKa)y+n%Ye+kbkF#Y>@D5FRh1EU^JM=<}>Mo-;3C^LyxpSI#n zmb|*;HXa>tUmkra@@_Sc=R(3Ad$*4M2MUXSQvZ%`>S|aJH&4{$NX~e z?%(SbFO$FYpG)Ubh4j15I}qG9sa0&NFdQ6}{qBb>=iNznarE}5-M&&8$?VjP4spHv zmAZ^HYkW@HHFT3|6rS4}(^*;X__6*CyDyjOPl;yN13ulH0>f#q^Gvs3zm-EP9hd;G z)6aT%cl6w@S1HEI!Uq79-9Jol-xyHWB-b_6k0uRuqKel^`VpaD=Pvd>I86Fz^N+uX z^%83l)Ic0!N$||NIb!U$4gsyZRohxxt*{=k$(du;)nM0hf?^qFj?EHQd&{1J)g=C_cnk62MI7c zu{&Xn;07LD<3J1sdwwvcJu!}te;ead-(P)YuLiyHd4c^s!&)EK(M4DfnVbiJlhnNh z^PgzXET@5q4WNE$%%qXNgWLPJ6-6ludBRxaTk sft2Y^>%h_0H{wtO$^@xTQnD5 z5`iPQ=_%EW)C%W#AJwjLl{}#5gX!b#9v^bHZx6`@0Cpg!Rh;dxKv^G^@7Z~vXb=fm zdp9Ps^3|DiqKNVbhtjal>sE(<_LgSGA8Xj5Xl2PF*4@C(w)*bv^ zzubojtd{5g_aUtp$)h^HIg0t?aqQQs>nFRY_r*40djGz!q zfre?KfC4_?H}E#6aujO?veHBPZ~Toon}P{BF533LMYUmuart2GQ;yzqTl2&vf$SHZjNY;u=sg%rZmgJ%ifWkS|MNnWTIoxH#)27k}!`s zLlJ1L@V#@9!Dn3X<`A>Z_EuF_@hxuUf_qk07poHILdz3{Vx3p1Sjoa=T^?2*;DTIx zVB6}TM?{oSCFsMOBP&i0m4{u(wj4wc{5I6I884OZDr;xi`Rq#qL z?4LNIN(>9@X}g8Qlv8!ZU;4A)T=mWS|D??*P*`Qyty&>0c_QKpx(-}KvHF1`bT<9@ zdBUvBQ?l|blG4|C3$J{}#-Yf6wR=I5jEsoJw)9gCoOkmAEF106tmVepYjuC6WU(8c z5Nq<8La9!~ce*Lv_v@zIwf(AN+e6AAbIcGWZr-BEx=QC@o{CNanf${)d_Jla(f0UH zu)sJSY0?IXL0%63T}8bgPEJp4kqfAP5Agz(_PBO?VrVnWd|2@%iH9X7gE?8>m~^c@ zBtDp^`iBvIvLEBr~b5*V2Boy0LX&|HQ&x=7WP@BR? zZxpCoM)vrR&pO37G|pj!3Xd5*U-8?;DGmG3TlSrvT>`BOBdUNF1<>E zRo<`~P|QSIo`h_82%~xMw!O@rGgcRf=<8=JYMT9OTN!;;&J;hff41m(W>dw<(yH?^ zU7J!Y(iAUS(KY2d_F`c^j;r0uNu#kYszNLVyO`3mWah{?Zu)*H6Pc`LOji8&PTTkU4C%n-}lZ{&0 z!pMWk;cO39xIOU(MeiFkptBq|NB3>Lfou9Y5ZASA=N0p2x+w)G(jv{(q86L!xY1It z_lK^trJcJ8N`4~s%P}1t5ML0;KEkF3M1QOud}NdubTJizwn;E{wr&vPTy6-y94j;i zs2zuCb^mTPn7E>ifr|XBzlVQ@kV{o^tQbl=nfc=qDAogN*#b;L@bM)Vs%w1pN&R9G z;uB-$=YW2F2RE1p=`pOk{|q#PN#eOll0zLBH$oB5c|b_HT!?lIb!2eCXno&G=x{p1 zZjK95wRFsr$AI*gA z$vjPaXQShg;W-^cLhGl zTqX{zzu!|hx)gS8nvg#K&Hy)G`gQ-qN&sQv_+Q-w%YW%6{?lEMa3FWhs2?JPP3_A4 zp6yGkW9nY}k8Yxz)-@My;PX2ani+GGIho9Lsh6^pl&Nj-%_uLW2VH~ot8ag4Ul)~# z{(^w>7Ph;)$vv;2|NEzxu#eXD{WdkfeQ!A`Nj;jF@d_vAK1xZgfrSDv7%JjZgdQDT z37Au5clz;4_X?D$QB@_(Z=N>Hy+%C3+jG811=sD~=x3VUhkrOF$W8jz4xBBl4`GM8@ZB|-Iu+p0cfx^Fm^q}E0SEu^@(3I=zU>17vXgxH@BC zr-nVWWotbNi;3$CL@&{#0&PVlYn1l`u3EKva^%@Dk-M7}_Sib#r%C(r)o5zQSMfpR+-HIh77queQ& z1hbWmrWmBOV`&R}W0IEokJ@ERc_ZW&r?Q-`mQuS%)g|{@&6E>ZXYGleSmDVfnaD2{ zqLwiiqGIV2+Np?x(BEcdHh1s)qPaByWOJoF0;wuEPcWyp-D3VMfq%(L0|(PJYRfs-a-^S zDPxoQ$1^ofa)Q2#feYe|{<6+oHjLF!p;t#}*sJ{spiMig7ikbH)OTSX60paq!C(+U zP!=Fm`dgbY$3<`r4EH2%bJiM*h1#0=X;C7lr#@ZDE zhTXbOOD&~Wg@jib3FS(oX^+C^+Ip85;AI{-1v4rAsQ!U|W-6=Ot@Qr@kw9+04ePH= z8@!P@^trrR3)?=iEw$5$ZG|9b69RH!=qb!$AuBK{e@#}xnNLGIVvZG;>=z?hIx+02 zmY=Gys{$FQyk`|`LdyGP)AI!u22#L^sG*thQW&>eUi<(-+ha@+(cAf)R92W<%UCLy z)Fgr`@Cmku+&Vem3>s5<{3xWGsD!pmoO{brE!%E3s4gpRhhNy^4$d#@w}w>m9!`RJ zVjm{8e}WEW`jEg*l9qs~Q)}sPn-H>Ke13aeV0JRLeaOg=hobzojPez zkggA8hYPn(BK;=h`D1Itq1VKg&X(+X&ZG!Te|WnpH{SfPvQ=E!5~T-~ZONy05z3%f z=oZAR+lzD>)zEk`WCJrjPy=`-|A3wnTleVkPI%Sw@NA-)y}Jr{sQS;wqyr!VOaWky zbS>mlB+PYHF1J&3Q<2L|Q)-TF)&F##t`_HyKu{@anJDl1$UU}M(A+R!yyebG&0T3U ze{^{lBX#S&gdssKBk$+8%w3n^&D3z!UDJcESF)RV8LdQ@dDTb*CDha7DIeSmL$-$k z`o{J-sR5cED01A7Mzf0nAQYHB2`ER|S0af`B&8EGN$x|jNm*Lh?Zhcw$?vJAx3x;X z-9@xuMY;>9h!%FE%c+{2<*a`+w0mN5e;(ts8$5qcJ;>pZyq9ARttUZ9rhS|Hu{=u@ zD)|RE-IuJj7r}kNR%&YM*)#L4?#(jK-~6rD(SFG~u6QtZ6LHsig@SWo)z$H*ZjHMg zUUTf&4)t+5d~1A<_a?PwsVj$mKrgL#<8_xEc6D_w=3?nrB}FPFVu04R$cV?qe?f)Z zzUk1>+he{jHzB5C|HZ{~FW}uSaM_i#Z*xBa0jIdC<8ceKU3_imT<((}T>J+XADJ zq=mAjMUMpMnJV?MTFBLUg`^v|cdg~bnhsE+FAY}ku_zV0;<2s;B6)BcYdsp(KdK^ z>lKlp`a!;{$eNLtUFTTD0xM0rt4Z&nqBOmBJls`_dW=#!bkjm*r6I2nCC0e zseSv9vCV}7nw;~^|E{sp@?eM0!JPsD^b~Npz^$!^j{Iz159Ih|6WI%hM<-eSXoI|` zwqR}E$hY=*?Vx22NM@Gi#Pc@tRYr$9Ir#8T&H|dN>wCvu8DT4cLfjZ1Py};s%XL7_ zNw<0P=lvi~DT(+RXZq3|sWzV|1tYcaY9j}}ph)lO2TS>8I%(i5eJ;OdyW`2f{o{W> zddL`;k(dG#0Wy&gC4VtEFfcwo3UhRFWnpa!c-pmD$&MU1487+o^aCwxUw}p*f+R+A zb27Iehw19JFBt^(CBGk16t&bco{7gZ=%rLDk|H117N3O{3jVc-M#PY1%3^tfh4=9K z)IX)o2Hu_b@a~-#zrWvE{PIcs_#oc>VnvoYm>?cL3N543!hb~AoW#TJ&$CiNU9^W! zeN)Q&`ZBPtFDH|6!y~*bT*L%dW}(||xtziow_jY{gcOW}TUjY<%R_%X{3d>WcnisG z4j{XoWq09X)tR}$wL&o?tVEEl~jp(bLwT zy&T)=j#HI*Q z*bT&*Agl)5Ns1!HqF|(^F5E=}-ST8*?$0c}tNJUCH-9-Bb{P+LMx^P)!=-?GL=I8K z6%rzl4mZ~88Uib$wUjvNAf0VM(HHh8SLyT72rK5c^$cS*!XMvqsE)B(`?8DF;?k!C zD=t?7mg}=f*Lz5x2s8*Ai%Lix_$3%{M)J>gWS#6!=mraIZEWJ+e`1EX_li{UP~H-87c2A06)8&E$};6MdOGniMRA2nMe zzvXC+`7zcQ(XT3vf@T6dgM7zva;ndVJ74krF&`rh#AwYt`*0*#YP_KpPS_xmUbZrz zYwn=ML@%>W9WFPf-f&$gqlCqUt5ym?Mt7JZ;g~d729u{4JIOs}FV3MsQ(9@7ZXjft zcYgyhr^%=XbLdM%6PNtuHGIRrkS?c);5XMP@vwW-&JiY#VA9JFV!1|&^{QK%bUx$J zMycX9!aW-^CaZby3FA6}gXu2do(dTL7^aeqoa z+R$zJ4G_-QHvdH<0OVA-)7Aig>a`N{^ndia2H4yUqrPi3;5Il8-KbM@yIZ7qB~{2PjK$t% zG1rS7)VHLHylma$JYz8CGQ=S z_Z#{_9Sn8UvqPOZ*qk^>9}IS&@(+mk0(+-P=+zp?{8>nslNSFrRpbb!On>4@IG3T< z@qOuU@rU^E8pAZV{BEYyQ9@XCgDEZ4Yc|Bmlss|z-zFW{xCEZ=*L;lSmaXOVgV^y| zSnMIS%85pb8R}!_Y!D%VeXKZnZ={BvdU+D>etlBnr+-lEhXXO6q=^9@gF-t5#u)T| z_U52XUZ|3;Q64>TY2a$Zj(@!|8|ZUGb?2CUaMJSDoIY?y5o-C`$7|Gq_F)k1YQbnI zS6b*`m*KR@;T3}DK)7X@cB%Uyayt?Xo8jLO2=z7+4kM=)#D<`?mni&BqVYLN=NUcj zI#for3!@5tFTgr4y)`YqUYET=g?2JQRi^qZxkQ(}u%m}r@*cY+p?@5D3A^6o9Js?e zQ#f)q_SMqxmHq*&9hbSQF5OR1)p+!6YCwWIFdor7G9GnR`8);DO6>(rSr{4gws^vS z6RxAW%hV;@cZ=FFxhMnJbFQ|FCQ=w@WqF`l?zBnreO%#Dk=gBe6J)TV<)wK6jl@eK z*NBhfz7_FS^ijn}cYkNmzFJA|Rj>d@Yx6WQBWlBFv6ZW4!MlEKTvFL%u?7gI)p;9H z=rO&NO-J#pYQfRS`Y5ImsnPmn!Caq>X*4~t7Sht*2yf`gwsCGZCcXA29!PX0^0%NB z!C`$9;?8}D#`(-V)gYOMQgb`hf4d)`jDdnP=e2y8_T9%f)Fc9AQyI-h513Q&N3D`u z1C_I}@UH#zGL;^y4DsQI{{S~A5_*@Bm;w`*;b;LS1T!%-G?zQx0waIhz_-O7W@^gL z!A`QyA%8!VL`syZs@>xqW1R%MO`J$!ov7d%q|Vq*`=<$#Hr2{L@8BFR#kM zr+SvsIsDTwuC5O{;*Sbq8$KAwMJ?OXUel0s`El3fC@ z8x5IIm7r5PU%qGQNBj?k`#xoMA{qV7h2F{j{ir~BFSIj@vQY%9Ut0&HhUg@CAULe| zK4kH=fla|_mpA|X@c#GfqXLb5_*d185-Dl$>`!=UfVE&!T0YVMz+A946N*X80NhIv zymz*(?IY{5SnGd8j26;}OiS>AW;rWj==pIk6De`*C`;6S_J;?XItU>e~%ZevXW z@EH_W6ZlPlZ~r}NCWcYXBvHOZaf#5Rqe9}S7-;^tf{=26MyU!K|4)L@ zHle zGoH{wrG<-1hfbRf(e{~K8>PKI@eNyk4}B(ZRukZBnc)np=J@pB$OQSYSL-yU^6M3o?9js^#%RV!3#6tU=FwiYV$ zAQdXWD+Fo@j-M+Pp;hQ?*Gk2BDxhr4^-3$Ja=m|KYR#1EC%d(I+ZC!Wtz0i}{3De& zxk$^?dtH|2*sa%RxqNdMzE!1=@4z3Kb<$lSNa&Zy_S(}Mx)guo3p>4i0}iF~U2 zTzLD=%i5bs7E#Nh8nUv3172*ZXhXfD3?wwE7#x^`YW9d&R;0))KGV@9P{6wBhpsEB`^ zBh8IDBFYYvGW~7R>@GU=%NuM4i?c z039_1t-Ii%+COfK)@Kcmj@%D0zIB%F=z4b1cmymq_qcP}7K>}Vj`zlE!c{o;tZ4`e zS_`WTsaT^78#_sDE@TY(%JRMXxsrb))=v!Q2ZkNJP}1J?WH)NWsTCsW~x=JlOV%*3M z8}&0A&aGm6-?Tb-jVL8O1CtLhPCtEhdrX%N%cogzyo#Prl1O;JTrS;#7BVy!e< zPtP?}xRL?Vk>D}(L$C74v2gLjHk~m@RCO8>Rj$*z`E3|HJ}2%FHI(oe6`p35P$kw5 zu9a&w9JH7guO$NLD;acA_|`EQ@&2G7`X&X?-SvSAL1;*Nu|SAueO`aD@{s4XLtJs3 zptHh7maRVR;!2vsikIDh8dlQv?O*|=fK8{NqOgyI$c3k%t=y%#!$Fh`Qh3v&q{k$O z8ypeXg@CgAfW2OSO*||Q_+C(tB-1DJ;9P6Cp@8c^&{Wm@$k!53s@1fJdB9#yNrg-9 z(8aPl`g`I{p6D=Q?o)pe#Lh&;Phb{=VI>V=YXU?|L89+7!W9{tobpWM-r@;w7zHra zboolQBmIS{;>_%<4fCNo}VU?(B|z z2TH6n_F#g(4n+c)3r(bgwP*BSj09s5!Ln69uw&8Tjw_kwHQTW@vol6@Kkwhd&?fgW zOEWWhd>*A@@7I6z{V zZ{T4AmZ)YQ+thz7ZN)LzV3`#{UD`6a6_0*9Z6;WVoD81l?5?uOs{oH4Tk$xnH8*bh8>fL`v zJ>bk*wbV5T7RJtv2@JnDtmsjIvHCI>fVABAd<@CojBZ4X$~meB+JZ^pM`+KrZ;y>j znPR2(^${t84Qcml5NV)r1Cexo-b$XW>{F9U_FR%$G(*FrbO78PC}znfYiosNKPB7g zJ!{MeG6#P-lLf^eVUdIHDxG2x_B#&ZZ|_JP?9JA)5Z1!cNs`W#T!l; zlIYrA8WZ|{2%Q&daVrlgvDGK}sZMc*=QaxW%Vv-$7}lS5taqbVRwR~1?Ab!gguTlJA0n_Tgv;fY7(rG>+dDenf7)n^<&2^rOy0kr!63k@+$d>@j~!2pPO5rSq_ z?$NUsR^&>1c(}C24o}}kSVzC9b51hGdN+@ho#jy*xK}XyeQmokv3D5H*J9_ftfIsyN%ka)C_J8&E2(GgbX0a5L2St4LOiI!&K@|- zPIu#JgM&mx=h`W-#niQ?A}T)+xSEnB4z-fs&fhJVpDjpg?wHW0jy>6-Ay#U*#DwOB zEH9?mQx-a4C{{8_a>hp9vcef#YJLm=P0xQs=eTYP8-GiIvdz2kx(Q%7nfdV|)d5t8 z&u4q*CXea-0ap_|v2vwr#^#e$_04u{_EWMWRC!HGmz2pzU(=|0@biZk%%s;_pq^3A zvWyF45{Wz~^J4Igd%wP%pZzX2LR#jgZL-lHXPEA`4Yq#VjpXd~rvXM8NJ7gice#Jz znjFL-Yy9G)h!H^FTPL(HZ(G0l%O|<~@gJ<&x=%Qr5*}zhcwgWAYz}CiEyRG8cSZ6q z7yMH*l~&2`sRCCjhqI9?SJ+v-ciU@^6R8mp;ul)|kMlEHYOJ#i!mKmc>>%%Mu0~<# zsY&B=$i9eK7R=Ae26s?*_yh$C1$=+|#A1fw&&7`N8EZu_t&osU>ru+2tdJ0&B>SyS zI(cfXSR6UpE(jCt%eG_YgYHI6<`z(OiM zoX?omSkqtOLhsqvg|GLUuKaLYrZn?sj(@B5^;!U26+!RxkkK{&POPA;uTeE$=XQ1e zUVb-bPM^*F?mv7Qymyz8m;w`*v2+0^128f&mm&KB9DmRl1OXBQzB$;Nk;8ON@8+-$ zV&{;*A4(J@N~P-U_0HN(g7vtkR1!r}6d#||v;DNGO~OC51-%8IL`>WMBQ$&qKOg$1 zFiFGzt#j~yXWjPKZx@X}f7t%?Zu{mhW=kS@?YDRDHzk5pn-0)9Ztq@yxJc>cvvTmM zUZr%d{(op_SIz|Fh&}{H5Gn)Zg0`TL250~HU_57=3oUAjmH%aTfFNqYD+tj5hD{DmNzVz1K>VKOXZ}B(mt6M9Kz%`%=P`Paqm0v6P zSLCPhv>JWbG<(YL=?PXGDZr04$QSm{dLx~6zgN}Sw9M;@7t=}oAK^IxqY|&@HhB)g|Xkl&xZv~t&K=_b4Endwi!y$ zDSw?0KeF_F{a1zieah@aGWx3veUSZkqynjhv0+on3Gd8X>wwe{odhohPR)BCviRD- zWZ>AgZ~pn={U7%i1seJAFVu_@DQWP2Kh{SHtVO4`kMs<#FFKd$#H1xKFN5$Qc;ANh zmVMi-bs|O&UbQ!W%*|e;7))f%)&>zn%6|%Jv+%acNg>)=>zp5XBeB*e$*gxy@)J;@ zvH7uxhokZ%{`6<+3ZwqMZST8oQ;af`Pc9gnKed8DxzH_a@XS0@FSRfYx27gONE8%T z5^Ne&_J4_ziQlSZ5-2}bX^GIJp-I)h=|;_=-;i>U!v9tPQVBd!v_;VPe-ePcs(;MC z$oCqdT>_5C_j=f~-rDASqp-n8*yNC(CJR0QST|spG>)EO>zJs7lOcrGnB7Y>!pNvx z9~rF(Fp28KSrg4)=us<8_K`OdM}3AOPh+Aw0~%YM0gWP$%b%(15&EPvRpM`{=qNv%b44X!}Jy7$2#x;G~HJ^^bQR7tIa zDyfmUVpEWOot4_cR7y=itI0(I*U!2AM8%VKe|z8SA+`h|om&>5t(?PHV(T`uSkBFj zRL&`0V1Xi3=xk$&WqhkpBCl6?(dl^6iB*_cS2?+Yb<64qsq_j7>#tl&X@BKLFveCOAX^bW zWy^zAw-rff=u9z}var*}ihqzAdWZ#e9%i}NdEiWIVZlREXNKI2({qr4EWd*c9X2TK zG?Say^2!|9Qe12zSGS0=7bfMvc&=czYUH4k!fmmdTY*d zP?T?EGiX!EbF<7u(FMq5dQzERUIH%~U zGbvVbtHz}YHz*=$s!tPK9%#`3$~v39SlrklJ1ni*91lQGg=U5X$p_}Q1>B1Mb4b9; z>X@H(ehw9ngM+wxxxYn@6^>X z*t)(!Utt<^Wo1YtH)v)Q#UCSERz{HyWl^&G{?FJuBh2lebsAzU-zgoc9|D4Gdc}?8 z)ta~-u>Z&o2(kp>I;n$0>AyjNb7&azZT#ZXRb z>K4o9Ru5$SwSQ^h3#I`rBuJ&Ed9>+)mHY0wKsY7W30lDTV!}zCRWYZD@&FP zcLJ+y!aAXCY}v$hUuH5^xSq&(`CN*D)zO_|M|Z2)pk7X&H`^WUE+#Mud~DAwb6}~t zP&^3%)S)FRJ@C|#Lw0H&_s7^7++a~kP^h*1R__PmH-GL*DP?1^aBhFT~t=7T$-c6a{a)vNwJ#|m z+p5;kj3u8%jxbjpSsna!V{_%fb$d&zkuP*mv5s#_mUz;K7Q@xB_JZ3sXSzZsH|>9z zU1yJEv|;x+jHViyoHW`By+mv6@5)XX+&Yc`)DUzBW3x>}FiRD_)A>mt;(; zRDV@o$U25asls#@!Lr(-i*S~Forp3~C*W?`HBNq1pmq0#T%q{}f7O=Vz5fx~Jmbd34NtRGv)NM+qv-3}#FMsyaQ zOsgpJaf%!;CdQ1dydW|`FM75emwS$*=YPP0YVxX{5Y$Y)LUMd>D^seq*Og0oB93!) zpQ0kqa_yJ4Tn)UQHn@E#hs+&wAs9mbO<#@ZCD?#u07NaQv+-3y&;r5Og>TK}IMbRPA*9s!Hc(Cx2ZC z6|ZmE##{s|tCBFG$K8cV6_-o>4w-Tef~BUX9gtJjmTwJlzitnI9*L6($qaz6j`1%4 z`CPKY)Y+)y0K&3Zn@vBa1D+czZo~PvC$4UtxgoHIy5`~J9ajx$XyevW@{vM=& zdTp{YDlQ7eWX{rwWM&7DTW}fnUd%8_DWVk}r+YQ5PTe6;Un3hJ+6)FMjeY5u2PRkQ z+JUV;>yS;MfI~=OXn8sx$FtsScg!K75{7Z=b(y=ma@V16!JVBuy>RoGRDUf8v~*=V z9%>TRPIK^}BTcYFb2U-VhcHtzqT~VAeiS^eKoz2oXLokzTnS_OCvB;cN9pcfFkUEM zD=U<-_xEuFR5K3+JlMx&@dU=2669CAI0$yEMzvb?LD@n_o$~PRcz6#h*LjvDF3?$_ zO)h6kO`DHYOQAcp{e0)V*MHi4&5ml%{d)KI`4~0e*L=)3QgHSRnwg7bJaX$gN6SAGI_wQ$Q>2!ZRv0D##h|^koR(|r0gjEV{8&x}406BBJ z^F*I>G2ZS-m-#&}K`b+0JQ_%c6RMs8u`_;{X>5F`}+;pnqpPB}yh-SdMH0 zKe)lCc&KOii9}IaA=f+!61VI~hnKa5sJo(Y1At+MlO+@G!iH;RS=tLkihJfUjYFAT z4~wu&>Wa|87>Lo$a*r(zQ;nh4_cXL=3R=55FC` z63B})t;!;+jyIReBFW)iKqYnn80GOQrtWun(5dZ{Wq*}H{PJ-7&MCWp$m-!aeObq_ zdak4!V0oKsG0JwQrmx|PLd;+O`VwC>P{>i^kXjje7iY*l)cX2rPgvGv6Z~%2zf|uvSlDNlT#fkgi3~I>b9x*E4f&jtldrS_mkP% zm&Dm9Ie#&-cvM>Al3_w-)R^I;@2T25)=x)|4m7N}4`ou_!oe>b)Us)7r`vH1`M(H+Xg;<6-=uj61!}sUpdvzPWuZ8 zZOv=-ILg+Tc)&fDR3{(Yq4@KTz-)>(c+*fnPp7$NoT;|9ES|eAyaE6+dpTbL@P}jF z^?xBWD>T@-a~kcQZLEh8;s^iM!`tl+?}YVV5TFMF`4lJIxwi9Ul|h_T>imtUKrPIi zZCT}<#d?jmgM)ky@^T)ft?-e3MW%i+4e1=G8gB!@_H8G9%_=P!TJzE+geVqQ!`Uc` z$2i#oTL6b~W9dWBkn^fWH)hmLm1$7roqsZ?c40SGULr+r)atZ7*ty9H^zA`Y)yn{Q zStaz_Ao=WFb8z3`iF27w8p#qhB5ACiz`bwnq8g2*~onq-Wrfb30mM+B=Mw$g4j20%4=vBq#iz7WFtAPh(0>ffB^kwY$&$dp?kDd(W>N219Ly zYX)#rbU-EL>~vA__b&Ip;a?llS_+xc%%3TjrQT2rRvHoXK@W5}D}Q>Az9B#7?{^kI zu`l_cuM_;?KalOYhL@3;0uur`w-xF@L~DK%Sq?Qx65B%FE4&_OIA7BxV(RU zvlx}MS!{yA>+<^Hms_o)K9~Sc^PqJI^s&%y4mURAfIIj&XrvWd_E=y0+=-z)vAcgr~>V?(mJhEp)IZ94ykO zQ1k@z%)vH{2MV7`M=tLOHh!z`xdTaS9^UIorpr35O)$nqf^;Ms7Mzyx*;{Y+PCE)g5$5`_s=+ zdPP69x$kq&z+uil`=-zK{{4`^d6huHhpe1)*QP)tLhztkRP;$lcxK~3wS$paUi|0n zn?E1#43OjPe=Avx%GrV?dS9CXEt0ctc}HynU|d9SYqmNq!NSTRt!;l|>o_ip_d%^| z1oPH<-(hc5c};HZz1}CartDK+Ky)z<1tRzIdhcW>W7U^Hq&phe0w_Een`L8l7B=gr zw|OYc`scE|=|lz9=<$(E!jU-x{U03dA=QgiJo=JhP-;#v&Mt} z2R;w$S@5}x)!xF&K4c#k11qPT+dOWg*rNFaHPrf0>Q<#=0-@qQpH;9)+YY(+@Y)EA zJ9(XVqK|QDz>ECz>LmU=k;I?pux^!Y9=Z{HJc2FBj55xFTnT@&ahD`r>kGJX&l zE&$Z_!yBdNwtt7T8>)VLEgQj#fMBLD4fI>stP8WnVuZ5sHEiB?a@ahCaT~NLmTN(Y zhhWm^5}?Z3 zPutH7Bnsdw3fv=8NI|BMB& zWQsX(;s%G}bl)-#EfU{K8-#M5RDPAjh=ewZS=uDpOi#3N@*~~$@E?cU6Qw1bQ*O1C zmWaI6Nv0a2NJtp2cWK*FN! zMi7jP%jP7qx;9IOWeM;k(1H(VS~@9{Vv|A6f5$MD*q}^wL$LMlJW=)QF_PgvQ>Y zS1EsNP)ARusKc$?8t$jqYTp*}w?k%kmJT-%f3Iz&5QVzT)j|~|16~`Q2~({a32&@2 za2#Xq8f{BVWMD!8lga%{x+a*-5nsirl@@m#q$)#VK!UC_vj_~QP2Ue)Tr@>bc4|Th zXEAV?oBc;D4AV7}WYn<*9q-#}FTSQ*N$cpDiu428t6 zbIw`OvCsxB?uS6ZkC!h}cvAOLE3uCQdl0DNUX*W)ZUyR((&-s)!Ws^RB<;#2$oqdW z>;+GhfYDg<5Vp&VS5L&pSy+-_x+Tf`hUCWj;t3kTTml@6+31xt^!mjhks=F^<1&VL zo_cbSEZkL1kAeuhnJA0m21zOu+!Is{>sGK5?=ozv3 zzrkJEwVS}z%DTkzp0s=Lz)MHs?P`BJkcxNQ0)8Nz&VhoT5Wo;@pkA^u73S7qXe-gT zPpN)q^<4FvjqV3{#re)f7kghMmwSiMsbVl!oDHjfU0gcO0f@+EssK%QjM|hYM}vGL zi{QYb)yURUa3k)xQEkx{KVh*vm~7w{WG2wqbo_J#p5qS@>*htVq7lFA{ix!Iu_!RI?jOiSM5Le_aWu2LwRiSIHaLAZgmKgl8xF+UhTwnO9r@G zx^B$f-2-e|tQhAwR~R>87)A8Gky~bBv7^^D>^$9Jl!$cpw4M!KCOLoV!mghXA9;P# z{iAR(L)J2O75v;Dup<&Eba;#%;wTec`YB4j(}oXuqJN*NcR@~-)%FRBcZ2|eH@eed z%Q3E5o~;@H#jD3$meaNs>aaA~Y2K6NanY$fgnerX1I*bH?o+0nSK2s_ zwc|6Fo(P#AtuhTnv*&-Q>glXvtE{!_&s381FagjjtdGf&z(&rAVy${?=ke~~@Tas{ zvj}S1&$`SMQv?Usbi>wMmxm(Y{=?1_*fJ zx0A;PN*L2CCQG6v>FKVG^3jf|Y@sa{_-Zwt-!Sw-X2m%{5jnXt&aIWq$;$0{tzk<_xLYE8~JRNPJILf$HN0dTJ; z-AyMYtGrlfNGAzuU8(`*BgJ`~!7L6HDudKZORebzxjo@;hDy{Eg|;UWMALH&SU5A@ zY@C-w%KUxH<)wci#vo$2@vdRG?wIihn!k%hmN7w48@^)EOJDG6C6>;sVEq@@fOL$W zr|9LnJFW$m&%EX9)>mGW090O?f?9pJ^8T+-T^UoE9k}Wzt`IW@-z(%|R@$e~=5c!{ z|KrY~`j0z%`CbDqx_JHHRAr^4DtEQLQ#B%!hl>@zd%}NytE$RtN@FdmEVK#JqRJoJ zyj-Ec@P{%k&$iz`>M8%Ir~JLtQ(jlUeyws!!unfQQ|_vcClpgoV19Rp{Kch|(W4KM zC$)Jt*0jxmD^@m{8yV8_V7;lZi|aPbjCmed^ObA|wkg+}6;NR2C7L1W6e)~uFi*WO zPQV@#PeXtC$%y;Ks!X~F-K_u&PDR_3C};oGu~Da#ITgxaJilA$6L$)I@TO5&?>0Gt zlH{J<#g~EkEQwdcr>D;RXgvI|!qF9^tf? zacjAZX$cD6+B)E}setI0F=DAtR6?~~ZZbG^x!r%;8t1(l3T-wy=DNxMa{8O|&we<|N6NfyiR1J7*;#qlZB?5Vp}CF!Hh#uExjzx03muMi(aSx=h4#}4f{PliqcxOZFa%VN_g8#~?T zF9SVX?{5)U3*G|wz#IIqGCm%kyiTWr*LWvzMh7f^!Fy+X;@vi;VX=+((dF(O`EK&J z7an_lum1FZF1L^DfmyHqB+Hox0FwppH6DiI>Eoj z!|N@iaQf@Fi^gBRz)*N`g3rxn1OGbj;a~538!KCQF$3Fw@`tiZ!(%}jIEIh;nPv+8IyUXK4IG65^4m%YH z#=)zsl(lux-#&eJ`tzrcz})5lxZ4qT7apxT(`@jp3YrmGBH-fz4`1F~+V}Xh$LDU^ z-`%rfu1`8{Mtj$Z{s1j52E@GJWf8{^7_VP3i+@X7j((cPIo!-^*-&`Cwrz5m2nBf1 zX0;vG@L%2K+TY^-UmHXC^U>B;g#1aXEvu}>vTfdMV0@n2_<~2-umZO}=L)ZExq#q& z`3m6M;O7f=8mI9<0?nO(osBwRXD!%yHC@`gf8%r`fgj5Rg2szUtlvT~Jl?}b*jyyY zC4VHhMS*H!X>I9XQSxB6GEM1Fh46}>xqaG8gW*9qE08MzaJ<#!$+rpeSs>5zHC||Q z3Q=xAZgEFGPo2e#4uR6ZjWtvs zenY9y1aDLzRT0#YPZ<@HQ(LmLVk+2Gv6pUBB^zsn(bVSql_|1&ZDhpC0%#HLBKY@- zb=%Q~2v>~&*-ja9riA8Ucgffy)_+yDb3pHHYU|G1Lh6Etjv7gJdEQMR#a0ghK$+Mu zL#W)6XKwMxm89xdG8HzRx~VrWG>qBF+e2<_@Y5s7GqDivTp56bq|T&MDlhoiu(p<{ zn}!hkC=4<(;z3W|$Z6hzDcg#kx^;@4T77^~u6xWr$@#)Fss7y?^sH^3-+xs98(Mrs z?NVE-v3RmmW52 z^X$W+adIS39EjWySPk;u<$smEvuYIB$Aht^K#klQkxR>8;mOLE@vA5|1B=WYn!ZQ5 zjS!Z~&8mE6zU%hmDotP8o-rC<(KJDHoq8x!_PELq?{D)&E8Ev;3$cY2B0iquejjN- z(W|zy9!o;Qf2CLZ9MG|CV-ynH1`0*mT80QkE5ZFvh!!c(jtmPU{YF6r7h*|a(@T2oUvId?AvxXOuHMJH`#-hTP`04pDR%XuP2+X|H_UTjv}Qk za6@D&SjTB;2L_(&7L&tNc7yn-uK6NYlNMY&>y0HUM_k;r^;4F^$a|@2zNJeo=uEAn zlk;_yeMp;2{(Z_mM>j(#EH~2De`5<=O&`Tl@uVp!5 zkl_S)(;`uw$A7HReVqO-$pFUITP|5Tk?(~y1{-mWg$db2_gu%LqgA=kX~-PHF^fX3 zuidoKG@$Bl9xfDM-HI_hF1@1gax3$o=B-N9q0w?YnSaL=;VhKn!Gr|c3k$ecPJ5+e z4bU{_nYhd252nxPnJ`f^Q{JC<_)qyrVTvmsTjQ#Ox_@(J8Vnl_V`l3n_0c-hW*0O_ zHMB66dQj2?e5(h27TG9%xhr~b@t~e8ZN`{&dOMSXObn)gGP#(%e(;C2MzRN z@9K?9!{Db*6%0BBOe@eGWi!>Zi%TQ#l*HEQ!~La>d#|Ssx+5@cAga?*h7=aa-I*?r z&5Di2)PDm*DslRwn^)7Nxx%NkAv%OTOH${o9*5J??P*V!?)HJ6J}{zM#H9dJca^2i zGZ~0kf}XgkXE*BrMZ?KEtHfe3M+o2AntRe!RUcMCH$H7DxH)jY3PMc#utgC{7Op4V zK?2<53_Lkf1w$-B_JK71u`8VQrcL+F3xIg(zJGp=1gz_!#?Gj)D_LWnDkLs}?VZCL zMv2&vY?ihAtQF0Pp%9Xr_Il1Tb+e7os%dMu%$5Z)yvn7KRw?jznIJn_7(ov49&`LR z5B)tQyj1||hgn~w!7q8kAwa~?G@oU*8eP{1eLmbWKFYf`H8m;aG5gKQJ`UPs9-b|m zm489ba1cI5&$p{0%CU+0T>Nb@cwC=(F3Px_L4thd(M{{cA?QX0XEvK+sSx@3R9%DW z8`NqEh#LNVD$VVGwJChYZ{OpahXxZrDx~k(%eXb;&4KFl-RXzF!N&`X{TBYdEMOXA zx9q?@bejvc%!OCbAsr9jqx3!ghl~4tu74S#bZfr)qIYZm{g6OM4g48T;B9Ov$B(7~ zks*1N%PF%9A;hwL@O5m7N>1PW^ULQyUY;~iZ6p6inRs4WXofF%jI-(<#Zy2Y((>qr5OOH2!MODb8$;CyC`OnZG=jNN`K8X z>5sCJaO<-a+&e1e382u}@>ti`MwQ3**Pp2?jQacO^tn?vr=$w_?4xz%)0DHa1$9Bh z%PEZ@Ft!Xd<~Ak(EGcS9CXkyLL-_9@Ga-y*rWKV(TrL5cXfzS~Hx<@WTxW?o4Z{DH z5z;|}!{X{f)BlMP`l@Swm;SYz@qalv(!V8x5rcE2eswDKiV4i-=6nXbsR4Bhj7j6@ z8AQjz_n=bZwxV7P420OAH(F!16cU6l3|lnO^aUKZ(p*2vMnb61P?tkEpemrDs0wHh zXROP0UhmD7oCkPYz%D7=5BY!p4bt{PB zOb%jfEs}CEcle@dASAU8grr77N=!k^V@$9?5R{q#6t9Ywi=T7*0f8sl{`Pt|fL)23 zD)!)~-H}1h#5OzPP{dqI#mT-!%%{)%z5C@f%B0JSJ_|hPEC!>{3!Dl$$wfFje-IS)7ob|FQC?V#pLnRa8#WGb0%E0rIU`) zv28o)*tTuk_M47v+qP}nwr#6#=9`+TJOAO-slC?T>shx(O*tBh@N5FN41s52Vrl8; z|3DVXP+tLo53vs;_dXqGTM`PMIFioECqPxzRvHWPWXmUA))9l*48QDTB#@+N33g=3 zG&`y@S{Fg#?$9Xu|&xwU0*}7$y`#5WoENetkGRtz$nVtjuGd$5eium$(28S}$#ezAARBdABnmgM_)YRj%rxQeZnTpuyf zR!o^8H}7CO1{^ceyJHmcm%MTO>)|S4Fd%Qa)O9_uIxKU-egGG+8St2psez!DyGz0b zY61YbEX#1~c0x&!2CMRU6_Llm>q!TCDN<_eX7DT@Rsdi2OKrblcxq!kNZ=09*U&U7y zva6WTyVIWTJ>>THK*qQXq5Q&ULIphAzN3c4j@9%l_Yu{BOGc`TXz90Z$K$fe0yVUk@8O5VY+VDZBjo+0z!zNX{648A*&d zTQF(PDoOUpc+zsLp;+|MB5%1xHBkW2HB~*+Z=|H|`>lBUt+&rBQU{5o$CI)FfBf{{ z0`*az=0`DUwU3)ZM7FRNHVe|Rq@XyR#CFPYkP524$dm)|VOA9|F=R7M4$A(~U;FuZ z%IE$dDuxI0kvw+~sVougJCxBP%qvLF8kiWD(tay~%S)+g=H`NUkZY9Vsyu*s2Qw;K zEUJY}A)@xUP?)}OOQ~TT6oEFEIw-0#Mh5m%*?N8U%9E)FE;ZvwSh zhKsMwH{L!sTd|^mdTK5;ZZz%MU{o_BBciC%hOM?ruh7S=>kL`7&nT>Yxu^7Qv~lQ> zn*3;;w2Jhaq}a>G7Uv#A4J*JBc#0PJ9+PzHnGdR2cJZpY2q+%CU>G%mi!3J1N^sF@ zMV_cySUE9s?Q%k^cxp(|SY=bZifBOuReApq*!j?(z?w8A-z(&s;g=kB%8|1pQ^8w{ z2g%_t=LLw0RE`klkB{N%{aIY(CEXyI@D7QdMMb+b(PjF5HmhC{M<&3U&tS~ zqJh}*y0@t1?Rh!2+8UELh+pP&U(G@41YlBj*_E69(8YK5X8&AU;O@|A>>$?cf=bZa zUxY-wqr&0W`;O!3gIR+vft5x(-n!e}CN>5FSRZX$1!%Xc{Ea&)BHOi9{bBT1)Pm88 zQa%c+Ug5wy38D_|`?mq(e#x2;Y0tinymOMr28wCJ@or`%`~2;1m&P>5J7~I!MXqZs zJS!VwK80ub>7at2-*P=?O)wq#-5yZ<&*hs{?S3utcRnpX>#GGBDWOZFP%bgVYIZAh zN|`)KR<3-NI`~TU{azN=sy!989aH#E?v{5}zE{wXc98dK>iMF7easR7OWtAfSIjoF zuDdk1+0?w(Ev*E%tIA;_VxQJa`}pEuC6{PZ(QwWw9orXhGJVNrFPJqPy5;B&##aAs z@gKDG{r>YdfSD8OCVuL;ng9PT$s_EE=>OKVrTFGm+wl6cKYjZ-z!Jt2<|p*oJRyehiuv7jBWDmF z+_TK_dQrLY{rz)lzwmKIZ`YFZZQ4u{zXI^}`sk!YEL{ITsZXzu$BTf(_pC%&DGU0O zgdn$*>9;-nB4xSTrl1auBa+JZA|Ly@{m zf0)jF;#*bu(bHBYoiYE3x0aS3Yk}}vMTe++R0H=_~wF=NkE!3x=t^8#uq)q;;7hWcTgN5 zKv3lY@ANZE)x(Xw zyCPDj+j7xex)Dk`fO?cDN}q|Y8p?Ico5X*Sz@*OR8cD$AxssMV=)4d}62LckNa3zy z&nF9`bi{jsr9BUHR?@{fOXj3%P;*$plse2?W!J#a94}hxnLN>v(a)pvOOj3RhTq`) zgr>qioQ08yBB$K3i#O#1&QV1FCdlc^F-a9PqQXeA^L@R$%US2Tm5z^EEeXmH2;p3d zKU*5!{U7m#^IboW_mE#;Ex^E@(7q0MM5`N_ezVoikS?I`kSDT`Ug9%=Xb~o`d;Q0g z4{*t+KBF)_&!Os)8;7wf&o;g1FRr<3j7MHq85r$8XMXX6@kWCVv))yjyEmsrB$FKF z_-$!jqqJ^Ott4mrnlD%gTxt6@DM8kfEcaAe1(xGI7tC=_ z`|z7+W?j?1^^s!M1;FgW7=S=nwrTIA>pn!Wq;p6$b(oxN=9GZUybN-dg)G|+UK8jI%z>i0owQ%sclh}PUIiNlSG_xP>zB@j*F7K_)SujguVEXjXP=!Ua)f8;_%D+9vJ^p1eagl&t$%i0~s!UHuVdj?||iKHuUkUl&h7U-8)b# zKrrA2ouP@9XwAEfv(XP)?ni&n|LBk6Z#DQAqijsSpL8>P7uQFO&@UqqKY~e80|EkX z6KEEq*2@4GyMV{Feo7EiNbAk#g`OS0e{c7temFP(0*n=iNPm}l52`_^&Y3mc=59!R7B*%xw@DNw7KXS%p8)-B~0v ze0Bo@6?O=TH{(X_fP$8fR~#v{ll1QN03~I$=Q$hCn^5WeE+=C0gfr4mn;~F40{I0> znT0NaN=d=%YkTYhF894t!9a1x@N(*cjYvOpp56r}0BTk^C9HEt1@Z{wT$kadLn%mPA`7VBy2okm6GsewtZV-H%St_7B_yP&{lvOJ z#FB{tt6rqMB3i(c$L#{s4`Q(z^D+CuyPV@UpoDU=Od=-0-q*J~{P|Vw>sFE<&=~># z2GmsH$F--5#^#f1GDk(7Bm9D96?nav*tuwnx_r=>PQ%1QFr?o}cZNw#J^~A#lsv2w zen02Y6~C9MuDmf$U~?g2n_EnmSkUNq0p-zOQQSiDq)%FeDAlN)OsT|VmYlp8+uf}I z;H6aH9UCKt?plsJ8i+j@TxdDvgGtPc$MG5O#aZFp%BO@_Tku_N?R7|@Go;2n0onY4uIVUt#-N3Y#O$3wNYAQ6DYHe0j zn8LwV<@5|sJ5u8ae`eo#qkUS^H2^dK1od1G3^A026$M9nseR5WsJ&r38}toaZ#}>~ zCzEq72Mkc#QS*v6Q1f0;%}1zbnI@rbSmT2tOggUAeWn#O>LTA_RNU_^E|t1n?M?H4 z(Ra-+n8(V$-XaQe$#=JnN(kXM0}i!%{<-p%Y)3iu2ih0Z+K{MhFjTA?hc-3?;*wWF z$DPTjM5HAGltm=pG_)lPik=x1dakQsz)J_jW;^3I`e-qYQ@pBR>J6*R^)!AvO-_^B zpElmS&1AIj&8=g!4zX7rp~`J$pp4ZEMv|Jwd;e!$V+h_^L0%{&B%m@#&^B*mK~3jOljPNz0R) z0tFO<~}MYjGQthM~<$&a7k-jGIZip zwJTJOqID;Ib=pZ&@$$S~wa$MaxBPhm(1OdZ+)|3b>-(vTH)yY!R_bx712XpC<=QoA z+t*sLbgXJX*>Zw8D<~NOLM|N02Y+KvCwgy%yys6Met2y+uJszW)M-zb!(o%&Z|gir zkjjDXYIOQ2eomiCeZ9Ijf)&a7CNyx@n-6m zj^$2v3+?`Z6*N5j@7AOOT~>d?lqe84LWU;<6g2tah}gx`!pL>1wT&p&u#x039QAM0 z&CrT?AAK~wwQb3f?R&IEOd+inb0!Tu;zYy|y6Jlm6q{s8% zij8n|+CZtw75WDNk*H9hpMg$1O>LR2i(M4~bToQwN*=ZF^n{6JcL(dmkg7~0f0>*r zdkxz3=(JF(SKf121YW&Sd|$H?tC;g?0na6CcR|Q`w{002^-j^(GWcGjjyv7=efd|* z0B_q3dnfj5|CJ|`*vTz>q9N^pknL$QG8c~R_vC$5k|XZIV9LVti%c&C`=5@tfZHArFve4= z2`zaNcH)nt54LA97s~oa(<|?!S;(w)aBm|Ex|8hmEIzYsB4xv+^sk_V(0u{vJhbrm zeA0dI(`@NlU)+qSP2|E9Pj%l2F8FN)2wslBMfprRQy7h>C z$cIjH<83|-2b{z@AxVzhv{%KAbQzXUAS^YgArtS@Ms2_tk@>@gw zJkbZxch)-TRLMPCs;fyA%QVj|{txZfZdd3xY1K6`DBr*P++s41IpZ{adPD&$nVx5B z`k|d`1}X(kXw1t;>Le~f)y%{>BJ_;M_&gvJaSkk4#b-Rv8$5hyL?_1$5}$!z7tI6c zT7ouCMsDiq{l24I(gxqO`B?K?!kERpvZ^P5j+GC0&$vU@97cXG)7;D^4d+sLoKzg| z#TIDts>%(V$ZG=iY~dIlH1^ju5tO2WL=)F``h_n7&Zetoy{-ynxrB^c69s-?iLs`F zbk(g_hnE6UM<(~A+<H{*Qak`> z<#2gu%}apxZ*5wo^t*|=Di3cFDs>CtSuV~){HJ65x8stRRQidVXH32n{4cy`x~{i{ ze?Y2Vpe+ASMFHcIg0L{KrqJ~QqXA0sQ#HHrd-V}ON9k(W$h&Eu^;_90O@%+O z9Lc4FyXnDgn#=5K!=I{P6p_2Rjhc*Yl+Oh%7A;s0IggIdUw-(zPkB8ztk|y?ogqEj zZ&!AFG=4d})&P$#pO>z!`|JRyjfA_n#4bnA~lJC5p-|3_q}Q3 zSEA0pDM2BrwQ2rpUeL){dU*}Lv#Z_d8KVkLwncIFlx+f=Zr{0il3)ZUV%6huPfS5TOw_eNx z3s$GRrsI3K3nRfKOqVZ!YLC&pLB}^JD9F0S7aFI|c}ka)PH#ZPBC&p+&2{JfOT5e| zi}l|CU?))4uy>*feU?LvpF=EAUnRt+l#mXly{74y9W6uB<~zi(W$|XKtlgSBf;KPl zyn0)3-}l?V1qZhE4!S0{?Q2RP3l#wxzXxF@=Bb zsaM|!SF$(X4VlZT%&SXYdva`U7n)<$i zutWl6-67P*w&en#LnDwj%tIhQPdW78Z0$q9I#HrGd%lZ&syOd~yFq6@Q*DKIUG3jr zov_w#`gk`XJqMQrM!#)ZTi@(RLkbfz2&bTC_i>+jm0{(2V($XHo-Ra@R9T6o$tD$TH=Zqp(E63ItyX^5Ib4X`r zhga?C(i%++=uM_veDK{IlqOdb);M2)<5($SzFxmvW_z|+2u36)#m)4&NL<_cYF*2M zvqmWmMrN%~Y1??Otez0UtQ3mF;P?wxDZ_HSCyEuAG4E7<{x0vxg%=vh(rHv4r3s#* z`#Zu?+6Oahq3TRt=aN6vrNIJnbB>3MU03H7}K}GX!o%7aMA|bi=~puJNLRwaA^+R(m(+x}rIi!UY`G z(B@2uQ={7NX!T4Q5zNit&SULh55rH0;xRK*vs190#MmR!15XUPWa2l|b2knAY?kig z9l|X!5>iIuAeg^kBU>P+?CFr{TZ zA(cXv(W=vWlU!XerD~kNBZb&OR9+-j#~ENnP1MoGi;6q@BD~N)kZqnG&bjAI(_C;* z;`B1V^{WpK3kkip#2oIJD$MgXqJsf=4F|yR-EB8uN)6Go{+2ge(FCQ~iZ!VFu0q-k zKrhdIo^}jYkS&*Gl7d;IXxXS9oY2rgcSI;5`dV3#O4;se?XEI=AeeMborEclm(V?3 z`>mvlJa^(P^p_uEK@44W&{FS=r--}Y{&bogym;v|SF{q7O_{5S3bjauQQ-lOM~vfC z;#iO^s-#Me4Qc(|77JJOC`;+_YnS8-qc!uiZq&8C6!Zcq|>}@yY-cYdayZM9m+TIq}`T2DX7>gP0*Bs1aa%ZA&5?#Q2 z3Lmb;5_RYJG&ft?OgE*DhtPf0KU#!-&9YMt(|XEt-?dTBhQ|7bofb8$<$kOHj7?_o z*1A7dLS6vfoxlqQAGbG>Xk91PyWe+GKCt$oy$+T?v3MK-74}EG5^JD?*k{OP8S5{s z-Flm84|#dQ47jgQd3hvC|9)Hf6QWqcj@4mwgyCG+QTgoa0#GJJA8_F|qrY+t&@V$H z^_A-|_!kv?PD-awF*aU166rF4(O0yl=GW`kPndW#_~2w^(ohnb0QV~jB+Xa|SBXf% zPdxL9QY0G>%2)A4TD`i3VBFc=CB&|hwwHNg5ipsgnTq%^Jsu*j1DTcKb=Y=%`H zV9Dl~Z9*0vxM7CuvjRUxPMcdtlH13^U#-MtK-UB3D!fh$77xV8ruk<&S{a=rI02w{ zSfe+pCkSjH+IPT^n6Ai8Qdxem7?7j<+AIuP8o62^;GGwqv!*tWaEPGBsU!pg7+JjR zgqsQkegq@|`nke`0$ZmN#*b~Uoqk|a0K zQ6Lf<@r5j^#?h;y5A^Vnk`YU^>6EuG4U`eSvjOJc?^kyiuA-CfN=v=*UF5b;eD3NN1m}&vu+=V0k|#S)(f8-LCs| zFMWjUVC-!RgHntEz$L^AP{cCgh*kb2-Cn&iw#r&DRtdSlHeh=g2y_=!u8tTFosllc3YS7eXE{SXhuR-$JrWI{UXx|uW-6}aCEc8u|m~k=afaSW=fMAoD+by&%!|l&^yxL#0;WKAu{FGDkY|` zzSlyKI|T~9?r#z>cDi-khuo!LqW-jz-f^dMHVvn-iASMLuudYn{e*rk zBNq6-IE5+5&afacdsD`{ZyED>2m; z(SC%VFGH>rV}II@&kgI%AdU7@xkG1|*eoO5ud1dC4nvISgH--e{TN9T7y=aE zy_C`M@%=}p_*K*ns9E<>{LodY0T7OJzn2*R`MW|R83o9CUR!!9u~yCnL>DQ0Q9Et^ z%f0d!OmvIi0*?<;bZ-$X3phJ|*mxLFb(AHI1x=@3e#8T>xryxy8NjtE(&74ZA}O3G zK6{im!~G`pLNdV-;?c{`P@tg>y@=S#?q?!O{pE6h#obuCH4BAe>-zTb3YeQmfadW4 zGV*x|@xa8Cn402g&$%wQxZzSSeTD<90uU04tfeF-AWW);(9s&s%H3C~Bg|HMksgCO zZ(@dcPmNBEOE@0HWV=;ZQqH0>tJI%s~nM-fu|I8zN@z7KDMXKk{UFt>mH|Aljh2VWs_^RKf5g* zjl^@Q<`U6PCs+4%q{Ity5-c^qe43FSmQp_XPjW9b54BzS4l5OQ_@?*^Al8JK%n@_~ z=Nz=cNFmTim@>SQtTezGjP%oB1$*Cgp2U`{nz7bLJ}!96B>N2MCZyCDqB%CrSUc@o z22B`C3~%8`;;--$^ZrsjlZ5e7jKz7LFp&CFmiuoBfvm!#2$mNkfsz_Pyf#Z(+vE=C z&Y`5}AI#qxiY%r%Qsi}y(Gk75S52$^oMY5%R28ahDA+?*)GCS5+V|PhsH40o`S!aT z*NsQ@s}mqg%n`8zNn0nj_WwdM#4^yUU97$m5oWz$HmX>?7MUu2;Fnxn=`JcsFHvr; zO9Kap*mtc&(tb>M4~;WG$>X)i9|w-g%AOPpAfi8h%~q3};a5tU<85?TQ+DPvnsj-y zj!Yv6U8+j09zLuG7FHRIgfkc)tC&V_gICb=%-6|-^vYv}QIJ~k)->TPUowS|k&4|asL+bl!Xk_T^YmZ(8ll^`@LUY- z#TMFHe*g-4@y$moFu|dk%WRM2Cp?R)32n+&Pa+)UMUCl9V@y?K6=grCWW%;j8?HKEqN4JtvYaa*`Mi!>C_t95bMp=O3gR8a`W+CpRHx))QloHy+ z{8E8uDu7BHbNmtj&jq&_UXhHFWYH7YqcL@!t6b;-oiOU;>8@vLtGHoe&&;(97r^C|slfO3HA|ShJ?bh&sNoCD2sPkD~+Sy=U?p63|qWxF} z+TdqJ(l4cZS-Ou0JG|e{uMZG|yr?{L`trX$gFjVtBSHcZXfa z*R$J)o!tWq5n3NvqssB?-G+I_eOMm|eASE|utNApdYkKy%?7o#bhmYu8?HC7Quxq6 zfd~#S!=4eKUaxyOdfETn-wS~W^}baQ3seBp;HB?so{JI!cBlU)_I9AqX2{DYI+cg1m3M<{9Fmf!wTvwj!Yd13U>t(6HdXi>Spiuyx zaQna&dkUCNi}`Q!F3n8BJq&S6bVfbUh6#$=JBfh=4wbR!eC!}{iKl|TYKn8*F*jxS zX-Bh5KgnIC<1l%H-H%g`ynIyipTg)x5equtXHhWyg#!O=nki(;Y^D&Im{w^O<=gwg zUi%Q-szIu%DI@$5@LvD9?q|1>5n@0&qjDHSa*08dXnLififfd3(+tq~8RFc5vkE;L z1NN^PgvwrPEp!9YKN^TknYneqrOWuBypb0Rp?}3P>hHIKOu&w;bGI9V4$c?NBl4z! z3eF~>ktF4G!usww!W&u`+6_$}P|%H)^@W08BOm0{TM&Ct6OS#;K zbl$5RWa+zUJ*c_NJ=8)Wzu63n>A=eSvlK3)K_y3kG3VVAC!B+)K>3*6^CTd>!<`{l z_sJ8on)|nK;)D|V&Gso*{7TNIl&~L|5YnCt*9orgoz=XT^`DzYG{NiL`bN|!k6dkN+s{mdU@;j z&0)Qg=q7U8Zn8%zscz6&OB}yzVo$n~RXOG2HwQ4E;gXvt_Ic#`lai@~V66X&=R&2@ z#a2W|(Fj7T3%1N@4~zj+Rn1b>4XePMcv?6;GWciBiYSWnz!fGl$jxK}vl0(9Vp$9jVmuM<}1D*%QXVw_# zYTmg1<FL@;=e(`a1<)CU8;zW0|x&X+=z2KuR! z9iX%!`D%=K;f1+VNlrxNVfgkaN0gh|-5&Pj#f@y5E>g!4Yf)hXe+Lp+4BgC1#)Yrd ztX3_+2y`3R7Z-A0rpmMwdyPAn3LpB*(4Fv`Lmk)IOKZDN5~MOqL=j_y3F6vd#*_I6 zq=^bK!8DWiO(OuD%?Acbox@I+?~5ZM41G;jxT6jBXuzSUXKV85>IEClRlMAo8paUlQNtohe79o6epd zf^Mm__p1V+9JX%S1y85__@T3Ab4t|)bt8VGdHuz{;j*d^A%Ti`>pTZuw@8A;t`IGd zo{jIQraK3RX`&ki*L6=m+*Ft}SiJzUxJq2O)*%e9Yi366c+f%6jE$N2lZmUtH2%i^ zwjWoVtjY-R_b1TU=(^tEAA>0^TqV$`_*!|$j)C^`#=HT$I>zLuY#jrxj-mKfEx<8H+FD(Tt#u@#FwlQh6_X=PJee65^v%1vMFKjkv5t zL)>e3y`BpD5_VO;e)mF>_DH0SQ17m(byYWTETAQG&{QJf$yw` z{7`r5d5*o(h6Jk3c4Feb{;)`@*6+7r)k|+lU%jZi@6N4dG*ti_QFlYL@DlTCxwY$z zV>PAf5K5g06e@|E!qzRp5v_ZHK0%RXWWWJ9&x_qzMz&72_RhKjP#UNG8NAB=LO{Ew z6CN_s?c&jKDqUta&}vIRTz!<$dJR@MCpfTUi7?^x(=BGvCA3M+YimEYjd*4FvfL*> zUTKrD-73s}{ls~DfUyqqqnkhdMV`Dg!B~5dq})Yn4Kn*erNBwd-Qsc;whKQ_*TMjh z=x!=9JpREkf1(3@#j{+J-*+k9x?66HXh}0*@?K8JY3atEp@I@ zk0!2hsM&k`UUlm`nrpRR`Zt)l!#+4L;6dD-I5y9*^-D_7V3vPQl$qixyoeWYC7Q*7 z36ptHxpK07b3-P-BS6BLqU`+58pocWEWFPS;zde(Vle)f|B}eC9knB1Z?{N&fQvp% zANQuK^MGYRdk{lY(QTl=}tfoPD)X~z0z{S zAWG+fDYHpev&IxXc*)in$8rFW#JMb#iZeI0v9GolR6=z+Wr@-E$BuVYZ=} zK*^2<8d5h10bL3CsgAG|!!WJ(xbgzwZwNC+C3ka6CL9rVH(Lscr)(PFW{PN|yNYN7 zBXPk^B~)L#j4xDu88`>?abL#-5^12S$|lDWj5EG9b+-q6%ueO&wu6aE)l*|wHfs+c z{pCjjo$Fg$c<@j7qjM)(kI0?&^%HYb4y{wxK%nr#QOzP?i5ZJU#I=+;F@~eX;WN@3 z2F@ACV{tw9Dqd4icuyK&Lq}~FJGU{}6|^$k)*83+Cep}AV{=agQ(z?vq_?`vfk;qhUwJ1S`1HtfI3Qhx_CSC8{}8pj{~#U(cI0( z`RGsu=kvTxq%azV6tp>f`?v ze7UWQbUiRqWp!L7#o6}LGY>7Q?^sKHTGf6RLh%dDOS~H$!-l=;hhZrm!_oGZl;HbQ z)1&q?RRqs&i2TJu!<=na!LOupu+$Nn&?pklplr~6NrzY1`?2;&=oV6Sy9Y^sc|?vZ z=Y#NpS&?+&0B|>}#aPfPDQNHY<`b(~(Jl-KZ&mfHLNAZJ-som(9r_}ms`SB)mFw}zY%}p34C}J<|_YyrIgKRUzN{ZE% z5DQA?LymL8~ z(dEEiNMXhn4tS1y>ujfy&S4XG~@DDRuFo7&6L(;l{#Pvf&_jnw6u6apWFt6T?z z#J4nx7l#Q8yiqnZ-!ccmjOFZFey`q@m(S`zFPvoz=GPO_z1*Q7yKMZjd3ko0u1K@G zJCEm~&qrrNlC6z6*mby1ImlWxngjtPq6WIu15|63+VvoB9|Al2yhRUGJ&qd#(7vl8 zvNMKDle*Dhy?Y!ylp|#4d%IWT=^+X(JYM(ir}E_(b@FcyWwn3*UT-;H14dAM0W0GI zL+gCW>a4|hcliTz!TLgKkd>sYyckbVCj?JKIovXGKijQ0>-0cx%6KssF(VFfB z2Dl>QBP&uO_Cn$IE$TCC%`AP66Hy8ReyH;NXI_QI2fIst)msmkACFcn#CHv6teEqy z4idf6B`sb_MEtGxdwe<_`X3b3@!|??U8ykiKi_#{GFkW&I=D8?^-NJ%F`iBStIz8` zq$_(5aVv2d^@>zCWwaIW4PQ)`;vAw`W5D0A%MsQb?TDoCt=G9QneoSQ=`hXD3?N+S zX>|jSV>b=V%Gkmc$^&DeKgutke!fQO2i6|*b^vwypwCo;XuBh@)m1ks!o_c^@?EwJ zaH?)+D=*^Z7vbxTSBBe8$T@^bL}jyGY!l6o8nv49CAR zDS9}mJkHb6Z1{&mkchKSv!Uzldr@)6_6f!YqWcj40E@3<+vA!Q4c4NLIt2~y9v9sM z9g?HY6|IPtbGM@Tn!i;OAkUVw0~x~i2@%zJkMiH5J|&6|gdFe_H{}Eu{CPe*n7Zcg=Mjs#qlxdlVaG!-=bdm|aaH`6LxT^nH|?_**Nl-0cL7$qXr;sxoKpn z9ph%zPpfXOruBMsAR7&#?fgWkRyNrcWF@c0?YGS5jAfdeBED=7()P1TaTwEuC#lj7 zHo;>DK~ICE<>5L9OO*@P)LG3FaIwCLgkedSN4K33K`d1%9fcfv5t+g~3JqZtpw7}1y z?cESe(CM!uu!4l588)e#u3R)_fDo6VCLBX8ytd?k8|oW5--m6`hyjS$)QM1V4%$kS z$jyf(W5)N`!z#z^x6dh)H*Z!bS+Un$vC_%;DNv7GQnf_IfyX{$JBuoP$-Lag2Qk~q|QI3 zerSm^)=mYV#U$KW)>f*6T_oA8^($W0_B|5wocw))Na8Nz!tH(&4G$A3-k` z%q*)d%{)buSWZuV|BxxY`jsPS_2-7s!npJQJl& zmi8p++Z-5VJ#0d1LiWOzQPt8RGIR%@D57D85}De_@@jyfK!<=wkRBP0R$m9#G$#ES zwGakzX7#Ye}US0hg=l^bg~*|iG80>OqKmK=yhqbB8FT^)Eiill12wM zYwMIm*`~qNGzaOaf#WXa!hQKgL!`GYb1FHtAzJ&M#40jqakLIwiTR!rAHP~kO4W{~ zLd|;*#8`Iq70@r1pVVKVWm)$_Aq8W0aYQ2`f_6WVTmiC(a%9}@kG!>@wldMBo$bdw z0-vrYsgZNJzaH~3q9odcT0z+8wp|kTj5K+H85ZIxy zMrQA-fW-o)%ln$5HKo}!soU@Tbsn(rHrE<5<&=W)0=`DOJatz6cSEiYk2i~6n;;H5 z7P8mdzsvndh+S~w`Mt$QhZ6}~vM`+kM}>O7TF#7Hh%Od6c40h|#3QdD2}atwquaGh zV*yhSN*W77&84Xfu1?PGbc%`%hO#_dAr`PeU0*V&@VD6#SWn5hajY9N?S{ zOO6&FfEUlUr}kYdJPPz7I}iwc@Z~eRetVx2qYu6U^;z?FW-3g3prdYGuidNQE!SH0 zEB@L&@%^K*^Xwi|519L;;-9y#ahh}aBV-0>_XWx4qDIaXfJ+p&UH<`Gsmd8&PLkm z#O5RF7V^Xv2|}nttJ9}zkuDP-XG^9h{>f1n<|OHc92MRslbCBtd{(~$ilu%Tn;ysM z%_OmIZ5< zlYoKQLZ|<`_n(br#a3~t_g8|coH!U+kQ^i0Tbzzq*=JJpc^(|46F%Qpcu>)J`J(?z zwqf}X()qKbVB+}S6VVa2P7HS2RacMUx1S4fSiGp80SZ_(@HMT?Hrz{XUDrm4wnGZ< z<4p>RNLV_iUFH-WosHMlYC#-@Z$kd**~^$L^S4>aVKGXB#=Y}5D#Z5Qj?zCCUan7c z!CnAJ;F;ZxD>xsY4B(C7>sF1=Bmqzmu@Evcmd>&80fR?mf5rd7UIPeNFeC1A&zp1B3DPFRn+945 zxJ{~H_VDm*r|7eR;YHObCkJqy9U_|0bw?=+0YkRR?SU^HJho3!c=q7pnYq2wb2U+z zDGy@z#C5zzpRoB%Tv<(N!x6?<|%AhIQA%k5P_x^ z@mnDG{bHl{rwrh{Ir+=W`{0Kfv%|!UUX9CRw6vnJ1153P41p+f8MYOAdF7mJQNm8wAYeCe`3Ij7Y zHd%Zg!Ll?AmC3@t#*t9OxV6xz)y(!jk$nh2s;=8nji#r@Mg%A_Q)V<*v1jVBCc;Ly zX!;#OBQ8oAmtHm4FPM>Aki+F>&w`Iy$Xd4NtcthpS^(NnQwL&TSE%XH7fBOtd4zt< zj0H)`;_rx~je&i>OZ&xripobx==OI`i`{jG(H)k)+}?1GGUn8*#lUBw>a^prN%l_= z4J40oRB0C^l@-8na1CyK^kRSPNCj`g&Nfy_y0jrsI-i|Z>yv3DhttBgQ8gl2#MhRY zgXpqiZ5*;%)JZ5HGg>h*c_|Z+d2UGw+y!R(hQX#g(jhcMj?vN z#7kFa1&AT02*NhtGKUV3$%e^41X3Aj|VE z9?QaAwi0lzccp6Px{+F6XJmM`ZY5@h#w{>2?(T+KVlLibb+mwS+M9%N3!98B4V@L) zjstbj#Kn50l?E2|SXF;~aH?c@fb$nkemy4q)ru3Ueiw#u%)Y?%$Fbi(aUbhm*R{@bS^7o!dv#>Z2k(mS)ZxA_=Zrx1d(1h@x!@uX z0A7YKYEHJ>1W14-V4U37)<3oJA9fZ@$-(I#9nK1$=0i z>{kHf7ECN`+(C+;tRUSn$%RqBdgS%#7FZwz%n9u)&thMGJ5E%{xaDNcAQzHxBv7-?aMG**1DdnLEkWyYOINYq;2Bmn^6O>GjS7;eCF#kB}JB!eR& z6hlbTg&T(jbT){D`ftuYJJ&^i6)993xhwDjm{_4W-$12~ERPbX zwr8oNqtQ<`_CBGW+PTN+`cu}BulI1zIkNW_AP~uFKbZ_bPfA1Qp>6?L;eC1pr(A{3@77X+{{%SojvuMCAacs z%o?QRyFy|!0Q4ZKHC3F=X;&C6w0jwQrATZ*YsZzoxvZBgt$Djc;Y`z64h4kzp|ykV zibP<0jl^A+-9n1};;dflC=gKtpDb4Sq3FyW3Y(>{9Z>_&P-y z&gT4^(sI#9PcJ+L^5r{(Wzma;J;Pe0Zg$!DY}X)?5e-wz1NF<0L-9}}aQRrN8w=hf zFIAPjn&-nF9`tJxV}{>aa}Mu)A;&e1Q!d3(ydXpnNxx9uKdl4*D8X!P({~FGVQHN& zWu`b#%&7>Vuexck;+dFqa%5Lj>xf41*Y;=5<*R&C_)6z*!o3mSNbRjG-~7^N1l`DA z-tqV#CSur(31%ltObMd5o_X>!dgLx|V+PxcZ-R!LD^aj$8Yi4xGylGwxKk@#_>>GK zYI8c6&8gg8d}I%N7K(-x$`w-qpPfLIv-BzDEm0bPukH#ct0>hFkI5;keY~79)R8ITQ&v68I?LPU8E;8Tb{+d!c_mg|o1gM*D*e@>Vsj1tsh8ycgC!7su z*>wZJ^%Qc-1i#)sMD7&NZsbM()H!VvP5Ca2)^yxyev60j$W52gPTy56quRb{cf0lP z=4_VCqo;mtK--uY&#WuBxjeesLYe|CWCE$0jqJ$2nhvQHv@4NV%p)ti{VunH2*w8! zqfZTMTABSUk$S1LrN_K%GevdzxJy%}4G$cE`}F)`s+ynYikr>=!C82FT4mZtx_0Kk z$@s(%CliW?qgyw1RdTjkDC|+)>*p_z6yw85aAeci9;O zs|IjNmTVUxw;7AKQ@gBvB0zhJfAb)64dD%6uEFTIQ_EXPM}iJp81ExeNVQWX5_}hM zG<_hdO-ZGpv}ed=eai28p!PL3tAUE1rVq)8&@7G#T0dpVUKw^z}sHmlOo zSV)w^*r*nTV-80<8=26m4ZVXt0+k;OviA7kBS;u*PlrB98AxDTkc)mzlY=e{LT&E9 zHU2P$S;L=ZCk7)Ng)G9aS`U6DsEZBI0SME|j;pRnvcu|ho;llEoCy@a#1$_(_3ub zp5=8}CAH_}DK=`1C5T6voG(9gIL#*NANoa8c*-xw@ts?1{ zhu5|EOMfZ67L5a5(Whv_I*WbVzx1HG83Y9%zk#5tpKjZ!fUdjJ9{&#P_lJ>%-0ke{ zmni-t1^_gg!nHuwJ!Y~%x)x>WLPshQqfDspRE7UpB@@xBcsUk)?l8^K_%-8zMXP>u z2X*!RgRL=rqaXFo^R3l5Fcz+ePtdS=!cBv=aa^U4KYCgF2xl)j`Q{bqv6KDzekB`c z;X)0u9xsGd(}HSw{wEn%e08K9kUzEJaP`lp6L9&jZ-$XzcYn03o}`<08rXm3o)O&i5D{tthrlMvTGK6i*x=LPw~ua7^wjcw z0p@mvmEn)~Q{U6WzmYe2EdTdsrroG*XO1J&ujp8AJ4WqGMZr0tZQq7`oq#0xeaIJM z_=4DsmB;wE)5EMCm2ZpD{a)^wPfKr1B(AEt2{&Hr$3J$;AC=Yxa}3it%}v9#d~POxi@hop^J@_d(uSC zpM=_atjO8Y_{UyP_*e+@&vfdFSgCKmnI~KQD<^;1QoZ&*qR=yui|`OCq=$^|YJ|KSV}e2);j0frO^7(xI!B$UY9yJt`vs+!M7a}IP)6-4v<3D>AQ)TJZ_Hm0QJ z;7(J*qRygS9VoSB{%hkV7MA*?h}P6|?G^l?Oq=c|u?b-G5j9r2U{A!uGxX(W;s21%*&yHeRoloIJc1PU%$l3wF=q_i#4W6u4#WSi3xyQEh70Zae>NM zINf=QR->uF7;Byl%$1N%iW`bTH(cflH0!^FWI_1UcV3Nwpcz>Gp>=E?&s9o1%_$t+ z-w!7p)NDw`*`2GBMeH*_M4OJd+AWl*sQom^RRF&ejeuoT&_dyX@UdwhL!s*)=E7qZ zsc(hhG{+$7kBkE2p;drR!qXp^gFu*--(0+;{UPw9#R|2u=ZdO7HX_d7D-1o&Lj!Fn zBJKVpxr+ykvsKe4#`oe)g7=}(Bd2cK%%((Gs!tOmQ8Lg>=V42l7tcUU_@2o9&Upma z(Pp64lT^prX#N>TA2T2x1F^*wIl3$GiFh9-8M;0lZ>W@ryUzuHo7!&6pn%gYYM`RHJ#*hxZCKn?!l18xKAzZ$iS)|S@46-zBM&?g{=k2 zuA>X?Z>kQugwY5gs0b3z>&4(Q@mqo^q)iDZ6aImzd>4URP6S^)RpNjfyzG5s3%ku1 zUll|6bmVr7T89qUz%#%3<6gFo+=80lt{#;FG=fwl4#=+I{>k=gJ&KN#iHnz6Rk=vp zHS}j6OFp@1tH+HHO+&rMs3i}EML7MWBfB~;3ljP_6#uBfn-S4w;lEgS{OUp>;U0QB zGkSkwvt|A!vzT;Kk21CMsybecvNcnmXw}JgZTyQg!r)%OE-@J+v2|pER>W98+O9j% z6zrOjz&)7)JjNsiwxb5B|LA3VTYR|kYLf=p%$8vK;cOJU1`0Upx#X2aWvu&ktScNH zPuk_C9kLIqmeT|T@((b6_R12FV&MV}p1NsZjqyopfQSELh8mx(>0b=`d*rTY;iU5N ziO2C?iokM!N{GHnl*3lIS}|NKS?PuRlmX6YwZo4Q{I!4tH;2~68`tuMUSt#V zMvdk&m-XnHj>1{bYdQ*8Pi64G39H09uYcwchbnskt(2`umN3y9t~ZE<&%bZ%{4M>5 z`?F)5+{AEQ(-z`-_AdUK7)JlVqJ{o*l~jHSnj`g(abfa7xwtziC=*da^c7$E!|K;JrSX&J)l2qGbN7^<~7NHWpS zOm2jY-J{tcfcBA6g`$CpGw=ch^V_%nUtr#kPI;;V(q$N+Qoycy?2ZIUv6cj%w zU??K~hd}&VQ($%+hMF_|jC8_%v+gV`7Qfk4_SRO$VMUM=Q^RMuwOUjuh*-mc`T>^K) z_XiVml_3s$agVI3Rc{~-o-}M_y5k}npkMDf+D>oX`TkA-nS~+d2lVk~@PL!#dGkw_ zeU^3=g1+FgXmzC?O<4Cz{R4qZm|IG|ct-wCPVSJ7!nOO;hhK7pWk+*|GaP*u`KQbS zf5}&~_XRFO@`4g5D}%O2xl$NAjn#Un*+{wzPyTHt8Xs8S680 zO#=_CAd^)JX6a>@4_5$NYP2)AeIrCYv@y73E#C+W64W+QmY&{w9oD*fq?s_~No5@` z;QcQe`r_Z1xcA_bguXkf^f<*`Ko&i245IjLN}{x3c69BA)LoI6q5FdaqVajC$$E74 zBM@f~zb@Gk_d8>rid||myXftIdn20xw%%4=a@&U~x$GNY91!!&kgOOfnF0+D938+7 zp$xRE`h&dY^p&ZdLv-?!he&1FgRyCv3g!syVkz#okz|mk5REgxPBe)CzTTFv=e_5X z+{e6mFVyDe_s*fc(XZI#%_eTW>nv}9AaD@%ind$r1{gr;DSsBt$8gwvy z3=xYLuUNK>EXly19>p}l#G!Pm8TUcJ-buZ501lN8f;!-DP%1ckWM?#Ygbw|ajo zRXY668>sx_8?TnA<4ytt5dWpl;I}Qee1Ji7m$7eY5lL7ijUY9G+w)9X%JcN@UCd3@ z_pY5QI~>ibMd*q&YsgeH@eOE29dum1vuWWkrJ@>THuO?5qM~I!qOT!nn_v1ndW&b% zaAqTA@uE}ii)LZ&vQ|+w+SGFWJ=qAJe`tp}=f?yKN7dpqImTlGXnw=P@2k+Q{)=vH zG(O}nzZK`S0kP98w7q-6q?^WI>A>QGYGh75hX{($EH5e(+jN_2p8hy1;VLe-*M<9; z^o~E5g~U5NtgZqtTn&yqc9!$WvDqz>w5v9@9rO`RZ4|;R20I6fGLe?n!v{yQ$}y-9 z$@$t=up%)$i{usq=s{q*bc;8)oO&yslWTb=EJ<4!JL}RG-ad8HT{sX0DCu72a}?{} zFrG8WVJ%fG#VJNaj@DdgzV!`e`(72}lbT&}+FnloG3keSFJ+~a9;64mVk?hJxy5uh zTii>qw|=zDX4R5E+K+Rj)fmc;4^2_(f>VUnujzA5FMxCcAlQ1oWMsBxEnq5_UC8r& zXF1BdGmvfDGQ!oOR_!7j9{siH0{3$Z8LG1b$`uT zrF(WUqhi+R$EDru!|cPVBP!)YmFeC4^eQ{~szv06U0vMG?lS8ktzKbAxx|wc{Zzbz z%n)?Xnr;6L*db9ZRMKU&@&jejVVzQUDK;yrh<40zC4!603$OH~{Z*G`N`|;k?mfx2 zWS6|=WEhjOo{s_j`t-zQZEY0yK@dfE&26zvYLQbZ+aUG9j<3D=;1)6CWC{*32|mq{ zF|pd&Qn!E3C|*79nXpgXYMB}|;DYk7`OMmUUKei(P}-mf?tGYjpAx8Z=MzSn5;=#5 zGfF`r*R7TS<80cb>c9$zV2srG$;uliibc{!a@|d9d%2A@uMeu zn0&$~q@!nFWGY9we|PUxSZC|R)9%l^F?IRwOnDR~A@ly|Cwdb@F%ISJDapUqF=4u1 z@aDP#x+hI1$IuwPyVB}APnt9TbYroUnwQjzIjZ40H)HW5S*NmVyXtC~BwJ>Gxg`xt|ur7h}|jrL$q` zX>4IS!3$iyN?pLTmOgD}Mu!2S-!#V))%?gb3_?lN7;DqWJ0Ro}mPYe2FT~63ETahx z=s;jpANur5`is^efs|v1+w?Z^=G|q0pH9FN?fCIQP6It(gbg(P^gHenNia*Ssk3>! zoFq%n0_Vu95yssQBWBH3d5sC5g&>>h$`ZYCpQYJXZIX`i%&nI7aq=(UbSk;yh>3i| zO+zS+YwacGuH$vhaO^*DNNjy$SielSBAo&F1hz{VYz_QNLBB0AX^3szpIAEqm_AoC zqZe*ihrVV9VbN$g0&T@Osy7-hC6A(;yGyCldaFr~qWmEoG$=gXwbUCgu=g>XP}tQE4I88nI(G_$rTsaUtQoT;OQ-8Hnj3 zOh5s&=Mx`Jj3(A}`Z+hujp@JMpR@dUu&iPEGYE*zh@LB^OZ~$@_G@OYz~8q&K~_IQ z-#90(-@XfY7!ZDu{~_hDfAdBu0jwPVbrUzGGx3K`{KwWE#s|37*+*OfeH_T|t`QvAH4LTcxb-U{|}<$rB~ktL)Q)P$>{#*Q}CMZ3pqW#D>^Q_Hzh?b zJw1I{DOBOeDF{;R%CddAPhIF6)Vf+#4U2XB3oisIIPUX&tW5*lcX-)hbOTjdX1CFX z_AvTA@r*;S0w#D#`zBGU*u($9=J$0U0ltpE8{y7Z0Po4RQREn@fG#Y7bo=6L(f4)m zqLTyQ%siP&Qg>z!%`}N@-}non8SNr}`;D2Myxr{E>d<~+@Tv22oO<-P@p(er$VJ4( z&>LMiM|2$cbOF0r%9@aFSn%weV&~fWwQ%7)zaUt|nrm^saDRNLm*aQudlY+`db$uU zauKDiH?n4V9WvySk@zQgXpBzK)~h~B)`by3i__wS1s8601=TAtz!#M8)Q&fFp$8EkDy~Y?aL|GoRfb56v1m+5+rIXo)PYo zCe?-h+tCb;E7eF`TB1(zP&i9NN%d|FG;F$eiUoBzlXpzo6SKx)Nd8_LQaI5qQqP6gZ*j4h`N?&xnc&2(WHyc}5+vAge zvuzyPBrTey9wU%r-3WwZ(SBlY$vXy(L1Zv}IZ z@wT}gw3g{~_#Mig0WJsp7C~bY>yT=^0oIC-InPF=Se@>wI$I*79yLHsZCD)FGffr3 zlCkMCP4)1k1;4d`!mYAkiMN*0zoddzK4SqyZ?5C)K~4Ki1+1tb#H=Q3v{338IKMOu zmXYb25L5zAa*DLOdrQrNZ>AFNhY`+JQ|Im7y3N-r{-DR* zn$K7AVyQve+i+>R?%G3k*x~6E5+H%gR97cmF0wP9>t92uS!nr#U}X3-E?aSJc_-6x z*(9D!h;`FuzKnLiPf`$13@Q+tL)1r?!bTQ^RSW&m8RF>MsEilJE|+)F8ImElu9cRx zxYpo?A^yR~6$-$Hixzh-CG?ZmvKEEi?Sz(PiVZ0~5I{}AC5T=8L}ep6gz;$WnO#~; zP2BtGR^F|XNtLA%M5+95Sgn7WzXSp+p68ZviWf2Kaz&s*^qCf1>xYf&d+vcoDJsYh z0JJ>;cW0PxH7jrH5>I+!HJuklO;@#J3li49D=0EzU0eW~vLUI}RNXtmt(?H4M2L~N zh4ZDd5b!|dl3I8=gX6CFFB~b*BsoUhXFR0=5?OSJn_JsFUKNtXtCkU*QmVuRbv0+a zM=4~mHOWF`rCFHkl)aj!@x!_ER`=6E|D;bxaKre7MIT7dPFhwO_G)B*Mz8kb8JmfS ze`QrWJPx>f<`}2AJ4;c8B?WPhanwEG}!IF*w@rr7WVBT_?Q{#2`uR=RG z0em}89BGypq5u!7Q}~HzcUuoCB6;tUndskNKI^u1nOklTQ15T}68O`U78~DO9>R^7 zbY*3-8>2p_ckUkuf|>IH;%BL+@epG`mVQIk$ZtS5#)3JMsD!a*j+mpBh^k_|MS1%zMaAl~_v+PkZ|vsJPFD9sQo zru7A{2n02c{u(S!-w5Hy!~Kx3=*w8mIj3>qq!Wg-y~hn(K|FJT=*`n22X57KVcQg- zqy)e?iJWLp=~Z~VGd*iTNTz0A`f1sY0cmYmO))%6_@zZ5qUZ|xurLE9nXDNX7j+% z-Tc`K-dqc*+-nLW`g4eK5`!UoEvE>$y$*L1qWOWNXRM1CTPx7^d7Z?#+W3UCP!|5Z z9+#{Kyx^t2_ypdA(DXNyqF_9|B~&V2o!jdkFHF>|2y0xRc0`tf-K|ha$a6^IbdSodaj4Bl5BF$mhdZDq-rp+Hw`fZ`V6oJ20PFHCuYeO=p zkCQav*-wIEsN#`oV^_|=V8dCHRG9+u1X4kj`_nHfKb4nU0dbc|5(XO{JHTPs&fQMz zXWOdj?br0%dymKGeeMobj36NE^X2k>!r?I~1sx#A<@e@4FRS)y`i)4)>F)Y^s!EDU zExRqIKah z=3A7M>Qn5zo=$vqf1KZ+11)T4OX7f*N0|Bn)oU$qrH+y;wR6W0FSq8w*Eep!oX0iu z1%S0Q?dkMPDm~4?hUxYa!^`awwHe2*c23?jk!p?)!pkL{AAEA`eGyYAf2exs+>zq^ z#5uH%y-n^4WpVR8Z8dea$QlO@c1Hdz?7bTGU)Y9K!~7;GMOmAIp`+xQR|Hc7>H%3K zTw~XTKhIEuv>%#D{3jyX!aGh|B_wvE)c|o0p*E@L5`T=%ay(m^&_vgLH$#*np01NR zt|+i^^d{@f>VdPW#cA#e{sr7zDJIP7mG_>Tmrh0_guGmngBc>E`>OV0MIOfVz(1!N zlaaZCna;`0e$f2Qxmq*+m_SCFS0miJ<rnF4}=s4xBTO7eENO z67LPe*&iit&qf)N!khrt+rgGq{4ar=jg8|adY(P=t@UN>%`Wz^lAX)3o2|Z%=8TCk z5j+z|Ez7W4+@Tfj`i%)kW1Q}>AX#{vll8q!LcVncc8KJPwf1^U)LK73?4s^Kkj`4$ zuOrSG9J?<4<+uk$W-lNSHjS9+0wBQ;24Rya{?AjTX-llD#4p>1D0e5Wmu>xMLD_GU zm1FBPcXjMHBDmm&cEC%Kt;3QSPkYD?tL3V+_Gd@Jn5^-5n{z;DIH7WIY;pEgimr}q zt?Sh{B(^DgMPb)ta(RaR7bxRnO2hVsv&qDy&@p7M3IewIyj9jPVkul7EPxfKWVNu- z(RRUv8Rs?s@QXt;kCl9JT+UY*X67E!Ij3V-3i);Tl z`Ln{vju@k{&$VQ|YB@*;4xrc<>0+yLTrWY$C2L;XVs-`{;}`IbTP1P7%=pg!j7mP!D-}y zS=N?{HBENuqO8u%e5*1@nOUWFAjqzUXU_8Up}Y%kG2D8JN%P9QwqFZ zTUQp-)7C~l3=P=J)*jLzCGy~T6%FBAL-NuZNtQ}97C4%ydOR>%M|$$7kggRU9Ui0U zonuM4h?m8UbfTM?djOepj*q`sZMSrEfRmSQ(>7-`(w?Q{$F7rW>>To~#!RC#UZKrB z%&mjPm>SjBx(ry-UrmGi?6FL+?(0OFVN5Ia z_~Kb-8qtjz-l!(+hK!4y$44P|azKoIUR+x&H*^zgqL4JrDnRZHs-d{leLN#v`No@S zHa%Jrr}19KIM836KSh#SLAG>$z-ZsbL<)V(y3h&C{LIkMK%d=Zs)g&u8RFcu33aN& zxg`%itb4GEgyoRlqLn=CYW49TSN^< zsc7aaBIa~A*#L~dY77!5FA*BvISbB$e{IL^k31>Vn4D$C%xuiaq0%BJtdyhwh6Wnh z(?NO{FrpuNBZDjH>>LVRHD`T+exFfJ_!O&Gm7B?ltiebUs8h-6G%h5CiiciY9oAPA z@Z@u17jaxAMGMcrsk%Q5fsm!q3Zer>iXOqXFTY9B-h=x zAo~?vI3eBN?lC+DXz-mmmqSPn1cHV#=YCh?6emnTlj1W7MLiKX7;+iQ+oCK7+KMJO z2IeXB=K#Fzd;J%Av$pSqUS(V_0@=Y;G+r}4=odNyP`360pszXZ)v_h$1yro#R9a2` zRv&5hT{&}JjG?cym?!>TGEtBx^}Wi=h%P!c8FibiND2g@3gVsVvgj&^cVJf}e2UqD zfD)zErp(_`bD2x^M3LK>JiH`040T3>u(xO%hXVM_g_qm+yha0lKa8!K-(1ldYASI- zxi_!r1C%NupK`XU%q&5h(Ny1)WLBJ|`7;;Z1GW!IBL!RIks8iXvri$m-!j7P)OD|Y z>`v~A!IxZ-oX3BRSY9>9$YB!XT3~Ss8RzWnB4C1dq?5ORQt;}waqvT{XM8r!PbxR- zR|Al#6~kW}9p%sh{}w~aYW@sZne^u60i_bR95z%5#c|rl6~)}uC7cWkh{CQwTnEz$ zL$q{fHCV$W>puB*KOH92^gY$_HO44-C;x%fCkj*Zy-H< z9+ywpQFtL}y80Jw498B%Y(0Szux5L1sSMEQIBBM87~qv^N%Ytk01?5zC)nofApWy1 zrfQ;-fRO?X!@0qwomMX^^U+>U5P=joW@}B9l2^b*uJHT5gD0Zaa5Y9&y|viYUYR8t zm?Vtz_BqCvwHl=ywX}xW_9H@b#s;SDYRK=4OY_L}(ZXA@7@yCKYGvjj2_{9jhYS#x zivhnj+qMd^okJUw=_HOgxoJnJ5&hayfHJxfbwwVXDJRrgI zqb@-%q^>4PS!WT)byUe*dD39PS{=|697tF=<7(y152}x{w%1!YT7%LRxJ%3snw?W7 z(|#hBBPaalfU#fKzui-A0<#XS0E7NEYJHQID;8Q zN05Vo?W6kMzV{qU-enBJX#hYhOwby>qHuu|<-D=mKI*<{qWQ4=mz7G4-S9wtd=1rVsz+U#eO_!r0@LB9(eE}dRP8-4DKcof& zC;UKLA{n(?*}Vn&*OkIz%w~}!&4QVOpb1lGFQwU1t=u7_Qao29iFDsknc%=dZ3KVI zbzycr-5Y7 z!QuwHE^ACx*2-lehyyS_-My3JrA4WfitGz1hK^5mJvMooQiA8)d3 zD70b?tk*A3i{EL-xHV9BvSaUjI>9|Pq0qWusOvIOFr7E4u>k=XJ#czQ!$te)KB!)A zyzh_0wm_TOkvuPha@R^jP+7T&gcXBYRWm-7A2na?1*DMpTn+z)J^ z=lms|(Rb`Nc-^PRx*kpOCX#ae(LIgbT$VKa5UNDKn|gez=Qz$dck!&`U#RJvdek_n z{X@fx@geHS2l)eG$CNT5$fWA=v}$X&;D z_u%HO=$v`^q5#h|o)M_Ar>fN!bNLIUhZ z=3bQ&0a#Sg=d3ul069tjROHtW4w<(EKfK~q1ZAw6lm5{cv#Q{}2i~JCyuB#+#5P7} zT`!7Tn>n#f`JOb++vt?%yn4o`X@tvu6m>OD_*yWD|5CEVSuYKDo$-UkY zbwVSku$Nfso02dXUq}EWme!Abs|S>2$mh}@IegV^%~Zx-EwSEB-$V#K@2U$n^5V~d zxE)(5+6qD!=_1e93<2QLXjh|u0}t}6$oa~4?fZja%Ko6USYZldxM`Y{U9LzA{jh$G zwY4l{=8*eE89-eHv^&f_jXhnEukpkMOTx0$H-w20hB$(TK$dZk0P}%^KmjC5508v; z6Ug&)@1^A~q{jid)DfAp6AaFbn2x9j+;U<7`#nw82jc5RF-KeCn%Um$9INkcchaI1 z1VS=^I*9n{KdB(Sc-uhJTwHTC|57Ys6K(t82XS%~PQ z*ag)x^sy_1O=KV}d4>#q6962g4_^rd42*Y|BMEV;?9GlkGgXiCCaZLV$M>FpK`}s) z-F9|8p}6ynDir=I3NHP4+4wy(I@3yu949(y1L)z!i@PJkqaXDKyajm00XQ#4$kX=t^d|5FSUC) z-dIkdG_6diWig@if}-(v{lOt&G@EaXNQkqSMklaga;36geOj-PgKJX%-v3)NfJLT` z*@jj-;Ko*g8xt{_1xYqOfmHY=JI7?{{&Bot<0k2wp2$(ZB9IViv*M_)K#{rj8qrDnmU8| zbwsU1*M%#W3bBRuL6avR#k_ak>L#T&2RbN|%3#WGrz($FRq@0>O#kLIjtA?vS+clp z5B&JObD%SYMm*tc0yx3$HktH!y5)XxB_k zw%>6c|L6du9HG5U3L%=KnN!79+xEA|=o0`(=Ew7S+nJmu9d)IrrD^??FW8?pTUM&aNFy>~1 zMrei4q&w?~Ws;#P8hQdo z&J%CAGu*VxewzNyxU?E{9KMjKs^OyRs!NMKBSVN2?zP|9yieL3Dddl9>aq1|L3`)5 zjC#|1N-jf5ZrsktQhC*7hut3$w)?s6ryyb60o1`EfodwSQCyy4I!91BkW%ru_`InB zi<$v+aDv6nQ}1LDcI105WqV>LFhu_3>p7P1MblcM38p$g%Ckw@&3Salz-ublZW5g; zo|#%f=HDu9u!2^Fv0g!0qWFVswzZZcQ4(H(IvWrncs-$Bw>II(z%%Q-WVyL!iK8SX z(rt28e#*b>5FGExN~&nDM5B8yAb~T@^C<)9&H#$J52CDVnwjFe*YTIAPr()2`Ig3D z4L_?tJj4DUs}%V684(3Jyq}?(qfOtu57{8M@V}%p!nlxS9m9vFhL!D_6O7byckN(R zHl>IRw{GXUh#I(U!V$;(^EQDVKbW`irYyT96iDs4O9j@LE9Uw?6V*kT1^(Y<9Cu>; z4+;P~Gwc6f7H|GeJWbjVwvOqpkhH3wz?nN?;s}X5q^+?`jXJiv(zPdTZ{PC8lnaW^ zX?J9xAye(N3#bK<$~!*lzwL^->p|N=ilil%pS(Zb!D@*<^wWy=dfX5{dCyw_?+vWa z$FY~S4jZYp=CY(-mq6fkCID5qLLNE|9|&~#;*l-CQdx)Yj&8GrHtExk<~zPt@pgH6 z+Smd1bbUmB$lvw`;&9jHTtn>~9Tka=`sx+C!Wz7!^7$fhOcA0!)k*`QGO-j`w#g zU?umw+2q9J3e&iywB*$|2$3y$I$2)&gM9r^c-$uUE=DuXv6n?~5_G-~q z?cQ<}WrGS&8b0=9W$G5*#{)kz6se83$56L0%*RW_+hY<((;-ecX(t0PE!r)O2Z zr2#HvOS@KM{jSbTTG-R$-sj87k{>wDTKDcfS&qv?8mL~z9I|XR&OuDSmU=7M`-fqu zggK(Z@f516Yvg*VbQ$2GeVbF?y*vhlxs>6viehg~iG< z{8@O}6&S)sTWu9o>CXwq^pkjJAn3I>3h_9&tFQI^=)wi8F2y3OTh`l4CYe~0z$8p! zxPxn%2oYT)jvGYiRtA2k&`gFaF_bZy{vk<EBogf5n+1K=~D{`O=P?#sC%Tu2Ci&a$%m zRUZEfy&f0*`^a}@gKg%lbT-_ttu4tom53Y0>1wh93fJzwF{=q;G(0tte~7Gz^7n8s zH!&P&gHGTCpBv56G$g6b6471gZ`+#-wNF2BV7eiiXND=vL;Xq>jcV;oeY*h6d3mP| zqCECB1bmf|U{`S{fO?MQQNZ+M2|qawyJWfuYn^*WRJ@sp79P?5g#Znb>4a)1R%z8hpf@~8Tpj83cQu!@fq){4DolqAIeG6a}e@D9K z0hf*H^sG>zXyy~qqw_8rx^aF>+tD{7z(|SA;|E3{RnWIhoBle7gYA)?4(_^tGA@Sv z3U=7Su_g?x{~}HHML5?&&0!iE4pJY&0F-*cLLm|D2G_#cpt?sTS;c%TJ%;|D%HNG< zIa2ROzAEO!Q@7wYYh#eUF4@zJifW1gK%jX8`xTY`g9*zFAYaRZsLPYrIJ^%wm#tA0 zu8O@D5PcXO!K08Q1G~;jCMDGIhh{hPcUYx{Q)ScX3Vx)guMlyxb**jt(Qb(c?CYk;rHo5C(vhAbj@f15qS;VfRfG~Bpz7a` zki81s<=RLf5mFd^WtP&`_8 zItbHxvH>9iCfc}5D~w{dt8iaqz#S^sJWuurSh)7+m}q{BVN0}-OyQG6%#Qz6HhIf& zS*Ks)F`uOv->Y-L5^ja)CztukG+Ze|qYa&QFv`5V*}W*;l)lXQ6LnBXGg9ZZ7#t}) z1%V?xgZ_3TXF5u3OeLAReQ*u=h5I}m9Ty$|oSX&xMSwf2oKxAwt+x^b(4?*5XYALi z>xYW;V~X>oSBrgPjoB|>BB_?N@GN} z;vA}@`Uc55L-%>^?7=t*Na~_|%ZGzkFd?hddXr+&e$#$xiYmoYDa(4SPS!#@Yc5LS zBMZqf9u`>Fp}e|OsiJzwUOz(iVx4aH1NnQNpVSqIDc2u;^Yb4g9Xj#|WfJQ4v?F&e zkj)UmpdjqriT5XLn3g>Mou=PVxW@F(Y)Qql#q(EwHmFJp%_Oe{fcIQ8!xg^aJfFrD zU&IR-SX)jMtjRd?>g?uV!IU9$?@zKQb&0<5SnqChADnHLY)5JvU8?g@%o#L4`{jqM z>fb(`?mZX73@J4@X+YhD{>vOxv@3UxY_*P`;=pQ}uv^eLi3GL~ELgh)NZ@n!+RD=? zL^*ZuE~=v?vYr(Rpb-i2I8c&BZ0;MlKDaQsoSk=|Q+lRu3ZZG*%;DJOE|hgOqCbAa zha<~{p)mMMx7O?IJJrw23C2`JCbVfqsv~FAgExyI4q^lQZq>5Le%&*hjJKUT{D(qJ`4`Z(?)Z|fmEX&Nst*f$Xri|ng2hI5L#`S02u(D+MkwEjgHvd+9 zWlczGw*GHyz`fks+LMxJ8lw)PkKp$8@nD-PiO@)Gjy5Gy2i z`~|5W{m}kK3d#W-)Eea{YNIYnomda`DV=D`9cG;FYce32I~UD#;qI>ZUdjrdck?_2 zOKm!n$&H)q6w`Y4(Oo!%8jev9%NDV*gOYqcdiGR5EK1E@KJ6AolJe!;Lu;sYyS|#% z`${6nf$@v>vN1g&=r8#3aCbjZyQd~M zCXg!t0AzjN0r0*QBN`~??tJm+p4%@E^L~an-jsKLpG>W~__T0@PxJ_W`M+))=hpjE zw>cwAAQ~@ME?M}z{F|76Zq><@`LNpA{C9oSzGv|oqP{hzt!nG!w?U+yCva1+t@BmL zcxK@|*pzXV2*GZ0!51i@!F`Gxr@tEZM<*I{Dgf4^NMc1nD^9i(mA7BabMHzKm9BK zBx;vgln=zx!?TBK@0Uf?P7NE=H&MfL*{C}02`ir=YsKHF0`W$v2Ca3@bW`UN{V_Im zV}Se$wZl0I4J>R4dmA8cj8EfsohdE!2PJZv`wi>_)1&b3BbcYt%V#I;{$m;q9C1a!#kOzVcg=wn2h`;Ms^w&xWylf+fS zDQX)6oj@hNqQw_^ae$k3-O=NALL+5#yT9XQ;P5^%-0q$y*U59lFHm%;iuJsMq5?Wq z!c{k_c06l6o54p8ENndUR}=O7CCAF)L+l=sFG-lWB53)S*3!-Q*=)2wQq@*x=m9;& zeIOWCo0dC-r(}4?ZqpZLK`(N{-eszicc$%}O7xin$?^rO# zmAJvuPT80aEX`yv$X0^_b6R_=h{Q&<{5X62YRQ%%I>}Q0a-rV6?=h2k?b7H3>7X5x zZt@i4qCwSblVE7qg0Nas%|cO)KL7%n^$iAaPH)m}7fAJfJGCVATUVtDgomymJY`+2 znrhfSr5oEBN(Wb$SgIUy=?KU8!TQ_VV3K8XG@d{tg(A;&glObQC8u9Fa&^>;Y00TY zTGb=$k&p6QpJe_=vi!R-0hSP=rm*8C83i?ZxJyd~ju`?Ko1p!By^7hsBY+`8#xYmk zt?1=ONe2+c)!W=MGdY~d8sJX)dzTHDXXDD0)wrtJCFkLOo>*+FQBE&!=G{HxaGWfk z*A!vnEUgQ>9nmyxRD|B+wNj#Bul1kVz$c3ER3t*BfAA4gFn4-86$Vqv0g_de8$Oi;(4P^T9F1whnKUt zW1WIE6{bmdU|fM=k^q*gq-0y0X>T-73%tO;TL%dX2m2dOvY~yaJ$X=LSqu<-r0{N( z+vuA%aGO!!9H)_EI0n>cBq1rAj&-ea|N+5rs+s$*t` zI&+vLfpu%Doha{}?xhSnea=Z(F;0m~8euw%$tK9G+6CYc)(N>W?qJW*H_&h0O_kXb?}H- zidrhl1n|s)%CVf%GYv&B%~6*+<^3gKmknJ`x;fAh)+|k2c*t+OhM->*M=ggfD+Qu6 z$H68R#|CVWIc+NAEN{7P`9f*^+$WalIlE5nDV&_hjC^{^b@iodr$l?6Ey@g)mhIf( z9hf9aW|V;*s3xH)l&P1jF_=uyQbo)wU{6G2uN*jLvgRerzGVM8 zU*+vbA(&b6&*8ZP)27H44k$)izCIDs^0N#ZG6vifSMp*yIBbjpMqDw>vJL_mef*gk z;vpgy=OK8Ihyo;dXPg@d7t5rw;7SX`uDTXPvTN7(XaUck9H!T#LudDm>_hw{^RsZM zo`44Jf}wokG5N5G^fhIF9@>l0_=3d&eSo9pi?kiiOf}CGVrXsqB7l&-vuOAfL&-+S z(FBw71QLKduyL@B7FYtAd?LoVO7Ga6)>%mFr0(@2JV5o=+68j4P4~}P3UA5SRUNXN zu_BUAyfoxC7#_=_-(~EA?(z}$t-;G`8{lnp^s346=yqK5`nrs&E1HAP$C)TS?Ys27|sxi`p@pN+C;@2f+hw=)w;G%*vnU0UeAaMo^k=dQ9fX@FB5 z;XQl+2E&%7W-#{vEL6|yL$yJYvR;ubwjw2J?XfP}%in60S#Y0&yjEtQvhe$P7)eIr zYFv!ZO~mSpwR#KdhmF)H4P5qK_^jgDvZMihQ>6~Spe`g>ZWDemr+{Guv+tN1`bD1(VFFw;@Uj;XDT2s@KsHzq`a~(^jM3r1pI@m=KUR*$YdVrFF z)G|u0D10$&=*co7c_f!<54%0PW#)R5qhd7agZl=>lO5F{Gtsu4@*g8qIzXW`b_Lpr zut)BPSY=b)-A*jLyWLjq;kGQ>K}(>Tr5R4jOs!L%e$%RysJ8bmw>Qfb%0!5w0pbN6 z_X-!OGlX4WNgrP*wL(`=IzlaZtLc6`KRlmyy^|q}QK#0=)MD$tV>+6922h{WUs{Vg6Teg4bi`4IZ zU}ON^gyaAC7-6-)*{aHvDON}59AzeUwRx*IiDm|7jdO;6`T_+I3Ao0(5+roR9GHtm z6AK`L@po!2oJhzqeU_A%7h@%8&NqJlnUcH!|7IcRRyBZL*y-5;{(grp?#D#+{+$W; z`Op6N9|42e2&_NKj!$~W+pMHek--4)e#>ovZpM=i6_|Q9upl?$v3$YZf%>n1Tc5i( zA5i;%oO{Bt8k)=Z$?$bGpjBz71O#I(=MU@1;+{i@Kp|RqaKQZm#`iiU*)tNW8qns4Wxc^Z1{F5CqObv_KE3}lT;{lrjKs}8#>l^;Gaw1EN>6!XPIplk5d9%&uM zn0*PC`h|kcZ!05Z9A0oc*g`XaEfdvn84wi07|<0#)%eLT#EFA?Te>j9zE^k@gE-*Y zgw+h*_7vjlX+rFD960?@e@dZyM zW~Xa2aAkzAtbt|;1Q)}t2*`%=u&?=PdIXFnco(F=hU>klKRkAr710Zz$p5_2VF7m; zCHt>6iic@ab)rv3WXZ+_!#dojl$aP)8y|t*bYI!AY zN`MQMWlg!U$aQM)xTY8I2^1t_%{WNBZ#NNd%fhN+JM}_iIHpx>W7Q7i@!Qca$G%=rHw?eB$qHXl#wFTne`YHDJX;V*8UD)-hWgyLJv$@JDl#AuTM!L49rU7J2yy4#^e=OIZa_=HDyW@}h?Q&$*>ccRlcmdP9AiC9vusgX6kJ__NcnvxG}#D z+4FT*`|xf%iG=g-rEBb{M3$TGv#ISK0t|I~zP&$CqIm_k!+53Aw{!-Tr~A_5W_==J zC5M>+famwcy83y4dAto=g1;d_HN>rKmn)@7y2aLG_z z_L8i|W9{m+A&s#qBx9T-Z=2s-LhP?4kIyJe+$3oIkl%1k2-S;70%ces-AUmM7HPOa zE27$Phc3EcQ5I#R4c`yvB3~Yc3V@t}W)rggLUa?o~Wd!Z7uPSRlEig1D)g3boP^~|n)BPCtwglcp9bj3s z8sNm*F-@JnEUF+Qj9H=Ym~!n5d4|2EHdlA@dvgg>=F^Bhq88In!RnM z=-~0A*u;>{Q{N?|4BrdD_S~aEr5vDzG~jNXTBqtfhEE4rD@x-L#i6|Y;6K|FA~1Yt zx zUTZ&kICe=r?Gg+Z4|#|qE5VRrW1<;AQV;}{+WaX8i2Ua_rktnnkhzv1!?H9fLPyTN(V465 zuCdm5N7a+Uc@f@qWTLcgGhCGf@LV;ArgksRn$V_a-K;LydXw3_w8^@|Wi?9QCHkZH z++IhpQ;%Dr2YHa)t-nMTf(uRskuN2fbl9d$M#h*r*cyIuFrfNa3m2A3z%~#pW*c>Y!*T-Z- zwZLg2X7k-$mctdvPk)uc5GovjZyJ$Uforu7M~_viT-Uc{kmkMFQ*?-GvEIPK+(`sQ zQ6$bo4odr1QcAF5c#9W~HU|-_Aakp0wR=#|-rvo#8t#yGJjtysW>^cuU;%}jh$<^& z^kz>hyDxK0Le!sO!Ihg}0K5Z5s3%_XkXN45bE`2Inx_EErvmfsUqAZ1~mJ>S{QH{j623j}$MyhKxTU z=RFNcDc=en%}@8pl?G^i5q~;t2pH~&W?wtI8bAh7)ZE%h=hv*%0HywKNNxDA;0+Kk zWpk!72y(JIlY;z(@R@a|;l^Xl)xq^dQITlpZ``g>0CPve-P@x~#Ppf!$x34l4x8iN zypH>+-!HR+JytA9(Na4(T&I7vY^!1=WIrF$QPYyTw@lO zZ--^g2ki`u=7c``!HhrZM)y$u%hoNXo}`P{QBOpO&=5mSfT#Wql3$0)%v05Q-Zqny zq)#pok=(?I8%n>j=d}YOnoVR}-#OMs@4GewB6vk-*MRs0Mnc%S>03EGOLN(Mo>v*xqbF2q(oa940JE8NJ(Dj}w=lv@h&!2Z zve(6Y9%IMlCrVt7!9uRZu76cc;z<4z%N+M3h1Bp zE?y;^lC^u~luL~-zbeE$gP4xFw=Kb=Xw6T@Sa}yLz-(i-46fDvO@_}xB&uu;-}A$z zcIXwPz>=cMhOq}jPSLXtNsIS@PGP}JMg|j41b6({_PDvA^9c?Y7ey^B>gIA6aefw2 z>WxJTuG>2MYE3DH4TIaat(Ufipp-RPZDm?-5e*&mG|+@c%EYiwPL^w1I1(7i@0%&K zUCkIAfE2IjK-$7%RC=z|O4k*9B|CXh;Eab@sLgo;HaXf=W8{5spK8=3FTb%;t4i~Q z`ObNi7F=ATLdWiDJE*I{$m;GK6zm{E-|zj(g<+F6#Smd2J>3v5daVZGhxGLWby z{xi#j@F2o}7Hs{~;x6{O8QJr;@>9Jr2q|h{0IP!b>N~cJ20}?$S+wF&yDBg^^kT!1 zpiD&?aXStw-1_eP+XQ#){-msGjU$-r{*Os&D=o+#pGx*dZPS@Q_tOld;=Vi*7(Io9 zvIoN%x#6+(k_-Y}lHAo#K1%{H87sPl`ZwgkKn_Bi9U6$hTE8L(G)N=F$(Gbmm#+kN z0kP7gS7!W7BQXW2zYZE5pNe?3(`xSAcg8**2`+}O5w5NnTSuDXKQ(p&NF@wnE8ilV zHwQCMylF^AXm|E^2jZ!=AdXJMZORAlC#nTEf4A!l9phK@-5ACCME!pCf^&>DpnP&* zeMETHrpxFqzv%&U^PbB71v(9J*B33|4G8%M|8B(%e3jl?BjT+syL$X1v=#TuVoZ@E z;{JC3jI&>xD1T5-xHm9+1Z{nlo^kMm#CYB5*z4pOy`}KZU~;-;Mayf+Gw?<2Bj~cj zTyB=)hb3aeQs|WG1&jaLsTXxZMPtFx^Co|o%~f%N(^cV~qA2~@RsiY~Rz~8>=+XsQBXM%5i{|fxc!g~gzCLef->#Q>p zX5-qfhtUhJ56wcZD$*4<&fs2)V)3*Lr&no*cYat^i{@lAsj!wajEh!HzK4I{4-6e? zcv3U#r4`Gg#uj+`N*5BGnOuA$QB|D~21okpRzMww$6%M1)29;DHPzQOFW_kHbJ8hh zw>OWkfe8nr03&StTD}S)(9JnJ1?-~FlUjv}Z|VJEoupN!4+YZ6q+w+N$3n_!y@l*n zvObF9KzKH!7(9nM<~x3CLQ%GQQ8jRPOWn8Vt82TBI#kgtE-9f{c?e-{`j^cv*)Ja4 z3Yk%1olnKyE&!G_a5_wVo+Nd@&Td6<&MN&>c8Cubeu-oGNZZYyo~c|~CQ}GK+UQ4c z>4jpp!+^}H2-5+`N}0W?h}_m<>Mi~2wnf3^uFU47#W!V zmt-}iJ{5Do3fKMe>k*_NS@;|g%A0DIZgzw2pEuGddpgHe-otljLcXz-gK3FCLLZ5| zwN#_H?`QH-&-ia4zKVP$M&s=I48ZK58cxlHijeDL?Dj&BSWP#ei{b6L+;^vf9uP3j z35mJ*Euw)RtpcD!aZvm?*x@p|w1&$u29lm$(0)Ws!{@_A)qgkpNqsj0Ab!VzN&ppr zVR_m zKhUlT5+dq;7JsRoO=WSjOz_ZCb=;zwhngT^C*EDzngldhP)Y((qiMv`mLkxQjsQ56 zrDu+}>3(ZizaJ)uI;)Jmy?Mrkz!S;FG(E>Wp*ds4+p8n-D_$4LtAdXy*{}GIAefw6 zsa~rAc_g!qECkHd6VHjcPMVO10Yy<}M){eElEg~aXmwd-?{6pjZB=4%;3&&3Ig0V`Pm2jwc&NNBn_9FOJq6 z`-Hc5XIAygcUxZ7{m8^^=&%c3L+lK!wFJ+?2P|0BVLM>Yj&zaZ6@)(7No;o)=A~vv z3l8PO@f3M(hJymyUfjKpz2{6fogTz8WFkQqv;aqvA8)xF-7c;Ts3Ho;BZz%x_AEkU zj-g}k{Z|IZwaaJH=>LRTEDi;Ncre>#8bz2K_uQIN@1QHlXC#2Gxy;SS5W$0`hJo4z zXpBSMEPw7-tGJUcjcRl(6S6wK^eY@Hd{(QzXp-OVhdx<|U`~D687B>e)!BywgFA`a zEdZMVE=$mKi5l!s=~i*0p+H#_vyk;pvqFiR3cw8)5$D`L!=G<Y?>sP8y?R8Hkx?$TC@nHPBQYeRpP^h(GWplezz^e8%d!dhX8 z!6`O_GsNJG`ZB1xFK2&)rN5+U@mlyjD+J#a)zy#h>fPj25=(A z&y{md3L)BeU>b9p;r7H|V>$$M^SV*-OpZE>6BoB&d93up@g zeyGOM-Pp%Y`h_GxoveTUmQ{Ga{s}$rh;clEr2Oqj92IK1NU%=%H__&V+(@O6TbV8T;u(u9@nexdhAQ( z{Z-2k5PZJ@)qiam!0d^@_!KSLO`w(Fz$_`#prolZ9iT)2R_6b=(eX-Ks|JtF(RZfi zR7?)&DNaNc9h)Yo@@kc_ew5A{a#i?tAf7ozmL}WDb$ZnG{^EtY-t1pSQyHgXNcus< zq9L;UbWr4JDtlY!q;p&LfGl^9ZxU;EX;@q0TImk4BB97FlscL-e}r{AneCRn1%)(S)TG4j(%5lmFn`Pwymcj)v zdaMKH$TUbD$6>+gL^l6Xdl-h~J&_1wcmYrfhWgY&++M}hMG}ARLa-!;PH~MY;4BWJ zVBm2$zq~vhio#K(WT8e;2n=l9{&DLZ7?ectUr70Ml z{Ql%1GTS14czHrnc;*nQCFEHEh-VrM(a9nHMj*zUF)d+WZ#yXD-Epr&1X}Y4n6dljRrPimn}-iq{gP@Lb9d4ujUktU`*w}I<4AhfJ8fxY#;=3iE77b&8Jf5(6`*mX48CD zx*8RB`p?NgQdP!LV>?8tjvRMFdtK(f}1{V?yXatKDd z;b09Owmz+XyncrS%Vsb(lFz2$3@@{4L)4}o2VXLFY^pzMEle}&%3w$PiUIGUj?^3q z&c!i0EMjGl8%$+IE(BJ&QV&7fBgx;cu9}=+0*gWPYgy6CK!u2XIUw26ittB(U_Us; z#&(XT_H@-X6yvxIkZlKp6=a&Y5LRV^f#wmR{dvsx0eF9V zJYp-!T*zy1db#*Ad$GHY-QnKsWZ$i1?8kg@qm23h!8N}UQno>Z(|9;r_#y=SeDWdE zTkopbNy$RI6UMyIcXMRrT>ad>VSd;l^5ypSc(}Y)^72*zVr#m+-XCwiC7UmMYK@Ea zGBf}UCJQM&Z$Cf$C(at*c4a-MpGWS9tkn;U--3RxQsJ{TaEUwLD+2~U8yy`V3*VV; zGjhuxTUT3%A96Rb^oS#72!FQRJ^<|fk_`gVo;Cxn$w0S?5mztX$g6CK^zU>`?Ty8u&*_MC!xm5tb%gyO!|F#w2@bkRq{P6ia zb#(@~J+$|9F)6H=B~^@!HS&J4HlOh(+xIAbbV)(=DckIy?#2=eth(m!njsuW8`;Rs?c@Y(_jsKG=Y8$j{@8Z{-mZtv?#2%GUJt4a zF5afL5L?`8MEeh*mv`{=w!dGZX;;2-W90lg*(%fzDt%17y6|twyf_vbLVEVI?|=3Q zKJqvTb$x#9y`S&`?PN;^?reYF`UKyeQgdTKzwd^kZ}8XYZ8?7)EsB1w|IOst-5P*0 zcc;f4q#(9ri&8He!Oq6()Apx^%?=S&cQoa*CX|@V>xtbJ|9$Z>ny~U`OIhV;UL!s# zVj9nDfp;AGRB9pi)M~-(QE375@u653|1lx+VoArjiVym0IXB7|>!Y#ZrOIgK>e6Q= zMDyOPiCYO));XHX*b&!_>hiK6z79a>Hpiyn*L(UEvF#xmc?@Xtd3&AQE;jOhYc1j} z;^nbfr#JiMVcT<=Z+GCw@J`_q^q+u))Ma}H9hiY+YKa8CBTP)RzW6UhERxaxCm`OF z*aR>RHmozNMVc!e$nZCo-BI*FO~ch5CW#6x!_*f z#_$JxY!e{;fbpP!)zW4(^=gX^h%BL0 zQ*A7R66Fkcixatjfl@vn8oW-zKv2M1u@fQ4P;f}e=a)LKcFl}-ZukzsotFGfKoE;; zdA;15wFe)gTU>L0V0xChFO-AZAM$f5vDHBGdE?zLh%L5+ttFOn)B1zbHxJc(G%*eY z&I1HdpI8ns3rq!vnOAn2w&Ggn3WsiWz1`*G%Wf#T17yTWvz)o$+_>Y=^-FpTtX?kx zbhMF7EN-AZ{X~FDz;%IF=F)n^q04G&6v0^|Z^KDHz zuM&6vc%5-!K%vB@E#}TcDTfp~fGc{lLx%V44QY4;)sMUkE zglG$>klnen_pi_?v~BIpE@G;(X-bwdBh65{pWO2d#eNT4`8VhTX4%@i#7n}gy*mw( zKL4Nf-@G)CK9KhWsQ^{QXAfxYQvp5=DTaL!`h;cpV@dCK;F(OfLs>r4UU7eZRU`Xs z2KeQ049bp|p&o~wp22H~oq;9@B-Twy}tD!3^6@Biea*8dtgfR}_ihKQ#k zPRpd>Cvs}fn96T4%-=4WSQ#EqgtP`!>i(P`a*JEQ#xsQ$g@a_pQ@BtGt7k0cAsJX? z1#_p!Pz^g(=T9#N49tTOXKYh=P!X$V9Oeb-+x%O>mA*z~X%EEDz7qqvm_d^L#N`!zqM5{JUr3bWlRi~ca z+w{HTAyh6&={Q|a9sPs!8^ozq1m3F*R8eKU%!(kzH7p+Dfy0+joKTswvj_ytcsRWv zyDk6OIqKcp9F&b~*eukJi~R3!hzG)9cg^b=L8tKmlIp5S zxye{Qc^`I;_e+b(-E7=u`3F8~i?2tOb0XR2)J~!!5eQd`0JVVcJs7Eu7LnnZyDiF@ zB!%w7uNFBNDIq%g1?~UuHrku`hu6rMIYiZ30L%QCF@T>b6#!hjm*Y$f%s!xEfkt0= z!#ET%CKm0~+aL$nbXESRW4a}sKE4rUNs0WKVl`U7XrsW8C@=2qGN2ALt zyM*m-N~#2Dn;{w{AGdf+%zl9RCCo-$$mfH&C|RfhkrwxHMpP2da5*8qW-p(Gn6!Vu z$BM$2a-hp951>k-SGT;R!k9Pc;xr*2-d;Q|ElUkoaDvY1jcse=wABAyKGvz_9d0sU zNEEYT81nd_ISCZ7m%>&A3Nv71SFNsI<7aPR0q)Wjm|C-zN{P6u+r=W=!~JI>Ew^%g zO+4Nt3wCg@W;Px(51sR##2`54RkG8Ne(DOoORpt00?;+Vu2iR{c4}&;nU~omwV7_H z;5M`kWP>WWS-&D0F&{y&T4WH6(7{|ii!7$IPlw@k6w&2iQB%2r8`)(c61$eEc3R8U zhR%5@;~N~~l6)$3hdc7JqBs$AuaxkPM~A=hV%*VUE%7jb0~@%gDV3a?uN`?N{aX&< zY)xFE9)JRSiERC@5w--!u?hC2tE|-=wu6J~n|Dem2u682XK;dP+fAJ?kIs zedQkirknjyYg-eY)ETAr`a%FI(UYf&;?H9H`?)zm;fP?5r~CA9-l4ohu`_1G$i zIqMNz{g-~%$aCooGQUw%D=!!FbS1y!v%H+Q27pJ;ydh(5#8l^SzBO+jwDv)cy?5V8 zMykn6GX|B?Q!hF;^^AOvyZW1Si9YZ$0ugpHp8j9~W^4H03}qElpTN9fH@kysnfyhm zDivrnN2cXkO4?6=GqUh!rz@z*`o#^}w@f!6GD0G+=zK_yT@a|1fZVm4YAj4V8=9vw z3}{!eqE+{$RL^~)np;9KCJ8kAvrzuPDho<~1>qh@Gp2qjkb2m-U~eLG4tLh>u-D*# zg9)q}`B6VB|9=BRHqHTyPzmOCql4u-dvyJxKNc-4ToV@UM5J=(l+>TjBkD-Tsp+{+ zgVJcF;v$4Wa}9N}$hrFJ+5&)Ns)-0knL00Al*i|)q}4A?-(}4lZlLU%EZn!t&&Ttw zI{f6o#1RHLSU#t!GKS(IWrKHGicUMr=wRC4S{Kf&Nf^^Xg4yg1X$Ack?GtkwXv9p# zQ;RI7Se3F`(wXTs9cYqedsa~5Y#%z_3 z4CbXbP~(WRVq;GYm(1SYNg*pR7OQx}2`AV~s9rxKfFH!L%d1#~U95<$tqwN3ZqN>b zcG64`+ZqewhotE7x^%#m$uPwj4_#~KWf(kHWnEVl+nKt(N#4~P*g^9eHW5M5#c8YC zDCJDi#iWb%29I7VI&q-G^U!U@g3jgEXf;Ra1N6v>$`eVMn2fXO3YSkdJ&;T%XKpr! z$Fz186U0{@r1J5DSuatn(GLo5z5=(#KZgx-u_M;|%v%tq=M{i(9KNs(IOq0u|541J z>3LXp!7wGz08X^mOzZvGG{WpXYdnvh zJ&68$=h$&nqaGjt2O|c1x5Z5C4xRwBp-bNC&D%9@wt6_&0?2FiP_+Jsp5Hd^y3Qlz z328XFmvhfZM^;Vqwo%(6b75jC8`F{6|yGhv{!tV!;2K1Y@xjp!!*geg9jup2J zer?{-Dj#Whd6Q_V%4KBdgM#+TIyzzcr^lcUHsl0!UuhA+2eLR@=wEBki=H>b{Ifa$ll#cMPzYRyZH&q{OIf6VjFp;mn!F-;X-VRoyiRBrhnX-;;1LAT)54)X@fr zM9h8tRKBHMB6!n3UfDR9RIZR>8vLXXn3nBd+p)b94HCSVX_ztzgo^@aY$~{bJw+TG zfCh`s}?JRPzzM;uHnK#Bx4&1XJ6#!r;fiH_)e63h}_|8mm|YXO!X(G+B{ ziG*kjNDVCbZWhMly%(fgSl>Tu6{Rf?5JryiSNozCK^QI005~297m`C6ioRK%ymM0? zn*K$hKeJXu^={QNh^-FMV@Oy5MM}WXB>z{fYL0}1?^6s96~!K!wpzW^UQvQB3@7!& z*60CqLaOkw#F^-bgoQenQ_i<@PLi$jvG;X|1SB1l-8?8t^dRLNo*5I$l-E!`An}HZ zi!37$s2R0tfFb<2choiC0G4#l9f));syfab;ut49WU5vlUdR_~#>Q_3PT$Cj28r4_ z7TnE5z%T@+IK;tzlu^nri4^8eGcrw8 zYw>*Wd?WlA$QWbLQAo#ah(9aH018|(-d_s16$CLl(ayUd^uw~6*2j~(5Co8xAmXsS zBh%8C($lb(AvZ;kW38zC>NZ58SNalt6ELoj)WHE;d527KP~=U$>-tyCn+lX8twjF_ zFR*KZuVcCaV8+?qL`3R-ilSE)QY6KgqH5*8qN#SOU%V8 zyxOPMU6QU-e^_xRlvqZCn0D`!e8=SXs!BEazM%b3b$YMBfT5VFrwr+BnDSA!(^R8i zP2j}s!9V%lM)_3eCq}>ufFWT|TFaWY{3Xd~H5+MK5xwNT(s{%qz+ClS?{=sK(j8oD z)SrD8%@g;{My zHa>zf>dBUr$lHy$j(vzL?Nv=RCo!7F^V3DorB-#-gY7V8xKa}(j_FD^2Pv`z~+ch({$lW}wWRfUH(t>u(nGWyKN=00TfmOJ47tk>QAQW;t!5 z9doQTX{#4+eVFv8JEJmXo4_m*QoaV&c~>7h_HknL_p>^tLdm~ku{qL{MvYGm%!?&3r$+j}d> zHmx}<+{euU(6E2UZhN&kg!cQ6ld3&mtW)YD{8g<^B$u9vjZy2P!{X`>;VV)W??|sn#Ib$vEHtLZhE|tN zN9SSUT&^Tq%;Co&^o@b;(jyG_$2mE}QgNq-WZ9=1;Ieb{ch*^}eWGfHtWCE$Uu(`A z(`b7Bd0oUk@xdE&jKrf}J2$N4xw6Ab!E)7{z9A0;=oiE1vsJkYy{9nQ3Vyv_53d0Q z2Tt@eqT9_yW8l8S;KMh##bW+9Cs^@ff&X?$=hwutVAMxmcWl_cQ8(94Pha!d#DHm3^`69H+*ZZGX2RdUA`}p?ToO8fuK{VvTy>RA=^_EZl61VxkKk01$ z5v)Mj*%?!b-$8K!tV|65OZe{6nfi}x)pJs_%h(px9NP4lEAYWg z&!E5RPYyW-$StQ>(>aW@o=&_BeDo~t7<)&*h#Vpr!75-qQlN$($Ev{leUcrK@eKxU zerl(zeUcRwhEL&LXyBjG%wFI15>Bj>eT#l?(DgVUS^e8t;>K3eAM&_T z@fZFJ{x`d4$~#Paj)SU5)OhIM{GuLY<-%ugVQ~II4-yDwg*{T@Ch;k;l3q|fcK~=i zZaA1KY6rIuzg6iQ7W$tlgAKIdsabHUF61TYXi>XUY$2LCVZ^Ovgx3@m&P&XURv~+k zc)MPvkY4ZoQ0&5u(|}p8Zt%fjaWXKXtiNZpmi?CRXnsgqwn&xrIm5(AH`Y*}@bDOW zd9XONS8upD7`Ue{g|Ihg7$}#~+W@Gr@c~~cCa*UPV0~gJb8K3`zzoNCh(7LN%2^M=zwr`*cbx? zw@ES-qh5YbBylu;JGiuN$SMz@GVLT2|*yeEMI z%ycC(Ra_+#jiR``FW0hj+MzdcDJxNG4F+}fdAK5;vn8RJSlv;zn6j(WlNlf;$6If@s=r`8MI8H zJD*n^vkftyzP{{n^5j%J;4R09dy!RiW?m>L?&+WgB)vIWc=k5MS z_U9P%f-AFMN5K9C;Zi7!8@_j6)=(-?CK@Q5RA&yUUec<*A3!tgy;5sIqR-EMpGZJWpG5vQABOZ8&0DKtT*S>dMCfY*Y0?ETgsNuwIzCo zz7X(<@97)UvjCPsP)t35m&bY*fh2;$*Tn^D=~n|a<(0B8^Yyj`_JqYdb0@O&jly9)U;{K9u+v=7XwHw?$aD8Cq=)+A9bj020*6CD;9I?^bp)lx- zXNa=ZVqgQ|CYyT8GpaV`8c7^9izDxdii?!T+;J>y+8QsSoyf12B$`=-R1cq z*D^e#|A$AxO^=Kg_I)s>9CNpO4d=ah#OFJCa5`#u+j!&lX#3*&djIO+lL!2KU*`h8 zCuVNGUte+oUmGVM6RCv0-)9hPuc0?RfR~cko=w1eeP}t{{?7H!he@pQNuyzL@^{;c zaOg-#bsDc4Rx?hZk@RC{N3Qzj1kBZH-j`etVDrZ;b{HJV2Uytb_zcXLa?nKZI@}z> zadTpZD-qHH%22eE0F}>(dm%iXU?Wcj3UtA3w$KY?eqJriX#V>wRF4M&0GUexqMnWf?-8OCL6dS&tveChA^QMbT@3p;woWTvk=raR|Y`x{1`quAiho~R53CtzLJ1rW|H;)6!w)dbwusf#T|;rUvi4fb z@cTCiIkr?1^B{aULl)do`cbL}4>cqv&Vh^h!T6nW5M5*liYoP;01^I5R7WQN4w4Gv zF*|9Fh;}+_QZ%&(E8XXpHA%;CZ1p?pxMvR)E%drd(p>^#FPFVuOnf6HcdppyGx!FH zYOQQaZ90=mC14>%$P$81Xhic_WnA!-G(Z$_8Y5$9o*HrH7{O#Jw5+Wy5lUzAApU3w zHPVfRk7{SGV?=|(kQzEHYAvuui?cpsS?-?%A9B1+6B|cLf{KNkr_9#$mp{=_j+2Bn zH=41FEzSPy`aFv;;voxbSF~lnO&(b0XqeFs?xbu%1mf7Mq&!!}d7Cg7$T@a?-SNa{ ztWN@esk=}v1EW*o+7kC(;z*swF`2Ws*t3$G-kg%7|Jly5iMhx@efX;)^=+fxN+!XzwE zK0Y>%6fnf*?k!kTZb3jY$k65L^PBfQHk*iw{L1(gZs%5Z#mDr?-$Bb4f8aQY94(rn zvv1-x(>0yLFvbTfGJLxU{dluX9@zlv8}rC6ZZQU$Hv%tq7 zv^WAa(@#v;x6I(xtprB?~akU9PMWnYkNXy6x#{ByU_ZRhbRy{{BID98(E zbgQJ>>+gIVp6sRW;V!P}NolUsZB~4dR|iAtAHU8+;sV1_-z?d+!PCY0v7ajZSCV1d zo?Rd2R0MZMp;R>JSj3wic}Jwm;vGHfO2Bstyt+~;+wYX?+(&H@W3)z(evoV5Wrqjc zkMxG2>B*4`#Ac3;feChj4#f!W$yH3BS#@=)}TB z^m~FgF?NkNL=;09SASNb+`L^`*rGUji@!@ao71D}bVbdh`#0DU>n^bo8MIIRv-E#S zTEb>sY0{F5=XMFM&=k?xk7}A}@Y(~87>*vmiodK$UK`;z<3r+C@1>t%B&2V zf3hy0Ao&)jqogP1xRF9lTAi<~G&SCYOZ`Cx%ym!4M(K8d{w$oI;-jsuuHnb4J4)(J zHe%6O^UJ_1R{7{^OxpKGrwsV^F>U7cTo9a9r`P2G*&=r`6K9_hFm1@eTI$R2ksS}E zc2%kgYQ9IXwvAt|et0Zdv$}I-mA&>Z76GSe{_9O39$6;stUk--Tb!b--(5e8hwKx~ zBQ4}@Sy}{l|_XLe+nXd;@)4$9i5m0X);46xHb~Wr6ILl%*76A21=sN-14_X^R4rRYvHQ- z1fM|D&DrBVNGv4%DdIpHEVkZ7Lx4c_w}4EAzG^42)nye+Pj1xQ@OR6WH!&>O?3y8NP{7 z4%$d`T))7?{Ls^gs__2646c-++PLAon@dAj>i7$eN86{lsCsW8%l6m-Di$m!1&>2M zY}t9YYEzLD%Ba94!$@qGYa(WMV7J>k%rx;KM^5s1GDyl@#JKNP! zoDI!-qvS#Hl9bdsTcXj;ZkUzjOzR%ZhjEWguC&o%-?PGVd5{Yt$E}6#zaxnJYskxa zEuh$Gk@m|3*Y+8}w{@*SzG=tmO`L;9F0+(5B`;$z9Xttni@K1X`YTUzty;hYySQn?F*AttV9cdvfIbMeom`a{t-dIo*GR@M*fAr`17BQWfb8hqx zrfv#i-^T_~k#p=WJ-cOfD2i3akG3c`@msyG)gQQoXc-s)FL+TNj5l=>O;6G!@$-FY zsY6rFT-js%rM`da-xw?!>c`T1_;D!zW^NKr;nXbDM${}RM-K8hoI|)Q&`trRes^8}8 z5#+}^_?U{oos;(Cl~k|NF%m1zVfmwIjIgfq=2iFzT!Axaie7pDFy9YV8^=gdc4B4Z z95qvl*CZ`SH~TBBKSZs=>$oT9tT{#mfFAmCV(V#V9?`lMT^;L&)Wt4d;49HEC9!2l z?k37J;umM9m;4%X(O>P1?gzzR)*kZt8dVbYTbbSrVhZ1^SKPSX7hqxohMEpxjM#3< z%!k2(iu0CyoH96T-Mwoy-hSf26L?P`h zQTalM_LS+;qmF+|5sx(CUgI`+0O{w1zAH6=0B<9cGy63yo5N~mYuGWEvvX+n1D(BJ zqPxhLS&@ND-t}?#nx3EjP4vC9L1 z@8k74vhvs(Wb>=clL6oJ298TA6H>PSNJ#rjo2DWLk4q|}R0R-eVv51o5If1)39byC-z^HEdO&LSb7_lY5l4G|TSMQ6GaTvF&M93sd{`UdOkkwM z7lxKSJ~S}gTcqra>{Ey@XpkpQ{CwI5ES{!S_U47NouAyLO1N{U2+eaitw3^+`?$SM8%>gm!{PRpZ<_txyq>RC#9Wsz%_(LT+Uh6TGetc>S~ZG3MYMO($v*)BDnj*kBKrkqZ2h>HGX}s(dFUqnse_CP+%??27QQW@f1k9AgFNS zFmvEt=yR_!P0ZnzdAjV!$FVo$$-!3TT^Ah}L_wi!50C1$47$D5seFI3_OwXireqR! zdR~{e9#pxNV|P&+iL#5%o5&$cPGmXzJh(wdUljxt)%} zmsY?EBIgE4t2PMyu=Lswa$LcuYWUQwx(t{AcIc^SK*hh_I|w&N+BJU~_lVtiooo zY9DPO0W&cp+fi76S=t^aXJZih=+5Zdz-V*TPc3^d6f-}PSvt+pKb?FqDb*7`eKcOs zQI#NlJ$OsUw=BcIf(*N9nW0Ts;*hmmLiY=%aZ08M9jV{`SvGGgyEMBWX_AoI_WZr@ zDn!z0oGJ~(i2=H@2lCA=#JSfN zlg81SwdCunuSdkER&ybmXGu5kt5+8hanL3P3och9cZ!RnA~*p|R(R;J&EHo4Bi4ms znlbq#f;WrkDS8cUrn$#ahVR(LrK?RTb%vzQRMnbEM}vP9D2q1$>rW=A<9NGlsG#wJ z_mu|W`gsaJ&GOpO-0hyU*C^lMbT*f){ZEz#UR zblZjIyqnUeHL;0?0OH*;%_8V1sbo+88F#_TjYq*Z!d}Wqj4@F!3U<+{MPc&dq-alS z5Z>8{3`@vG4vy1~Kxu*6$WA(h`gD$d`HkW`#HLw!mUYam!$(UsAMDX@>I!G4X-S4z zH*1!j$|Q=^B;N_fiGX*UNd>xqm-|Iilk);??C6&(;9nq6hW&C9^kz8a=ieiYz?UbH z!4)Q??Dyr_cNO;Oham&{B% zzGCGw>i$gcAZ0W1PE2RbT1P&V>5~H*N0e;o*n=8JPt@s?A2p6nDbu~+E1JJ)mhRYS z33lvCP80zF*h4&#Gq{xR*Ppk_3W8p)eE%gcb8}zJ=KH4e=2kWDnUqf4GMx?T*u8I3 zcns2BMyQ%G(M+#w&|W_KGLs>ke%oBpT%=e!p~{p#TCR0bGs8xZep^tk#ZWiH3zyD| zTCSB-tD)ScVE=_F{h(ZHx>7^AR)MyLDP5#ei=!0Ks7O{o(q&3dsnBvQ)i6#(7tW8+ z&X33_8?^6IH7HD}uWDYcoe}(|aMH_E8l`Ozp<(B!Tns-m%b!|EVaQvIU zuNwuL#+~&KTUlU+M31bk-nNQPIBxz)E1|0<(?!Kn(y=jQR>C3Ga!EXBjZESTv?D(e z^>YBq%S*|s6Lam~si){&V;)An&+fd;`wsNd0spT0%YI#OS?M?7Q6p;mc5$)pTUF1KZD2*;s0Gx9qkBVYG94m4@@%ir2NgMP_ib8dJcy#ZrQW zN9(UPolT0WuTj}Bv~|$Z-xx-F={|s zHRCl|g39ltKNC8woLa5CKTUEpyiHL9mEraaH%O;VSpElTy|370-hLpP$A8?P-nnJMT_E7Y^9sB=_q=)> zBb32s5?&zCwjy64Ji%vDMZMnA!w|gTGvQ$fbgct-2%7Mj`FF3V&;tTG92`%p>?6V| z-2aA#w8}jrXu-kpXNv0~3bro3ARrSazopAWAVAcIt0>}Lym!KOKL~A(S z|HA34h%xxDdLi0ODRD$iILLp|8F54`A~??f7^G(+=^=iA<8FQZG6l+iCZo#?H$~)v z=Ms1&YDlnNg^BDkc5dz-lmZa`|4=p{K!A_;{|b%18Q43o$Phhub%$(xEO8OI7^a47 zE_tT%+J*4{KFyd%k%od;L8D2#EiZS%-V}J!$>{0S$h)MIs{-!tZP?fueeE!GP|q>! zF{q?^)1|5~dIPoy*~6qw+a&no?RDfV(5a-0U!4Gm3@NY zuG;(wj@-<-Wxi&1r)5@KSl?PUmCW(`(I?wO!o-I;JS=<&LP{=ms5Ra3LFRw z)f(9RJ=;GmEG95&S>LdFf^_V%tmANQU3NxEbY1N2+L_uynfk<+%)=LxiU|A6HJytD znu##W`}#qXb{H(cCiXue0qhvXp%E_RcW&{-hhmDQcHSP%Y9yDIV0JJ{M{rk)#ZQH3 zY{r8?D(R&xBSeJnyVJT9pG@@OX-VUeJi@2sdK*A)cqh~9H@$8%@~)UPLmItkDcfb{ zwPCuYxZH{A2+Ti%bHhjRWEAK>NxCI~kVMBII6MRv%~~ZuXpbKPwTp7KOE@09QxPKr z7Q}?%Dh$OuEex_bec6oOhpvlv%!Fm5fT(JB0Z*RB0WLweLK1-EOz8D$!;$G3Mv{n= zBQ&t^cP4Bbt|u^CcB>Xs!=#54lEs=Q9_mvSVvIMCE&uK#QyU4rSG!*-WQYL0wI?59%r>))X!~z(fDE;a-*3r>PLt~0k#(;WEAxf zWlV;ZMPP}7^o)^x|9a4mP=l`q=Pm6M;D&snO^|ynHl12d=iKQ zvG_hC(NX zD(y_bZ5eZDhIP-Q)$PZ^XI?WL;dqi5v%f_>F)ged^~*zMq+YfS=ho73a^(b2a(rWr z8l1QlEQ6n#a1+B8X-yK=P78by6CM#0HxU!x0_U)ERL%R-PC{B7Ak7=$2`Xq)Ivx^L zMN>L0h4(~)?|5^zZ9mkwgx1tMG7l7Df`0h{#o_XIA5uNHE9O_kI?~i~Pv>;+4y#rU zrYbVJKJJiZws_~UT{`?Ba?JF2@5vqs?>8Md_k%R9FmmrtUVR$@awfyh>^EKE0$kcK}{%>#05a&ZTk!hCdTq+a6CBxB6Kj*wQj%Y8c!lBeo<^B3NA!Kos zfdAW7GsKnnDZ`>;9g$>V!phScwL9G1`jyX4VbWiz)zCRkTNdIflwbnFut8o5l5Y1{ ziU7k{M&9?^^&6v=oz*k_qqw_*8LL)ggNrnp#n)zO5?J_YzS zvujx3%^~ zpHfn7rsLUTzP&KrfK8}y<_A%dDXh-u?4dl#L2sS2r5rs$ zDC4wcj7{LE00TYt1;8oN#ZcwgeH*&Z!1%NNMC{)@8;X^?xzRf8u$z z@DhFgdtbMGhu<4!9n-X-s!Ahk9eY3TV_abLOs*N>Yu>ge^YbG6Z$iU`3QeBL+TwLA z8k+7Ug_p2lBD|+5+gHHz-buULPj6#l-;5Khiv%dY(9yd)=|9jOoN8yRoBZW_{^`qJ z-OevLG+v4uPWE9jp|mYM2W)MA#>^eqUnf^ZG%V{zT;WC?BEqT#Yp~}b*`%z({l;}uWjb=*2y!d7T&}3F<#qn`$?}|TH*S!fo}A2i zS!RSM2B)Y*A4U_r4j+j1gn~FWE9Gmjz^lY zSM7r}^}Hl23gl`Kwnf8wP5P_DfzLW#B}eveyW-{_Mrd6sCqJ= z?ffe^=(^2dv7q3WC|h62(3!(G6Onu)CF1wTvsd$O2JAN1E2#&XX@^a)9Y(K??(Qm4 z&A~~v@V778KR&bA+%aVK>+Wkg8~rG|c=Pja%l~fe?!J@xXY71feYGZSyv9Ez7tM9C zs(-)~JHJ}Xs%Vu>YeDhU;QQ7K8_l#GgOyLPw4Z;uSbbe(oV5gmOLsmUtKWn6CLP>3 zx_?w*=$LH8JNGXd8gUhh7KZ>KOmpV==$n- zDC|HdkRbf(V0FQ z@KzTU#BNP?jeX`~!*gbNWZDL~KvVgwGx^R@*KaOdSR8%)?%O3nK|-$i#wJgoEZtUpuer=VB2j_~eC7SdUlX>mj+4eS1Q#yAAgtplc0AJOIpyo~01KR{p}U#= zLJaZ&`3)(XZ68-eSL`!aAx}ZKNq{(g>%;b?qk;>J>1mn0%SgX=A+umeQR{9m_Ohj$ zW9Rr_te`F*0Pe5$f!wp?et;_8LDDgIeB6xOq+W+tO%#&u+t-q9K8D_basPKm$1hjr zXoiImnnHvBwl>x6yAj)q(ad?-%GAY@`)aSh=k*FKr@#RBS!Uk+VN_yAYVOD!ZRJF! z&stD7(xFT+Ol_l~n;tYQZlGe@KiM5*~uYa=$rr5I4*sicc?{To|fOg zB`f6@mNP~_>aD;pW16GK-pm%qH;%d0KQiy!E%U{l(I7YdZ$!1k%JU;V!S%3cONH+E? zQui<5YWGfr_*~#l_(I^|8_U?k&1g%mg1yM3eQT%ZVhaEsA`1x}CAJW>Hvg)ZQD^nd z&`H0=%C~jV%d@i6=^w_OJffq=R5-;F+y^NAXmym!70+1-`3wjb68#}_ldbGKjK zy;=TV)46Y=4XJgTcQwH`D-o#;!F!vHWnirh1-RQ5SxuDod4mJx$k?P}8SM8#OpKY| z;MjN~*?2-*wBThUwAaffeNLGsYmmTSTeLg9XyavGJE&RPgXXb7@`jHBm==&sQ`3@# z@K1s&5enrQC+aJbc6%PsBellHI+79yVzBJi3u%Ud(Wa=G#(UFH^3JRZrgQq?b0)^ z(XR(wUO8WyR%xdJ?MsSdzelkzgDW>$Gdna_%BAuYDThDxX|INH z=<4V$=&p8J>3pTO>8}b@>3o%n?T_#qe$eH7k1$%CFM~clW`EBxT3oV$;t-}TCZG5{ zf=(L5Ptk^ze_{9`tMhFuHrUK%P1DJEz6%PUfZF78m9WNP6Mo>E1J}t%MCSp(BG_3j zWPpv8choRt3!3@-3w$9b6J_k$4;BPuV%9|9xb0}sh#n_{Qk#?Gh&q4w=QVE#v%2O7 zK^E6Y8dhSI*uWNpFK0M(4e~?L&&Fx;pcF_$_rGv?Bk&gV-_tsn0*pV9Y)E$`#;FeK zwx>Ii!(*K60dl_J%ct>E1p{<_Nh1EY?U*1m3wEU_1FM9PiI%Qfb$DU zk=PFQ+x|DFi3kJjrZx{KGX-FkZ;A4&zup9$cY68%$nhh0q6bJ7@*?l`K7DYZ7w#|j zHm5qL+K_yv^`wNkvh=$N1=Am)^=*O6{&43Hy2mB-1cG_~64+mome5q3{1I5NK*yE+ayF0RsCj>(P&|tkIgAvprx)HSe{LYY z9TJHnvOta4rUHcGWL??U2QGauE;fY&KBbdH;549DUE&g%^NaM71;l(SxABKp89)t4 zKxsf!pg}(D4vtQN`pS(G?4KW$o>*KzMng| z1mJ_>iPdE_|KOSQ+6Uay9UF@Q7^bH&Yg0I#vnAbHIH%DsOBl|xO;Lh~!#l>`6a5Iw zieG+e|3!UYV$y!JBm=cTNy#+g$*bTh|Dk?i)Pel^`;Fo{&c9uGBMR5!`_dhQPT~XA z>n7m%qw-mfOWQ!YTOa4R>Pj;IpqmWZ0|BbW92x4JQwSf-=uah7dD(^uce~2FrAae( z6oi63^%Akl?-I0^M)>3hx%7ffIX+7UQm}7Ie~;_9t|wbeItD);mLjEB7++!WZnv6rAH4qT* ztlphii$l~=Pjf3Cs4zWWB8olG+lHw-Rbn4o%jBn{3l(JBkGc`CVTQdq9xHE>F8Fx! zQvQqgu^h%U`=jvD=N)YH0Qney@^wcY&idbC9pMPTHNeZ963 zDdGOJ)&>anPIKep26$H7I1NzWm>$}Bb2GfYM2rA=3ky`ZOOiEbh}ZT=J8b zZDA@6Nx8l};oK=)Nv5uJKlh#uOSFo=)mrK>)9mw-BUbp_E5cn|qxB b13*Z6%b6D*h!hxn`~tk_w6xNyGU)#Un-Om9 diff --git a/docs/manual/fr/user-manual.tex b/docs/manual/fr/user-manual.tex index f4586c39..25dde525 100644 --- a/docs/manual/fr/user-manual.tex +++ b/docs/manual/fr/user-manual.tex @@ -961,7 +961,9 @@ \subsection{Enregistrer et annuler un règlement}\label{sec:reglement-client} \begin{itemize} \item \textbf{la facture a été créditée} par un avoir~: le règlement est alors un paiement - \emph{à lettrer} à une autre facture --- il ne s'annule pas~; + \emph{à lettrer} à une autre facture --- il ne s'annule pas. Ce cas ne se présente plus + que sur une facture créditée \emph{avant} la version 0.12.1, ou restaurée d'une sauvegarde + antérieure~: un avoir est désormais refusé sur une facture réglée (\S\ref{sec:avoirs})~; \item \textbf{l'exercice du règlement est clôturé}~: un administrateur doit d'abord \textbf{rouvrir} l'exercice~; \item \textbf{le règlement est rapproché} d'une transaction bancaire~: il faut d'abord @@ -1016,6 +1018,16 @@ \subsection{Avoirs et notes de crédit} L'avoir est consultable dans \emph{Avoirs} (menu \emph{Quotidien}), avec un \textbf{PDF « Avoir »} téléchargeable (mention « Avoir », référence à la facture d'origine, sans section QR de paiement). Le \emph{Décompte TVA} de la période tient compte de l'annulation (la TVA de la facture annulée est exclue). +\begin{keshwarning} +\textbf{Avoir refusé : « la facture porte un règlement ».} Un avoir annule \emph{toute} la facture. Si +le client en a déjà réglé une partie, l'avoir lui laisserait un crédit du montant encaissé, que +Kesh ne sait encore ni afficher, ni rembourser, ni imputer sur une autre facture. L'avoir est donc +refusé dès qu'un règlement existe --- même partiel ---, et Kesh le dit dans le dialogue. +\textbf{Annulez d'abord le règlement} depuis la fiche de la facture (\S\ref{sec:reglement-client}), +puis émettez l'avoir. Si le client ne paiera simplement pas le solde, ce n'est pas un avoir qu'il +faut, mais passer le reste en perte --- fonction prévue. +\end{keshwarning} + \begin{keshwarning} \textbf{Avoir refusé : « compte de produit archivé ».} Si une ligne de la facture impute un compte de produit qui a été \textbf{archivé depuis la validation}, Kesh refuse d'émettre l'avoir et nomme la ligne et le compte concernés. @@ -1037,7 +1049,7 @@ \subsection{Avoirs et notes de crédit} \end{keshwarning} \begin{keshnote} -En version actuelle, l'avoir est \textbf{total} (il annule la facture entière). L'avoir \emph{partiel} et le remboursement d'une facture \emph{déjà encaissée} (un avoir est refusé sur une facture marquée payée) sont prévus pour une version ultérieure. Les paiements sortants par fichier \texttt{pain.001.001.09} sont, eux, \textbf{disponibles} (cf. \S\ref{sec:paiements-fournisseurs}). +En version actuelle, l'avoir est \textbf{total} (il annule la facture entière). L'avoir \emph{partiel} et le remboursement d'une facture \emph{déjà encaissée} (un avoir est refusé sur une facture \textbf{réglée, même en partie}) sont prévus pour une version ultérieure. Les paiements sortants par fichier \texttt{pain.001.001.09} sont, eux, \textbf{disponibles} (cf. \S\ref{sec:paiements-fournisseurs}). \end{keshnote} \subsection{Supprimer ou dévalider une facture} @@ -1453,7 +1465,8 @@ \subsection{Annuler un rapprochement}\label{sec:annuler-rapprochement} \begin{itemize} \item \textbf{la facture a été créditée} par un avoir~: son règlement est un paiement - \emph{à lettrer}, il ne s'annule pas~; + \emph{à lettrer}, il ne s'annule pas --- cas hérité d'une version antérieure à la 0.12.1 + ou d'une sauvegarde restaurée~; \item \textbf{l'exercice de l'écriture du rapprochement est clôturé}~: un administrateur doit d'abord le rouvrir. ⚠️ C'est l'exercice du \textbf{paiement} qui compte, jamais celui de la facture~: une facture de décembre dont l'exercice est clôturé, payée en @@ -2070,7 +2083,7 @@ \subsection{Nouveautés et limites (v0.3.0)} \begin{itemize} \item Mono-devise CHF (multi-devises prévu pour une version ultérieure). \item Décompte TVA au format officiel AFC / e-décompte ESTV (les montants du décompte Kesh sont à reporter manuellement sur le formulaire AFC). - \item Avoir \emph{partiel} et avoir sur facture \emph{déjà encaissée} (l'avoir total sur facture non payée est, lui, disponible — cf. \S\ref{sec:avoirs}). + \item Avoir \emph{partiel} et avoir sur facture \emph{déjà encaissée}, même en partie (l'avoir total sur une facture sans aucun règlement est, lui, disponible — cf. \S\ref{sec:avoirs}). \item Sélecteur de langue dans l'interface (la langue est fixée à l'onboarding ; le changement en cours de session est prévu pour une version ultérieure). \item Pas de signature électronique qualifiée (QES) sur les exports (verdict (b) de la recherche réglementaire, prévu Epic 14). \item Format PDF standard, non PDF/A (migration prévue selon ISO 19005). diff --git a/frontend/src/routes/(app)/invoices/[id]/+page.svelte b/frontend/src/routes/(app)/invoices/[id]/+page.svelte index fb992f87..c4c2ce6a 100644 --- a/frontend/src/routes/(app)/invoices/[id]/+page.svelte +++ b/frontend/src/routes/(app)/invoices/[id]/+page.svelte @@ -833,15 +833,26 @@ Voir l'écriture comptable {/if} - {#if canManage && !invoice.paidAt} - {/if} {#if canManage && !invoice.paidAt} + à un rappel) : les boutons Suspendre/Reprendre sont masqués (review + P1 BH1). ⚠️ « Créer un avoir » ne l'est plus depuis la Story 25-4-a : + il s'affiche, et le serveur refuse avec le motif nommé. --> {#if invoice.dunningPausedAt === null} - diff --git a/frontend/src/routes/(app)/invoices/[id]/invoice-settlements-page.test.ts b/frontend/src/routes/(app)/invoices/[id]/invoice-settlements-page.test.ts index 52f6cf4e..d707f4d2 100644 --- a/frontend/src/routes/(app)/invoices/[id]/invoice-settlements-page.test.ts +++ b/frontend/src/routes/(app)/invoices/[id]/invoice-settlements-page.test.ts @@ -1,5 +1,6 @@ /** - * La fiche facture, côté annulation d'un règlement — Story 25-3-a-1 (#414). + * La fiche facture, côté annulation d'un règlement — Story 25-3-a-1 (#414) — + * et côté avoir sur une facture réglée — Story 25-4-a (#456). * * Ce que le composant de liste ne peut pas prouver seul : que la fiche * **confirme** avant d'envoyer, **relit** facture et règlements après succès, et @@ -70,8 +71,9 @@ vi.mock("$lib/features/reminders/reminders.api", () => ({ vi.mock("$lib/features/projects/projects.api", () => ({ listProjects: vi.fn(async () => []), })); +const createCreditNoteMock = vi.fn(); vi.mock("$lib/features/credit-notes/credit-notes.api", () => ({ - createCreditNote: vi.fn(), + createCreditNote: (req: unknown) => createCreditNoteMock(req), })); import Page from "./+page.svelte"; @@ -176,3 +178,35 @@ describe("fiche facture — annuler un règlement", () => { expect(await findByText("F-2026-005", { exact: false })).toBeTruthy(); }); }); + +describe("fiche facture — créer un avoir sur une facture réglée (Story 25-4-a)", () => { + it("le bouton s'affiche sur une facture ENTIÈREMENT payée (mutation m6 : `!invoice.paidAt` remis)", async () => { + const { findByTestId } = render(Page); + expect(await findByTestId("invoice-credit-note-button")).toBeTruthy(); + }); + + it("le bouton s'affiche sur une facture réglée EN PARTIE, sans `paidAt`", async () => { + getInvoiceMock.mockResolvedValue( + invoice({ paidAt: null, amountSettled: "40.00", amountDue: "60.00" }), + ); + const { findByTestId } = render(Page); + expect(await findByTestId("invoice-credit-note-button")).toBeTruthy(); + }); + + it("le refus du serveur s'affiche DANS le dialogue, avec son motif (le serveur tranche, l'écran le dit)", async () => { + createCreditNoteMock.mockRejectedValue({ + code: "CREDIT_NOTE_INVOICE_SETTLED", + message: + "Cette facture porte un règlement, même partiel : annulez-le d'abord pour pouvoir émettre un avoir. (F-2026-005)", + status: 409, + }); + const { findByTestId, getByTestId } = render(Page); + + await fireEvent.click(await findByTestId("invoice-credit-note-button")); + await fireEvent.click(getByTestId("invoice-credit-note-confirm")); + + await waitFor(() => expect(createCreditNoteMock).toHaveBeenCalledTimes(1)); + const erreur = await findByTestId("invoice-credit-note-error"); + expect(erreur.textContent).toContain("annulez-le d'abord"); + }); +}); From 21ee2648d2bc1ed8b0003cf0690fd83a503e19d2 Mon Sep 17 00:00:00 2001 From: Guy Corbaz Date: Sun, 27 Sep 2026 01:02:32 +0200 Subject: [PATCH 17/18] docs(25-4-a): prompt de la revue de code P1 (refs #455, refs #456) Co-Authored-By: Claude Opus 5.5 --- .../25-4-a-review-prompt-p1.md | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 _bmad-output/implementation-artifacts/25-4-a-review-prompt-p1.md diff --git a/_bmad-output/implementation-artifacts/25-4-a-review-prompt-p1.md b/_bmad-output/implementation-artifacts/25-4-a-review-prompt-p1.md new file mode 100644 index 00000000..f19f3516 --- /dev/null +++ b/_bmad-output/implementation-artifacts/25-4-a-review-prompt-p1.md @@ -0,0 +1,51 @@ +# Prompt — revue de code P1, Story 25-4-a (le résiduel juste) + +*Versionné le 2026-09-27. **Une lentille** (Sonnet), contexte frais.* + +Dépôt `/home/gcorbaz/devel/kesh`, branche `story/25-4-propager-le-residuel`. Diff à relire : +`git diff ab9b9448 9bf2bb2b` (commit de dev, après la validation close). Fiche : +`_bmad-output/implementation-artifacts/25-4-a-residuel-juste.md` (AC, Dev Notes, Dev Agent Record). +Issues : `gh issue view 455`, `gh issue view 456`. Règles : `CLAUDE.md`. **Arbitrages de Guy** (fiche +§ Arbitrage, fiche mère `25-4-propager-le-residuel.md`) : ne pas les contester. + +## Axes — tous obligatoires + +1. **SQL** : les quatre constantes produites par `line_ttc_sql!` (`invoices.rs`, + `invoice_settlements.rs`) — développe-les à la main et vérifie le texte exact (espaces, alias, + `\` de continuation dans un `concat!`), l'égalité **octet pour octet** des deux constantes TTC + facture avec leur forme d'avant (`git show ab9b9448:crates/kesh-db/src/repositories/invoices.rs`), + et l'agrégation de la forme jointe de l'avoir (GROUP BY, jointure, filtre `issued`). +2. **La garde** (`credit_notes.rs`) : ordre verrou → lecture verrouillante, scoping `company_id`, + chemins d'erreur, rien écrit en cas de refus. Les tests prouvent-ils ce qu'ils annoncent, ou + peuvent-ils passer à vide (surtout l'entrelacement : que se passe-t-il si + `attendre_une_requete_en_cours` voit une AUTRE requête que celle de l'avoir) ? +3. **Tous les lecteurs** des grandeurs changées : `grep -rn "INVOICE_CREDITED\|amount_due\|INVOICE_TTC" crates/` + — un appelant change-t-il de valeur sans test ? Et tout appelant de `create_credit_note`, ou qui + matchait `IllegalStateTransition` pour le cas « payée » (API, frontend, tests) ? +4. **La fixture `monter`** : le gabarit tient-il pour les quinze cas ? Un motif lit-il la facture + auxiliaire ? +5. **Le manuel dit-il vrai ?** `docs/manual/fr/user-manual.tex` — les passages modifiés (§ Avoirs, les + deux limites, les deux « cas hérité ») contre le code ; **le PDF aplati** + (`pdftotext docs/manual/fr/user-manual.pdf - | tr '\n' ' ' | tr -s ' '`, vers + `/tmp/claude-1000/-home-gcorbaz-devel-kesh/ca5ce2e2-67a3-4eeb-817f-c2de35620a1c/scratchpad/`), + `??` compris. Le renvoi `\S\ref{sec:reglement-client}` pointe-t-il où il faut ? Le CHANGELOG + décrit-il juste (l'API, le bouton) ? +6. **i18n** : la clé ×4 — sens, registre du glossaire (`docs/i18n-glossaire.md` § Registre), terme + « avoir » du glossaire. +7. **Frontend** : le bouton, les commentaires, les `data-testid` ; les Vitest tuent-ils vraiment m6 ? +8. **Dev Agent Record** : chaque décompte (tests, mutations, gates) **recompté** depuis la source. + +## Ce que tu rends + +- **Findings** : sévérité (CRITICAL / HIGH / MEDIUM / LOW), fichier:ligne, **preuve** (commande et + résultat, code lu), correction proposée. +- ⛔ **La liste des axes réellement exercés ET de ceux qui ne l'ont pas été.** + +## Interdits + +⛔ N'écris aucun fichier du dépôt ; aucune commande qui écrit dans le dépôt ou dans une base — +`scripts/prepare-release.sh`, `scripts/regen-test-schema.sh`, `scripts/install-hooks.sh`, +`scripts/test-fast.sh`, `scripts/mem-guard.sh`, `make`, `latexmk`, tout `git commit`/`push`/`add`/ +`stash`/`reset`/`rebase`/`checkout`/`switch`/`worktree`, `sqlx migrate`, `cargo test`/`nextest`, +`npm run`, `npx playwright`. Autorisés : lecture, `grep`, `git log`/`show`/`diff`, `gh issue view`, +`pdftotext` vers le scratchpad, `cargo check`, `cargo expand` s'il est installé. From 41e14e4c2a90d1e63d004904d6c73f5a5cba04eb Mon Sep 17 00:00:00 2001 From: Guy Corbaz Date: Sun, 27 Sep 2026 01:11:17 +0200 Subject: [PATCH 18/18] =?UTF-8?q?docs(25-4-a):=20revue=20P1=20=C3=A0=20z?= =?UTF-8?q?=C3=A9ro=20au-dessus=20de=20LOW=20=E2=80=94=20story=20done=20(r?= =?UTF-8?q?efs=20#455,=20refs=20#456)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- .../25-4-a-residuel-juste.md | 15 ++++++++++++--- .../implementation-artifacts/sprint-status.yaml | 2 +- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/_bmad-output/implementation-artifacts/25-4-a-residuel-juste.md b/_bmad-output/implementation-artifacts/25-4-a-residuel-juste.md index 82b5e340..0f17fadb 100644 --- a/_bmad-output/implementation-artifacts/25-4-a-residuel-juste.md +++ b/_bmad-output/implementation-artifacts/25-4-a-residuel-juste.md @@ -1,6 +1,6 @@ # Story 25.4-a : Le résiduel juste — l'avoir compté TTC, et refusé sur une facture réglée -Status: review +Status: done **Issues : [#455], [#456]**, qu'elle **ferme** : `closes #455, closes #456` dans le **titre ET le corps** de la PR (squash ; un `refs` partout laisserait les issues ouvertes sans signal). @@ -399,8 +399,8 @@ Claude Opus 5.5. précédence restent verts. Doc-comments de `InvoiceCredited` et du rang 1 : état hérité. - **Frontend** (AC 14) : condition `!invoice.paidAt` retirée du bouton, commentaires Suspendre/Reprendre corrigé ; `data-testid` sur le bouton, la confirmation et l'erreur du dialogue. -- **Textes** (AC 17) : manuel (refus nommé en § Avoirs, deux limites reformulées « réglée, même en - partie », deux motifs d'annulation de règlement précisés comme **cas hérité**), PDF régénéré et +- **Textes** (AC 17) : manuel (refus nommé en § Avoirs, deux limites corrigées pour couvrir le règlement + partiel — l'une « réglée, même en partie », l'autre « déjà encaissée, même en partie », deux motifs d'annulation de règlement précisés comme **cas hérité**), PDF régénéré et contrôlé aplati (0 `??`, six phrases de contrôle présentes, « marquée payée » absente) ; CHANGELOG *Fixed* ×2 (dont **l'API** pour #455) et *Changed* (le bouton). Grep large du symptôme « avoir sur facture payée » : aucun autre site. @@ -456,6 +456,15 @@ Claude Opus 5.5. ## Change Log +- **2026-09-27** — **revue de code P1** (Sonnet, diff `ab9b9448..9bf2bb2b`, prompt + `25-4-a-review-prompt-p1.md`) — **0 au-dessus de LOW, 1 LOW** : le compte rendu attribuait la + formule « réglée, même en partie » aux deux limites du manuel, la seconde dit « déjà encaissée, + même en partie » — compte rendu corrigé, le manuel était juste. Les huit axes exercés avec preuve : + les constantes TTC facture **octet pour octet identiques** à leur forme d'avant, l'entrelacement ne + peut pas passer à vide (le motif ne correspond qu'à la requête verrouillante de l'avoir), tous les + lecteurs d'`amount_due` inchangés et resserrés, PDF aplati, i18n, Vitest, décomptes recomptés. + Non exercé : la ré-exécution des gates et des mutations (interdite à la lentille). **Revue close en + une passe ; story `done`.** - **2026-09-27** — **dev** (Opus 5.5) : formule du TTC de ligne factorisée (`line_ttc_sql!`), avoir compté TTC, garde verrouillante de l'avoir sur facture réglée, variante `CREDIT_NOTE_INVOICE_SETTLED` ×4 locales, fixture `monter` par le gabarit, bouton aligné sur « Dévalider », manuel et CHANGELOG. diff --git a/_bmad-output/implementation-artifacts/sprint-status.yaml b/_bmad-output/implementation-artifacts/sprint-status.yaml index 7502347b..ea88fb5a 100644 --- a/_bmad-output/implementation-artifacts/sprint-status.yaml +++ b/_bmad-output/implementation-artifacts/sprint-status.yaml @@ -354,7 +354,7 @@ development_status: 25-3-c-annuler-facture-fournisseur: done # 2026-09-26 **REVUE CLOSE EN 1 PASSE** (0 > LOW, Sonnet x3) ; non poussee ; PR `closes #454` titre ET corps, sur accord. **IMPLEMENTEE** — gates 2472/2472, 783/783, E2E 220/10/19 sans regression ; prochaine etape : code review. # 2026-09-26 **SPEC VALIDEE en 2 passes** (3 MED -> 0 > LOW). **SPECIFIEE** — `25-3-c-annuler-facture-fournisseur.md`. Geste par le socle (autorite `SupplierPurchase`), queue commune sur l'ecriture d'ACHAT, lot en dernier rang ; payee : reglement DETACHE (colonnes remises a NULL, ecriture libre, lien garde par l'audit) — **Q1 tranchee par Guy le 2026-09-26**. Aucune migration. Prochaine etape : validate. # 2026-09-24 NEE d'un arbitrage de Guy — une facture fournisseur s'annule DANS TOUS LES CAS, sauf si l'exercice de son ecriture d'achat est CLOS (l'exercice, pas le verrou de periode) ; payee, son reglement RESTE au grand livre et REDEVIENT A LETTRER (Epic 15 : un paiement doit correspondre a une facture, au besoin creee pour lui — « comme bexio »). Reecrit `cancel`, donc absorbe [#454]. Derriere la 25-3-a. 25-3-b-annuler-rapprochement: done # 2026-09-25 DONE — revue close en passe 2 (Sonnet 1 MED -> Haiku 0) ; gates : backend 2463/2463, frontend 777/777, E2E sans regression. Non poussee. — SPEC VALIDEE (boucle close en passe 4 : 2 HIGH -> 2 MED -> 2 MED -> 3 LOW ; pas de decoupage, arbitrage de Guy). Q1-Q3 tranchees. — [#418] closes — annuler un rapprochement bancaire. ⛔ NE JAMAIS compter sur le `ON DELETE SET NULL` de `matched_entry_id` : supprimer l'ecriture ferait disparaitre le lien EN SILENCE, le mode d'echec meme que la story repare. ⚠️ Le chemin d'ACCEPTATION n'a aucune fonction `kesh-db` a mirer : il est ecrit en SQL brut a **cinq** sites de `routes/reconciliation.rs`, dont **un seul** porte le verrou optimiste. Quand la transaction porte une facture, appelle le geste de la 25-3-a. Derriere elle. 25-3-annuler-reglement-et-rapprochement: split # [#414] [#418] — **l'Epic 24 a livre l'encaissement et le rapprochement ; il n'a PAS livre le moyen de les DEFAIRE.** Une erreur d'imputation est aujourd'hui incorrigible autrement qu'a la main — et le gel de la 24-4b interdit precisement la main. ⚠️ *C'est le prix d'une story qui ferme une porte sans ouvrir la suivante : le gel est juste, et il rend cette story-ci necessaire.* - 25-4-a-residuel-juste: review # 2026-09-27 IMPLEMENTEE — 7/7 mutations tuees ; gates 2484/2484, 826/826, E2E 222/10/19 sans regression ; prochaine etape : code review. # 2026-09-26 [#455] [#456] — l'avoir soustrait HT d'un TTC (masque a l'ecran, mais rendu par GET /invoices/{id} aux cles API : amountDue = la TVA sur une facture creditee) ; un avoir accepte sur une facture partiellement reglee rend la creance creditrice. CREEE ; validation : P1 Sonnet 1H/3M/1L -> P2 Haiku 1M retenu -> P3 ciblee Opus 2M/5L -> P4 ciblee Sonnet 0 > LOW (1M reclasse LOW) : VALIDEE en 4 passes. Arbitrage 2026-09-27 : refus maintenu, levee tracee par #471. + 25-4-a-residuel-juste: done # 2026-09-27 REVUE CLOSE EN 1 PASSE (0 > LOW, 1 LOW corrige) ; rien de pousse — PR closes #455 #456 sur accord. # IMPLEMENTEE — 7/7 mutations tuees ; gates 2484/2484, 826/826, E2E 222/10/19 sans regression ; prochaine etape : code review. # 2026-09-26 [#455] [#456] — l'avoir soustrait HT d'un TTC (masque a l'ecran, mais rendu par GET /invoices/{id} aux cles API : amountDue = la TVA sur une facture creditee) ; un avoir accepte sur une facture partiellement reglee rend la creance creditrice. CREEE ; validation : P1 Sonnet 1H/3M/1L -> P2 Haiku 1M retenu -> P3 ciblee Opus 2M/5L -> P4 ciblee Sonnet 0 > LOW (1M reclasse LOW) : VALIDEE en 4 passes. Arbitrage 2026-09-27 : refus maintenu, levee tracee par #471. 25-4-b-residuel-aux-agregats: backlog # [#416] + montant des RELANCES (TTC complet, trouve a l'inventaire) — Q1 TRANCHEE 2026-09-27 : la QR du rappel porte le RESTE DU (= TTC sans reglement), reste a payer sur le PDF et dans le texte ; point ouvert : frais de rappel non comptabilises vs trop-percu. 25-4-c-residuel-au-rapprochement: backlog # [#420] — DEUX sites : filtre SQL des candidats (HAVING total_ttc) avant le score, puis score et re-score a l'acceptation. 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.