From b83ba97ea6b51a7954454a7b020f996222be88cf Mon Sep 17 00:00:00 2001 From: Guy Corbaz Date: Thu, 8 Oct 2026 23:22:25 +0200 Subject: [PATCH 01/10] =?UTF-8?q?chore(story/15-6a-avoir-credite-la-creanc?= =?UTF-8?q?e):=20planification=20report=C3=A9e=20sur=20main=20(fiches,=20p?= =?UTF-8?q?rompts,=20registre=20par=20union)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- .../15-6-creance-juste-avoir-reglement.md | 161 +++ ...6a-avoir-credite-la-creance-de-la-vente.md | 1180 ++++++++++++++++ .../15-6a-validate-prompt-p1.md | 34 + .../15-6a-validate-prompt-p2.md | 35 + .../15-6a-validate-prompt-p3.md | 35 + .../15-6a-validate-prompt-p4.md | 35 + .../15-6a-validate-prompt-p5.md | 35 + .../15-6a-validate-prompt-p6.md | 35 + .../15-6a-validate-prompt-p7.md | 35 + ...6b-contrepartie-distincte-de-la-creance.md | 1212 +++++++++++++++++ .../15-6b-validate-prompt-p1.md | 34 + .../15-6b-validate-prompt-p2.md | 35 + .../15-6b-validate-prompt-p3.md | 35 + .../15-6b-validate-prompt-p4.md | 35 + .../15-6b-validate-prompt-p5.md | 35 + .../15-6b-validate-prompt-p6.md | 35 + ...15-6c-configuration-sans-ecriture-nulle.md | 631 +++++++++ .../15-6c-validate-prompt-p1.md | 34 + .../15-6c-validate-prompt-p2.md | 35 + .../15-6c-validate-prompt-p3.md | 35 + .../15-6c-validate-prompt-p4-ciblee.md | 15 + ...-6d-contrepartie-distincte-de-la-banque.md | 485 +++++++ .../15-6d-validate-prompt-p1.md | 35 + .../15-6d-validate-prompt-p2.md | 35 + .../15-6d-validate-prompt-p3.md | 35 + .../epic-15-choix-autonomes.md | 749 ++++++++++ 26 files changed, 5095 insertions(+) create mode 100644 _bmad-output/implementation-artifacts/15-6-creance-juste-avoir-reglement.md create mode 100644 _bmad-output/implementation-artifacts/15-6a-avoir-credite-la-creance-de-la-vente.md create mode 100644 _bmad-output/implementation-artifacts/15-6a-validate-prompt-p1.md create mode 100644 _bmad-output/implementation-artifacts/15-6a-validate-prompt-p2.md create mode 100644 _bmad-output/implementation-artifacts/15-6a-validate-prompt-p3.md create mode 100644 _bmad-output/implementation-artifacts/15-6a-validate-prompt-p4.md create mode 100644 _bmad-output/implementation-artifacts/15-6a-validate-prompt-p5.md create mode 100644 _bmad-output/implementation-artifacts/15-6a-validate-prompt-p6.md create mode 100644 _bmad-output/implementation-artifacts/15-6a-validate-prompt-p7.md create mode 100644 _bmad-output/implementation-artifacts/15-6b-contrepartie-distincte-de-la-creance.md create mode 100644 _bmad-output/implementation-artifacts/15-6b-validate-prompt-p1.md create mode 100644 _bmad-output/implementation-artifacts/15-6b-validate-prompt-p2.md create mode 100644 _bmad-output/implementation-artifacts/15-6b-validate-prompt-p3.md create mode 100644 _bmad-output/implementation-artifacts/15-6b-validate-prompt-p4.md create mode 100644 _bmad-output/implementation-artifacts/15-6b-validate-prompt-p5.md create mode 100644 _bmad-output/implementation-artifacts/15-6b-validate-prompt-p6.md create mode 100644 _bmad-output/implementation-artifacts/15-6c-configuration-sans-ecriture-nulle.md create mode 100644 _bmad-output/implementation-artifacts/15-6c-validate-prompt-p1.md create mode 100644 _bmad-output/implementation-artifacts/15-6c-validate-prompt-p2.md create mode 100644 _bmad-output/implementation-artifacts/15-6c-validate-prompt-p3.md create mode 100644 _bmad-output/implementation-artifacts/15-6c-validate-prompt-p4-ciblee.md create mode 100644 _bmad-output/implementation-artifacts/15-6d-contrepartie-distincte-de-la-banque.md create mode 100644 _bmad-output/implementation-artifacts/15-6d-validate-prompt-p1.md create mode 100644 _bmad-output/implementation-artifacts/15-6d-validate-prompt-p2.md create mode 100644 _bmad-output/implementation-artifacts/15-6d-validate-prompt-p3.md diff --git a/_bmad-output/implementation-artifacts/15-6-creance-juste-avoir-reglement.md b/_bmad-output/implementation-artifacts/15-6-creance-juste-avoir-reglement.md new file mode 100644 index 00000000..d06106fd --- /dev/null +++ b/_bmad-output/implementation-artifacts/15-6-creance-juste-avoir-reglement.md @@ -0,0 +1,161 @@ +# Story 15.6 : La créance juste — l'avoir crédite le compte de la vente ; un règlement ne vise pas le compte qu'il solde + +## Status + +split + +⛔ **Fiche d'index — elle ne porte ni critères ni tâches.** La story a été découpée **avant** sa +première rédaction complète, parce qu'elle franchissait d'emblée le critère de périmètre de la +§ *Règle de splitting préventif* du `CLAUDE.md` (choix **C-15-6-1** de +`epic-15-choix-autonomes.md`) ; la validation P1 en a détaché une quatrième (choix **C-15-6-9**). Les +quatre fiches filles sont `ready-for-dev`. + +## Les quatre sous-stories + +| | fiche | ce qu'elle porte | issues | +|---|---|---|---| +| **15-6a** | `15-6a-avoir-credite-la-creance-de-la-vente.md` | L'avoir contre-passe la créance **et l'arrondi** sur les comptes que l'écriture de vente a mouvementés, non ceux des réglages du moment. Lecteur partagé du compte de créance (trois copies de la même requête remplacées) et lecteur recoupé de la ligne d'arrondi ; **tous** les comptes que l'avoir écrit (créance, arrondi, TVA due, produits) verrouillés **en partage, avant l'exercice**, pour la course de lecture (un seul helper de verrou de liste, partagé avec la 15-5d), un compte de créance, d'arrondi ou de TVA due archivé refusé en le nommant (`ACCOUNT_ARCHIVED`, code de la contre-passation, libellé « Impossible d'émettre l'avoir ») ; la route est rejouée sur interblocage **par la 15-5e** (C54). Inventaire des sites qui relisent un compte après la pièce ; cycles connus nommés, sans prétention d'absence (#536) ; TVA due de l'avoir en angle mort tracé (#525). Manuel (§ *Avoirs*), CHANGELOG `[0.13.0]`. | `closes #473, closes #523` | +| **15-6b** | `15-6b-contrepartie-distincte-de-la-creance.md` | Un règlement — client ou fournisseur, par virement ou par compte interne, manuel, par rapprochement ou **par lot de paiement** — refuse une contrepartie égale au compte qu'il solde — comptes d'arrondi et de solde du reste compris, comparés par identifiant : code `SETTLEMENT_COUNTERPARTY_IS_CLAIM_ACCOUNT`, `details.role` distinguant le compte choisi du compte désigné dans les réglages (deux remèdes, un message par rôle — six clés —, 4 locales ; le compte d'arrondi porte `rounding` quel que soit le geste) ; validation et avoir en angles morts écrits (#537, #525) ; helper commun dans `invoice_settlements` dont la 15-6d emprunte la comparaison ; trois clés plates pour les comptes désignés ; les lots refusent la facture **avant** de produire le pain.001, et un lot bloqué à la confirmation dit ses deux issues. Les deux écrans de règlement ne le proposent plus. Manuel, `docs/api-external.md`. | `refs #474` (angles morts : #537, #525) | +| **15-6c** | `15-6c-configuration-sans-ecriture-nulle.md` | La configuration ne prépare plus l'écriture nulle : le compte comptable d'un compte bancaire ne peut être le compte débiteurs ou créanciers des réglages, et réciproquement ; exemption « inchangé » ; contrôles dans la transaction, les deux gestes sérialisés (`LOCK IN SHARE MODE`). Écrans des comptes bancaires et des réglages (débiteurs, et créanciers depuis la 15-5d). Manuels, `docs/api-external.md`. | `closes #474` | +| **15-6d** | `15-6d-contrepartie-distincte-de-la-banque.md` | Le rapprochement manuel et l'acceptation par règle refusent une contrepartie égale au compte de la banque, avec le refus **et à la place** du flux ventilé existant ; une règle sur ce compte n'est plus proposée ; l'écran du rapprochement manuel ne la propose plus (filtre local) ; le chemin par règle contrôle aussi le compte de banque actif (même réponse sur les quatre chemins). Manuel, `docs/api-external.md`. | `closes #524` | + +⚠️ **Ordre** : 15-6a → 15-6b (mêmes fichiers : `invoice_settlements_write.rs`, `reconciliation.rs`). +La **15-6c ne commence qu'après le merge de la 15-5b**, qui modifie les mêmes fonctions de +`crates/kesh-api/src/routes/bank_accounts.rs` (`validate_journal_account_id`, création, remplacement, +lien), le dépôt `crates/kesh-db/src/repositories/bank_accounts.rs` et le PUT des réglages +(`company_invoice_settings.rs`, AC10, AC11 et AC19 de la 15-5b), et après la 15-6b (fonctions d'écran +partagées, `ClaimSide`) et après la **15-5d** (compte créanciers exposé à l'écran des réglages). La +**15-6d** se rebase après la 15-5b (mêmes fonctions ; plus de dépendance d'ordre des refus depuis +C-15-6-23), **passe après la 15-6b**, dont elle emprunte la comparaison du helper commun de +`invoice_settlements` (C-15-6-28), et gagne à passer après la **15-5c**, qui réécrit le manuel du +rapprochement. Les 15-6a et +15-6b se rebasent aussi après les **15-5a à 15-5e** (mêmes fonctions : `accept_one_invoice`, +`settle_invoice`, `pay_in_tx`, `validate_invoice` ; la **15-5e**, réécrite par C54, rejoue sur +interblocage toutes les routes d'écriture — avoir compris — et réécrit le doc-comment canonique de +l'ordre des verrous, le « 5 bis » et le Pattern 5 : **une seule règle d'ordre des verrous pour +l'epic**, la sienne, que les 15-6 suivent sans la réécrire) — chacune a sa tâche T0. La 15-6b gagne à passer après la **15-5c** +aussi : le libellé de son code dans `failed[]` passe par le module des refus par lot que la 15-5c pose +(#492, choix C-15-6-12). + +⚠️ **CHANGELOG** : la première story de la version mergée crée `## [0.13.0] — Non publié` en tête si +elle est absente ; les suivantes y ajoutent leur entrée (règle écrite dans chaque fiche). + +## Pourquoi le découpage + +Réunies, #473 et #474 touchent `kesh-db` (avoir, règlement client, règlement fournisseur, lots de +paiement, comptes bancaires, erreurs), `kesh-api` (rapprochement, comptes bancaires, réglages de +facturation, erreurs), `kesh-i18n`, plusieurs écrans (`SettleInvoiceDialog`, fiche de facture +fournisseur, lots, comptes bancaires, réglages), le manuel et le CHANGELOG — bien plus de cinq +modules. La seule dérogation codifiée (cycle de dépendances Cargo) ne s'applique pas. Chaque fille +reste à cinq modules métier au plus, la plomberie d'erreur et d'i18n suivant le geste qu'elle sert +(patron de la 15-5a). La validation P1 a ajouté à la 15-6b les lots de paiement et #524 ; avec les +deux, elle repassait au-dessus de cinq modules — d'où la 15-6d (finding F4). + +## Décisions prises pour la story et où elles vivent + +| choix | objet | fiche | +|---|---|---| +| C-15-6-1 | découpage en trois, ordre (ordre révisé par C-15-6-12) | toutes | +| C-15-6-2 | l'avoir lit la créance sur la vente (arrondi révisé par C-15-6-7, TVA par C-15-6-8) | 15-6a | +| C-15-6-3 | refus dédié, deux modes, après les contrôles existants ; arrondi et natures résolus par le type (révisé par C-15-6-18, C-15-6-26) | 15-6b | +| C-15-6-4 | filtre d'écran par rôle singleton, sans champ d'API nouveau (précisé par C-15-6-14) | 15-6b | +| C-15-6-5 | configuration : refus du couple dans les deux sens, exemption « inchangé » (course : révisé par C-15-6-13) | 15-6c | +| C-15-6-6 | ~~voisin hors périmètre~~ — **retiré par C-15-6-9** | — | +| C-15-6-7 | #523 absorbée : arrondi lu sur la vente, compte archivé refusé par `create_in_tx` (voie b) | 15-6a | +| C-15-6-8 | TVA due de l'avoir : angle mort tracé par #525, limite dite au manuel | 15-6a | +| C-15-6-9 | #524 → 15-6d, même refus que le flux ventilé | 15-6d | +| C-15-6-10 | lots de paiement : refus à la création, garde à la confirmation | 15-6b | +| C-15-6-11 | gardes sur les lecteurs ; lecteur sœur fournisseur | 15-6b | +| C-15-6-12 | #492 relève de la 15-5c | 15-6b, index | +| C-15-6-13 | contrôles dans la transaction, deux gestes sérialisés, ordre des verrous | 15-6c | +| C-15-6-14 | fonctions d'écran par ensemble d'ids, partagées 15-6b/15-6c (la 15-6d filtre localement, C-15-6-23) | 15-6b, 15-6c | +| C-15-6-15 | spec E2E de lien bancaire : 1100 → 1000 (références de fixture corrigées par C-15-6-22) | 15-6c | +| C-15-6-16 | comptes de la vente verrouillés, refus nommé `ACCOUNT_ARCHIVED` (révise la voie b de C-15-6-7) | 15-6a | +| C-15-6-17 | écrans sans archivés ; clés conformes au lint | 15-6b | +| C-15-6-18 | comptes d'écart comparés à la créance par identifiant (révise C-15-6-3) | 15-6b | +| C-15-6-19 | lots : refus contextualisé à la confirmation, achat malformé en `failed[]`, écran de création non filtré | 15-6b | +| C-15-6-20 | décompte des modules, signal D5 déclaré, pas de découpage | 15-6b | +| C-15-6-21 | `LOCK IN SHARE MODE` ; réglages : 409 d'abord ; code des réglages hors guide (révise C-15-6-13) | 15-6c | +| C-15-6-22 | appels directs au dépôt, tests étendus de la 15-5b, code mort supprimé, signal D5 | 15-6c | +| C-15-6-23 | égalité avec le compte de banque testée d'abord ; règle plus proposée ; filtre local (révise C-15-6-9) | 15-6d | +| C-15-6-24 | comptes de la vente verrouillés en partage, avant l'exercice (rectifie C-15-6-16) | 15-6a | +| C-15-6-25 | un seul vocabulaire de refus pour l'avoir : variante propre, code `ACCOUNT_ARCHIVED` (révise C-15-6-16) | 15-6a | +| C-15-6-26 | un code, un rôle (`details.role`), deux remèdes ; tables de refus du guide ; comparaison séparée de la construction du refus (révise C-15-6-18) | 15-6b, 15-6d | +| C-15-6-27 | `FOR UPDATE` de `before` testé, attente prouvée, pas de verrou pour une dé-liaison, manuel, guide | 15-6c | +| C-15-6-28 | compte de banque actif sur le chemin par règle, gardes ventilées figées, comparaison empruntée à la 15-6b (révise C-15-6-23 sur deux points, cf. C-15-6-31) | 15-6d | +| C-15-6-29 | verrou partagé étendu à tous les comptes écrits par l'avoir, rejeu de la route, ligne absente → `Invariant` (rectifie C-15-6-24) | 15-6a | +| C-15-6-30 | rôle `rounding` du compte d'arrondi quel que soit le geste ; validation et avoir en angles morts (#537, #525) ; liste vide conditionnée | 15-6b | +| C-15-6-31 | clôture de la validation ; C-15-6-28 révise C-15-6-23 ; texte du refus selon le champ | 15-6d | +| C-15-6-32 | alignement sur la 15-5e de C54 : rejeu de la route par la 15-5e, verrou pour la course de lecture seule, aucun réordonnancement, cycles nommés ; helper de verrou partagé avec la 15-5d ; TVA à source unique (révise C-15-6-29) | 15-6a | +| C-15-6-33 | alignement sur la 15-5e de C54 ; chaque comparaison suit sa lecture ; trois clés plates ; critère de T2 par nom sur liste fermée ; sujet de refus typé | 15-6b | +| C-15-6-34 | 15-6b close à la P6 ; produit de repli et validation d'achat tracés (#525, #537) ; comparaison après son compte et la créance ; manuel d'administration ; dispersion déclarée sans découpage | 15-6b, 15-6c | +| C-15-6-35 | 15-6a : un seul contrat pour le helper de verrou de liste ; critère DRY à exception écrite ; partenaires réels du cycle (iv) ; dépendance ferme à la 15-5d | 15-6a | + +## Change Log + +- 2026-10-08 — Spécification initiale (bmad-create-story, en autonomie), découpée d'emblée en + 15-6a / 15-6b / 15-6c. Choix C-15-6-1 à C-15-6-6 consignés. Validation : à lancer, fiche par fiche. +- 2026-10-08 — **Validation P1** des trois fiches (Sonnet 4.6, lentilles F et R). Bilan : 0 CRITICAL ; + HIGH — 15-6a ×1 (#523), 15-6b ×4 (#524, `confirm_batch`, tests 5 et 7 irréalisables), 15-6c ×3 + (transaction, course, AC6 périmé). Tous appliqués ; 15-6d créée pour #524 ; choix C-15-6-7 à + C-15-6-15 consignés. Détail dans le Change Log de chaque fiche. +- 2026-10-08 — **Validation P2** des 15-6a, 15-6b, 15-6c et **P1** de la 15-6d (Opus 5.5, lentilles + R et F). Bilan : 0 CRITICAL ; HIGH — 15-6c ×1 (`FOR SHARE`, né de la remédiation P1, relevé par les + deux lentilles) ; MEDIUM — 15-6a 3 + 2, 15-6b 3 + 5, 15-6c 3 + 3, 15-6d 3 + 3 (R + F, avant + convergence). Tous appliqués ; choix C-15-6-16 à C-15-6-23 consignés. **Signaux de découpage + déclarés au Project Lead** : 15-6b (huit modules au barème des fichiers) et 15-6c (HIGH né de la + remédiation) — ni l'une ni l'autre découpée, arbitrage de Guy attendu. Détail dans le Change Log de + chaque fiche. +- 2026-10-08 — **Validation P3** des 15-6a, 15-6b, 15-6c (Sonnet, lentilles R et F) et **P2** de la + 15-6d (Opus, lentilles R et F) ; remédiation Opus 5.5. Bilan (R + F, avant dédoublonnage) : + 0 CRITICAL ; 15-6a 1 HIGH, 6 MEDIUM, 10 LOW ; 15-6b 5 MEDIUM, 9 LOW ; 15-6c 3 MEDIUM, 11 LOW ; + 15-6d 3 MEDIUM, 12 LOW. Tous appliqués ; choix C-15-6-24 à C-15-6-28 consignés. Points saillants : + 15-6a — verrou des comptes de la vente en `LOCK IN SHARE MODE` **avant** l'exercice (le HIGH, né de + la remédiation P2, inversait l'ordre canonique de `validate_invoice`), refus « Impossible d'émettre + l'avoir » sous le code `ACCOUNT_ARCHIVED` ; 15-6b — `details.role` et clé distincte pour un compte + désigné dans les réglages, helper commun ; 15-6c — `FOR UPDATE` de `before` épinglé par un test, + attentes prouvées ; 15-6d — compte de banque actif contrôlé sur le chemin par règle, **la 15-6d passe + désormais après la 15-6b**. **Signaux D5 déclarés au Project Lead** : 15-6a (sévérité remontée, + HIGH né de la remédiation P2) et 15-6b (MEDIUM → MEDIUM, deux défauts nés de la remédiation P2) — + aucune découpée ; 15-6c et 15-6d : non levés à cette passe (le signal P2 de la 15-6c reste + déclaré). Toutes les fiches gardent des MEDIUM : **P4 à lancer** (complète pour la 15-6b ; ciblée + possible pour la 15-6a). Décomptes : 15-6a 9 AC, 7 tâches, 14 tests ; 15-6b 13 AC, 10 tâches, + 21 tests ; 15-6c 11 AC, 9 tâches, 16 tests ; 15-6d 10 AC, 6 tâches, 12 tests. +- 2026-10-08 — **Validation P4** des 15-6a et 15-6b (Opus 5.5, lentilles R et F), **P4 ciblée** de la + 15-6c (Haiku 4.5) et **P3** de la 15-6d (Sonnet, lentilles R et F) ; remédiation Opus 5.5. Bilan (R + + F, avant dédoublonnage) : 0 CRITICAL, 0 HIGH ; 15-6a 4 MEDIUM, 12 LOW ; 15-6b 5 MEDIUM, 10 LOW ; + 15-6c 0 ; 15-6d 10 LOW. Tous appliqués ; choix C-15-6-29 à C-15-6-31 consignés. Points saillants : + 15-6a — le verrou partagé couvre **tous** les comptes que l'avoir écrit (la TVA due et les produits + demandaient leur S après l'exercice), la route `POST /api/v1/credit-notes` rejoue un 1213, cycle + avoir ↔ rapprochement de la même facture tracé par **#536** ; 15-6b — rôle `rounding` pour le compte + d'arrondi du solde du reste, « résolu par le type » remplacé par des angles morts (**#537**, #525), + message de liste vide conditionné. **Signal D5 déclaré au Project Lead** pour la 15-6b (F1 recycle le + discriminant de la P3 ; contenu, pas de découpage — un troisième recyclage en P5 sortirait l'AC3 bis + en story propre) ; la 15-6a voit sa sévérité baisser (P3 1 HIGH → P4 0 HIGH). **Validations closes : + 15-6c et 15-6d** (0 au-dessus de LOW ; « 0 » de la passe ciblée de la 15-6c vérifié par + l'orchestrateur). **P5 à lancer** pour les 15-6a et 15-6b (MEDIUM à cette passe). Décomptes : 15-6a + 9 AC, 7 tâches, 16 tests ; 15-6b 13 AC, 10 tâches, 22 tests ; 15-6c 11 AC, 9 tâches, 16 tests ; + 15-6d 10 AC, 6 tâches, 12 tests. +- 2026-10-08 — **Validation P5** des 15-6a et 15-6b (Sonnet, lentilles R et F ; remédiation Opus 5.5). + Bilan (R + F, avant dédoublonnage) : 0 CRITICAL, 0 HIGH ; 15-6a 3 MEDIUM, 13 LOW ; 15-6b 6 MEDIUM, + 10 LOW. Tous traités ; choix C-15-6-32 et C-15-6-33 consignés. Fil conducteur : **la 15-5e**, que + les deux fiches ignoraient, et **sa réécriture par C54** survenue pendant la remédiation (la défense + contre l'interblocage est le rejeu) : dépendances « après 15-5a à 15-5e » ; la 15-6a s'appuie sur le + rejeu de sa route par la 15-5e, garde son verrou pour la seule course de lecture, ne réordonne rien + et ne prétend plus à l'absence de cycle ; la 15-6b garde l'ordre des lectures du code (chaque + comparaison suit sa lecture), passe à trois clés plates, relève `sitesTotal`, et remplace le critère + de T2 par un relevé par nom sur liste fermée. **Signal D5** : levé par la sévérité pour la 15-6b + (MEDIUM → MEDIUM), non déclenché — le MEDIUM qui touche l'AC3 bis vient d'un changement extérieur + (la 15-5e), pas d'un patch ; idem pour R5-1/R5-2 de la 15-6a. **P6 complète (Opus) à lancer** pour + les deux. Décomptes : 15-6a 9 AC, 7 tâches, 18 tests ; 15-6b 13 AC, 10 tâches, 23 tests. +- 2026-10-08 — **Validation P6** des 15-6a et 15-6b (Opus 5.5, lentilles R et F ; prompts + `15-6a-validate-prompt-p6.md`, `15-6b-validate-prompt-p6.md`). **15-6b** : 0 CRITICAL, 0 HIGH, + 0 MEDIUM, 18 LOW (R 11, F 7) — **validation close** (C-15-6-34) ; produit de repli et validation + d'achat désormais tracés (#525, #537) ; manuel d'administration ajouté à l'AC11 (frontière de la + 15-6c alignée) ; dispersion (neuf modules au barème de la règle) déclarée au Project Lead, non + découpée. **15-6a** : 0 CRITICAL, 0 HIGH, 5 MEDIUM (4 distincts), 12 LOW — remédiée (C-15-6-35) : + un seul contrat pour le helper de verrou, critère DRY à exception écrite, doctrine résiduelle + retirée, partenaires réels du cycle (iv), dépendance ferme à la 15-5d ; **signal D5 levé** + (recyclage de la P5, contenu au texte de l'AC6) et dispersion déclarés, pas de découpage ; **P7 + complète (Sonnet) due**. Décomptes inchangés : 15-6a 9 AC, 7 tâches, 18 tests ; 15-6b 13 AC, + 10 tâches, 23 tests. À faire par l'orchestrateur : commentaire sur #536 (F6-6 de la 15-6a). diff --git a/_bmad-output/implementation-artifacts/15-6a-avoir-credite-la-creance-de-la-vente.md b/_bmad-output/implementation-artifacts/15-6a-avoir-credite-la-creance-de-la-vente.md new file mode 100644 index 00000000..1283962f --- /dev/null +++ b/_bmad-output/implementation-artifacts/15-6a-avoir-credite-la-creance-de-la-vente.md @@ -0,0 +1,1180 @@ +# Story 15.6a : L'avoir contre-passe la créance et l'arrondi sur les comptes que la vente a mouvementés + +## Status + +ready-for-dev + + + +**Issues** : `closes #473, closes #523` (P1 toutes deux) — les mots-clés vont **dans la PR** (merge en +squash). **Mère** : `15-6-creance-juste-avoir-reglement.md`. +**Suivie de** : 15-6b (mêmes fichiers `invoice_settlements_write.rs` et `reconciliation.rs` — la 15-6b +emploie le lecteur que celle-ci pose). +**Après** les 15-5a à 15-5e (ordre de l'epic : 15-5 puis 15-6 ; la **15-5e** passe avant la 15-5d) — +dépendance **ferme** pour chacune, la **15-5d** comprise (son helper de verrou, son test de C35) : si +l'une n'est pas mergée au moment de T0, la story **attend** (finding R6-10 de la P6). +Ces fiches touchent les **mêmes fonctions** : `accept_one_invoice` (15-5b AC6 y pose un commentaire de +classement sur le bloc même que l'AC2 remplace), `settle_invoice` (15-5a AC4, 15-5b AC18), +`write_off_invoice` (**15-5e AC5** : commentaire « 5 bis » réécrit en place), `validate_invoice` +(15-5b, 15-5d, et **15-5e AC5**, qui réécrit son doc-comment « # Ordre des locks » — l'ordre réel, +**convention qui réduit la fréquence** des interblocages, et **la défense, qui est le rejeu** : la +règle de l'epic, choix C54, que cette fiche **suit** et ne réécrit pas, AC6, AC7), la **route +`POST /api/v1/credit-notes`** (**15-5e AC3** : rejouée sur interblocage par l'enveloppe `DbError`, +`kesh_db::retry::retry_on_deadlock` — la 15-6a ne pose donc **pas** de second rejeu, AC6), le +Pattern 5 de `docs/MULTI-TENANT-SCOPING-PATTERNS.md` (15-5e AC5), `invoices_validate_vat.rs` (15-5b, 15-5d), le +passage du manuel sur l'avoir (15-5d AC8), le **helper de verrou d'une liste de comptes** que la 15-5d +pose (`lock_designated_accounts_in_tx`, AC6) et le **fichier de tests** `credit_notes_repository.rs`, +où la 15-5d ajoute un test (C35) que cette fiche ré-ancre (§ *Tests*, 13). Les numéros +de ligne de cette fiche sont ceux du commit `1920381e` : **T0 les refait tous** après ces merges. +**Angle mort tracé, hors story** : #525 (TVA due de l'avoir lue dans les réglages du moment — jalon de +la TVA, report assumé ; choix C-15-6-8). + +## Story + +En tant que **comptable d'une PME**, +je veux que **l'avoir contre-passe la créance et l'écart d'arrondi sur les comptes mêmes que la facture +a mouvementés**, +afin que **le compte débiteurs de la facture et le compte de différences d'arrondi reviennent à zéro**, +comme le promet le manuel, même si les comptes par défaut ont été changés entre la validation et +l'avoir. + +## Le défaut, établi au code + +- La validation d'une facture débite la créance sur `settings.default_receivable_account_id` + (`crates/kesh-db/src/repositories/invoices.rs:2005-2010`) — c'est le geste qui **crée** la créance, + il lit légitimement les réglages. Elle pousse l'écart d'arrondi en **dernière** ligne + (`generate_invoice_journal_lines_rounded`, `invoices.rs:1892-1912`) : au **crédit** du compte + d'arrondi si l'arrondi est positif, au **débit** s'il est négatif. +- L'avoir relit **les réglages du moment** : + - pour la créance (`crates/kesh-db/src/repositories/credit_notes.rs:360-364`), créditée en ligne 0 + (`generate_credit_note_journal_lines`, `:187`, ligne poussée `:219`, appel `:509`) — #473 ; + - pour le compte d'arrondi (`credit_notes.rs:525`, `rounding_account_for_write(.., Issuance)`, + `company_invoice_settings.rs:400-412`) — #523 ; + - pour la TVA due (`credit_notes.rs:511`, `settings.default_vat_payable_account_id`) — #525, hors + story (choix C-15-6-8). +- Le règlement, le solde du reste et l'encaissement par rapprochement lisent, eux, la créance **sur + l'écriture de vente** — première ligne au débit, `ORDER BY jel.id LIMIT 1` — par **trois copies** + de la même requête : `invoice_settlements_write.rs:104-115` (`settle_invoice`), + `invoice_settlements_write.rs:434-445` (`write_off_invoice`), `crates/kesh-api/src/routes/reconciliation.rs:1425-1452` + (`accept_one_invoice`). La génération garantit que la créance est cette ligne-là + (`invoices.rs:1870-1878`, doc de `generate_invoice_journal_lines_rounded`). +- Rien n'interdit de changer le compte débiteurs ou le compte d'arrondi par défaut entre-temps + (`crates/kesh-api/src/routes/company_invoice_settings.rs:274-281`, seuls type et état actif). + +**Conséquence** : l'avoir crédite le nouveau compte ; l'ancien garde le débit TTC ; la facture passe +`cancelled` et sort de la balance âgée — la créance reste au grand livre sans plus apparaître nulle +part. Même chose, au centime, pour le compte d'arrondi (écart ≤ 2,5 centimes par facture, mais une +écriture fausse). Le commentaire `credit_notes.rs:609-611` — « c'est ce qui rend la contre-passation +l'« inverse exact » même si le défaut société change ensuite » (cité `:574-576` par l'issue, sur une +version antérieure) — est vrai du compte de produit, recopié par ligne (Story 16-1a, D5), pas de la +créance ni de l'arrondi. + +## Acceptance Criteria + +1. **AC1 — Un lecteur partagé du compte de créance.** `crates/kesh-db/src/repositories/invoice_settlements.rs` + expose `pub async fn sale_receivable_account(conn: &mut sqlx::MySqlConnection, company_id: i64, sale_entry_id: i64) -> Result, DbError>` + (signature indicative ; l'exécuteur suit le patron de `rounding_account_for_write`), qui exécute + **exactement** la requête des trois copies actuelles (première ligne `debit > 0` de l'écriture, + `ORDER BY jel.id LIMIT 1`, portée par `je.company_id`). Son doc-comment porte la règle — « la + créance se lit sur l'écriture de vente, jamais sur les réglages » — et la raison de l'ordre (arrondi + négatif au débit **après** la créance, 25-4-c4-a). `None` = écriture sans ligne de débit : chaque + appelant garde **son** refus actuel (`DbError::Invariant` pour le règlement et le solde ; + `failed[]` `INVOICE_SALE_ENTRY_MALFORMED` pour le rapprochement, `details` inchangés — + `{ "reason": "no_debit_line_on_sale_entry", "saleEntryId" }`). **Erreur SQL** du lecteur dans le + rapprochement : l'appelant la convertit, comme aujourd'hui, en `FailedProposal` `DATABASE_ERROR` + avec `details = { "message": e.to_string() }` — **forme inchangée** ; le **texte** du message, lui, + dérive désormais d'un `DbError` (`map_db_error`, `crates/kesh-db/src/errors.rs:796`) et non plus + d'un `sqlx::Error` : son libellé peut différer. Le rejeu sur interblocage n'en dépend pas (il + repose sur la perte du point de sauvegarde, `reconciliation.rs:1057-1068`). +2. **AC2 — Les trois copies disparaissent.** `settle_invoice`, `write_off_invoice` et + `accept_one_invoice` appellent le lecteur ; `grep -rn --include='*.rs' "jel.debit > 0" crates/` ne + rend plus que le lecteur et la doc de `generate_invoice_journal_lines_rounded` + (`invoices.rs:1877`). Les `.sql` que le même jeton atteint — + `crates/kesh-db/migrations/20260729000001_invoice_lines_revenue_account_backfill.sql:293`, + `crates/kesh-db/migrations/20260828000001_invoice_settlements_type.sql:51` et le rejeu + `crates/kesh-db/src/post_restore/20260828000001_invoice_settlements_type.sql:25` — sont des + backfills, **hors classe** et légitimes (une migration appliquée ne se modifie plus, règle P8). + Comportement inchangé : les tests existants (`crates/kesh-db/tests/invoice_settlement.rs`, + `invoice_write_off.rs`, `crates/kesh-api/tests/reconciliation_e2e.rs`) passent sans modification + d'assertion. ⚠️ **Aucun test ne fige aujourd'hui `INVOICE_SALE_ENTRY_MALFORMED`** (vérifié à la + remédiation P1 : `grep -rn "INVOICE_SALE_ENTRY_MALFORMED" crates/ frontend/src docs` ne rend que le + site de `reconciliation.rs`) : un test l'ajoute avant le remplacement (§ *Tests*, 10), pour que la + copie remplacée garde un témoin. +3. **AC3 — L'avoir crédite la créance de la vente.** `create_credit_note` lit la créance par le + lecteur de l'AC1, sur `invoice.journal_entry_id` de la facture verrouillée ; `None` sur + `journal_entry_id` ou sur le lecteur → `DbError::Invariant` (« facture validée sans écriture de + vente » / « écriture de vente sans ligne de débit »), comme `settle_invoice`. La lecture se fait + **après** le verrou de la facture (étape (1)) et la lecture des réglages (3), **avant** l'exercice + (4) — elle ne dépend que de `invoice.journal_entry_id`, connu dès (1), et le verrou de l'AC6 en a + besoin —, dans la même transaction. L'avoir **ne lit plus** + `settings.default_receivable_account_id` : un réglage débiteurs vide n'empêche plus d'émettre un + avoir sur une facture validée. +4. **AC4 — L'avoir contre-passe l'arrondi sur le compte de la vente (#523, choix C-15-6-7).** Un + second lecteur, à côté du premier : + `pub async fn sale_rounding_account(conn: &mut sqlx::MySqlConnection, company_id: i64, sale_entry_id: i64, rounding_amount: Decimal) -> Result` + (signature indicative). Il lit la **dernière** ligne de l'écriture de vente + (`ORDER BY jel.id DESC LIMIT 1`, portée par `je.company_id`) et la **recoupe** avec l'arrondi figé + sur la facture : arrondi > 0 ⇒ `credit = rounding_amount` et `debit = 0` ; arrondi < 0 ⇒ + `debit = −rounding_amount` et `credit = 0`. Toute autre forme (pas de ligne, sens ou montant + différent) → `DbError::Invariant` (« écriture de vente : la dernière ligne n'est pas l'arrondi de la + facture »). Il n'est **appelé que si** `invoice.rounding_amount ≠ 0` — une facture émise sans + arrondi, antérieure comprise, n'exige toujours aucun compte (validation P3 de la 25-4-c4-a). Il est + appelé **à côté du lecteur de créance**, avant l'exercice (4) (AC3, AC6) ; l'étape (7 bis) de + `create_credit_note` ne lit plus `rounding_account_for_write(.., Issuance)` et emploie l'id déjà lu ; + la ligne produite (sens, montant, ajustement de la ligne 0) ne change pas. Son doc-comment dit + pourquoi le recoupement (un lecteur positionnel qui ne vérifie rien rendrait n'importe quelle ligne + en silence) et pourquoi **pas** `usable_designated_account` (doctrine « mêmes comptes que + l'origine, seule l'inactivité bloque », cf. 6 ter de la 16-1a). +5. **AC5 — Le reste de l'avoir ne bouge pas.** Compte de produit par ligne (D5) et garde des comptes + de produit archivés (6 ter), compte de TVA due (réglage courant — angle mort #525, AC8), libellé, + numéro, journal, projet, snapshot des lignes : inchangés. L'ordre des lignes de l'écriture aussi + (créance en ligne 0, arrondi en dernière ligne). L'avoir **exige toujours** + `default_revenue_account_id` (`credit_notes.rs:365-367`, repli D-B2) : seule la créance cesse + d'être lue dans les réglages. +6. **AC6 — Tous les comptes que l'avoir écrit sont verrouillés en partage avant l'exercice, et un + compte archivé depuis est refusé en le nommant** (choix C-15-6-16, qui révise la voie (b) de + C-15-6-7 ; mode, place et refus révisés par C-15-6-24 et C-15-6-25 ; périmètre étendu à tous les + comptes écrits et ligne absente tranchés par C-15-6-29 ; ordre, cycles et rejeu — porté par la + 15-5e — révisés par C-15-6-32). + - **Verrou.** Juste après la lecture des réglages (étape (3)) et les deux lecteurs (AC3, AC4), + **avant** l'exercice (étape (4), `fiscal_years::find_open_covering_date`, qui pose un + `FOR UPDATE`, `fiscal_years.rs:548`) et la séquence (étape (5), + `credit_note_number_sequences::next_number_for`, `FOR UPDATE`), `create_credit_note` verrouille + les lignes `accounts` de **tous les comptes que l'écriture de l'avoir visera** — tous connus à ce + point, le snapshot des lignes (étape (2)) précédant les réglages : + - la **créance** de la vente (AC3) ; + - le compte d'**arrondi de la vente**, si l'arrondi ≠ 0 (AC4) ; + - le compte de **TVA due** des réglages, si l'avoir émet au moins une ligne de TVA et si le réglage + est posé — vide, il n'est pas verrouillé et le refus existant de la génération reste à sa place + (ci-dessous) ; + - les **comptes de produit effectifs** : ceux des lignes (`revenue_account_id`) et, si une ligne + n'en porte pas, le produit de repli (`default_revenue_account_id`, D-B2) — exactement les ids + que la 6 ter calcule aujourd'hui (`credit_notes.rs:435-446`). + + En **une** instruction, ids dédoublonnés, `ORDER BY id` — **aucun réordonnancement** pour + fermer un cycle (choix C-15-6-32, sur C54 : la défense contre l'interblocage est le rejeu de la + 15-5e, pas l'ordre des verrous) : + `SELECT id, number, active, postable FROM accounts WHERE company_id = ? AND id IN (…) ORDER BY id LOCK IN SHARE MODE` + — **sans** filtre `active` : la ligne doit être prise même archivée, pour être nommée. + `FOR SHARE` est une erreur de syntaxe sur MariaDB 10.11 ; **syntaxe** (pas patron : aucune de ces + requêtes ne vise une liste de comptes) relevée à `crates/kesh-db/src/repositories/opening_complement.rs:502` + et `:540` (mise en garde `FOR SHARE` à `:38-39`) — la 15-6c, qui pose `claim_accounts_in_share_mode`, + passe **après** cette fiche (fiche d'index). + - **Un seul helper de verrou d'une liste de comptes — un seul contrat** (DRY, finding F5-5 de la + P5 ; contrat unifié par F6-1 = R6-1 de la P6 ; C-15-6-32, C-15-6-35). La 15-5d pose, pour la même + forme en exclusif, `lock_designated_accounts_in_tx` (`WHERE company_id = ? AND id IN (…) ORDER BY + id FOR UPDATE`, rend `id, number, active, postable`, sans refuser — 15-5d AC1, accesseur de + `company_invoice_settings.rs`). Il n'y a **qu'une** fonction de verrou d'une liste de comptes, et + **ce paragraphe est son seul contrat** dans cette fiche : + - **paramétrée par le mode** : p. ex. `lock_accounts_in_tx(conn, company_id, ids: &[i64], mode: + AccountLockMode)`, `AccountLockMode::{Exclusive, Share}` → `FOR UPDATE` / `LOCK IN SHARE MODE` + (signature indicative) ; + - **emplacement** : celui que la 15-5d lui a donné (`company_invoice_settings.rs`, T0 le + constate) ; la 15-6a ne la déplace pas ; + - **colonnes rendues** : `id, number, active, postable`, **toutes** les lignes trouvées, archivées + comprises, ordre `ORDER BY id` ; + - **elle ne refuse rien** : ni compte archivé, ni ligne absente. Le contrôle « ligne absente » + (nombre de lignes rendues ≠ nombre d'ids dédoublonnés → `Invariant`) et le partage des lignes + (créance, arrondi, TVA due → la variante ; produits → la 6 ter) vivent **chez l'appelant**, + `create_credit_note` ; + - **visibilité `pub`** : le test 17 (`crates/kesh-db/tests/credit_notes_repository.rs`, test + d'**intégration**, hors de la crate) l'appelle directement — `pub(crate)` ne compilerait pas ; + - **type de ligne** : un type **nommé, public**, distinct de `opening_complement::LockedAccount` + (`opening_complement.rs:383`, **privé**, colonnes `account_type` et `role` en plus) — p. ex. + `LockedAccountState { id, number, active, postable }` ; `LockedAccount` n'est **ni réutilisé** + (forme différente, privé à un flux que l'epic ne touche pas) **ni repris comme nom** (deux types + homonymes dans la même crate tromperaient la lecture). **Un seul type, jamais deux** : si la 15-5d a + déjà nommé **et exposé (`pub`)** le type de ligne de son helper, la 15-6a l'emploie ; si elle l'a + laissé privé ou anonyme, la 15-6a le rend public sous le nom qu'il porte (ou `LockedAccountState` + s'il n'en a pas) et ne crée pas de second type (finding R7-1 de la P7 ; C-15-6-36). + **Qui la crée** : la **15-5d**, mergée avant (dépendance **ferme**, en-tête ; finding R6-10 de la + P6). Si T0 la trouve sans paramètre de mode, la 15-6a l'y ajoute (l'appel de la 15-5d passe + `Exclusive`, son comportement ne change pas) au lieu d'écrire une jumelle. + **Critère DRY, relevé au Dev Agent Record** (finding R6-2 de la P6) : `grep -rnE "LOCK IN SHARE + MODE|FOR UPDATE" crates/kesh-db/src` lu site par site (requêtes multi-lignes et `QueryBuilder` + compris) ne montre aucune autre requête qui verrouille une **liste** d'ids de comptes, **hors une + exception écrite** : `opening_complement.rs:522-532` (`create_opening_complement`, `SELECT id, + number, account_type, active, postable, role … WHERE id IN (…) AND company_id = ? ORDER BY id FOR + UPDATE`), dont la forme diffère (deux colonnes de plus, pré-filtre `owned_account_ids` **hors** + transaction) et qui sort du périmètre de l'epic — la refondre sur le helper serait un changement + sans motif d'un flux livré. Toute **autre** occurrence est un site non résolu. + - **Plan et identifiant étranger** (finding F5-4 de la P5, sur le patron de la 15-5d, choix C51) : + sous REPEATABLE READ, un balayage ou un `filesort` qui parcourt d'autres lignes les **verrouille** + (piège mesuré à `opening_complement.rs:274-288`), et un verrou par clé primaire peut prendre une + ligne **avant** que le filtre `company_id` ne l'écarte (`opening_complement.rs:431-440`) — en + partage comme en exclusif, un S bloque l'`UPDATE` d'un archivage de ce compte. L'`EXPLAIN` de la + requête en mode `Share` (accès par clé primaire sur `id IN (…)`, aucun `filesort`) est **relevé au + Dev Agent Record** ; un test d'identifiant d'une **autre société** est ajouté (§ *Tests*, 17) ; + s'il montre la ligne étrangère verrouillée, le helper prend le patron `owned_account_ids` + d'`opening_complement.rs` — pour les deux modes, donc aussi pour la 15-5d — et le Dev Agent + Record le dit. + - **« L'avoir émet de la TVA » : une seule source** (findings R5-4 et F5-2 de la P5 ; C-15-6-32). + La condition vit aujourd'hui **dans** `generate_credit_note_journal_lines` (`total_vat > 0`, somme + des TVA agrégées par taux, `credit_notes.rs:201-214`, `:240`), appelée à l'étape (7), **après** + le verrou. Un `any(vat_rate > 0)` naïf en divergerait (ligne à taux positif dont la TVA s'arrondit + à zéro). T3 **extrait** l'agrégation en une fonction pure (p. ex. + `credit_note_vat_by_rate(lines) -> BTreeMap`, à côté du générateur, sans I/O), + appelée **par le générateur** et **par le calcul des ids à verrouiller** — aucune copie du calcul. + **Place du refus `ConfigurationRequired("default_vat_payable_account_id")` : inchangée** — il reste + rendu par le générateur, à l'étape (7), donc **après** `FISCAL_YEAR_INVALID` et après + `ACCOUNT_ARCHIVED` (un réglage vide n'est pas verrouillé, rien ne le rend plus tôt). Avancer + l'appel du générateur avant le verrou l'aurait fait passer devant l'exercice : changement de + priorité sans motif, écarté (C-15-6-32). + - **Pourquoi un verrou partagé (S), pas exclusif (X).** Le verrou a un but premier : qu'un + archivage concurrent (`accounts::archive`, `accounts.rs:604-634`, un `UPDATE … SET active = FALSE` + qui demande X et ne regarde pas les écritures) **attende la fin de l'avoir** au lieu de le + précéder. Un S y suffit, et il est compatible avec les S que tout insert de ligne d'écriture pose + déjà sur le compte parent (contrôle de clé étrangère des `INSERT INTO journal_entry_lines`). Un S + **n'ajoute aucun cycle** par la créance : `accept_one_invoice` y tient un S (clé étrangère des + lignes du règlement, jusqu'au commit du lot, `tx_outer`, `reconciliation.rs:887`), et S contre S + ne bloque pas — un X y aurait ajouté un cycle. Mais un S **n'en retire aucun** non plus : face au + **X** qu'un autre flux prend sur le même compte (compte d'arrondi du rapprochement, du règlement + ou du solde du reste ; compte de nature et TVA due du solde du reste), il **attend**. Les cycles + qui subsistent sont écrits plus bas. Corollaire : deux avoirs simultanés **ne se sérialisent pas** + sur la ligne d'un compte (S + S) — ils se sérialisent déjà, et seulement, sur la ligne des réglages + (étape (3), `get_or_create_default_in_tx`, `FOR UPDATE`, `company_invoice_settings.rs:109`). La + lecture sous S est une lecture **verrouillante** : elle rend l'état **courant** de `active`, comme + le ferait un X. + - **Ordre des verrous** : facture (1) → règlements (garde 25-4-a) → avoir existant → réglages (3) + → **comptes de l'avoir (S, `ORDER BY id`)** → exercice (4) → séquence (5) → écriture, dont l'insertion des lignes **reprend** un S sur chaque + compte écrit (clé étrangère `fk_jel_account`). La règle de l'epic est celle que la **15-5e** écrit + au doc-comment canonique de `validate_invoice` (son AC5, choix **C54**) : l'ordre des verrous est + une **convention qui réduit la fréquence** des interblocages — il ne peut pas les exclure, puisque + l'insertion des lignes reprend `accounts` **après** l'exercice — et **la défense est le rejeu des + routes**. Cette fiche **suit** cette règle et ne la réécrit pas ; elle ne prétend à l'absence + d'**aucun** cycle (C54 : « elles cessent d'affirmer l'absence de cycle et renvoient au rejeu de la + 15-5e »). + **Pourquoi ce verrou, et pourquoi avant l'exercice** : il ferme une **course de lecture** — un + archivage concurrent attend la fin de l'avoir au lieu de le précéder, et l'état `active` de + chaque compte écrit (créance, arrondi, TVA due, produits — la 6 ter comprise) est lu **frais**, + sous verrou, non dans l'instantané (C-15-6-29). C'est son seul rôle revendiqué. Sa place avant + l'exercice est celle que la fiche avait depuis la P3 (C-15-6-24) et la convention que décrit le + doc-comment canonique (comptes → exercice) ; elle n'est **pas** un instrument de fermeture de + cycle, et la fiche n'en tire aucune garantie. La ligne des réglages `FOR UPDATE` + (`company_invoice_settings.rs:109`), prise d'abord par l'avoir, la validation et la saisie + fournisseur (15-5e AC5), les sérialise entre eux — fait relevé, non promesse. Le doc-comment de + `create_credit_note` porte cet ordre et **renvoie** au doc-comment canonique pour la règle et au + rejeu de la route (AC7, DRY). + - **Cycles connus, nommés — non exhaustifs, couverts par le rejeu.** Les passes de validation en ont + trouvé quatre ; d'autres peuvent exister, et la fiche ne prétend pas le contraire (C54). Tous sont + détectés par InnoDB (1213) ; aucun ne naît de la créance : + (i) **avoir ↔ lot de rapprochement** (**#536**, fermée par le rejeu de la 15-5e, C57) : + `accept_one_invoice`, qui ne verrouille pas la facture en amont (`reconciliation.rs:1688-1691`), + prend X sur le compte d'arrondi du moment si le paiement solde avec écart (`:1487`, + `rounding_account_for_write` → `usable_designated_account`, `company_invoice_settings.rs:499`) et + X sur l'exercice (`:1520`, `fiscal_years.rs:548`), **avant** son `UPDATE invoices` (`:1703`), et le + lot garde l'exercice d'une proposition à la suivante (savepoints d'une seule transaction, + `tx_outer`, `:887`) ; l'avoir tient X sur la facture et S sur l'arrondi de la vente, puis demande + l'exercice. Préexistant (l'avoir prenait déjà l'exercice) ; le lot rejoue (`post_accept`), l'avoir + aussi (15-5e). + (ii) **avoir ↔ solde du reste d'une autre facture**, quand le compte de nature de celui-ci est + aussi un compte de produit de l'avoir (p. ex. une association qui a désigné elle-même son 3800 + *Autres produits* comme compte d'escompte — le plan des associations n'en désigne aucun d'office, + `admin-manual.tex:2029` —, ou un 3800 *Déductions sur ventes* de PME imputé sur une ligne de + facture ; finding F6-3 de la P6) : le solde prend X sur la nature, puis l'arrondi, puis la TVA due ; + l'avoir prend ses S par `id` (2200 avant 3800) — ordres opposés entre la nature et la TVA due. + Rare. **Variante fréquente** (finding R5-2 de la P5) : sans nature commune, le solde d'un reste + hors centime prend X sur l'arrondi puis sur la TVA due ; l'avoir d'une facture arrondie, par `id`, + la TVA due (2200) avant l'arrondi (6940, plus loin dans le plan livré, ou créé après lui ; finding + R6-8 de la P6) — ordres opposés entre deux comptes + courants. **Non réordonné** (C-15-6-32, C54) : couvert par le rejeu des deux routes. + (iii) **avoir ↔ règlement client par compte interne** (finding F5-3 de la P5), quand le compte + interne choisi est un compte que l'avoir écrit (sa TVA due, un de ses produits) et que le paiement + a un écart : le règlement prend X sur le compte interne puis sur l'arrondi ; l'avoir les prend + par `id`. Configuration sans sens comptable, rare. + (iv) **cycle à trois par un X en attente** : l'avoir tient S sur un compte et attend l'exercice, + tenu par un flux qui prend l'exercice **sans passer par la ligne des réglages** — saisie manuelle + (`journal_entries::create_in_tx`, exercice `journal_entries.rs:279-280`, puis l'insertion des + lignes), règlement client (`invoice_settlements_write.rs:200`), solde du reste (`:493`), + rapprochement (`reconciliation.rs:1520`, manuel et ventilé `:2083`, `:2432`) ; un `UPDATE + accounts` sur ce compte (archivage, modification, création d'un sous-compte qui rend le parent non + imputable) attend derrière le S ; ce flux insère une ligne sur ce compte, et sa demande S attend le + X en attente (file InnoDB) → 1213. **Neuf** (sans le S, l'`UPDATE` passait), rare. La + **validation** et la **saisie fournisseur** n'en sont **pas** : elles prennent la ligne des réglages + `FOR UPDATE` **avant** l'exercice — établi pour la **validation** (`invoices.rs:2006`, puis + `:2172`) ; pour la saisie fournisseur (`supplier_invoices.rs:359` pour les réglages), T0 **relève** + l'ordre par rapport à son exercice —, et l'avoir la prend avant ses + S (étape (3)) — une validation qui tient l'exercice tient donc les réglages, et l'avoir l'attend + là, sans encore tenir de S (finding F6-2 = R6-4 de la P6). + **Le rejeu** : la route `POST /api/v1/credit-notes` (`crates/kesh-api/src/routes/credit_notes.rs:183`) + est **rejouée par la 15-5e** (son AC3 : enveloppe `DbError`, `kesh_db::retry::retry_on_deadlock`, + doc-comment d'une ligne ; registre des routes, son test 5). La 15-6a **n'en pose pas** un second + (C-15-6-32, qui retire ce point de C-15-6-29) ; T0 le **constate** sur `HEAD` — si la 15-5e n'est + pas mergée, la story attend (ordre de l'epic). Le rejeu est sûr : la transaction est atomique et le + numéro d'avoir se rembobine au rollback ; un 1213 ressort en `DbError::Sqlx` (`map_db_error`, + `kesh-db/src/errors.rs:796`, ne le convertit pas). Le test 15 fige que ce rejeu couvre l'attente + **neuve** que l'AC6 crée (le S sur les comptes de l'avoir). + - **Ordre des refus qui en découle** : un compte de créance, d'arrondi ou de TVA due archivé (ce + refus) l'emporte sur `FISCAL_YEAR_INVALID` (4), sur `creditNoteTotalZero` (6 bis) et sur + `CREDIT_NOTE_REVENUE_ACCOUNT_ARCHIVED` (6 ter) ; il vient **après** les refus de l'étape (1) + (facture non validée, réglée, déjà annulée), après `ConfigurationRequired("default_revenue_account_id")` + de l'étape (3) (`credit_notes.rs:365-367`, AC5) et après les `Invariant` des deux lecteurs (AC3, + AC4). Le test 14 fige la précédence sur l'exercice ; les refus antérieurs ne demandent pas de test + neuf (ils précèdent déjà toute lecture de compte). + - ⚠️ **L'état `active` se lit DANS cette lecture verrouillante**, jamais dans une lecture simple + ultérieure : sous REPEATABLE READ, une lecture non verrouillante rend l'instantané de la + transaction — établi dès la première lecture simple, le snapshot des lignes (étape (2)), donc + **avant** le verrou — et ne voit pas un archivage validé entre-temps. C'est le trou de la garde + `active` de `create_in_tx` (`journal_entries.rs:96-98`, sans verrou), qui ne suffit donc pas + seule, et c'est pourquoi la **6 ter** (comptes de produit) ne relit plus `active` par sa lecture + simple (`credit_notes.rs:450-451`) : elle prend l'état actif dans le résultat du verrou, et garde + son code (`CREDIT_NOTE_REVENUE_ACCOUNT_ARCHIVED`), son message par ligne et **sa place** (après + l'exercice et la 6 bis — l'ordre des refus du produit ne change pas). + - **Ligne absente du résultat du verrou.** La requête porte `company_id = ?` : un id que le verrou ne + rend pas (compte d'une autre société sur une écriture ou une ligne corrompue — **inatteignable par + l'application** : les validations à la création d'une ligne de facture, anti-IDOR, + `invoices_line_revenue_account.rs:365-367` `create_rejects_cross_company_account`, et à la + désignation d'un réglage l'excluent, et les lectures sont portées par `je.company_id` ; **aucune** + clé étrangère ne porte la société — `fk_jel_account`, `fk_cis_receivable` et celle de + `revenue_account_id` ne référencent que `accounts(id)` —, si bien qu'un `UPDATE` direct y parvient, + comme le fait le test 17 ; finding F6-4 de la P6) → `DbError::Invariant` (« compte de l'avoir + introuvable dans la société »), dès que le nombre de lignes rendues diffère du nombre d'ids + dédoublonnés. Tranché ainsi (C-15-6-29) plutôt que de laisser le filet anonyme de `create_in_tx` + (`InactiveOrInvalidAccounts`) répondre. Pour un **compte de produit** d'une autre société, cela + remplace aussi le `CreditNoteRevenueAccountsArchived` à `account_number: None` que la 6 ter rend + aujourd'hui (`credit_notes.rs:471-495`) : changement de comportement **inatteignable par + l'application** (atteignable par SQL direct seulement), écrit pour mémoire — T4 n'écrit **pas** de test dessus (finding F5-7 de la P5). + - **Refus nommé, propre à l'avoir** (choix C-15-6-25, qui révise le « aucune variante » de + C-15-6-16 ; étendu à la TVA due par C-15-6-29). Une ligne `active = FALSE` parmi la **créance**, + l'**arrondi** de la vente ou la **TVA due** → variante neuve + `DbError::CreditNoteAccountsArchived(Vec)` (`ArchivedAccount`, + `crates/kesh-db/src/errors.rs:291`), à côté de `ReversalAccountsArchived` (`:604-614`), dont elle + reprend la forme : **400**, **même code `ACCOUNT_ARCHIVED`** (`error_code()`, `errors.rs:736`, + rend la même chaîne pour les deux, `:760`), `details.rejected[]` = `{ accountId, accountNumber }` + par compte, ordre `ORDER BY id` (celui du verrou ; sa sœur `archived_accounts_in_tx` trie par + `number` — sans effet pour un seul compte, dit pour qu'aucun test ne fige l'un pour l'autre). Un + **compte de produit** archivé n'alimente **pas** cette variante : il reste nommé par ligne par la + 6 ter (ci-dessus). Seul + le **message** diffère : clé neuve `credit-note-account-archived`, dans les **quatre** locales — + fr : « Impossible d'émettre l'avoir — compte(s) archivé(s) : 1100. Réactivez le ou les comptes + concernés. » —, sur le patron du bras `ReversalAccountsArchived` + (`crates/kesh-api/src/errors.rs:2945-2973`). **Raison** : sur la route + `POST /api/v1/credit-notes`, le compte de produit archivé rend déjà « Impossible d'émettre + l'avoir — … » (`CREDIT_NOTE_REVENUE_ACCOUNT_ARCHIVED`, clé `credit-note-revenue-account-archived`, + `fr-CH/messages.ftl:291`) : un seul vocabulaire pour un seul geste, face au bouton « Créer un + avoir ». Le **code**, lui, reste celui de la contre-passation : l'avoir en **est** une, et un + intégrateur qui gère `ACCOUNT_ARCHIVED` (lire `details.rejected[]`, réactiver) le gère ici sans + rien apprendre. Deux codes restent donc possibles sur la route — l'un par **ligne** de facture + (produit, `details.rejected[].lineNumber`), l'autre par **compte** (créance ou arrondi de la + vente, TVA due) — et le manuel comme le CHANGELOG le disent (AC8, AC9). **La lecture est celle du + helper partagé** (puce « Un seul helper », ci-dessus — son seul contrat), appelé en mode `Share` : + `create_credit_note` reçoit **toutes** les lignes verrouillées (`id`, `number`, `active` ; `postable`, rendue par le helper, est ignorée), pas + seulement les archivées, fait lui-même le contrôle « ligne absente », puis les partage — créance, + arrondi et TVA due archivés → la variante ; produits archivés → la 6 ter, à sa place. + `archived_accounts_in_tx` (`journal_entries.rs:1723-1754`, non verrouillante, filtre + `active = FALSE`) n'est **pas** le modèle : elle ne sert ici qu'à la comparaison de l'ordre de + `details.rejected[]` (ci-dessus). + La garde `active` de `create_in_tx` reste en place, filet de défense ; elle n'est plus le refus + atteint pour aucun de ces comptes, **hors réactivation concurrente** (finding R5-7 de la P5) : un + compte archivé à l'instantané (étape (2)) puis réactivé et validé avant le verrou est vu actif par + le verrou, archivé par la lecture simple de `create_in_tx` (`journal_entries.rs:96-98`) — le refus + anonyme `InactiveOrInvalidAccounts` reste alors possible, fenêtre de quelques millisecondes, sans + conséquence sur les données. Dit pour qu'aucun test ne prétende l'exclure. + - ⚠️ **Changement délibéré de deux refus existants.** (i) Un avoir sur une facture arrondie dont le + compte d'arrondi a été archivé rendait `ROUNDING_ACCOUNT_NOT_CONFIGURED` (contexte `Issuance`, + #486), dont le message renvoie à *Paramètres → Facturation* — remède **faux**, puisque l'avoir ne + lit plus ce réglage ; il rend désormais `ACCOUNT_ARCHIVED`, qui **nomme** le compte à réactiver + (le refus reste nommé : l'esprit de la contrainte de #523, « sans casser le refus nommé de #486 », + est tenu ; sa lettre — le code — ne l'est pas, et la fiche le dit). (ii) Un avoir dont le compte + débiteurs (des réglages, aujourd'hui) est archivé rendait `INACTIVE_OR_INVALID_ACCOUNTS` + anonyme ; il rend `ACCOUNT_ARCHIVED` nommant le compte **de la vente**. (iii) Un avoir dont le + compte de TVA due des réglages est archivé rendait aussi `INACTIVE_OR_INVALID_ACCOUNTS` anonyme + (garde de `create_in_tx`) ; il rend `ACCOUNT_ARCHIVED` nommant ce compte (C-15-6-29). Le test + `invoices_validate_vat.rs:846` (`a_credit_note_is_refused_when_the_rounding_account_was_archived`) + **change d'assertion** en conséquence, et le Change Log de la story le dit comme tel (ce n'est pas + une régression masquée). La **validation** d'une facture garde + `RoundingAccountNotConfigured { Issuance }` (`invoices.rs:2068`) : elle crée l'écart, elle lit le + réglage. +7. **AC7 — Les commentaires disent vrai.** Le commentaire `credit_notes.rs:609-611` dit désormais ce + qui est recopié (produit par ligne) et ce qui est relu sur la vente (créance, arrondi) ; l'étape (3) + de `create_credit_note` dit pourquoi la créance ne vient plus des réglages (#473) et quels comptes en + viennent encore (TVA due #525, produit de repli D-B2) ; l'étape (7 bis) dit pourquoi l'arrondi se lit + sur la vente (#523) ; l'étape du verrou (AC6) dit pourquoi `active` se lit sous verrou, pourquoi + en partage et non en exclusif (course de lecture, pas de verrou exclusif de plus), pourquoi + **tous** les comptes écrits (la course de lecture), et que la + 6 ter lit `active` dans son résultat. Le doc-comment de `create_credit_note` reçoit une section + **« Ordre des locks »** : facture → règlements → avoir existant → réglages → comptes de l'avoir + (S, par `id`) → exercice → séquence → écriture — il + **renvoie** au doc-comment canonique de `validate_invoice` pour la règle (convention de fréquence, + défense par le rejeu — 15-5e AC5, C54) au lieu de la recopier, ne prétend à l'absence d'aucun + cycle, et ne porte que ce qui est propre à l'avoir : la ligne des réglages qui le sérialise avec la + validation et la saisie fournisseur, et le renvoi au rejeu de la route (AC6). Le doc-comment de la + route `POST /api/v1/credit-notes` (« rejouée sur interblocage ») est celui que pose la **15-5e** + (son AC3) : pas réécrit ici. + **Ce que la 15-5e a déjà réécrit, et que cette story ne réécrit pas** (findings R5-1 = F5-1 de la + P5) : le doc-comment canonique de `validate_invoice` (dont la phrase « Aucun chemin ne verrouille + `accounts` avant `invoices` ou `fiscal_years` », `invoices.rs:1921-1922` au commit `1920381e`, + retirée par la 15-5e AC5), le commentaire « 5 bis » de `write_off_invoice` + (`invoice_settlements_write.rs:466-468` au même commit, réécrit en place par la 15-5e AC5, que la + 15-5d complète) et le Pattern 5 de `docs/MULTI-TENANT-SCOPING-PATTERNS.md` (15-5e AC5). T0 les + **lit** tels que la 15-5e et la 15-5d les laissent. La 15-6a n'y fait qu'**un** geste, et seulement + s'il y a lieu : si l'un de ces textes décrit l'**ordre de l'avoir** tel qu'il était (l'exercice + avant l'arrondi, ou des comptes lus sans verrou), cette phrase est corrigée — l'avoir prend + désormais tous ses comptes, en partage, avant l'exercice ; le Dev Agent Record dit ce qui a + été trouvé (y compris « rien »). Le doc-comment de `RoundingContext::Issuance` (`crates/kesh-db/src/errors.rs:729`, « Une pièce émise — + facture validée, avoir — ») ne cite plus l'avoir ; le message + `error-rounding-account-not-configured-issuance` (« Le total de cette pièce… ») reste exact pour la + facture et n'est pas modifié. Le doc-comment de `generate_invoice_journal_lines_rounded` + (`invoices.rs:1870-1878`), qui ne protège aujourd'hui que « la créance reste la PREMIÈRE ligne au + débit », dit aussi que **l'arrondi reste la DERNIÈRE ligne** — l'avoir l'y relit + (`sale_rounding_account`) ; une ligne ajoutée après lui bloquerait tous les avoirs de factures + arrondies (`Invariant`). Le commentaire de classement que la 15-5b (son AC6) pose dans + `accept_one_invoice` sur la lecture de la créance est **conservé** et réécrit pour désigner le + lecteur `sale_receivable_account` au lieu d'une requête et de numéros de ligne qui n'existent + plus. Les doc-comments et commentaires que la 15-5b et la 15-5d laissent sur l'**exemption de + l'avoir** à la garde à l'usage (C35 : « la créance sera lue sur l'écriture de vente par la + 15-6a ») sont réécrits au présent : l'avoir lit la créance sur la vente ; seule la TVA due reste + relue dans les réglages (#525). +8. **AC8 — Manuel.** + - `docs/manual/fr/user-manual.tex` § *Avoirs et notes de crédit* (`\label{sec:avoirs}`, puce + `:1155`) : la promesse « le solde du client revient à zéro » devient **vraie** et le texte dit + pourquoi — l'avoir crédite le compte débiteurs que la facture a débité, et contre-passe l'arrondi + sur le compte que la facture a mouvementé, même si les comptes par défaut ont changé depuis. Il dit + aussi, **sans la présenter comme juste**, la limite connue (choix C-15-6-8) : la TVA de l'avoir est + contre-passée sur le compte de TVA due **actuellement désigné** dans les réglages ; changer ce + réglage entre une facture et son avoir est une limite connue de cette version. + - Même section, un `keshwarning` neuf à côté de « Avoir refusé : compte de produit archivé » + (`:1171`) : **« Avoir refusé : compte débiteurs, d'arrondi ou de TVA archivé »** — il nomme + les trois comptes (celui que la facture a débité, celui qui a reçu son arrondi, le compte de TVA + due désigné dans les réglages — C-15-6-29), cite le message tel + quel (« Impossible d'émettre l'avoir — compte(s) archivé(s) : … », AC6) et renvoie à la **même + procédure** en trois étapes : réactiver le compte (en retirant d'abord le rôle *Créances clients* du nouveau compte si nécessaire : + la réactivation est refusée tant qu'un autre compte le porte, `user-manual.tex:358-359`), émettre + l'avoir, ré-archiver. Pour le compte + de **TVA due**, qui est celui **des réglages** et non celui de la vente (#525), il dit l'**autre + issue** (finding F5-7 de la P5) : rétablir le réglage — un administrateur désigne dans + *Paramètres → Facturation* un compte de TVA due actif, de préférence celui que la facture a + crédité —, sans quoi la procédure ferait réactiver un compte que la facture n'a jamais mouvementé. + Il dit que ce + refus est distinct de celui du compte de produit (qui nomme une **ligne**) : le premier nomme un + **compte**, et les deux peuvent se suivre (le compte débiteurs, d'arrondi ou de TVA est contrôlé + d'abord, AC6). + - `user-manual.tex:363` (avertissement « Avant d'archiver un compte de produit, pensez aux + avoirs », `\label{sec:plan-comptable-archivage}`) : étendu au **compte débiteurs**, au **compte + de différences d'arrondi** et au **compte de TVA due** — un compte que des factures validées ont + mouvementé reste nécessaire tant qu'un avoir peut les viser. + - `docs/manual/fr/admin-manual.tex:2027` (*Compte de différences d'arrondi*, « un avoir le reprend + et l'annule ») : une phrase — changer de compte d'arrondi ne déplace pas les factures déjà + émises : leur avoir contre-passe l'arrondi sur le compte qu'elles ont mouvementé ; gardez l'ancien + actif tant qu'un avoir peut les viser (sinon : réactiver, émettre, ré-archiver). + - Les phrases « un avoir le reprend et l'annule » (`user-manual.tex:847`, `admin-manual.tex:2027`) + restent vraies — les relire, ne pas les réécrire ; de même `user-manual.tex:1754` (« changement du + compte clients dans les réglages », balance âgée). + - **Frontière 15-5b / 15-5d — lire, pas greper une formulation.** La 15-5b (son AC17 (iv)) et la + 15-5d (son AC8) écrivent au manuel que l'avoir **relit la créance et la TVA due dans les + réglages** (« l'avoir fait exception … »), aux encadrés de `user-manual.tex` ~`:380` (§ *Rôles des + comptes*) et ~`:390` (§ comptes de clôture), et la 15-5b touche `admin-manual.tex:2016-2029` + (comptes par défaut, arrondi). Ce texte n'est **pas encore mergé** : sa formulation exacte est + inconnue, et un grep de la formulation des **fiches** (« relit la créance ») peut rendre zéro et + laisser la phrase fausse. **T0 lit donc ces encadrés et cette section en entier, après le merge + des 15-5**, aux numéros de `HEAD` ; puis grepe la **valeur** — `avoir` à moins de 200 caractères + de `créance`, `réglages`, `relu` — sur le `.tex` et sur les PDF aplatis + (`pdftotext … - | tr '\n' ' ' | tr -s ' '`). Toute phrase qui dit que l'avoir relit la + **créance** dans les réglages devient fausse : la corriger ici ; la TVA due reste vraie (#525). + - PDF régénérés (`make fr` dans `docs/manual/`) et contrôlés **aplatis**, sur des fragments + **absents du PDF actuel** (« revient à zéro » y est déjà : il ne prouve pas la régénération), et + avec `.` à la place de chaque apostrophe dans le motif (le PDF emploie l'apostrophe typographique) : + `pdftotext docs/manual/fr/user-manual.pdf - | tr '\n' ' ' | tr -s ' ' | grep -o "[^.]*"`, + puis le même `grep -o` sur un fragment de la phrase de la TVA, du `keshwarning` neuf, de + l'avertissement étendu, et sur `admin-manual.pdf` pour la phrase neuve de l'arrondi. +9. **AC9 — CHANGELOG.** Sous `## [0.13.0] — Non publié`, section `### Corrigé` : une entrée qui dit + l'effet pour l'utilisateur et renvoie à [#473](https://github.com/guycorbaz/kesh/issues/473) et + [#523](https://github.com/guycorbaz/kesh/issues/523) ; section `### Modifié` : le refus d'un avoir dont + le compte débiteurs ou d'arrondi de la facture, ou le compte de TVA due, a été archivé devient + `ACCOUNT_ARCHIVED` (il était `INACTIVE_OR_INVALID_ACCOUNTS`, ou `ROUNDING_ACCOUNT_NOT_CONFIGURED` + pour l'arrondi), nomme le + compte dans `details.rejected[]` et dit « Impossible d'émettre l'avoir » ; la route garde + `CREDIT_NOTE_REVENUE_ACCOUNT_ARCHIVED` pour un compte de produit de ligne (deux codes, un même + libellé de geste — AC6) ; un réglage débiteurs vide ne refuse plus l'avoir (`CONFIGURATION_REQUIRED`) — changements + visibles d'une intégration par clé d'API (`POST /api/v1/credit-notes` est ouverte aux clés, + `docs/api-external.md` § 7), rubrique choisie comme la 15-5a. `docs/api-external.md` ne cite ni ces + codes pour l'avoir ni la route (`grep -n "ROUNDING_ACCOUNT\|credit-notes" docs/api-external.md` vide + au 2026-10-08) : **sans objet**, à écrire « vérifié » au Dev Agent Record. + **Propriétaire de la section** : la **première + story de la version mergée** la crée en tête des versions si elle est absente ; les suivantes y + ajoutent leur entrée (règle commune aux 15-5a/b/c et 15-6a/b/c/d ; conflit de rebase attendu et + trivial). + **`docs/MULTI-TENANT-SCOPING-PATTERNS.md`, Pattern 5** (finding F5-6 de la P5) : le tableau + *Where This Applies* (`:307-319` au commit `1920381e`), dans la forme que la **15-5e** lui donne + (son AC5 : « Global Lock Order » devenu convention de fréquence, deux lignes de flux comptables + corrigées, « Deny list » retirée), gagne **une** ligne pour `POST /api/v1/credit-notes` + (`repositories/credit_notes.rs::create_credit_note`) — invoices → invoice_settlements → + credit_notes → company_invoice_settings → accounts (S, par `id`) → fiscal_years → credit_note_number_sequences → journal_entries (lignes : S sur les comptes + par la clé étrangère) ; rejouée sur interblocage (15-5e). T0 vérifie que la 15-5e ne l'a pas déjà + ajoutée ; si oui, la ligne est seulement mise à jour (comptes verrouillés avant l'exercice). + +## Tasks / Subtasks + +- [ ] **T0 — Rebase et recompte** avant de coder : rebase sur `main` après le merge des 15-5a à 15-5e ; + **refaire tous les numéros de ligne** cités par cette fiche sur `HEAD` ; **lire** le doc-comment + canonique de `validate_invoice`, le commentaire « 5 bis » et le Pattern 5 tels que la 15-5e (et la + 15-5d) les laissent (AC7, AC9) ; **constater** que la route `POST /api/v1/credit-notes` est rejouée + par la 15-5e (AC6) ; relever au Dev Agent Record les ordres de verrous de la validation, du règlement, + du solde du reste et de la saisie fournisseur (AC6) ; constater la forme du helper de verrou de + liste de la 15-5d (mode paramétré ou non, AC6) ; relire le + commentaire de classement posé par la 15-5b dans `accept_one_invoice` (AC7) ; **lire en entier** les + encadrés `user-manual.tex` ~`:380` et ~`:390` et `admin-manual.tex:2016-2029` tels que les 15-5b/5d + les laissent (AC8 — pas de grep d'une formulation de fiche) ; **recenser les tests que la 15-5d + ajoute à `credit_notes_repository.rs`** (dont celui de C35, § *Tests*, 13) et le nombre total de + tests du fichier ; **relever** au Dev Agent Record l'ordre des verrous que la 15-5d pose dans + `validate_invoice` (comptes de réglage avant l'exercice) — un relevé, sans exigence de conformité : + sous C54 l'ordre est une convention de fréquence, non une règle à laquelle l'AC6 se conformerait + (finding R6-3 de la P6) ; recompter l'inventaire (§ *Inventaire*) par ses commandes — tout site + neuf est résolu ici ou ajouté aux angles morts avec sa raison. +- [ ] **T1 — Lecteurs** (AC1, AC4) dans `invoice_settlements.rs`, doc-comments compris. +- [ ] **T2 — Remplacer les trois copies** (AC2) ; vérifier par le `grep --include='*.rs'` de l'AC2. +- [ ] **T3 — Avoir** (AC3, AC4, AC5, AC6, AC7) dans `credit_notes.rs` : verrou en une instruction + `ORDER BY id`, par le helper de verrou de liste partagé avec la 15-5d en mode + `Share` (AC6 : tous les comptes écrits, 6 ter lue dans son résultat, ligne absente → `Invariant` ; + `EXPLAIN` relevé) ; agrégation de la TVA extraite en fonction pure partagée par le générateur et le + calcul des ids (AC6) ; **pas** de rejeu à poser (la 15-5e le porte, AC6) ; phrase corrigée dans un + texte de la 15-5e seulement s'il décrit l'ancien ordre de l'avoir (AC7) ; variante + `DbError::CreditNoteAccountsArchived`, son `error_code()` (`ACCOUNT_ARCHIVED`), son bras dans + `crates/kesh-api/src/errors.rs` et la clé `credit-note-account-archived` dans les **quatre** + `messages.ftl` (AC6) ; doc-comments de `RoundingContext::Issuance`, de + `generate_invoice_journal_lines_rounded` et de `create_credit_note` (ordre des locks). +- [ ] **T4 — Tests** (§ *Tests*) — chaque test neuf **rougit d'abord** sur le code non corrigé, sauf + ceux des lecteurs (4, 5, 8 : fonctions neuves), le témoin 10, écrit en premier, et les tests 17 + (identifiant étranger : une mesure, dont l'issue attendue est le rouge puis `owned_account_ids`, + sauf si la 15-5d l'a déjà adopté — § *Tests*, 17) et 18 (bras d'une variante neuve). Aucun test de + « non-interblocage » (C54, 15-5e AC4). +- [ ] **T5 — Manuel + PDF** (AC8), **CHANGELOG** (AC9) ; `docs/api-external.md` vérifié sans objet + (AC9) ; ligne de l'avoir au Pattern 5 de `docs/MULTI-TENANT-SCOPING-PATTERNS.md` (AC9). +- [ ] **T6 — Gates** : gate complet backend (règle `kesh-db` : un dépôt est touché, le ciblage est + interdit **même en boucle de revue**), base remise à zéro avant ; frontend non touché ; E2E complet + au dernier commit de code (décision D7 — la spec `frontend/tests/e2e/credit-notes.spec.ts` passe par + cet avoir). + +## Inventaire — les sites qui lisent un compte de créance, de dette ou d'arrondi **après** la pièce + +Règle « Inventorier les sites NON RÉSOLUS » : l'ensemble clos est celui des **écrivains d'écritures**, +pas une liste de formes. Commandes de recompte (à rejouer en T0) : + +```sh +grep -rn "journal_entries::create_in_tx\|reverse_in_tx\|reverse_owned_in_tx" crates/kesh-db/src crates/kesh-api/src +grep -rn "INSERT INTO journal_entry_lines" crates/*/src +grep -rn "default_receivable_account_id\|default_payable_account_id\|rounding_account_for_write" crates/*/src +grep -n "create_in_tx(" crates/kesh-db/src/repositories/journal_entries.rs # appels intra-module (create_opening_entry) +grep -rn "delete_in_tx" crates/kesh-db/src crates/kesh-api/src # dévalidation +``` + +Relevé au 2026-10-08 (commit `1920381e`) : + +| écrivain | geste | compte relu | verdict | +|---|---|---|---| +| `invoices.rs:2005-2010` `validate_invoice` | **crée** la créance et l'arrondi | réglages | légitime — c'est la vente | +| `credit_notes.rs:360-364` `create_credit_note` (créance) | après la vente | réglages | **résolu ici (AC3)** — #473 | +| `credit_notes.rs:525` `create_credit_note` (arrondi) | après la vente | réglages | **résolu ici (AC4)** — #523 | +| `invoice_settlements_write.rs:104` `settle_invoice` | après | écriture de vente | déjà juste ; copie → lecteur (AC2) | +| `invoice_settlements_write.rs:434` `write_off_invoice` | après | écriture de vente | déjà juste ; copie → lecteur (AC2) | +| `reconciliation.rs:1425` `accept_one_invoice` | après | écriture de vente | déjà juste ; copie → lecteur (AC2) | +| `reconciliation.rs:1487` `accept_one_invoice` (arrondi `Payment`) | après | réglages | légitime — écart **nouveau**, né au rapprochement (cf. ci-dessous) | +| `supplier_invoices.rs:358-362` `create_in_tx` | **crée** la dette | réglages | légitime — c'est l'achat (import automatique compris, même chemin) | +| `supplier_invoices.rs:582-593` `pay_in_tx` | après | écriture d'achat (ligne de crédit) | déjà juste | +| `journal_entries.rs` `reverse_in_tx` (annulation de facture fournisseur, annulation de règlement, contre-passation) | après | **inversion ligne à ligne** de l'écriture d'origine | juste par construction | +| `invoices.rs` `unvalidate` (dévalidation) — `journal_entries::delete_in_tx` | supprime l'écriture de vente | aucun compte relu | hors classe | +| `journal_entries.rs` `create_opening_entry` (appel intra-module à `create_in_tx`) | — | soldes saisis | hors classe | +| `reconciliation.rs` `accept_one_split`, `accept_one_rule`, `post_manual` | — | aucun compte de créance lu | hors classe | +| `opening_complement.rs:641`, saisie manuelle | — | lignes saisies | hors classe | + +Le compte d'arrondi du **règlement** (`settle_invoice`, contexte `Payment`), celui du **rapprochement** +(`accept_one_invoice`, `reconciliation.rs:1487`) et les comptes des natures de solde +(`write_off_invoice`) sont des écarts **nouveaux**, nés au règlement : ils lisent légitimement les +réglages du moment (ils ne contre-passent rien). + +**Angles morts assumés** (même classe — « un compte relu dans les réglages après la pièce ») : + +- **TVA due de l'avoir** (`credit_notes.rs:511`, `settings.default_vat_payable_account_id`) : compte + **courant**, convention partagée avec le solde du reste (`vat_payable_account_for_write`). Écriture + fausse si le réglage a changé entre la facture et l'avoir : **tracé par #525** (P1, jalon de la TVA, + report assumé — choix C-15-6-8). Le manuel dit la limite sans la présenter comme juste (AC8). +- **Produit de repli** des lignes sans compte (D-B2) : inchangé, documenté depuis la 16-1a-bis. + **Tracé par #525**, depuis le commentaire de l'orchestrateur du 2026-10-08 (« Même famille … compte + de produit de repli … À traiter avec la TVA due de cette issue ») — la P5 l'avait écrit « non + tracé », vrai à l'écriture, faux trente-huit secondes après (finding R6-2 de la P6 de la 15-6b). + +**Classe « un compte lu sans verrou pendant l'avoir »** — fermée par l'AC6 (C-15-6-29) : tous les +comptes que l'avoir écrit (créance, arrondi, TVA due, produits) sont verrouillés en partage avant +l'exercice, et leur état actif est lu dans ce verrou (la 6 ter comprise). L'angle mort que la P3 +inscrivait ici (produits et TVA due en lecture simple) **disparaît** ; la dette « LOW acceptée » de la +validation (`invoices.rs` ~`:2096`) n'est pas touchée. + +**Sites non résolus d'une autre classe — cycles d'interblocage de l'avoir** (AC6 ; **nommés, non +exhaustifs** — l'absence d'autres cycles n'est pas affirmée, choix C54 ; tous détectés par InnoDB et +couverts par le **rejeu de la route**, posé par la 15-5e et figé pour l'avoir par le test 15) : + +| cycle | avec | né de | verdict | +|---|---|---|---| +| (i) facture X / exercice X ↔ arrondi | `accept_one_invoice` de la **même** facture (`reconciliation.rs:1487`, `:1520`, avant `:1703`), ou lot dont une proposition précédente tient l'exercice | préexistant (l'exercice) | **#536**, fermée par le rejeu des deux côtés (15-5e, C57) | +| (ii) S par `id` ↔ X nature / arrondi puis TVA due | `write_off_invoice` d'une autre facture : nature = produit de l'avoir (rare), ou reste hors centime et facture de l'avoir arrondie (fréquent, P5 R5-2) | préexistant (ordre après l'exercice), déplacé avant l'exercice | non résolu, non réordonné (C54) ; rejeu des deux côtés | +| (iii) S par `id` ↔ X compte interne puis arrondi | `settle_invoice` par compte interne = un compte de l'avoir, avec écart (P5, F5-3) | le S de l'AC6 avant l'exercice | non résolu, configuration sans sens ; rejeu des deux côtés | +| (iv) S tenu ↔ X en attente ↔ S de clé étrangère | `UPDATE accounts` + un flux qui tient l'exercice **sans** la ligne des réglages (saisie manuelle, règlement, solde du reste, rapprochement) — **pas** la validation ni la saisie fournisseur, sérialisées avec l'avoir par les réglages | **neuf** (le S de l'AC6) | non résolu, rare ; rejeu côté avoir et côté de ce flux (15-5e) | + +## Tests + +Tous en `#[sqlx::test(migrations = …)]` sur le squash — `"./test-schema"` dans `crates/kesh-db/tests`, +`"../kesh-db/test-schema"` dans `crates/kesh-api/tests` (tests 10, 12, 15 ; finding R6-6 de la P6) —, +y compris ceux des lecteurs, qui lisent une écriture réelle, **sauf le 18**, test unitaire du bras +d'erreur HTTP (sans base). + +**`crates/kesh-db/tests/credit_notes_repository.rs`** (neuf tests au commit `1920381e`, lignes +d'attribut `#[sqlx::test]` : `:101`, `:183`, `:227`, `:268`, `:309`, `:352`, `:513`, `:555`, `:593` ; +**dix** après le merge de la 15-5d, qui y ajoute le test de C35 — T0 recompte), patron `credit_note_single_rate_reverses_invoice` +(`:101`) : + +1. **`credit_note_credits_the_sale_receivable_after_settings_change`** — valider une facture (créance + sur 1100), créer un compte d'actif imputable neuf (ex. `1101`), poser + `default_receivable_account_id` sur lui, émettre l'avoir : la ligne de crédit vise **1100** pour le + TTC ; solde de 1100 sur la société = 0 ; **aucune** ligne sur 1101. *(Ce test rougit sur le code + actuel — le vérifier avant le correctif, pour qu'il ne soit pas muet.)* +2. **`credit_note_needs_no_receivable_setting`** — même montage, réglage **débiteurs** mis à `NULL` + après la validation, **par SQL direct** : l'avoir est émis (il échouait jusqu'ici en `ConfigurationRequired`). Borné à + la créance : le réglage de produit par défaut reste posé (AC5). +3. **`credit_note_refused_when_sale_receivable_archived`** — 1100 archivé après la validation par + `UPDATE accounts SET active = FALSE` (comme le test `:846` cité plus bas ; `accounts::archive`, + `accounts.rs:604-634`, ne contrôle que les sous-comptes actifs et permettrait aussi d'archiver un + compte mouvementé — le SQL direct est retenu pour ne pas dépendre de la version optimiste) : + `DbError::CreditNoteAccountsArchived` dont l'unique `ArchivedAccount` nomme **1100** + (`account_number = Some("1100")`), **rien** d'écrit (ni avoir, ni écriture, ni numéro consommé, + facture toujours `validated`). *(Rougit sur le code actuel : `InactiveOrInvalidAccounts`.)* +4. **`sale_receivable_account_is_scoped_to_the_company`** — le lecteur de l'AC1, appelé avec l'écriture + de vente d'une société et l'id d'une **seconde** société → `None`. Seconde société créée par + `companies::create` (patron `crates/kesh-db/tests/bank_accounts_repository.rs:401`). + +**`crates/kesh-db/tests/invoices_validate_vat.rs`, module `arrondi_5_centimes`** (`:592`) — ses helpers +`setup` (`:608`, désigne 6940 par `designate_rounding_account`) et `create_and_validate` (`:57`) sont +privés à ce fichier, d'où ce placement : + +5. **`sale_receivable_reader_skips_a_negative_rounding_line`** — facture à arrondi **négatif** + (234.52 → −0.02, une seconde ligne de débit après la créance) : le lecteur de l'AC1 rend 1100, pas + le compte d'arrondi. +6. **`a_credit_note_reverses_the_sale_rounding_after_redesignation`** (#523) — facture à arrondi positif + (123.44 → +0.01) sur 6940 ; puis un second compte de charge inséré à la main (`6941` — + `designate_rounding_account` insère toujours `6940` et ne peut pas servir deux fois) et désigné dans + `default_rounding_account_id` ; l'avoir : la ligne d'arrondi vise **6940**, solde de 6940 = 0, + **aucune** ligne sur 6941. *(Rougit sur le code actuel.)* +7. **`a_credit_note_needs_no_rounding_setting`** — même montage, `default_rounding_account_id` mis à + `NULL` après la validation, **par SQL direct** : l'avoir est émis, ligne d'arrondi sur 6940. *(Rougit sur le code + actuel : `RoundingAccountNotConfigured`.)* +8. **`sale_rounding_reader_refuses_a_mismatched_line`** — le lecteur de l'AC4 appelé avec l'arrondi de + **signe opposé** à celui de la facture → `DbError::Invariant`. +9. **Modifié, délibérément** (AC6) : `a_credit_note_is_refused_when_the_rounding_account_was_archived` + (`:846`) — l'assertion `DbError::RoundingAccountNotConfigured { context: Issuance }` devient + `DbError::CreditNoteAccountsArchived` nommant **6940** ; les assertions suivantes (rien d'écrit, + avoir accepté après réactivation avec le **premier** numéro) restent. Le doc-comment du test dit + pourquoi (AC6) et **réécrit sa phrase sur le compteur** (`invoices_validate_vat.rs:842-844`, « la + transaction emporte le compteur tiré plus tôt ») : le refus précède désormais la séquence (étape + (5)), aucun numéro n'est tiré ; l'assertion « premier numéro après réactivation » fige donc + l'**ordre** (refus avant la séquence), plus le rollback — le doc-comment le dit, pour que le test ne + prétende pas prouver ce qu'il ne prouve plus. + +**`crates/kesh-api/tests/reconciliation_e2e.rs`** : + +10. **`invoice_proposal_with_a_foreign_sale_entry_is_malformed`** — non-régression de l'AC1/AC2, écrit + **avant** le remplacement de la copie : l'écriture de vente d'une facture repointée en SQL + (`UPDATE invoices SET journal_entry_id = …`) sur une écriture d'une **autre société** — le lecteur, + porté par `je.company_id`, ne trouve aucune ligne : `failed[]` porte + `INVOICE_SALE_ENTRY_MALFORMED` avec `details = { "reason": "no_debit_line_on_sale_entry", + "saleEntryId" }`, HTTP 200, transaction bancaire toujours en attente. Il passe avant **et** après + le correctif — c'est un témoin, pas un test rouge d'abord. **Montage plus léger, au choix** : le + fichier ne crée qu'une société (`reconciliation_e2e.rs:145`) ; au lieu d'en construire une seconde + avec exercice et écriture, un **en-tête d'écriture sans ligne** dans la même société, inséré en SQL + et désigné par `UPDATE invoices SET journal_entry_id = …`, rend le même `None`. + +**`crates/kesh-db/tests/credit_notes_repository.rs`** (suite) : + +11. **`credit_note_waits_for_a_concurrent_archive_of_the_sale_receivable`** — la course de l'AC6, + sur la forme exacte du patron `credit_note_waits_for_a_concurrent_settlement` (attribut `:593`, + attente `:631`) : facture **sans arrondi** (pour que seul le verrou de l'AC6 lise `accounts` sous + verrou) ; une transaction tenue à la main archive 1100 (`UPDATE accounts SET active = FALSE`) sans + valider ; une tâche (`tokio::spawn`) émet l'avoir ; + `kesh_db::test_fixtures::attendre_une_requete_en_cours(&pool, &["FROM accounts", "LOCK IN SHARE MODE"], || avoir.is_finished())` + (`crates/kesh-db/src/test_fixtures.rs:560`) doit rendre `true` — l'avoir est **vu** en attente du + verrou ; s'il rend `false` (l'avoir a fini sans attendre), `panic!` avec son issue. **Aucun délai + fixe** : un « pas fini au bout de 300 ms » passerait à vide sur une machine chargée. La transaction + concurrente n'est validée qu'**après** l'attente vue ; l'avoir rend alors + `CreditNoteAccountsArchived` nommant 1100, rien d'écrit. Le doc-comment du test dit que ses motifs + sont couplés à la forme du verrou (`LOCK IN SHARE MODE`) et pourquoi la facture est sans arrondi. + *(Rougit sur le code actuel — mais pas par le `panic!` « l'avoir a fini » : sur ce code, l'avoir + n'aboutit pas, son `INSERT INTO journal_entry_lines` (clé étrangère vers `accounts`) pose un S sur + 1100 et attend le X de la transaction de test ; aucune requête `FROM accounts … LOCK IN SHARE MODE` + n'apparaît, et c'est l'`assert!` de délai d'`attendre_une_requete_en_cours` + (`test_fixtures.rs:585-588`, dix secondes) qui rougit — symptôme à attendre, finding R5-3 de la + P5.)* + +**`crates/kesh-api/tests/invoice_echeancier_e2e.rs`** — seul fichier de `kesh-api/tests` qui émet un +avoir **par la route** (`POST /api/v1/credit-notes`, `:742` ; les autres appellent le dépôt) : + +12. **`credit_note_route_names_the_archived_sale_receivable`** — la frontière HTTP du changement de + contrat annoncé au CHANGELOG (AC9) : facture validée, 1100 archivé en SQL, `POST + /api/v1/credit-notes` → **400**, `error.code == "ACCOUNT_ARCHIVED"`, + `error.details.rejected[0].accountNumber == "1100"` (et `accountId`), `error.message` contient + « Impossible d'émettre l'avoir » et « 1100 ». *(Rougit sur le code actuel : + `INACTIVE_OR_INVALID_ACCOUNTS`.)* Aucun Vitest n'est en cause : l'écran affiche `err.message` sans + lire de code (`frontend/src/routes/(app)/invoices/[id]/+page.svelte:99-112`, seul appelant de + `createCreditNote`, `credit-notes.api.ts:33`). Preuve à rejouer en T4, **code neuf compris** : + `grep -rn "ROUNDING_ACCOUNT_NOT_CONFIGURED\|INACTIVE_OR_INVALID_ACCOUNTS\|ACCOUNT_ARCHIVED" frontend/src` + — `ACCOUNT_ARCHIVED` y est lu par plusieurs sites (`journal-entries/[id]/+page.svelte:166`, + `reconciliation-cancel.ts:67`, `settlement-cancel-blocked.ts:46`, `invoice-cancel.ts:50`…), + **aucun** sur le chemin de création d'avoir ; les deux autres codes ne rendent que + `JournalEntryForm.svelte:178`, hors avoir. + +**`crates/kesh-db/tests/credit_notes_repository.rs`** (suite) : + +13. **Modifié, délibérément — le test de C35 que la 15-5d ajoute** (15-5d, § *Tests* : « avoir sur une + facture dont la créance des réglages est devenue non imputable → émis »). Après la 15-6a, l'avoir + ne lit plus ce réglage : le test resterait vert **pour une autre raison** et ne figerait plus rien + (passe à vide, forme exacte de la 16-1a). Il est **ré-ancré** : la créance **de la vente** (1100) + rendue non imputable (`postable = FALSE`) après la validation → avoir émis, crédit sur 1100 (la + doctrine « seule l'inactivité bloque », AC4/AC6) ; son nom et son doc-comment citent C-15-6-2 et + C-15-6-7 au lieu de l'exemption de la garde à l'usage. Le Change Log de la story dit qu'il change de + sens. La 15-5d est une dépendance **ferme** (en-tête) : si elle n'est pas mergée au moment de T0, + la story **attend**, comme pour la 15-5e (finding R6-10 de la P6). +14. **`credit_note_archived_sale_receivable_is_refused_before_the_fiscal_year`** — l'ordre des refus + de l'AC6 : 1100 archivé **et** date d'avoir hors de tout exercice ouvert → + `CreditNoteAccountsArchived` (et non `FiscalYearInvalid`) : le verrou des comptes de l'avoir + précède bien l'exercice. *(Rougit sur le code actuel : `FiscalYearInvalid`, l'exercice étant lu + avant tout compte.)* + +**`crates/kesh-api/tests/invoice_echeancier_e2e.rs`** (suite) : + +15. **`credit_note_route_replays_when_it_is_the_deadlock_victim`** — le rejeu de la route, **posé par + la 15-5e** (son AC3), étendu à l'attente **neuve** que l'AC6 crée (le S sur les comptes de + l'avoir) — les tests de la 15-5e font de la validation, du règlement et de son annulation la + victime, pas l'avoir. Placé dans ce fichier ou dans `crates/kesh-api/tests/rejeu_interblocage_e2e.rs` + que la 15-5e crée (choix écrit au Dev Agent Record) ; sur le patron + `accept_replays_the_batch_when_it_is_the_deadlock_victim` (`reconciliation_e2e.rs:4426`) : une + transaction de test **s'alourdit** — InnoDB choisit pour victime la transaction la plus légère — + **exactement comme le patron** (`reconciliation_e2e.rs:4482-4496`) : la table `lest_interblocage` + créée par DDL **hors** de la transaction, puis `seq_1_to_50` inséré dix fois dans la transaction ; + **jamais** un lest dans une table applicative, dont les clés étrangères poseraient des verrous sur + `accounts` et changeraient le cycle (finding R5-8 de la P5) —, puis prend `SELECT … FROM accounts WHERE id = <1100> FOR + UPDATE` ; une tâche émet l'avoir **par la route** ; `attendre_une_requete_en_cours(&pool, &["FROM + accounts", "LOCK IN SHARE MODE"], …)` doit rendre `true` (l'avoir tient la facture et les réglages + et attend 1100) ; le test demande alors `SELECT id FROM invoices WHERE id = ? FOR UPDATE` et + **doit l'obtenir** (`.expect` — sinon c'est lui la victime et le test ne prouve rien, patron de la + 15-5e) → cycle, InnoDB annule l'avoir ; le test annule sa transaction ; le rejeu repart à neuf : + **201**, un seul avoir, facture `cancelled`, premier numéro d'avoir. *(Rougit sur le code actuel — + aucune attente `LOCK IN SHARE MODE` n'est vue — et sur la mutation qui retire l'enveloppe de la + route : 500, consignée au Dev Agent Record.)* Il exerce le **prédicat** (`is_deadlock_error` sur le + `DbError` que rend le dépôt), pas seulement la présence de l'enveloppe ; son doc-comment dit + pourquoi le montage est déterministe. + +**`crates/kesh-db/tests/credit_notes_repository.rs`** (suite) : + +16. **`credit_note_refused_when_vat_payable_archived`** — le compte de TVA due des réglages archivé en + SQL après la validation d'une facture avec TVA → `DbError::CreditNoteAccountsArchived` nommant ce + compte (C-15-6-29), rien d'écrit. *(Rougit sur le code actuel : `InactiveOrInvalidAccounts`.)* + +**`crates/kesh-db/tests/credit_notes_repository.rs`** (suite) — P5 (C-15-6-32) : + +17. **`credit_note_lock_leaves_a_foreign_account_unlocked`** — l'identifiant étranger (AC6, patron du + test de la 15-5d, choix C51) : un compte d'une **seconde** société, **sans** `UPDATE` des réglages (l'appel + direct reçoit ses ids : le réglage n'y jouerait aucun rôle) ; une connexion ouvre une transaction et appelle **directement** le + helper de verrou de liste en mode `Share` avec des ids explicites (la créance de la vente et + l'identifiant étranger), **sans conclure** ; une seconde connexion tente `SELECT id FROM accounts WHERE id = ? FOR UPDATE NOWAIT` + sur la ligne étrangère → **réussit**. S'il échoue, le helper prend le patron `owned_account_ids` + (AC6) et le test reste. *(C'est une mesure, pas un test rouge d'abord. **Issue attendue, d'après la + mesure du dépôt : rouge** — `opening_complement.rs:434-436` a mesuré à deux sessions qu'un verrou + par clé primaire prend la ligne d'une autre société avant que le filtre `company_id` ne l'écarte, + et sous REPEATABLE READ ce verrou n'est pas relâché ; un S suffit à faire échouer le `NOWAIT` — + d'où, vraisemblablement, `owned_account_ids`. **Sauf** si la 15-5d l'a déjà adopté par son propre + test de C51 : T0 le constate, et le test 17 est alors vert d'emblée. Finding R6-5 de la P6.)* +**`crates/kesh-api/src/errors.rs`, module de tests** (patron du test unitaire du bras +`CreditNoteRevenueAccountsArchived`, `:3715`) : + +18. **Bras de `CreditNoteAccountsArchived`** (finding R5-9 de la P5) — la variante rend **400**, + `code = "ACCOUNT_ARCHIVED"`, `details.rejected[0]` = `{ accountId, accountNumber }`, et un message + construit sur la clé `credit-note-account-archived` (repli fr compris si l'i18n n'est pas + initialisée). Les quatre locales restent contrôlées par la parité de clés seule ; la traduction se + relit en revue. + +Soit **16 tests neufs et 2 tests modifiés** (9, 13) — périmètre : du commit de spécification à la fin +du développement, avant revue ; le 13 n'existe qu'après le merge de la 15-5d. + +Non-régression : les tests de `credit_notes_repository.rs` (neuf, dix après la 15-5d), `invoices_validate_vat.rs` (les deux +autres tests d'avoir arrondi, `:783` et `:820`), `invoice_settlement.rs`, `invoice_write_off.rs`, +`reconciliation_e2e.rs`, et **`crates/kesh-db/tests/invoices_line_revenue_account.rs`** — c'est lui +qui fige que la 6 ter, dont l'AC6 change la source de `active`, garde son code, son message par +ligne et sa place (finding F6-5 de la P6) : `credit_note_uses_materialized_account_not_current_default` +(`:279`), `credit_note_fails_when_snapshot_account_archived` (`:569`, `line_number == Some(1)`, +`account_number == Some("3200")`), `credit_note_ignores_postable_and_type_changes` (`:615`), +`null_and_explicit_default_merge_into_one_credit_line` (`:789`). + +## Dev Notes + +### Ce qui doit être préservé + +- **Verrous et ordre** de `create_credit_note` : facture `FOR UPDATE` (1), règlements `FOR UPDATE` + (garde 25-4-a), avoir existant `FOR UPDATE`, réglages (3), puis l'exercice (4) et la séquence (5). + Les deux **lecteurs** s'insèrent après (3) et ne prennent aucun verrou : ils lisent des **lignes + d'écriture**, gelées (une écriture enregistrée ne se modifie plus). L'**état des comptes**, lui, ne + l'est pas : il est verrouillé à part, **en partage, juste après les lecteurs et avant l'exercice**, + par l'AC6 — le gel de l'écriture ne dit rien de l'état actif du compte qu'elle vise. Ordre canonique + de `validate_invoice` (`invoices.rs:1916-1925` au commit `1920381e`) ; sa phrase, citée exactement + (`invoices.rs:1921-1922`) : « Aucun chemin ne verrouille `accounts` **avant** `invoices` ou + `fiscal_years`. » — en tension avec son propre « 1 bis » (`accounts` avant `fiscal_years`), et + retirée par la **15-5e** (son AC5), non par cette story (AC7). L'invariant juste, que suit l'AC6 : aucun verrou **explicite** + (`FOR UPDATE` / `LOCK IN SHARE MODE`) sur `accounts` **après** `fiscal_years` dans ces flux ; les S + de clé étrangère des insertions de lignes (`INSERT INTO journal_entry_lines`) viennent après + l'exercice — la validation, l'avoir lui-même (étape (7)), `accept_one_invoice` (`:1581` après + `:1520`) — et ne gênent pas un S ; ils attendent un X. L'AC6 place son verrou avant l'exercice pour + la **course de lecture** (`active` lu frais, archivage concurrent qui attend), selon la convention + du doc-comment canonique (comptes → exercice) ; elle n'en tire **aucune** règle de fermeture de + cycle — la défense contre l'interblocage est le rejeu de la route (15-5e, C54 ; finding R6-3 de la + P6). +- **`version` de la facture** : l'avoir l'incrémente déjà (invariant de `settle_invoice`, étape (8)) ; + ne pas y toucher. +- **Garde de postabilité désactivée** (`create_in_tx(..., false)`, 14-3b D-A0) : inchangée. La + créance et le compte d'arrondi de la vente ont été imputables au moment de la vente ; ne pas + re-vérifier `postable` (même raisonnement que la 6 ter de la 16-1a : viser les **mêmes** comptes que + l'écriture d'origine). + +### Pièges + +- **Ne pas changer `ORDER BY jel.id` en `line_order`** dans les lecteurs, même si la colonne existe + (`journal_entries.rs:417`) : trois chemins éprouvés reposent sur `jel.id`, et le lecteur de créance + doit être la même requête, pas une nouvelle ; le lecteur d'arrondi suit le même ordre. +- **Ne pas réutiliser `usable_designated_account`** pour l'arrondi de l'avoir : elle exige « charge ou + produit, actif, imputable » d'un compte **désigné** ; la doctrine de l'avoir est « mêmes comptes que + l'origine, seule l'inactivité bloque » (AC4, AC6). +- **Greper la valeur, pas la formulation** (propagation post-patch) : après T3, greper + `default_receivable_account_id`, `rounding_account_for_write` et `Issuance` sur tout le dépôt (code, + doc-comments, `docs/`, manuels) pour trouver les phrases qui disent encore que l'avoir lit les + réglages. +- **Migration** : aucune. Pas de P5/P6/P7. +- **Frontière 15-5a à 15-5e** : deux fichiers `credit_notes.rs` sont à distinguer (finding R6-7 de la + P6). Le **dépôt** `crates/kesh-db/src/repositories/credit_notes.rs`, que cette story réécrit, est + touché par la **15-5d** (son test des générateurs, `:933`, choix C50 — « `credit_notes.rs` aussi pour + les 19 tests des générateurs ») ; la **route** `crates/kesh-api/src/routes/credit_notes.rs` est + enveloppée dans le rejeu par la **15-5e** (son AC3). Les autres ne touchent ni l'un ni l'autre, mais + partagent des **fonctions** avec cette fiche — + `accept_one_invoice` (15-5b AC6 : commentaire de classement sur le bloc même que l'AC2 remplace, à + conserver et réécrire, AC7), `settle_invoice` (15-5a AC4, 15-5b AC18 ; route rejouée par la 15-5e), + `write_off_invoice` (15-5e AC5, commentaire « 5 bis »), `validate_invoice` (15-5b, 15-5d, 15-5e AC5 : + doc-comment canonique) et `invoices_validate_vat.rs` (15-5b, 15-5d), le passage du manuel sur + l'avoir (15-5b AC17, 15-5d AC8), le helper de verrou de liste de la 15-5d (AC6), le Pattern 5 + (15-5e AC5) — et la 15-5d **touche** `credit_notes_repository.rs` (test de C35, ré-ancré ici, + § *Tests* 13) et y laisse des doc-comments sur l'exemption de l'avoir (AC7). **Une seule règle + d'ordre des verrous pour l'epic, celle de la 15-5e (C54)** : l'ordre est une convention de + fréquence, la défense est le rejeu ; l'AC6 garde son verrou pour la course de lecture (réglages, + comptes par `id`, exercice), ne réordonne rien pour fermer un cycle et ne prétend à l'absence + d'aucun cycle. Les + textes que la 15-5e réécrit ne sont **pas** réécrits ici (AC7). Rebase attendu avec conflits **de + forme** ; T0 refait les numéros de ligne. + +### Références + +- [Source: crates/kesh-db/src/repositories/credit_notes.rs:187-258, :261-620 (dont :238-239, :365-367, :418-451, :507-545)] +- [Source: crates/kesh-db/src/repositories/opening_complement.rs:38-39, :502, :540 (patron `LOCK IN SHARE MODE`) ; + crates/kesh-db/src/repositories/invoice_settlements_write.rs:431-495, :466-468 ; + crates/kesh-api/src/routes/credit_notes.rs:183 ; crates/kesh-db/src/retry.rs:83 (`is_deadlock_error`), :105 (`retry_on_deadlock`, rejeu de la route par la 15-5e) ; + crates/kesh-api/src/routes/reconciliation.rs:1487, :1520, :1581, :1703 ; crates/kesh-api/tests/reconciliation_e2e.rs:4426] +- [Source: issue #536 (cycle avoir ↔ rapprochement de la même facture) ; `epic-15-choix-autonomes.md` C-15-6-29, C-15-6-32] +- [Source: `15-5e-ordre-des-verrous-reglements.md` (dépôt principal, réécrite selon C54 « Rejeu sur interblocage + des flux d'écriture » ; AC3 route de l'avoir rejouée, AC5 doc-comment canonique, « 5 bis » et Pattern 5) ; + registre C54, C55, C57 ; `15-5d-garde-usage-comptes-reglage.md` + (AC1 `lock_designated_accounts_in_tx`, C51 `EXPLAIN` et identifiant étranger) ; + `docs/MULTI-TENANT-SCOPING-PATTERNS.md:285-330` (Pattern 5) ; crates/kesh-db/src/repositories/opening_complement.rs:274-288, :431-440] +- [Source: crates/kesh-db/src/errors.rs:291, :604-614, :796 (`map_db_error`) ; crates/kesh-db/src/repositories/journal_entries.rs:96-98, :1622-1627, :1723-1754 ; + crates/kesh-api/src/errors.rs:2945-2973, :3155-3185 ; crates/kesh-db/src/repositories/accounts.rs:604-634] +- [Source: crates/kesh-db/src/repositories/invoices.rs:1916-1925 (ordre canonique des locks) ; + crates/kesh-db/src/repositories/fiscal_years.rs:548 ; crates/kesh-api/src/routes/reconciliation.rs:887, :1688-1706 ; + crates/kesh-db/src/test_fixtures.rs:560 ; crates/kesh-api/tests/invoice_echeancier_e2e.rs:742 ; + crates/kesh-i18n/locales/fr-CH/messages.ftl:291, :354] +- [Source: crates/kesh-db/src/repositories/invoice_settlements_write.rs:1-13 (doctrine), :97-115, :428-445] +- [Source: crates/kesh-api/src/routes/reconciliation.rs:1414-1452] +- [Source: crates/kesh-db/src/repositories/invoices.rs:1870-1912, :2005-2010, :2068] +- [Source: crates/kesh-db/src/repositories/company_invoice_settings.rs:400-412, :484] +- [Source: crates/kesh-db/tests/invoices_validate_vat.rs:592-930 ; crates/kesh-db/src/test_fixtures.rs:212] +- [Source: docs/manual/fr/user-manual.tex:363, :847, :1148-1180 ; docs/manual/fr/admin-manual.tex:2027] +- [Source: issues #473, #523, #525 ; `epic-15-choix-autonomes.md` C-15-6-1, C-15-6-2, C-15-6-7, C-15-6-8, C-15-6-16, C-15-6-24, C-15-6-25] + +## Dev Agent Record + +### Agent Model Used + +### Debug Log References + +### Completion Notes List + +### File List + +## Change Log + +- 2026-10-08 — Spécification initiale (bmad-create-story, en autonomie). Statut `ready-for-dev`. +- 2026-10-08 — **Validation P1** (Sonnet 4.6, lentilles F et R ; prompt + `15-6a-validate-prompt-p1.md`). F : 1 HIGH, 3 MEDIUM, 4 LOW ; R : 1 HIGH, 3 MEDIUM, 4 LOW (les deux + HIGH convergent, deux MEDIUM aussi). Appliqués : + - **HIGH F1 = R1** — #523 absorbée : lecteur de la dernière ligne avec recoupement sens + montant + (AC4), refus du compte de la vente archivé par `create_in_tx` (AC6, **voie (b)** : le test `:846` + change d'assertion, délibérément — la voie (a) aurait gardé un message renvoyant à tort aux + paramètres, la voie (c) créé un refus neuf), deux tests neufs (réglage redésigné, réglage vidé), un + test neuf du recoupement et le test `:846` modifié (compte archivé). Choix C-15-6-7. *(Phrase + corrigée en P2, finding R-5 : elle comptait le test modifié parmi les neufs ; le total, lui, était + juste.)* + - **MEDIUM F3** — TVA due de l'avoir : angle mort tracé par #525 (ouverte par l'orchestrateur) ; le + manuel dit la limite sans présenter l'écriture comme juste (AC8). Choix C-15-6-8. + - **MEDIUM F2 = R2** — le `grep` de l'AC2 se limite aux `.rs` ; les backfills `.sql` sont nommés + comme légitimes. + - **MEDIUM F4 = R5** — l'erreur SQL du lecteur reste `DATABASE_ERROR` dans le rapprochement ; + `INVOICE_SALE_ENTRY_MALFORMED`, qu'aucun test ne figeait, l'est par un test neuf (AC2, test 10). + - **MEDIUM R3** — « dix tests » → neuf, lignes citées. + - **MEDIUM R4 = F5** — tests des lecteurs en `#[sqlx::test]`, placés dans le fichier dont ils + emploient les helpers privés ; seconde société désignée. + - **LOW** — citation exacte du commentaire `:609-611` (R6) ; `accounts::archive` et choix du SQL + direct (R7) ; `user-manual.tex:1754` à ne pas réécrire (R8) ; contrôle aplati de la phrase neuve + (F6) ; test 2 borné à la créance, l'avoir exigeant toujours le produit par défaut (F8). Les trois + LOW restants ne figurent pas dans le rapport condensé transmis à la remédiation : rien n'en a été + appliqué, et ce n'est pas affirmé autrement. + - Décompte après passe : **9 AC, 7 tâches (T0–T6), 10 tests** (9 neufs, 1 modifié). +- 2026-10-08 — **Validation P2** (Opus 5.5, lentilles R et F ; prompt `15-6a-validate-prompt-p2.md`). + R : 0 CRITICAL, 0 HIGH, 3 MEDIUM, 6 LOW ; F : 0 CRITICAL, 0 HIGH, 2 MEDIUM, 6 LOW (R-2 = F-1, + R-3 = F-2, R-1 ⊃ F-5, R-7 = F-6, R-8 ⊂ F-8). Tous appliqués, décisions de l'orchestrateur comprises : + - **MEDIUM R-2 = F-1** — le verrou `FOR UPDATE` du compte d'arrondi (#486) n'est plus perdu : les + comptes lus sur la vente (créance, arrondi) sont verrouillés **sans** filtre `active`, l'état actif + lu sous verrou ; la course est fermée, testée (test 11, neuf). AC6, Dev Notes. Choix C-15-6-16. + - **MEDIUM R-3 = F-2** — refus anonyme : le compte archivé est **nommé** par le refus existant de la + contre-passation (`ReversalAccountsArchived`, 400 `ACCOUNT_ARCHIVED`, clé existante) — aucune + variante ni clé neuve ; tests 3 et 9 suivent. Manuel : `keshwarning` neuf au § *Avoirs*, + avertissement `:363` étendu, phrase `admin-manual.tex:2027` (AC8). Choix C-15-6-16. L'écart à la + contrainte de #523 (le code change, le refus reste nommé) est écrit en AC6 ; **commentaire à + poser sur #523** par l'orchestrateur. + - **MEDIUM R-1 (⊃ F-5 LOW)** — frontière réelle avec les 15-5a/b/c/d écrite (en-tête, Pièges) ; T0 + rebase et refait tous les numéros de ligne ; commentaire de la 15-5b dans `accept_one_invoice` + conservé et réécrit (AC7) ; phrase de la 15-5d sur l'avoir au manuel corrigée si elle est mergée + (AC8). + - **LOW** — R-4 : site `reconciliation.rs:1487` ajouté à l'inventaire ; F-4 : dévalidation + (`delete_in_tx`) et `create_opening_entry` classés, deux commandes de recompte ajoutées ; R-5 : + phrase du Change Log P1 corrigée ; R-6 : `:100` → `:101` ; R-7 = F-6 : invariant « l'arrondi est + la DERNIÈRE ligne » écrit à la source (AC7) ; R-8 = F-8 : texte de `DATABASE_ERROR` dérivé de + `DbError` (AC1), montage léger du test 10 proposé ; F-3 : contrôle PDF sur des fragments absents + du PDF actuel, apostrophes en `.` (AC8) ; F-7 : `### Modifié` au CHANGELOG, `docs/api-external.md` + sans objet (AC9). **R-9 non appliqué tel quel** : la consigne du registre interdit de réécrire une + entrée existante ; la révision de C-15-6-2 reste portée par les titres de C-15-6-7, C-15-6-8, + C-15-6-16 et par la fiche d'index. + - Signal de découpage : sans objet (lentille F : quatre modules de code ; aucun défaut recyclé). + - Décompte après passe : **9 AC, 7 tâches (T0–T6), 11 tests** (10 neufs, 1 modifié). +- 2026-10-08 — **Validation P3** (Sonnet, lentilles R et F ; prompt `15-6a-validate-prompt-p3.md` ; + remédiation Opus 5.5). R : 0 CRITICAL, 1 HIGH, 2 MEDIUM, 4 LOW ; F : 0 CRITICAL, 0 HIGH, 4 MEDIUM, + 6 LOW (R3-1 ⊃ F-5, R3-2 = F-4, R3-3 = F-10, R3-4 = F-7, R3-5 = F-6, R3-6 = F-2). Tous appliqués, + décisions de l'orchestrateur comprises : + - **HIGH R3-1 (+ MEDIUM F-1, LOW F-5)** — le verrou des comptes de la vente était placé **après** + l'exercice et la séquence, à l'inverse de l'ordre canonique de `validate_invoice` + (`invoices.rs:1916-1925`) et de celui de la 15-5d : cycle avec chaque validation sur la ligne de + la créance. Il est désormais pris **juste après les réglages (3), avant l'exercice (4)**, et + **en partage** (`LOCK IN SHARE MODE`) : un X sur 1100 ouvrait un cycle neuf avec + `accept_one_invoice`, qui tient S sur la créance avant de demander X sur la facture. Ordre des + refus fixé (compte de la vente archivé avant `FISCAL_YEAR_INVALID`), test 14 neuf ; section + « Ordre des locks » au doc-comment de `create_credit_note` (AC7). La phrase « c'est celui de + `validate_invoice` » et la « conséquence assumée » de C-15-6-16 étaient fausses : rectifiées par + C-15-6-24 (le registre n'est pas réécrit). + - **MEDIUM F-2 = R3-6** — test 11 sur `attendre_une_requete_en_cours` (motifs `FROM accounts`, + `LOCK IN SHARE MODE`), facture sans arrondi, aucun délai fixe. + - **MEDIUM F-3** — test 12 neuf, frontière HTTP de `POST /api/v1/credit-notes` (code, `details`, + message), dans `invoice_echeancier_e2e.rs`, seul fichier qui émet un avoir par la route. + - **MEDIUM F-4 = R3-2** — le test de C35 de la 15-5d est recensé en T0 et ré-ancré (test 13, + modifié) ; doc-comments de l'exemption réécrits (AC7) ; « neuf tests » daté, dix après la 15-5d. + - **MEDIUM R3-3 = F-10** — T0 **lit** les encadrés `user-manual.tex` ~`:380`/~`:390` et + `admin-manual.tex:2016-2029` après le merge des 15-5, au lieu de greper une formulation de fiche ; + grep de la valeur ensuite, PDF aplatis compris (AC8). + - **LOW F-9** — un seul vocabulaire de refus sur la route : variante + `DbError::CreditNoteAccountsArchived`, **même code** `ACCOUNT_ARCHIVED`, clé neuve + `credit-note-account-archived` (« Impossible d'émettre l'avoir — compte(s) archivé(s) : … ») + dans les quatre locales ; manuel et CHANGELOG disent les deux codes de la route (AC6, AC8, AC9). + Choix C-15-6-25, qui révise le « aucune variante, aucune clé neuve » de C-15-6-16. + - **LOW** — R3-4 = F-7 : `map_db_error` est dans `kesh-db/src/errors.rs:796` ; R3-5 = F-6 : ce que + le verrou ne couvre pas (comptes de produit, TVA due) écrit à l'AC6 et à l'inventaire, angle mort + assumé ; R3-7 : lignes d'attribut harmonisées, ordre `ORDER BY id` vs `number` de + `details.rejected[]` dit ; F-8 : phrase « deux avoirs se sérialisent sur la ligne du compte » + retirée (S + S ne sérialise pas ; les réglages sérialisent déjà). + - **Signal D5 — levé et déclaré.** La sévérité remonte (P2 : 0 HIGH, 5 MEDIUM ; P3 : 1 HIGH, + 6 MEDIUM) et le HIGH **naît de la remédiation P2** (C-15-6-16) : c'est la forme « recyclage » de + l'amendement D5, déclarée au Project Lead. Pas de découpage : trois modules de code (`kesh-db`, + `kesh-api`, `kesh-i18n`) et la correction est un déplacement de verrou, tranché par + l'orchestrateur. Une P4 est nécessaire (MEDIUM restants à cette passe) ; une passe **ciblée** sur + l'AC6 et les tests 11-14 est la forme indiquée. + - Trend : P1 (F+R) 2 HIGH, 6 MEDIUM → P2 0 HIGH, 5 MEDIUM → P3 1 HIGH, 6 MEDIUM. + - Décompte après passe : **9 AC, 7 tâches (T0–T6), 14 tests** (12 neufs, 2 modifiés : 9 et 13). +- 2026-10-08 — **Validation P4** (Opus 5.5, lentilles R et F ; prompt `15-6a-validate-prompt-p4.md` ; + remédiation Opus 5.5). R : 0 CRITICAL, 0 HIGH, 2 MEDIUM, 8 LOW ; F : 0 CRITICAL, 0 HIGH, 2 MEDIUM, + 4 LOW (R4-1 ≈ F-2, R4-2 = F-4, R4-6 = F-6, R4-9 = F-5). Tous appliqués, décisions de l'orchestrateur + comprises (choix C-15-6-29) : + - **MEDIUM F-1** — la TVA due et les comptes de produit demandaient leur S **après** l'exercice, face + au X que le solde du reste y prend avant le sien : cycle préexistant que la 15-5d décrit et ferme + pour la validation. Le verrou de l'AC6 couvre désormais **tous** les comptes que l'avoir écrit + (créance, arrondi de la vente, TVA due si l'avoir porte de la TVA, produits effectifs, repli D-B2 + compris), en une requête `ORDER BY id`, avant l'exercice ; la 6 ter lit `active` dans ce verrou + (son code, son message et sa place ne changent pas) ; un compte de TVA due archivé rend + `ACCOUNT_ARCHIVED` nommé (il rendait `INACTIVE_OR_INVALID_ACCOUNTS`) — AC6, AC8, AC9, test 16 neuf. + L'angle mort « autre classe » de l'inventaire **disparaît**. + - **MEDIUM R4-1 ≈ F-2 (+ LOW F-3)** — l'analyse « S + S ne bloque pas » était fausse pour l'arrondi : + réécrite (un S n'ajoute aucun cycle par la créance, il n'en retire aucun) ; trois cycles résiduels + inscrits à l'AC6 et à l'inventaire — avoir ↔ rapprochement de la même facture, **préexistant, tracé + par #536** (ouverte par l'orchestrateur) ; avoir ↔ solde du reste par ordres opposés entre deux + comptes ; cycle à trois par un X en attente (neuf). La route `POST /api/v1/credit-notes` reçoit + `retry_with` sur interblocage, figé par le test 15 neuf, qui exerce le prédicat. Le registre + C-15-6-24 est rectifié par C-15-6-29 (non réécrit). + - **MEDIUM R4-2 = F-4** — Dev Notes : la phrase de `invoices.rs:1921-1922` est citée exactement + (« avant ») ; l'invariant juste est écrit (verrous **explicites** sur `accounts` avant l'exercice, + S de clé étrangère après) ; le commentaire source et celui de `invoice_settlements_write.rs:466-468` + sont à corriger (AC7). + - **LOW** — R4-3 : `code()` → `error_code()` (`errors.rs:736`) ; R4-4 : patron existant + `opening_complement.rs:502` ; R4-5 : doc-comment du test 9 réécrit (le refus précède la séquence) ; + R4-6 = F-6 : ordre des refus complet (`ConfigurationRequired` du produit, `Invariant` des + lecteurs) ; R4-7 : sérialisation par la ligne des réglages nommée ; R4-8 : grep du test 12 avec + `ACCOUNT_ARCHIVED` ; R4-9 = F-5 : ligne absente du verrou → `Invariant` ; R4-10 : lecture de la 6 ter + citée `:450-451` (et non `:445-447`, numéros relus au code). + - Signal D5 : sévérité en baisse (P3 : 1 HIGH, 6 MEDIUM ; P4 : 0 HIGH, 4 MEDIUM dont deux convergents) ; + F-1 est un défaut préexistant, R4-1/F-2 corrige un raisonnement écrit en P3 (recyclage partiel), sans + nouveau module de code (`kesh-db`, `kesh-api`, `kesh-i18n`). Pas de découpage. Une P5 est + nécessaire (MEDIUM à cette passe). + - Trend : P1 (F+R) 2 HIGH, 6 MEDIUM → P2 0 HIGH, 5 MEDIUM → P3 1 HIGH, 6 MEDIUM → P4 0 HIGH, 4 MEDIUM. + - Décompte après passe : **9 AC, 7 tâches (T0–T6), 16 tests** (14 neufs, 2 modifiés : 9 et 13). +- 2026-10-08 — **Validation P5** (Sonnet, lentilles R et F ; prompt `15-6a-validate-prompt-p5.md` ; + remédiation Opus 5.5). R : 0 CRITICAL, 0 HIGH, 2 MEDIUM, 7 LOW ; F : 0 CRITICAL, 0 HIGH, 1 MEDIUM, + 6 LOW (R5-1 = F5-1, R5-4 = F5-2). Tous traités (choix **C-15-6-32**), selon la décision de + l'orchestrateur — **une seule règle d'ordre des verrous pour l'epic, celle de la 15-5e** —, telle + que la 15-5e est **réécrite par C54** pendant cette remédiation (la défense contre l'interblocage + est le **rejeu** ; l'ordre, une convention de fréquence ; correction de consigne de l'orchestrateur + reçue en cours de remédiation, appliquée) : + - **MEDIUM R5-1 = F5-1** — la fiche ignorait la **15-5e** : dépendances « après 15-5a à 15-5e » + (en-tête, T0, Pièges) ; l'AC7 **ne réécrit plus** le doc-comment canonique (`invoices.rs:1921-1922`) + ni le commentaire « 5 bis » (`invoice_settlements_write.rs:466-468`), que la 15-5e réécrit (son + AC5) ; elle ne corrige qu'une phrase qui décrirait l'ancien ordre de l'avoir, s'il y en a une ; la + route `POST /api/v1/credit-notes` est **rejouée par la 15-5e** (son AC3) : la 15-6a ne spécifie + plus son propre `retry_with` (révise C-15-6-29 sur ce point), son test 15 fige que ce rejeu + couvre l'attente neuve de l'avoir. + - **MEDIUM R5-2 (+ LOW F5-3)** — cycles avoir ↔ solde du reste (TVA due ↔ arrondi) et avoir ↔ + règlement par compte interne : **nommés**, non fermés par réordonnancement (C54 : ne pas + réordonner l'avoir pour fermer des cycles) ; le verrou reste une instruction `ORDER BY id`, gardé + pour la seule **course de lecture** ; toute affirmation d'absence de cycle est retirée (« ensemble + clos », « pas un de plus ») ; les cycles connus sont listés comme non exhaustifs et renvoyés au + rejeu. + - **LOW F5-5 (DRY)** — un seul helper de verrou d'une liste de comptes, paramétré par le mode, + partagé avec la 15-5d (`lock_designated_accounts_in_tx`), créé par la première story mergée (a + priori la 15-5d). **LOW F5-4** — « patron existant » → « syntaxe » ; `EXPLAIN` relevé et test + d'identifiant étranger comme la 15-5d (C51), test 17 neuf. + - **LOW R5-4 = F5-2** — « l'avoir émet de la TVA » : agrégation extraite en fonction pure partagée + par le générateur et le calcul des ids ; refus `ConfigurationRequired(TVA)` laissé à sa place + (générateur, après l'exercice), écrit. + - **LOW F5-6** — une ligne pour l'avoir au Pattern 5 de `docs/MULTI-TENANT-SCOPING-PATTERNS.md`, + dans la forme que la 15-5e lui donne (AC9, T5). + - **LOW** — R5-3 : symptôme exact du test 11 sur le code actuel ; R5-5 : « 15-5b AC12 » → AC18 ; + R5-6 : test 14 « comptes de l'avoir » ; R5-7 : garde `active` de `create_in_tx` « hors réactivation + concurrente » ; R5-8 : lest du test 15 repris tel quel du patron ; R5-9 : test unitaire du bras de la + variante neuve (test 18) ; F5-7 : le manuel dit que rétablir le réglage de TVA due est l'autre + issue, et le remplacement inatteignable du refus de la 6 ter par `Invariant` est écrit (sans test). + - Propagation : jetons `15-5a, 15-5b, 15-5c et 15-5d`, `15-5a/b/c/d`, `15-5b AC12`, `comptes de la + vente`, `arrondi d'abord`, `(S, seul)`, `retry_with`, `ensemble clos`, `formes A`, `patron + **existant**`, `1921-1922`, `466-468` grepés sur la fiche — résidus restants légitimes (Change Logs + P2-P4, références au commit `1920381e`, AC7 qui nomme ce que la 15-5e réécrit, références de code). + - **Signal D5** : non levé par la sévérité (P4 0 HIGH, 4 MEDIUM → P5 0 HIGH, 3 MEDIUM, dont deux + convergents). ⚠️ **R5-1 et R5-2 ne viennent pas d'un patch** : ils naissent d'un **changement + extérieur** — la création de la 15-5e (C52) puis sa réécriture (C54), postérieures à la P4 —, non + d'une remédiation de cette fiche ; ce n'est pas un recyclage. Aucun module de code ajouté. Pas de + découpage. + - Trend : P1 (F+R) 2 HIGH, 6 MEDIUM → P2 0 HIGH, 5 MEDIUM → P3 1 HIGH, 6 MEDIUM → P4 0 HIGH, + 4 MEDIUM → P5 0 HIGH, 3 MEDIUM. La remédiation P5 retire le rejeu propre à la route et réécrit la + doctrine des verrous de l'AC6 : une **P6 complète** (Opus, rotation D6) est indiquée. + - Décompte après passe : **9 AC, 7 tâches (T0–T6), 18 tests** (16 neufs, 2 modifiés : 9 et 13). +- 2026-10-08 — **Validation P6** (Opus 5.5, lentilles R et F ; prompt `15-6a-validate-prompt-p6.md` ; + rapports `target/gate-logs/15-6a-p6-R.md`, `15-6a-p6-F.md` ; remédiation Opus 5.5). R : 0 CRITICAL, + 0 HIGH, 3 MEDIUM, 7 LOW ; F : 0 CRITICAL, 0 HIGH, 2 MEDIUM, 5 LOW (F6-1 = R6-1 ; F6-2 = R6-4, MEDIUM + côté F, LOW côté R — retenu MEDIUM ; F6-7 observation). **4 MEDIUM distincts**, tous traités (choix + **C-15-6-35**) : + - **MEDIUM F6-1 = R6-1** — deux contrats pour la fonction de verrou : le paragraphe « Refus nommé » + (`lock_accounts_in_share_mode`, `pub(crate)`, « à côté de » `archived_accounts_in_tx`, « le + contrôle « ligne absente » y vit ») contredisait la puce P5. Retiré ; la puce « Un seul helper » + devient le **seul contrat** : paramétré par le mode, à l'emplacement de la 15-5d, colonnes `id, + number, active, postable`, **ne refuse rien** (« ligne absente » et partage chez l'appelant), + **`pub`** (le test 17 est un test d'intégration), type de ligne **nommé et distinct** de + `opening_complement::LockedAccount` (privé, autres colonnes : ni réutilisé ni homonyme). + - **MEDIUM R6-2** — le critère DRY était faux dès aujourd'hui : `opening_complement.rs:518-537` + verrouille une liste d'ids de comptes. Nommé comme **exception écrite** (forme distincte, hors + périmètre de l'epic) ; toute autre occurrence est un site non résolu. + - **MEDIUM R6-3** — doctrine retirée en P5 restée aux Dev Notes (« d'où la règle de l'AC6 … se + verrouille avant l'exercice ») et en T0 (« vérifier que l'AC6 lui reste conforme ») : réécrits — + verrou avant l'exercice pour la course de lecture, aucune règle de cycle ; T0 **relève** l'ordre de + la 15-5d sans exigence de conformité. + - **MEDIUM F6-2 (= R6-4)** — cycle (iv) : la validation, sérialisée avec l'avoir par la ligne des + réglages (la fiche le dit elle-même), ne peut pas en être le partenaire. Partenaires réels nommés — + saisie manuelle, règlement, solde du reste, rapprochement (flux qui prennent l'exercice sans les + réglages) ; validation et saisie fournisseur explicitement exclues ; table de l'inventaire alignée. + - **LOW** — R6-5 : test 17, issue attendue **rouge** d'après `opening_complement.rs:434-436` (puis + `owned_account_ids`), sauf si la 15-5d l'a adopté (T0), T4 aligné ; R6-6 : `"../kesh-db/test-schema"` + pour les tests de `kesh-api` ; R6-7 : frontière des deux `credit_notes.rs` (dépôt : 15-5d ; route : + 15-5e) ; R6-8 : 6940 « plus loin dans le plan livré, ou créé après lui » ; R6-9 : `retry_with` + retiré des Références, remplacé par `retry.rs:83, :105` ; R6-10 : la 15-5d est une dépendance + **ferme** (en-tête, test 13 : la story attend) ; F6-3 : exemple du 3800 corrigé (le plan des + associations ne désigne aucun escompte d'office) ; F6-4 : « inatteignable par l'application » au + lieu de « par les clés étrangères » (aucune clé étrangère ne porte la société) ; F6-5 : + `invoices_line_revenue_account.rs` (quatre tests de la 6 ter) à la non-régression ; F6-6 : relève + de l'orchestrateur (commentaire sur #536 : l'avoir est couvert par le rejeu de la 15-5e, la 15-6a + ne ferme aucun cycle) — **signalé**, rien dans la fiche. + - **F6-7 — décompte au barème de la règle**, écrit ici et déclaré : modules métier de premier niveau + touchés — `kesh-db/repositories/invoice_settlements` (lecteurs), `…/invoice_settlements_write` + (règlement, solde), `…/credit_notes` (avoir), `…/invoices` (doc-comment seul), + `…/company_invoice_settings` (helper, seulement si la 15-5d l'a posé sans mode), + `kesh-api/routes/reconciliation` — **six**, plus la plomberie `kesh-db/errors`, `kesh-api/errors`, + `kesh-i18n` : **plus de cinq** au barème de la règle (le décompte « trois ou quatre modules de code » + des P2-P5 était par crate). Le critère 1 ne vaut qu'avant la validation ; il est **déclaré**, non + rétroactif. + - Propagation : jetons `lock_accounts_in_share_mode`, `LockedAccount`, `à côté d'elle`, `y vit`, + `règle de l'AC6`, `reste conforme`, `se verrouille avant l'exercice`, `tenu par une validation`, + `validate_invoice` (table des cycles), `vert attendu`, `clés étrangères`, `créé après le plan`, + `Autres produits`, `./test-schema`, `retry_with`, `pas mergée` grepés sur les fiches 15-6 et le + registre — résidus restants légitimes (Change Logs P2-P5 ; « clés étrangères » du lest du test 15, + autre sens ; `LockedAccount` cité pour être écarté). + - **Signal D5 — levé, déclaré au Project Lead, pas de découpage** (C-15-6-35). Sévérité P5 → P6 + MEDIUM → MEDIUM (2 → 4 distincts), et **trois des quatre MEDIUM recyclent la remédiation P5** + (R6-1 = F6-1, R6-2, R6-3 : la refonte doctrinale de l'AC6 laissée incomplète — un paragraphe, un + critère et une note non grepés) ; le quatrième (F6-2) date de la P4. Recyclage **contenu** : tous + portent sur le **texte** de l'AC6 et de ses Dev Notes, aucun sur une règle métier ni un module ; la + remédiation P6 retire des phrases et en unifie d'autres, elle n'ajoute aucun mécanisme. Découper + l'AC6 n'en retirerait pas le défaut (une propagation incomplète) ; le geste qui le traite est le + grep du symptôme, fait ici. Avec le critère de dispersion (F6-7), le signal est déclaré deux fois ; + l'arbitrage revient à Guy. + - Trend : P1 (F+R) 2 HIGH, 6 MEDIUM → P2 0 HIGH, 5 MEDIUM → P3 1 HIGH, 6 MEDIUM → P4 0 HIGH, + 4 MEDIUM → P5 0 HIGH, 3 MEDIUM → **P6 0 HIGH, 5 MEDIUM** (4 distincts). Modèles : P1 Sonnet 4.6, + P2 Opus, P3 Sonnet, P4 Opus, P5 Sonnet, P6 Opus ; remédiations Opus 5.5. La boucle **ne s'arrête + pas** : une **P7** est due (rotation D6 : Sonnet, passe **complète** — la remédiation réécrit le + contrat d'un helper et un cycle, et trois MEDIUM de la P6 venaient d'une remédiation). + - Décompte après passe : **9 AC, 7 tâches (T0–T6), 18 tests** (16 neufs, 2 modifiés : 9 et 13) — + inchangé. + +- **Validation P7** (Sonnet, lentilles R et F, prompt `15-6a-validate-prompt-p7.md`) : 0 CRITICAL, 0 HIGH, + **1 MEDIUM**, 10 LOW (R : 1 MEDIUM + 4 LOW ; F : 6 LOW ; R7-2 = F7-1 : la même phrase). + - **MEDIUM R7-1** — le type de ligne du helper : la fiche (« la 15-6a prend le sien ») et le registre + (C-15-6-35 (1) : « ou celui de la 15-5d ») s'excluaient. **Un seul type, jamais deux** : emploi de celui de + la 15-5d s'il est nommé et `pub`, sinon rendu public (AC6) ; C-15-6-36 révise C-15-6-35 (1). + - **LOW** — R7-2 = F7-1 : phrase d'en-tête réparée ; R7-3 : quatre colonnes (`postable` incluse, ignorée) ; + R7-4 : C-15-6-32 (6) et C-15-6-29 marqués révisés (C-15-6-36) ; R7-5 : cycle (iv), `invoices.rs:2006/:2172` + cités pour la validation seule, T0 relève la saisie fournisseur ; F7-2 : test 17 en appel direct, sans + `UPDATE` des réglages ; F7-3 : lignes de `credit_notes.rs` et `opening_complement.rs` corrigées ; + F7-4 : `keshwarning` du manuel, rôle *Créances clients* à retirer du nouveau compte avant la réactivation ; + F7-5 : « par SQL direct » aux tests 2 et 7 ; F7-6 : note de PR (`closes #523` renvoie au commentaire du + 2026-10-08), consignée en C-15-6-36. + - Propagation : jetons `prend le sien`, `s'il existe`, `première story mergée`, `chaque validation`, + `LockedAccountState`, `518-537`, `523-545` grepés sur les fiches 15-6 et le registre ; résidus restants + légitimes (entrées historiques du registre, Change Logs P2-P6). + - Trend : P1 (F+R) 2 HIGH, 6 MEDIUM → P2 0 HIGH, 5 MEDIUM → P3 1 HIGH, 6 MEDIUM → P4 0 HIGH, 4 MEDIUM → + P5 0 HIGH, 3 MEDIUM → P6 0 HIGH, 5 MEDIUM (4 distincts) → **P7 0 HIGH, 1 MEDIUM**. Modèles : P1 Sonnet 4.6, + P2 Opus, P3 Sonnet, P4 Opus, P5 Sonnet, P6 Opus, P7 Sonnet ; remédiations Opus 5.5. Le MEDIUM de la P7 + recycle le texte de l'AC6 (remédiation P6), sans règle métier ni module. + - **Suite** : la **P8** (dernière passe du plafond de 8) sera une passe **ciblée** sur ce commit, la + remédiation P7 ne touchant que la fiche et le registre (aucun code de production). + - Décompte après passe : **9 AC, 7 tâches (T0–T6), 18 tests** (16 neufs, 2 modifiés : 9 et 13) — inchangé. diff --git a/_bmad-output/implementation-artifacts/15-6a-validate-prompt-p1.md b/_bmad-output/implementation-artifacts/15-6a-validate-prompt-p1.md new file mode 100644 index 00000000..e9529ab5 --- /dev/null +++ b/_bmad-output/implementation-artifacts/15-6a-validate-prompt-p1.md @@ -0,0 +1,34 @@ +# Prompt — validation P1 de la spec, Story 15-6a + +*Versionné le 2026-10-08. Deux lentilles (Sonnet), contexte frais chacune, en lecture seule. Rotation (décision D6 de la +rétro 25) : passes complètes Sonnet ↔ Opus ; Haiku réservé à une passe ciblée de fin de boucle.* + +Dépôt `/home/gcorbaz/devel/kesh-15-6`, branche `story/15-6-creance-juste-avoir-reglement`. **Fiche** : `_bmad-output/implementation-artifacts/15-6a-avoir-credite-la-creance-de-la-vente.md`. C'est la première passe : la fiche n'a pas encore été remédiée. +Issues, par `gh api repos/guycorbaz/kesh/issues/N` (et `/comments`) — `gh issue view` échoue ici : 473, 523. +Choix autonomes : `_bmad-output/implementation-artifacts/epic-15-choix-autonomes.md`. Règles : `CLAUDE.md`. Fiche d'index 15-6, sœurs 15-6b et 15-6c. ⚠️ L'issue #523 (l'avoir relit aussi le compte d'arrondi dans les réglages) a été ouverte APRÈS l'écriture de la fiche, qui l'avait laissée en angle mort (C-15-6-2) : l'orchestrateur décide qu'elle entre dans la 15-6a — juge comment la traiter sans casser #486, et signale ce qu'il faut changer. Le dépôt principal (autre branche) contient des stories sœurs 15-5a/b/c qui touchent des fichiers voisins : `/home/gcorbaz/devel/kesh/_bmad-output/implementation-artifacts/15-5*.md` (lecture seulement). +Aucun code de la story n'est encore écrit. + +## Lentilles + +- **R — Auditeur d'acceptation** : applique `.claude/skills/bmad-create-story/checklist.md`. Chaque phrase des issues couverte par un AC testable, chaque AC par une tâche, chaque tâche par un test ; implémentable sans deviner (fonctions, fichiers, codes d'erreur, clés i18n 4 locales, replis Svelte) ; faits cités revérifiés au code (`grep -nF`) ; recompte des AC et tâches. +- **F — Full-scope adversary** : la conception elle-même. Les chemins de code qui écrivent ou lisent ce que la story + change (inventorier les sites NON résolus, pas énumérer ceux qui marchent) ; transactions, verrous, ordre des + contrôles ; tests existants qui changeront de sens ; frontend qui lit les codes ou champs touchés (`grep -rn` dans + `frontend/src`) ; i18n 4 locales et replis ; le **manuel** (`docs/manual/fr/*.tex` **et le PDF aplati** : + `pdftotext docs/manual/fr/user-manual.pdf - | tr '\n' ' ' | tr -s ' '` vers `target/gate-logs/`) ; + `docs/api-external.md` ; CHANGELOG ; la règle de découpage (§ « Règle de splitting préventif », amendement D5). + +## Ce que tu rends + +Findings numérotés avec sévérité (CRITICAL/HIGH/MEDIUM/LOW), `fichier:ligne` (fiche et code), **preuve** (commande et +sortie, ou code cité relu), correction proposée. Pour tout finding affirmant qu'un code est absent ou présent : la +sortie d'un `grep -nF`. ⛔ **La liste des axes exercés ET non exercés** — un « 0 finding » sans elle ne compte pas. +Rapport compact. + +## Interdits + +⛔ N'écris aucun fichier du dépôt hors `target/gate-logs/` ; aucune commande qui écrit dans le dépôt ou dans une +base : `scripts/*` (dont `scripts/prepare-release.sh`), `make`, `latexmk`, `git commit`/`add`/`checkout`/`stash`, +`sqlx`, `cargo test`/`nextest`/`build`, `npm run`, `npx`, `gh issue create`/`comment`/`edit`, aucune requête SQL +d'écriture. Autorisés : lecture, `grep`, `sed -n`, `git log`/`show`/`diff`, `gh api` en lecture, `pdftotext` +vers `target/gate-logs/`. diff --git a/_bmad-output/implementation-artifacts/15-6a-validate-prompt-p2.md b/_bmad-output/implementation-artifacts/15-6a-validate-prompt-p2.md new file mode 100644 index 00000000..a6aa3524 --- /dev/null +++ b/_bmad-output/implementation-artifacts/15-6a-validate-prompt-p2.md @@ -0,0 +1,35 @@ +# Prompt — validation P2 de la spec, Story 15-6a + +*Versionné le 2026-10-08. Deux lentilles (Opus), contexte frais chacune, en lecture seule. Rotation (décision D6 de la +rétro 25) : passes complètes Sonnet ↔ Opus ; Haiku réservé à une passe ciblée de fin de boucle.* + +Dépôt `/home/gcorbaz/devel/kesh-15-6`, branche `story/15-6-creance-juste-avoir-reglement`. **Fiche** : `_bmad-output/implementation-artifacts/15-6a-avoir-credite-la-creance-de-la-vente.md`. La dernière remédiation est le commit `cb54ff72` (`git show cb54ff72`) — lis le Change Log de la fiche. +Issues, par `gh api repos/guycorbaz/kesh/issues/N` (et `/comments`) — `gh issue view` échoue ici : 473, 523, 525. +Choix autonomes : `_bmad-output/implementation-artifacts/epic-15-choix-autonomes.md`. Règles : `CLAUDE.md`. Fiche d'index 15-6 ; sœurs 15-6a/b/c/d. Les fiches 15-5a/b/c du dépôt principal (`/home/gcorbaz/devel/kesh/_bmad-output/implementation-artifacts/15-5*.md`, lecture seule) touchent des fichiers voisins et seront mergées AVANT 15-6b/c/d : vérifie la frontière. +Aucun code de la story n'est encore écrit. + +## Lentilles + +- **R — Regression hunter** : la dernière remédiation (`git show cb54ff72`) a-t-elle introduit des défauts ? Contradictions entre AC, tâches, Dev Notes et choix du registre ; frontière avec les fiches sœurs ; faits recopiés sans vérification (numéros de ligne, fonctions, codes, clés i18n) — revérifie-les au code (`grep -nF`) ; recompte des AC et tâches ; implémentable sans deviner ? +- **F — Full-scope adversary** : la conception elle-même. Les chemins de code qui écrivent ou lisent ce que la story + change (inventorier les sites NON résolus, pas énumérer ceux qui marchent) ; transactions, verrous, ordre des + contrôles ; tests existants qui changeront de sens ; frontend qui lit les codes ou champs touchés (`grep -rn` dans + `frontend/src`) ; i18n 4 locales et replis ; le **manuel** (`docs/manual/fr/*.tex` **et le PDF aplati** : + `pdftotext docs/manual/fr/user-manual.pdf - | tr '\n' ' ' | tr -s ' '` vers `target/gate-logs/`) ; + `docs/api-external.md` ; CHANGELOG ; la règle de découpage (§ « Règle de splitting préventif », amendement D5). + +## Ce que tu rends + +Findings numérotés avec sévérité (CRITICAL/HIGH/MEDIUM/LOW), `fichier:ligne` (fiche et code), **preuve** (commande et +sortie, ou code cité relu), correction proposée. Pour tout finding affirmant qu'un code est absent ou présent : la +sortie d'un `grep -nF`. ⛔ **La liste des axes exercés ET non exercés** — un « 0 finding » sans elle ne compte pas. +⛔ **Écris ton rapport complet** dans `target/gate-logs/15-6a-p2-.md` (R ou F ; autorisé), et rends +comme dernier message **seulement** : ce chemin, le bilan par sévérité, et une ligne par finding MEDIUM ou plus. + +## Interdits + +⛔ N'écris aucun fichier du dépôt hors `target/gate-logs/` ; aucune commande qui écrit dans le dépôt ou dans une +base : `scripts/*` (dont `scripts/prepare-release.sh`), `make`, `latexmk`, `git commit`/`add`/`checkout`/`stash`, +`sqlx`, `cargo test`/`nextest`/`build`, `npm run`, `npx`, `gh issue create`/`comment`/`edit`, aucune requête SQL +d'écriture. Autorisés : lecture, `grep`, `sed -n`, `git log`/`show`/`diff`, `gh api` en lecture, `pdftotext` +vers `target/gate-logs/`. diff --git a/_bmad-output/implementation-artifacts/15-6a-validate-prompt-p3.md b/_bmad-output/implementation-artifacts/15-6a-validate-prompt-p3.md new file mode 100644 index 00000000..253df8d1 --- /dev/null +++ b/_bmad-output/implementation-artifacts/15-6a-validate-prompt-p3.md @@ -0,0 +1,35 @@ +# Prompt — validation P3 de la spec, Story 15-6a + +*Versionné le 2026-10-08. Deux lentilles (Sonnet), contexte frais chacune, en lecture seule. Rotation (décision D6 de la +rétro 25) : passes complètes Sonnet ↔ Opus ; Haiku réservé à une passe ciblée de fin de boucle.* + +Dépôt `/home/gcorbaz/devel/kesh-15-6`, branche `story/15-6-creance-juste-avoir-reglement`. **Fiche** : `_bmad-output/implementation-artifacts/15-6a-avoir-credite-la-creance-de-la-vente.md`. La dernière remédiation est le commit `4964eeb4` (`git show 4964eeb4`) — lis le Change Log de la fiche. +Issues, par `gh api repos/guycorbaz/kesh/issues/N` (et `/comments`) — `gh issue view` échoue ici : 473, 523, 525. +Choix autonomes : `_bmad-output/implementation-artifacts/epic-15-choix-autonomes.md`. Règles : `CLAUDE.md`. Fiche d'index 15-6 ; sœurs 15-6a/b/c/d. Les fiches 15-5a/b/c/d du dépôt principal (`/home/gcorbaz/devel/kesh/_bmad-output/implementation-artifacts/15-5*.md`, lecture seule) seront mergées AVANT : vérifie la frontière. Axe : verrou FOR UPDATE sur créance et arrondi, refus ReversalAccountsArchived, test de course. +Aucun code de la story n'est encore écrit. + +## Lentilles + +- **R — Regression hunter** : la dernière remédiation (`git show 4964eeb4`) a-t-elle introduit des défauts ? Contradictions entre AC, tâches, Dev Notes et choix du registre ; frontière avec les fiches sœurs ; faits recopiés sans vérification (numéros de ligne, fonctions, codes, clés i18n) — revérifie-les au code (`grep -nF`) ; recompte des AC et tâches ; implémentable sans deviner ? +- **F — Full-scope adversary** : la conception elle-même. Les chemins de code qui écrivent ou lisent ce que la story + change (inventorier les sites NON résolus, pas énumérer ceux qui marchent) ; transactions, verrous, ordre des + contrôles ; tests existants qui changeront de sens ; frontend qui lit les codes ou champs touchés (`grep -rn` dans + `frontend/src`) ; i18n 4 locales et replis ; le **manuel** (`docs/manual/fr/*.tex` **et le PDF aplati** : + `pdftotext docs/manual/fr/user-manual.pdf - | tr '\n' ' ' | tr -s ' '` vers `target/gate-logs/`) ; + `docs/api-external.md` ; CHANGELOG ; la règle de découpage (§ « Règle de splitting préventif », amendement D5). + +## Ce que tu rends + +Findings numérotés avec sévérité (CRITICAL/HIGH/MEDIUM/LOW), `fichier:ligne` (fiche et code), **preuve** (commande et +sortie, ou code cité relu), correction proposée. Pour tout finding affirmant qu'un code est absent ou présent : la +sortie d'un `grep -nF`. ⛔ **La liste des axes exercés ET non exercés** — un « 0 finding » sans elle ne compte pas. +⛔ **Écris ton rapport complet** dans `target/gate-logs/15-6a-p3-.md` (R ou F ; autorisé), et rends +comme dernier message **seulement** : ce chemin, le bilan par sévérité, et une ligne par finding MEDIUM ou plus. + +## Interdits + +⛔ N'écris aucun fichier du dépôt hors `target/gate-logs/` ; aucune commande qui écrit dans le dépôt ou dans une +base : `scripts/*` (dont `scripts/prepare-release.sh`), `make`, `latexmk`, `git commit`/`add`/`checkout`/`stash`, +`sqlx`, `cargo test`/`nextest`/`build`, `npm run`, `npx`, `gh issue create`/`comment`/`edit`, aucune requête SQL +d'écriture. Autorisés : lecture, `grep`, `sed -n`, `git log`/`show`/`diff`, `gh api` en lecture, `pdftotext` +vers `target/gate-logs/`. diff --git a/_bmad-output/implementation-artifacts/15-6a-validate-prompt-p4.md b/_bmad-output/implementation-artifacts/15-6a-validate-prompt-p4.md new file mode 100644 index 00000000..5dfb04b8 --- /dev/null +++ b/_bmad-output/implementation-artifacts/15-6a-validate-prompt-p4.md @@ -0,0 +1,35 @@ +# Prompt — validation P4 de la spec, Story 15-6a + +*Versionné le 2026-10-08. Deux lentilles (Opus), contexte frais chacune, en lecture seule. Rotation (décision D6 de la +rétro 25) : passes complètes Sonnet ↔ Opus ; Haiku réservé à une passe ciblée de fin de boucle.* + +Dépôt `/home/gcorbaz/devel/kesh-15-6`, branche `story/15-6-creance-juste-avoir-reglement`. **Fiche** : `_bmad-output/implementation-artifacts/15-6a-avoir-credite-la-creance-de-la-vente.md`. La dernière remédiation est le commit `89e8987a` (`git show 89e8987a`) — lis le Change Log de la fiche. +Issues, par `gh api repos/guycorbaz/kesh/issues/N` (et `/comments`) — `gh issue view` échoue ici : 473, 523, 525. +Choix autonomes : `_bmad-output/implementation-artifacts/epic-15-choix-autonomes.md`. Règles : `CLAUDE.md`. Fiche d'index 15-6 ; sœurs 15-6a/b/c/d ; les fiches 15-5a/b/c/d (dépôt principal, lecture seule) sont mergées AVANT. Axe principal : l'ordre des verrous de l'avoir (LOCK IN SHARE MODE avant l'exercice) contre TOUS les chemins (validation, règlements, rapprochement, 15-5d), l'ordre des refus, la variante CreditNoteAccountsArchived. +Aucun code de la story n'est encore écrit. + +## Lentilles + +- **R — Regression hunter** : la dernière remédiation (`git show 89e8987a`) a-t-elle introduit des défauts ? Contradictions entre AC, tâches, Dev Notes et choix du registre ; frontière avec les fiches sœurs ; faits recopiés sans vérification (numéros de ligne, fonctions, codes, clés i18n) — revérifie-les au code (`grep -nF`) ; recompte des AC et tâches ; implémentable sans deviner ? +- **F — Full-scope adversary** : la conception elle-même. Les chemins de code qui écrivent ou lisent ce que la story + change (inventorier les sites NON résolus, pas énumérer ceux qui marchent) ; transactions, verrous, ordre des + contrôles ; tests existants qui changeront de sens ; frontend qui lit les codes ou champs touchés (`grep -rn` dans + `frontend/src`) ; i18n 4 locales et replis ; le **manuel** (`docs/manual/fr/*.tex` **et le PDF aplati** : + `pdftotext docs/manual/fr/user-manual.pdf - | tr '\n' ' ' | tr -s ' '` vers `target/gate-logs/`) ; + `docs/api-external.md` ; CHANGELOG ; la règle de découpage (§ « Règle de splitting préventif », amendement D5). + +## Ce que tu rends + +Findings numérotés avec sévérité (CRITICAL/HIGH/MEDIUM/LOW), `fichier:ligne` (fiche et code), **preuve** (commande et +sortie, ou code cité relu), correction proposée. Pour tout finding affirmant qu'un code est absent ou présent : la +sortie d'un `grep -nF`. ⛔ **La liste des axes exercés ET non exercés** — un « 0 finding » sans elle ne compte pas. +⛔ **Écris ton rapport complet** dans `target/gate-logs/15-6a-p4-.md` (R ou F ; autorisé), et rends +comme dernier message **seulement** : ce chemin, le bilan par sévérité, et une ligne par finding MEDIUM ou plus. + +## Interdits + +⛔ N'écris aucun fichier du dépôt hors `target/gate-logs/` ; aucune commande qui écrit dans le dépôt ou dans une +base : `scripts/*` (dont `scripts/prepare-release.sh`), `make`, `latexmk`, `git commit`/`add`/`checkout`/`stash`, +`sqlx`, `cargo test`/`nextest`/`build`, `npm run`, `npx`, `gh issue create`/`comment`/`edit`, aucune requête SQL +d'écriture. Autorisés : lecture, `grep`, `sed -n`, `git log`/`show`/`diff`, `gh api` en lecture, `pdftotext` +vers `target/gate-logs/`. diff --git a/_bmad-output/implementation-artifacts/15-6a-validate-prompt-p5.md b/_bmad-output/implementation-artifacts/15-6a-validate-prompt-p5.md new file mode 100644 index 00000000..6d2f369e --- /dev/null +++ b/_bmad-output/implementation-artifacts/15-6a-validate-prompt-p5.md @@ -0,0 +1,35 @@ +# Prompt — validation P5 de la spec, Story 15-6a + +*Versionné le 2026-10-08. Deux lentilles (Sonnet), contexte frais chacune, en lecture seule. Rotation (décision D6 de la +rétro 25) : passes complètes Sonnet ↔ Opus ; Haiku réservé à une passe ciblée de fin de boucle.* + +Dépôt `/home/gcorbaz/devel/kesh-15-6`, branche `story/15-6-creance-juste-avoir-reglement`. **Fiche** : `_bmad-output/implementation-artifacts/15-6a-avoir-credite-la-creance-de-la-vente.md`. La dernière remédiation est le commit `bc4986d2` (`git show bc4986d2`) — lis le Change Log de la fiche. +Issues, par `gh api repos/guycorbaz/kesh/issues/N` (et `/comments`) — `gh issue view` échoue ici : 473, 523, 525, 536. +Choix autonomes : `_bmad-output/implementation-artifacts/epic-15-choix-autonomes.md`. Règles : `CLAUDE.md`. Fiche d'index 15-6 ; les fiches 15-5a/b/c/d/e (dépôt principal, lecture seule ; la 15-5a est MERGÉE sur main) passent avant. Axes : verrou partagé étendu à tous les comptes de l'avoir, retry_with sur la route, les trois cycles résiduels écrits, les choix C-15-6-29 (TVA due archivée → ACCOUNT_ARCHIVED nommé ; ligne absente → Invariant) que l'orchestrateur demande de relire. +Aucun code de la story n'est encore écrit. + +## Lentilles + +- **R — Regression hunter** : la dernière remédiation (`git show bc4986d2`) a-t-elle introduit des défauts ? Contradictions entre AC, tâches, Dev Notes et choix du registre ; frontière avec les fiches sœurs ; faits recopiés sans vérification (numéros de ligne, fonctions, codes, clés i18n) — revérifie-les au code (`grep -nF`) ; recompte des AC et tâches ; implémentable sans deviner ? +- **F — Full-scope adversary** : la conception elle-même. Les chemins de code qui écrivent ou lisent ce que la story + change (inventorier les sites NON résolus, pas énumérer ceux qui marchent) ; transactions, verrous, ordre des + contrôles ; tests existants qui changeront de sens ; frontend qui lit les codes ou champs touchés (`grep -rn` dans + `frontend/src`) ; i18n 4 locales et replis ; le **manuel** (`docs/manual/fr/*.tex` **et le PDF aplati** : + `pdftotext docs/manual/fr/user-manual.pdf - | tr '\n' ' ' | tr -s ' '` vers `target/gate-logs/`) ; + `docs/api-external.md` ; CHANGELOG ; la règle de découpage (§ « Règle de splitting préventif », amendement D5). + +## Ce que tu rends + +Findings numérotés avec sévérité (CRITICAL/HIGH/MEDIUM/LOW), `fichier:ligne` (fiche et code), **preuve** (commande et +sortie, ou code cité relu), correction proposée. Pour tout finding affirmant qu'un code est absent ou présent : la +sortie d'un `grep -nF`. ⛔ **La liste des axes exercés ET non exercés** — un « 0 finding » sans elle ne compte pas. +⛔ **Écris ton rapport complet** dans `target/gate-logs/15-6a-p5-.md` (R ou F ; autorisé), et rends +comme dernier message **seulement** : ce chemin, le bilan par sévérité, et une ligne par finding MEDIUM ou plus. + +## Interdits + +⛔ N'écris aucun fichier du dépôt hors `target/gate-logs/` ; aucune commande qui écrit dans le dépôt ou dans une +base : `scripts/*` (dont `scripts/prepare-release.sh`), `make`, `latexmk`, `git commit`/`add`/`checkout`/`stash`, +`sqlx`, `cargo test`/`nextest`/`build`, `npm run`, `npx`, `gh issue create`/`comment`/`edit`, aucune requête SQL +d'écriture. Autorisés : lecture, `grep`, `sed -n`, `git log`/`show`/`diff`, `gh api` en lecture, `pdftotext` +vers `target/gate-logs/`. diff --git a/_bmad-output/implementation-artifacts/15-6a-validate-prompt-p6.md b/_bmad-output/implementation-artifacts/15-6a-validate-prompt-p6.md new file mode 100644 index 00000000..16f90d32 --- /dev/null +++ b/_bmad-output/implementation-artifacts/15-6a-validate-prompt-p6.md @@ -0,0 +1,35 @@ +# Prompt — validation P6 de la spec, Story 15-6a + +*Versionné le 2026-10-08. Deux lentilles (Opus), contexte frais chacune, en lecture seule. Rotation (décision D6 de la +rétro 25) : passes complètes Sonnet ↔ Opus ; Haiku réservé à une passe ciblée de fin de boucle.* + +Dépôt `/home/gcorbaz/devel/kesh-15-6`, branche `story/15-6-creance-juste-avoir-reglement`. **Fiche** : `_bmad-output/implementation-artifacts/15-6a-avoir-credite-la-creance-de-la-vente.md`. La dernière remédiation est le commit `e871cd58` (`git show e871cd58`) — lis le Change Log de la fiche. +Issues, par `gh api repos/guycorbaz/kesh/issues/N` (et `/comments`) — `gh issue view` échoue ici : 473, 523, 525, 536. +Choix autonomes : `_bmad-output/implementation-artifacts/epic-15-choix-autonomes.md`. Règles : `CLAUDE.md`. Index 15-6 ; dépend des 15-5a à 15-5e (dépôt principal, lecture seule : la 15-5e a été RÉÉCRITE selon C54 — rejeu sur interblocage, b92f3319). 15-5a mergée sur main. +Aucun code de la story n'est encore écrit. + +## Lentilles + +- **R — Regression hunter** : la dernière remédiation (`git show e871cd58`) a-t-elle introduit des défauts ? Contradictions entre AC, tâches, Dev Notes et choix du registre ; frontière avec les fiches sœurs ; faits recopiés sans vérification (numéros de ligne, fonctions, codes, clés i18n) — revérifie-les au code (`grep -nF`) ; recompte des AC et tâches ; implémentable sans deviner ? +- **F — Full-scope adversary** : la conception elle-même. Les chemins de code qui écrivent ou lisent ce que la story + change (inventorier les sites NON résolus, pas énumérer ceux qui marchent) ; transactions, verrous, ordre des + contrôles ; tests existants qui changeront de sens ; frontend qui lit les codes ou champs touchés (`grep -rn` dans + `frontend/src`) ; i18n 4 locales et replis ; le **manuel** (`docs/manual/fr/*.tex` **et le PDF aplati** : + `pdftotext docs/manual/fr/user-manual.pdf - | tr '\n' ' ' | tr -s ' '` vers `target/gate-logs/`) ; + `docs/api-external.md` ; CHANGELOG ; la règle de découpage (§ « Règle de splitting préventif », amendement D5). + +## Ce que tu rends + +Findings numérotés avec sévérité (CRITICAL/HIGH/MEDIUM/LOW), `fichier:ligne` (fiche et code), **preuve** (commande et +sortie, ou code cité relu), correction proposée. Pour tout finding affirmant qu'un code est absent ou présent : la +sortie d'un `grep -nF`. ⛔ **La liste des axes exercés ET non exercés** — un « 0 finding » sans elle ne compte pas. +⛔ **Écris ton rapport complet** dans `target/gate-logs/15-6a-p6-.md` (R ou F ; autorisé), et rends +comme dernier message **seulement** : ce chemin, le bilan par sévérité, et une ligne par finding MEDIUM ou plus. + +## Interdits + +⛔ N'écris aucun fichier du dépôt hors `target/gate-logs/` ; aucune commande qui écrit dans le dépôt ou dans une +base : `scripts/*` (dont `scripts/prepare-release.sh`), `make`, `latexmk`, `git commit`/`add`/`checkout`/`stash`, +`sqlx`, `cargo test`/`nextest`/`build`, `npm run`, `npx`, `gh issue create`/`comment`/`edit`, aucune requête SQL +d'écriture. Autorisés : lecture, `grep`, `sed -n`, `git log`/`show`/`diff`, `gh api` en lecture, `pdftotext` +vers `target/gate-logs/`. diff --git a/_bmad-output/implementation-artifacts/15-6a-validate-prompt-p7.md b/_bmad-output/implementation-artifacts/15-6a-validate-prompt-p7.md new file mode 100644 index 00000000..ce212698 --- /dev/null +++ b/_bmad-output/implementation-artifacts/15-6a-validate-prompt-p7.md @@ -0,0 +1,35 @@ +# Prompt — validation P7 de la spec, Story 15-6a + +*Versionné le 2026-10-08. Deux lentilles (Sonnet), contexte frais chacune, en lecture seule. Rotation (décision D6 de la +rétro 25) : passes complètes Sonnet ↔ Opus ; Haiku réservé à une passe ciblée de fin de boucle.* + +Dépôt `/home/gcorbaz/devel/kesh-15-6`, branche `story/15-6-creance-juste-avoir-reglement`. **Fiche** : `_bmad-output/implementation-artifacts/15-6a-avoir-credite-la-creance-de-la-vente.md`. La dernière remédiation est le commit `32290dd3` (`git show 32290dd3`) — lis le Change Log de la fiche. +Issues, par `gh api repos/guycorbaz/kesh/issues/N` (et `/comments`) — `gh issue view` échoue ici : 473, 523, 525, 536. +Choix autonomes : `_bmad-output/implementation-artifacts/epic-15-choix-autonomes.md`. Règles : `CLAUDE.md`. Index 15-6 ; dépend des 15-5a à 15-5e (dépôt principal, lecture seule ; 15-5e = rejeu sur interblocage, C54). ⚠️ Passe 7 sur un plafond de 8 : signale clairement si un défaut vient du recyclage du texte de l'AC6 (contrat du helper de verrou). +Aucun code de la story n'est encore écrit. + +## Lentilles + +- **R — Regression hunter** : la dernière remédiation (`git show 32290dd3`) a-t-elle introduit des défauts ? Contradictions entre AC, tâches, Dev Notes et choix du registre ; frontière avec les fiches sœurs ; faits recopiés sans vérification (numéros de ligne, fonctions, codes, clés i18n) — revérifie-les au code (`grep -nF`) ; recompte des AC et tâches ; implémentable sans deviner ? +- **F — Full-scope adversary** : la conception elle-même. Les chemins de code qui écrivent ou lisent ce que la story + change (inventorier les sites NON résolus, pas énumérer ceux qui marchent) ; transactions, verrous, ordre des + contrôles ; tests existants qui changeront de sens ; frontend qui lit les codes ou champs touchés (`grep -rn` dans + `frontend/src`) ; i18n 4 locales et replis ; le **manuel** (`docs/manual/fr/*.tex` **et le PDF aplati** : + `pdftotext docs/manual/fr/user-manual.pdf - | tr '\n' ' ' | tr -s ' '` vers `target/gate-logs/`) ; + `docs/api-external.md` ; CHANGELOG ; la règle de découpage (§ « Règle de splitting préventif », amendement D5). + +## Ce que tu rends + +Findings numérotés avec sévérité (CRITICAL/HIGH/MEDIUM/LOW), `fichier:ligne` (fiche et code), **preuve** (commande et +sortie, ou code cité relu), correction proposée. Pour tout finding affirmant qu'un code est absent ou présent : la +sortie d'un `grep -nF`. ⛔ **La liste des axes exercés ET non exercés** — un « 0 finding » sans elle ne compte pas. +⛔ **Écris ton rapport complet** dans `target/gate-logs/15-6a-p7-.md` (R ou F ; autorisé), et rends +comme dernier message **seulement** : ce chemin, le bilan par sévérité, et une ligne par finding MEDIUM ou plus. + +## Interdits + +⛔ N'écris aucun fichier du dépôt hors `target/gate-logs/` ; aucune commande qui écrit dans le dépôt ou dans une +base : `scripts/*` (dont `scripts/prepare-release.sh`), `make`, `latexmk`, `git commit`/`add`/`checkout`/`stash`, +`sqlx`, `cargo test`/`nextest`/`build`, `npm run`, `npx`, `gh issue create`/`comment`/`edit`, aucune requête SQL +d'écriture. Autorisés : lecture, `grep`, `sed -n`, `git log`/`show`/`diff`, `gh api` en lecture, `pdftotext` +vers `target/gate-logs/`. diff --git a/_bmad-output/implementation-artifacts/15-6b-contrepartie-distincte-de-la-creance.md b/_bmad-output/implementation-artifacts/15-6b-contrepartie-distincte-de-la-creance.md new file mode 100644 index 00000000..1c08e661 --- /dev/null +++ b/_bmad-output/implementation-artifacts/15-6b-contrepartie-distincte-de-la-creance.md @@ -0,0 +1,1212 @@ +# Story 15.6b : Un règlement ne vise pas le compte qu'il solde + +## Status + +ready-for-dev + + + +**Issue** : `refs #474` (P1) — la fermeture vient avec la 15-6c, qui traite la configuration. +**Mère** : `15-6-creance-juste-avoir-reglement.md`. **Dépend de** : 15-6a (lecteur +`invoice_settlements::sale_receivable_account`, mêmes fichiers). **De préférence après** la 15-5c +(le code neuf apparaît dans `failed[]`, dont la **15-5c** pose les libellés traduits — #492, +`frontend/src/lib/features/reconciliation/failed-proposal-label.ts`). +**Après** les 15-5a à 15-5e (ordre de l'epic ; la **15-5e** passe avant la 15-5d), qui touchent les +mêmes fonctions : `settle_invoice` et `pay_in_tx` (15-5a AC4 : refus `ACCOUNT_NOT_POSTABLE` à +l'étape du compte interne), leurs **routes** et celle du règlement fournisseur et de la confirmation d'un lot (**15-5e** AC3 : +rejouées sur interblocage — aucun ordre de verrou ni de refus n'y change, C55), le commentaire « 5 bis » +de `write_off_invoice` (15-5e AC5, réécrit en place), `accept_one_invoice` (15-5b AC6 : commentaire de +classement), le manuel du rapprochement (15-5c) — **T0** rebase et refait les numéros de ligne (ceux de +cette fiche sont du commit `1920381e`). Cette fiche cite les étapes **par leur nom** (lecture de la +créance, compte interne, compte d'arrondi, trop-perçu…), les numéros d'étape pouvant changer aux +rebases. +**Voisin sorti** : la contrepartie égale au **compte de banque** (#524) est la **15-6d** (choix +C-15-6-9) — hors de cette fiche, **écran du rapprochement manuel compris** (`ManualMatchModal`). + +## Story + +En tant que **comptable**, +je veux que **Kesh refuse un règlement dont la contrepartie est le compte même qu'il doit solder** +(le compte débiteurs de la facture client, le compte créanciers de la facture fournisseur), +afin que **le reste dû ne baisse jamais sans mouvement au grand livre**. + +## Le défaut, établi au code + +- **Règlement client** (`crates/kesh-db/src/repositories/invoice_settlements_write.rs`, + `settle_invoice`) : la créance est lue sur l'écriture de vente (`:100-115`), la contrepartie résolue + selon le mode (`:118-170`) — virement : `bank_accounts.journal_account_id` ; compte interne : tout + compte `active && postable` (`:159-167`). **Aucune comparaison** entre les deux. Choisir 1100 produit + `D 1100 / C 1100` : l'écriture s'équilibre (`kesh-core/src/accounting/balance.rs` n'interdit pas un + même compte au débit et au crédit), le règlement s'enregistre, le reste dû baisse, la facture peut + passer « payée » — et le grand livre ne bouge pas. +- **Le virement aussi** : un compte bancaire lié au 1100 (la route l'accepte, `bank_accounts.rs:288-293` + — type actif ou passif) donne la même écriture nulle. +- **Règlement fournisseur** (`crates/kesh-db/src/repositories/supplier_invoices.rs`, `pay_in_tx`) : + symétrique — dette lue sur l'écriture d'achat (`:580-593`), contrepartie par virement (`:598-621`) + ou compte interne (`:622-656`, `Some((true, true))` `:648`) ; rien n'interdit le 2000 : + `D 2000 / C 2000`, facture « payée » sans paiement. +- **Lots de paiement** (`crates/kesh-db/src/repositories/payment_batches.rs`) : `confirm_batch` + (`:304`) appelle `pay_in_tx` en `BankTransfer` pour chaque facture du lot (appel `:360`), dans une + seule transaction (ouverte `:311`) — troisième appelant de `pay_in_tx`. `create_batch` (`:68`) ne contrôle du + compte bancaire source que « existe, non archivé, journal lié non nul » (`:84-103`) : un compte + bancaire lié au 2000 produit un lot **et son fichier pain.001**, que la seule garde de `pay_in_tx` + rendrait ensuite **inconfirmable** — alors que le fichier est peut-être déjà déposé à la banque. +- **Encaissement par rapprochement** (`crates/kesh-api/src/routes/reconciliation.rs`, + `accept_one_invoice` `:1175`) : compte de banque (`:1384-1412`) et créance (`:1425-1452`) lus, jamais + comparés. +- **Écrans** : `SettleInvoiceDialog.svelte:105` offre tout compte `active && postable`, 1100 compris ; + la fiche fournisseur (`supplier-invoices/[id]/+page.svelte:83-84`) offre de même le 2000, et les + deux offrent tout compte bancaire lié. +- **Comptes d'écart du même geste** (finding F3 de la P2) : le compte d'arrondi du règlement + (`settle_invoice`, compte d'arrondi du règlement, `invoice_settlements_write.rs:189` ; `accept_one_invoice` (c-bis), + `reconciliation.rs:1487`) et les comptes du solde du reste (`write_off_invoice` : nature `:433`, + reste d'arrondi, TVA due `:489`) sont lus dans les **réglages** — l'utilisateur ne les choisit pas + au moment du geste. Les comptes d'arrondi et des natures sont revérifiés **charge ou produit** au + moment d'écrire (`company_invoice_settings.rs:400-447`, `:484-507`, `usable_designated_account`). + ⚠️ Le compte de **TVA due**, lui, ne l'est **par aucun type** : `vat_payable_account_for_write` + (`company_invoice_settings.rs:454-477`) n'exige que « de la société, actif, imputable » + (`FOR UPDATE`) — le type n'est contrôlé qu'à la **désignation** (route des réglages). Pour lui, la + comparaison à la créance (AC3 bis) n'est donc pas un filet du changement de type : c'est la + **seule** garde au moment d'écrire (finding F4 de la P3). + Le contrôle de **type** ne suffit pas non plus pour les autres : le type d'un compte qui porte des écritures **se change** + avec confirmation (`accounts.rs:497-512`, `confirm_retype`), le rôle `Receivable` se retirant dans le + même geste. Le 1100 devenu `Expense` porte toujours la créance des ventes antérieures (lue sur la + vente, 15-6a) et passe le contrôle de type : `write_off_invoice` écrirait alors `D 1100 / C 1100`. + Ces comptes sont donc **comparés par identifiant** à la créance de la vente (AC3 bis, choix + C-15-6-18, qui révise le « résolu par le type » de C-15-6-3). + +**Ce que la story ne résout pas — angles morts écrits** (finding F2 de la P4 ; le « résolu par le +type » des rédactions précédentes était faux : l'argument du retypage confirmé, qui a motivé l'AC3 bis, +le défait aussi, et la créance lue dans les réglages n'a **aucun type contrôlé** à la validation, +`invoices.rs:2008-2010` — présence seulement, puis `active` par `create_in_tx`) : + +- **Validation d'une facture — créance retypée** : le compte débiteurs désigné, retypé avec + confirmation (`accounts.rs:497-512` ; le retypage ne touche pas les réglages) en `Revenue`, reste + `default_receivable_account_id` ; une ligne de facture qui l'impute en produit donne à la validation + `D 1100 / C 1100` (HT) ; de même le compte d'arrondi de la validation, s'il est la créance retypée en + charge. Tracé par **#537** (ouverte par l'orchestrateur). Hors de cette story : la validation + **crée** la créance, elle n'en solde aucune (classe de la story : « un règlement ne vise pas le + compte qu'il solde »), et la garder ici ajouterait un sixième module (`invoices.rs`). +- **Avoir — TVA due et produit de repli** : l'avoir écrit la TVA due et le produit de repli + (`default_revenue_account_id`, lignes sans compte, D-B2) **lus dans les réglages courants** + (`credit_notes.rs:205`, `:511`) ; un 1100 retypé `Liability` puis désigné TVA due donne + `D 1100 (TVA) / C 1100`. La **TVA due** est tracée par **#525** (TVA due de l'avoir lue dans les + réglages du moment — angle mort de la 15-6a), dont c'est un cas ; le **produit de repli** est + tracé lui aussi par **#525**, depuis le commentaire de l'orchestrateur du 2026-10-08 (« Même + famille … compte de produit de repli … À traiter avec la TVA due de cette issue » — posté juste + après la P5, qui l'avait écrit « non tracé » ; finding R6-2 de la P6). La créance de l'avoir est relue sur la + vente (15-6a), ses autres comptes ne le sont pas. Hors de cette story pour la même raison (un septième module, + `credit_notes.rs`). + +## Acceptance Criteria + +1. **AC1 — Une variante dédiée.** `crates/kesh-db/src/errors.rs` gagne + `DbError::SettlementCounterpartyIsClaimAccount { account_id: i64, account_number: Option, claim: ClaimSide, role: SettlementAccountRole, batch: Option }`, + l'énumération `ClaimSide { Receivable, Payable }` (`as_str()` → `"receivable"` / `"payable"`), + l'énumération **`SettlementAccountRole`** — d'où vient le compte en cause, donc quel est le remède + (finding R1 = F1 de la P3, choix C-15-6-26) : + - `Counterparty` (`"counterparty"`) — la contrepartie que l'utilisateur **choisit** (compte interne, + compte bancaire du virement) ou que porte le **compte bancaire** du geste (lot, rapprochement) ; + remède : un autre compte, ou relier le compte bancaire à son propre compte de banque ; + - `Rounding` (`"rounding"`) — le compte de différences d'arrondi des **réglages** + (`default_rounding_account_id`), **quel que soit le geste** : règlement (compte d'arrondi du + règlement), rapprochement (c-bis), et au solde du reste le compte du reste d'arrondi comme le + compte de la nature + `rounding` — `write_off_account_for_write(.., Rounding)` lit la **même colonne** + (`company_invoice_settings.rs:438-441`) que `rounding_account_for_write` (`:408`) (findings R1 = F1 + de la P4, choix C-15-6-30) ; + - `WriteOffNature` (`"write_off_nature"`) — le compte d'une nature de solde des **réglages**, pour + les natures `discount`, `bank_fees` et `bad_debt` seulement (les trois champs de la section + *Solde du reste* des réglages, `settings/invoicing/+page.svelte:29`) ; + - `VatPayable` (`"vat_payable"`) — le compte de TVA due des **réglages** (solde du reste) ; + pour les trois derniers, le remède est de désigner un autre compte dans *Paramètres → Facturation*. + `claim: Payable` ne se combine qu'avec `Counterparty` (le règlement fournisseur n'écrit aucun + compte d'écart) — et ce n'est pas un doc-comment qui le tient (finding F5 de la P5) : le helper de + construction ne prend **pas** la paire `(claim, role)` mais un **sujet** qui rend la combinaison + `Payable` + rôle désigné irreprésentable, p. ex. + `ClaimSubject::{ Counterparty(ClaimSide), Designated(DesignatedRole) }` avec + `DesignatedRole::{ Rounding, WriteOffNature, VatPayable }` (un compte désigné est toujours comparé à + une **créance** : `claim = Receivable` s'en déduit) — `ClaimSubject` et `DesignatedRole` vivent + **tous deux** dans `invoice_settlements.rs`, à côté du helper qui les consomme (finding R6-8 de la + P6 : `DesignatedRole` n'avait ni fichier ni place au décompte) ; `SettlementAccountRole`, dont + `DesignatedRole` est un sous-ensemble, reste dans `errors.rs` avec la variante ; la variante garde ses deux champs, remplis + par le helper. Le doc-comment dit aussi que **le rôle suit la colonne de réglage lue, pas la + route** — un même compte désigné sort sous le même `details.role` partout. Et + `SettlementBatchContext { payment_batch_id: i64, supplier_invoice_id: i64, supplier_invoice_number: Option }` + (signatures indicatives). `batch` vaut `None` partout sauf à la **confirmation d'un lot** (AC6), où + `confirm_batch` le remplit en interceptant le refus de `pay_in_tx`. `error_code()` → + `"SETTLEMENT_COUNTERPARTY_IS_CLAIM_ACCOUNT"` — **un** code pour les quatre rôles (le défaut est le + même : une écriture `D X / C X`) ; `details.role` et le message distinguent le remède. Doc-comment : + le motif (écriture nulle, reste dû qui baisse sans mouvement), #474. + **Helper commun** (finding F3 de la P3, DRY ; choix C-15-6-26) — dans + `crates/kesh-db/src/repositories/invoice_settlements.rs`, à côté du lecteur de la 15-6a, la + **comparaison** est séparée de la **construction du refus** (signatures indicatives) : + - `pub fn ensure_not_claim_account(account_id: i64, claim_account_id: i64) -> Result<(), ClaimAccountClash>` + — **pure**, sans I/O ni transaction ; `ClaimAccountClash { account_id: i64 }` à l'égalité. C'est + la seule partie que la **15-6d** emprunte (sa garde compare la contrepartie au compte de banque ; + son refus reste le `VALIDATION_ERROR` du flux ventilé, C-15-6-28) ; + - `pub async fn claim_account_refusal(conn: &mut sqlx::MySqlConnection, company_id: i64, clash: ClaimAccountClash, subject: ClaimSubject) -> DbError` + — appelée **à l'échec seulement** : lit `accounts.number` (`SELECT number FROM accounts WHERE id = ? AND company_id = ?`, + dans la transaction de l'appelant) et rend la variante, `batch: None` ; une erreur SQL de cette + lecture est rendue telle quelle (`DbError` de `map_db_error`) ; + - `pub fn claim_account_refusal_details(account_id: i64, account_number: Option<&str>, claim: ClaimSide, role: SettlementAccountRole, bank_account_id: Option) -> serde_json::Value` + (`kesh-db` dépend déjà de `serde_json`) — construit les `details` pour les **trois** + consommateurs : le mapping HTTP (AC2), le `FailedProposal` du rapprochement (AC5) et le + `PaymentBatchFailedItem` de la création de lot (AC6), pour que leurs clés ne divergent pas. Les + clés **de lot** (`paymentBatchId`, `supplierInvoiceId`, AC2 et AC6) sont **hors** de ce helper : + le **mapping HTTP** les ajoute, et lui seul, quand la variante porte `batch: Some` — le seul + consommateur qui en ait (finding F6-5 de la P6). + Tous les sites des AC3, AC3 bis, AC4, AC5 et AC6 appellent `ensure_not_claim_account`, puis, sur + `Err(clash)` seulement, `claim_account_refusal` ; la lecture du numéro **à + l'échec seulement** remplace les « lectures neuves » écrites site par site. +2. **AC2 — Sa réponse HTTP.** `crates/kesh-api/src/errors.rs` : **400**, corps + `{ "error": { "code": "SETTLEMENT_COUNTERPARTY_IS_CLAIM_ACCOUNT", "message": …, "details": { "accountId": …, "accountNumber": …, "claim": "receivable" | "payable", "role": "counterparty" | "rounding" | "write_off_nature" | "vat_payable" } } }` + (`details` construits par `claim_account_refusal_details`, AC1). Le patron de + `CREDIT_NOTE_INVOICE_SETTLED` (`crates/kesh-api/src/errors.rs:2838-2845`) ne vaut **que pour la + forme de `details`** : lui est un **409** et appelle `t(` ; ce refus-ci est un **400** et emploie + `t_args` (`errors.rs:52`, privé au module, utilisable). Avec `batch`, `details` gagne + `paymentBatchId` et `supplierInvoiceId`. Message par `t_args`, variable `$account` (le numéro ; + repli `#` s'il manque), **six** clés **plates** — la clé suit le **rôle**, pas seulement le + côté : + - `error-settlement-counterparty-is-receivable` — fr : « Le compte { $account } est le compte + débiteurs de cette facture : un règlement doit faire sortir la créance vers un autre compte + (banque, caisse, compensation…). » + - `error-settlement-counterparty-is-payable` — fr : « Le compte { $account } est le compte + créanciers de cette facture : un règlement doit éteindre la dette par un autre compte (banque, + caisse, compensation…). » + - `error-settlement-counterparty-is-payable-in-batch` (avec `batch`, variable `$invoice` = numéro + de la facture fournisseur, repli `#`) — fr : « Le compte bancaire de ce lot est lié au compte + { $account }, le compte créanciers de la facture { $invoice } : le lot ne peut pas être confirmé. + Reliez le compte bancaire à son propre compte de banque puis confirmez de nouveau, ou annulez le + lot et réglez la facture depuis sa fiche. » — le remède de la confirmation n'est pas celui du + règlement unitaire : l'utilisateur n'y choisit aucun compte (finding F4 de la P2). + - **trois clés plates pour un compte désigné dans les réglages**, une par rôle — **aucun + sélecteur Fluent** (finding R5-1 de la P5, choix C-15-6-33 ; même choix que la 15-5c, son AC4) : + un sélecteur neuf rougirait `no_new_select_expression_reaches_the_frontend_dictionary` + (`crates/kesh-i18n/src/loader.rs:482`, liste `SELECTEURS_RESOLUS_COTE_SERVEUR`, `:367`), et le + dictionnaire du frontend le résoudrait sans argument, sur sa variante par défaut. Textes fr : + - `error-settlement-rounding-account-is-receivable` (rôle `rounding`) — « Le compte + { $account }, désigné dans les réglages comme compte de différences d'arrondi, est le compte + débiteurs de cette facture : un administrateur doit désigner un autre compte dans + Paramètres → Facturation. » + - `error-settlement-write-off-account-is-receivable` (rôle `write_off_nature`) — « Le compte + { $account }, désigné dans les réglages comme compte de cette nature de solde, est le compte + débiteurs de cette facture : un administrateur doit désigner un autre compte dans + Paramètres → Facturation. » + - `error-settlement-vat-payable-account-is-receivable` (rôle `vat_payable`) — « Le compte + { $account }, désigné dans les réglages comme compte de TVA due, est le compte débiteurs de + cette facture : un administrateur doit désigner un autre compte dans Paramètres → + Facturation. » + « Un administrateur doit » et non « désignez » (finding F4 de la P4) : + le solde du reste et le règlement sont ouverts au Comptable, les réglages à l'Administrateur seul + (`crates/kesh-api/src/lib.rs:207-210`) — c'est la formulation que C36 a retenue pour la 15-5d, + vraie aussi pour un client d'API. L'utilisateur n'a choisi **aucun** compte dans le dialogue de + règlement ou de solde : le renvoyer vers « un autre compte (banque, caisse…) » serait un remède + faux (findings R1 = F1 de la P3). **Repli** : chaque clé a son `default` de `t_args` + (`crates/kesh-api/src/errors.rs:52`), la phrase fr complète — plus de `match` **de sélection** + à construire en Rust (il n'existait que pour le sélecteur). ⚠️ `t_args` rend ce `default` + **tel quel**, sans les `FluentArgs` (`errors.rs:58`, `None => default.to_string()`) : il est + donc **construit par `format!`** avec le numéro (et `$invoice` pour la clé de lot), patron + `errors.rs:88-92` (`let fallback = format!("Ligne {n}"); … t_args(…, &fallback, &args)`) — un + `default` littéral « Le compte { $account } … » laisserait fuir le gabarit (finding R6-5 de la P6). + Choix de la clé côté serveur, **un rôle, une clé** : `role = Counterparty` → `-is-receivable` / + `-is-payable` (et `-is-payable-in-batch` avec `batch`) ; `Rounding` → `-rounding-account-is-receivable` ; + `WriteOffNature` → `-write-off-account-is-receivable` ; `VatPayable` → + `-vat-payable-account-is-receivable`. + Dans les **quatre** locales (`crates/kesh-i18n/locales/{fr-CH,de-CH,it-CH,en-CH}/messages.ftl`), + placées près de `error-credit-note-blocked-settled` ; `npm run lint-i18n-ownership` vert. +3. **AC3 — Règlement client, deux modes.** Dans `settle_invoice`, **aussitôt après** la résolution + de la contrepartie (compte bancaire du virement, ou compte interne) et donc **après** ses refus + existants — 404 compte bancaire, configuration absente, `INACTIVE_OR_INVALID_ACCOUNTS`, et + `ACCOUNT_NOT_POSTABLE` que la **15-5a** (son AC4, mergée avant) y insère pour le compte interne —, + et **avant** le refus du trop-perçu : + si `counterparty_account_id == receivable_account_id` → `SettlementCounterpartyIsClaimAccount { claim: Receivable, role: Counterparty, batch: None, … }`, + par `ensure_not_claim_account` puis `claim_account_refusal` (AC1). La garde compare l'identifiant que **le lecteur de la 15-6a** + a déjà rendu (choix C-15-6-11). Numéro : lu par le helper **à l'échec seulement**, dans les deux + modes (le `number` que la 15-5a ajoute au `SELECT` du compte interne n'est pas réutilisé : un seul + chemin vaut mieux que l'économie d'une lecture sur un refus). Rien n'est écrit : ni écriture, + ni ligne `invoice_settlements`, ni `paid_at`, ni `version`, ni audit (la transaction est + abandonnée). Ordre écrit dans le doc-comment de la fonction. + **AC3 bis — Les comptes d'écart ne sont pas la créance** (choix C-15-6-18). Le même refus, par la + même comparaison d'identifiants avec la créance de la vente (lecteur de la 15-6a), garde les comptes + que le geste écrit **en face** de la créance. **Règle de placement, unique** (findings F1 et F6 de + la P5, choix C-15-6-33 ; précisée par R6-4 = F6-6 de la P6) : **chaque comparaison suit la + lecture de son compte et celle de la créance** — aussitôt la plus tardive des deux —, et donc les + refus de configuration ou d'état de ce compte ; l'ordre des refus est celui des lectures du code — que la + 15-5e, réécrite par C54, **ne change pas** (C55 : ni l'arrondi avant le compte interne, ni avant la + nature) ; T0 le relève : + - `settle_invoice`, **compte d'arrondi du règlement** (rendu par + `rounding_account_for_write(.., Payment)`, `invoice_settlements_write.rs:189` au commit + `1920381e`) — `role: Rounding`. Ordre des refus, dans les deux modes : **contrepartie** (compte + bancaire ou compte interne : leurs refus, puis la comparaison de l'AC3, `role: counterparty`) → + **trop-perçu** → **compte d'arrondi** (`ROUNDING_ACCOUNT_NOT_CONFIGURED`, puis cette comparaison, + `role: rounding` ; trop-perçu et écart d'arrondi sont exclusifs) ; + - `write_off_invoice` — ordre des lectures **nature → reste d'arrondi → TVA due** (`:430-490` au + commit `1920381e`, inchangé par la 15-5e) : le compte de la **nature** — `role: WriteOffNature` + pour `discount`, `bank_fees`, `bad_debt`, `role: Rounding` pour la nature `rounding` (il **est** + alors le compte d'arrondi, un seul compte, une seule comparaison ; le reste d'arrondi n'est pas lu + séparément, `:471-472`) — ; le compte du **reste d'arrondi** (lu quand le reste n'est pas au + centime) — `role: Rounding` (même colonne que le compte d'arrondi du règlement, AC1) — ; le compte + de **TVA due** — `role: VatPayable`. La **nature** est lue **avant** la créance + (`write_off_account_for_write` `:433`, puis la créance `:434-445`) : elle est comparée aussitôt + la **créance** lue ; le reste d'arrondi et la TVA due, aussitôt leur propre lecture. Aucun refus + métier ne sépare la nature de la créance (seul l'`Invariant` d'une vente malformée) : l'ordre des + refus n'en change pas — et la lecture de la créance **ne s'avance pas** (C55 : aucun + réordonnancement). + **Ordre des refus quand plusieurs coïncident avec la créance** (finding F7 de la P4) : celui des + lectures — **nature, puis reste d'arrondi, puis TVA due** ; le premier égal est nommé (un seul + `account_id` / `role` par refus), ce que le doc-comment écrit, pour que le message et + `details.role` soient déterministes. Et vis-à-vis des refus de configuration (finding F6 de la + P5) : une **nature** égale à la créance est refusée **avant** la lecture du reste d'arrondi et de + la TVA due, donc avant leurs `WRITE_OFF_ACCOUNT_NOT_CONFIGURED` / `CONFIGURATION_REQUIRED`. Le + test 10 ter le fige. + - `accept_one_invoice`, étape (c-bis) : le compte d'arrondi (`reconciliation.rs:1487`) → + `FailedProposal` comme l'AC5, `details = { "accountId", "accountNumber", "claim": "receivable", "role": "rounding" }` + — **sans** `bankAccountId`, qui n'a pas de sens pour un compte d'arrondi (finding F1 de la P3). + `account_id` du refus = le compte d'écart fautif (qui **est** la créance) ; message : la clé plate + du rôle (AC2 — `-rounding-account-`, `-write-off-account-`, `-vat-payable-account-is-receivable`), + qui renvoie à *Paramètres → Facturation*. Ferme le chemin du changement de type confirmé (`accounts.rs:497-512`) ; « résolu par + le type » ne vaut plus pour ces comptes. Le contrôle de type existant reste (il garde ce qu'il + gardait) — et pour la TVA due, il n'y en a pas au moment d'écrire (§ *Le défaut*) : la comparaison + est sa seule garde. +4. **AC4 — Règlement fournisseur, deux modes.** Même garde dans `pay_in_tx`, après l'étape (3) et ses + refus existants (dont `ACCOUNT_NOT_POSTABLE` de la 15-5a — absent du code de `1920381e`, sa place + se relit en T0 ; le bloc du compte interne est aujourd'hui `supplier_invoices.rs:622-656`) : + `counterparty_account_id == payable_account_id` → `claim: Payable, role: Counterparty`, par + `ensure_not_claim_account` puis `claim_account_refusal` (AC1). Mêmes garanties « rien + d'écrit ». Le commentaire « jumeau exact » de `:623-641` mentionne la nouvelle garde. **DRY** + (choix C-15-6-11) : la requête de l'étape (2) (`:582-593`, ligne de **crédit** de l'écriture + d'achat, **et** le TTC) n'est pas celle du lecteur de la 15-6a (ligne de débit, sans montant) ; elle + est extraite en lecteur sœur + `supplier_invoices::purchase_payable_line(conn, company_id, purchase_entry_id) -> Result, DbError>` + (signature indicative), appelé par `pay_in_tx` et par la création de lot (AC6) — deux appelants, + une requête. +5. **AC5 — Rapprochement d'une facture.** Dans `accept_one_invoice`, après la lecture de la créance + (étape (b), par le lecteur de la 15-6a) et **avant** le contrôle du trop-perçu (étape (c)) : + `bank_ledger_account_id == receivable_account_id` → + `FailedProposal { error_code: "SETTLEMENT_COUNTERPARTY_IS_CLAIM_ACCOUNT", details: { "bankAccountId", "accountId", "accountNumber", "claim": "receivable", "role": "counterparty" } }` + (refus rendu par `ensure_not_claim_account` / `claim_account_refusal`, converti par une seule fonction de + `reconciliation.rs` qui appelle `claim_account_refusal_details`, AC1, pour cette étape et (c-bis)) + — **jamais** d'`AppError` global (§ *Pattern batch* du `CLAUDE.md`) ; HTTP 200, la proposition dans + `failed[]`, les autres du lot traitées normalement. Les refus antérieurs à l'étape de la créance + gardent **tous** la priorité (`RECONCILIATION_SCORE_TOO_LOW` de l'étape 7bis compris), notamment : + `BANK_ACCOUNT_NOT_CONFIGURED` (compte bancaire sans journal), `INVOICE_SALE_ENTRY_MALFORMED` + (étape (b)) ; le trop-perçu (`RECONCILIATION_OVERPAYMENT`) vient **après**. Erreur SQL de la + lecture du numéro → `DATABASE_ERROR` comme les étapes voisines. Le commentaire de classement que la + **15-5b** (son AC6) pose dans cette fonction — « reste tel quel … ses comptes ne viennent pas du + client » — est **amendé** : le compte de banque vient de la configuration et peut être la créance ; + il est désormais gardé ici (sinon le commentaire deviendrait trompeur). +6. **AC6 — Lots de paiement : refus à la création, garde à la confirmation** (choix C-15-6-10, + précisé par C-15-6-19). + - **Création** (`create_batch` → `validate_invoice_for_batch`, `payment_batches.rs:226-300`) : pour + chaque facture, la dette lue sur l'écriture d'achat (lecteur sœur de l'AC4) est comparée au + `journal_account_id` du compte bancaire source ; égalité → la facture va dans `failed[]` du lot + (`PaymentBatchFailedItem`), `error_code = "SETTLEMENT_COUNTERPARTY_IS_CLAIM_ACCOUNT"`, + `details = { "bankAccountId", "accountId", "accountNumber", "claim": "payable", "role": "counterparty" }` + (par `claim_account_refusal_details`, AC1) — patron batch, pas d'erreur globale. **Source de + `accountNumber`** (finding R7 de la P3) : `create_batch` ne lit du compte bancaire que + `journal_account_id, archived` (`payment_batches.rs:83-90`) ; le numéro est lu par + `claim_account_refusal` **à l'échec seulement** (une `SELECT number FROM accounts` dans la + transaction du lot), puis le refus est converti en item. Une **erreur SQL** de cette lecture + (finding R7 de la P4) n'est pas un refus de la facture : elle est **propagée**, échec infra de + toute la création, comme les autres `DbError` de `validate_invoice_for_batch` + (`payment_batches.rs:229`) — seul le refus métier devient un item. **Signature** : + `validate_invoice_for_batch` ne lit aujourd'hui ni `purchase_journal_entry_id` (son `SELECT`, + `:251-253`) ni le compte lié du compte bancaire (`create_batch` le lit `:84-103` sans le + transmettre, appel `:114`) : elle gagne la colonne et un paramètre `bank_ledger_account_id: i64`. **Écriture d'achat sans ligne de crédit** + (`purchase_payable_line` → `None` ; `pay_in_tx` en fait un `Invariant`, 500, + `supplier_invoices.rs:593`) : à la création, c'est une donnée de **cette** facture — item + `failed[]` `SUPPLIER_INVOICE_PURCHASE_ENTRY_MALFORMED`, + `details = { "reason": "no_credit_line_on_purchase_entry", "purchaseEntryId" }`, patron de + `INVOICE_SALE_ENTRY_MALFORMED` du rapprochement (§ *Pattern batch* du `CLAUDE.md` : une erreur qui + dépend de la proposition individuelle ne devient pas une erreur globale). Ces contrôles viennent + **après** les refus existants de la facture (`SUPPLIER_INVOICE_NOT_FOUND`, `…_NOT_OPEN`, + `NO_PAYMENT_COORDINATES`, `ALREADY_IN_GENERATED_BATCH`, IBAN). Si **aucune** facture n'est + retenue, aucun lot n'est créé et **aucun fichier pain.001 n'est produit** (comportement existant + `accepted.is_empty()`, `:120`). Le `fn fail` local (`:235-241`, `details: None`) reçoit une + variante avec `details`, ou le contrôle construit l'item lui-même. **Ce refus à la création est la + défense principale.** + - **Confirmation** (`confirm_batch`, `:304`, boucle `pay_in_tx` `:359-370`) : la garde de + `pay_in_tx` (AC4) **reste** — le compte bancaire a pu être relié entre la création et la + confirmation. `confirm_batch` intercepte ce refus et le rend **avec son contexte** (`batch: + Some(SettlementBatchContext { payment_batch_id, supplier_invoice_id, supplier_invoice_number })`). + **Source de `supplier_invoice_number`** (finding F5 de la P3) : `pay_in_tx` le lit + (`inv.supplier_invoice_number`) mais ne le rend pas, et l'item de lot n'a que + `supplier_invoice_id` (`ITEM_COLS`, `payment_batches.rs:47-48`). Tranché : `confirm_batch`, en + interceptant le refus, fait **une** `SELECT supplier_invoice_number FROM supplier_invoices WHERE id = ? AND company_id = ?` + dans la même transaction (permise : le refus est une erreur métier, aucune erreur SQL n'a rendu + la transaction inutilisable) — **pas** de champ de plus dans la variante, qui resterait vide + partout ailleurs. Effet : + la confirmation entière échoue en **400** `SETTLEMENT_COUNTERPARTY_IS_CLAIM_ACCOUNT` + (transaction atomique), `details` portant `paymentBatchId` et `supplierInvoiceId`, message + `error-settlement-counterparty-is-payable-in-batch` (AC2) qui dit les **deux issues** : relier le + compte bancaire à son propre compte de banque puis confirmer de nouveau, ou annuler le lot + (`cancel_batch`, `:431` — les factures redeviennent réglables à l'unité) et régler la facture + depuis sa fiche. Le lot reste `generated`, aucun règlement écrit. Le doc-comment de `confirm_batch` + dit les deux contrôles, pourquoi le second subsiste, et les deux issues. + - **Écran** : `failedItemLabel` (`frontend/src/lib/features/payment-batches/payment-batch-helpers.ts:78`) + reçoit les **deux** codes neufs, clés `payment-batches-failed-counterparty-is-claim-account` et + `payment-batches-failed-purchase-entry-malformed` **écrites en toutes lettres** (règle de la 23-3 + rappelée dans le JSDoc), quatre locales. Textes **fr** de référence (finding F6 de la P3) : + `payment-batches-failed-counterparty-is-claim-account` — « Le compte bancaire du lot est lié au + compte créanciers de cette facture » ; `payment-batches-failed-purchase-entry-malformed` — + « Écriture d'achat sans ligne de crédit : facture à vérifier ». Le libellé n'affiche pas le + numéro du compte (le `failed[]` du lot ne montre que `#id — libellé`, + `payment-batches/+page.svelte:200`) : le compte en cause est celui du compte bancaire choisi à + l'écran, il n'y a rien d'autre à nommer. **Les « six » deviennent « huit » aux QUATRE sites** + (finding R4 de la P3, jeton grepé) : `payment-batch-helpers.ts:59` (« Les six libellés sont + RELEVÉS, pas inventés » — à amender : les deux neufs ne sont **pas** relevés du glossaire, ils sont + écrits par cette story, et le JSDoc le dit), `payment-batch-helpers.ts:73` (« Les six clés »), + `payment-batch-helpers.test.ts:59` (commentaire « Les six codes sont désormais assertés ») et + `:61` (`it('mappe les six codes …')`). Le refus de confirmation + s'affiche par son message traduit dans la fiche du lot + (`frontend/src/routes/(app)/payment-batches/[id]/+page.svelte:40-50`, `confirmBatch` : + `catch (err) { if (isApiError(err)) actionError = err.message; }`, `:45`) — **vérifié à la P2, + aucun changement**. + - **Écran de création d'un lot** (`frontend/src/routes/(app)/payment-batches/+page.svelte:53`, filtre + `journalAccountId !== null`) : **non filtré, choix écrit** (C-15-6-19) — il ne charge pas le plan + comptable, et le refus y est lisible par facture (`failed[]` libellé) ; voir § *Angles morts*. +7. **AC7 — Libellé dans le `failed[]` du rapprochement.** Si la **15-5c** est mergée (module + `frontend/src/lib/features/reconciliation/failed-proposal-label.ts`, #492 — vérifier par + `test -f` sur ce chemin), le code neuf y reçoit son libellé, dans les quatre locales, **à deux + cas selon `details.role`** (finding F1 de la P3) : `counterparty` — clé + `reconciliation-failed-counterparty-is-claim-account`, fr « Le compte bancaire est lié au compte + débiteurs de la facture : reliez-le à son propre compte de banque » ; `rounding` — clé + `reconciliation-failed-rounding-account-is-claim-account`, fr « Le compte d'arrondi désigné est le + compte débiteurs de la facture : un administrateur doit en désigner un autre dans Paramètres → + Facturation » (C36, finding F4 de la P4 ; préfixes à + ajuster à la convention que la 15-5c aura posée, sans changer le sens). Le test de la liste en + dur du module (`failed-proposal-label.test.ts`) inclut les deux cas, et ses deux sites neufs entrent + dans le relevé de la **borne exacte** `sitesTotal` (AC8, dernier point). Sinon : le signaler dans le + Dev Agent Record et à l'orchestrateur, pour que la 15-5c l'ajoute à sa liste — **ne pas** poser un + second mécanisme. +8. **AC8 — Les écrans ne proposent plus le compte soldé** (choix C-15-6-4, signature fixée par + C-15-6-14, archivés tranchés par C-15-6-17). Trois fonctions pures dans + `frontend/src/lib/features/accounts/account-options.ts`, partagées avec la 15-6c : + - `accountIdsWithRole(accounts, role)` → `Set` des comptes portant ce rôle ; + - `withoutAccountIds(accounts, ids)` → les comptes dont l'id n'est pas dans `ids` ; + - `bankAccountsNotLinkedTo(bankAccounts, ids)` → les comptes bancaires dont `journalAccountId` + n'est pas dans `ids`. + JSDoc : pourquoi le rôle ici (singleton par société, réglages dérivés), que la garde serveur, + exacte, reste l'autorité, et que **les ids se calculent sur la liste que l'écran reçoit, avant le + filtre `active && postable`** — sans quoi un compte bancaire lié à un 1100 devenu **non imputable** + ne serait pas écarté. ⚠️ **Les comptes archivés n'y sont pas, et c'est voulu** (C-15-6-17) : les + trois écrans chargent le plan **sans** les archivés (`invoices/[id]/+page.svelte:554` et + `invoices/due-dates/+page.svelte:102` : `fetchAccounts(false)` ; `supplier-invoices/[id]/+page.svelte:80` : + `fetchAccounts()`, défaut `false`). Un compte bancaire lié à un compte **archivé** reste donc + proposé — mais il ne peut produire **aucune** écriture : la garde `active` de `create_in_tx` + refuse tout compte archivé, et une créance archivée est refusée de même. Le serveur suffit ; le + JSDoc le dit. + - `SettleInvoiceDialog.svelte` : `selectableAccounts` (`:105`) et la liste des comptes bancaires + (`:208`) passent par ces fonctions avec les ids du rôle `Receivable` calculés sur la prop + `accounts` ; le compte bancaire **présélectionné** (`:89`, primaire ou premier) est choisi **dans + la liste filtrée**. ⚠️ Le `$effect` de réinitialisation (`:82-91`) lit aujourd'hui `open`, + `amountDue` et `bankAccounts` ; choisir la présélection dans la liste filtrée le ferait dépendre + aussi de `accounts`, et une liste `accounts` arrivée **après** l'ouverture **effacerait la saisie + en cours** (date, montant, mode). La présélection vit donc dans un **effet distinct**, qui ne + touche ni la date ni le montant ni le mode, et ne remplace `bankAccountId` que si la valeur + courante est **absente de la liste filtrée** (`null` compris) ; l'effet de réinitialisation garde + ses seules dépendances (finding F6-3 de la P6 ; test 18). Si la liste filtrée est **vide** **et** qu'au moins un compte bancaire a été + **écarté par le filtre** (`bankAccounts.length > filtered.length` — le seul compte bancaire est lié + au compte débiteurs ; finding F3 de la P4), le dialogue l'**écrit** — clé + `invoices-settle-no-eligible-bank-account`, fr « Aucun + compte bancaire utilisable : le seul compte lié est le compte débiteurs de cette facture. Reliez + un compte bancaire à son propre compte de banque, ou réglez par un compte interne. » (premier + segment `invoices` : conforme à `lint-i18n-ownership` pour un fichier de + `src/lib/features/invoices/`, sans entrée neuve dans `KNOWN_VIOLATIONS`), quatre locales — au + lieu d'un menu vide silencieux. Une société **sans aucun** compte bancaire (installation neuve) + a aussi une liste filtrée vide, mais rien n'a été écarté : le message, qui accuse le compte + débiteurs, y serait faux — le dialogue garde alors son comportement actuel (aucun état vide + n'existe aujourd'hui, `grep -n "length === 0" SettleInvoiceDialog.svelte` ne rend rien). + - `supplier-invoices/[id]/+page.svelte:83-84` : idem avec `Payable` ; les ids se calculent sur + `accts` **avant** le filtre `accts.filter((a) => a.active && a.postable)` de la ligne 84 ; liste + vide **et** au moins un compte bancaire écarté par le filtre des comptes créanciers (la page filtre + déjà `journalAccountId !== null`, `:83` : une société sans compte bancaire **lié** n'a rien + d'écarté, et garde son comportement actuel) → même traitement, clé + `supplier-invoices-pay-no-eligible-bank-account` (alignée sur ses + voisines `supplier-invoices-pay-*`, `:96`, `:100`, `:322`), fr « Aucun compte bancaire + utilisable : le seul compte lié est le compte créanciers de cette facture. Reliez un compte + bancaire à son propre compte de banque, ou réglez par un compte interne. » + - Les écrans qui alimentent le dialogue (`invoices/[id]/+page.svelte`, `invoices/due-dates/+page.svelte`) + ne changent pas : le filtre vit dans le dialogue, et ils lui passent déjà la liste des comptes + actifs non filtrée par `postable`. + - **Borne exacte `sitesTotal`** (`frontend/src/lib/shared/i18n-keys.test.ts:456`, assertée `:742` ; + **elle existe déjà** — 1868 au 2026-10-08 —, ce n'est pas la 15-5c qui la pose ; finding R5-2 de + la P5) : elle est relevée pour **tous** les sites `i18nMsg` neufs de la story, comptés — + **deux** dans `failedItemLabel` (AC6), **deux** pour les messages de liste vide (AC8 : + `SettleInvoiceDialog.svelte` et `supplier-invoices/[id]/+page.svelte`), et **deux** de plus si la + 15-5c est mergée (libellé `failed[]` du rapprochement, AC7) : **+4** attendus, **+6** avec la + 15-5c. Le chiffre se **recompte** par le relevé du test sur l'état **rebasé** (l'ordre de merge des + 15-5c, 15-5d et 15-6b change la base), aux deux bornes, et la ventilation (`sitesNonResolus`, + `relais`, `sitesGabarit`) se recompte avec lui ; le commentaire de la borne reçoit une ligne qui + nomme la story et les sites, sur le patron des lignes existantes (`:441-455`). +9. **AC9 — Les refus serveur s'affichent** dans le dialogue client, dans le **dialogue de solde du + reste** et sur la fiche fournisseur (message traduit de l'AC2, non le code). Vérifié par lecture + (P1, lentille R ; P2 ; P3) : les `catch` qui lisent `ApiError.message` sont dans les **parents** du + dialogue de règlement (`invoices/due-dates/+page.svelte:265-268`, + `invoices/[id]/+page.svelte:582-585`), qui passent la prop `errorMsg` ; le **solde du reste** + (AC3 bis, rôles `write_off_nature`, `rounding`, `vat_payable`) s'affiche par + `invoices/[id]/+page.svelte:509` (`writeOffError = err.message;`, rendu `:1577` + `errorMsg={writeOffError}`) — c'est **là** que les messages des comptes désignés (AC2, trois clés + plates : « un administrateur doit désigner un autre compte dans Paramètres → Facturation ») sont + lus, et, pour `rounding`, **aussi dans le dialogue de règlement** (compte d'arrondi du règlement, + AC3 bis : `markError = err.message`, `invoices/[id]/+page.svelte:584`, + `invoices/due-dates/+page.svelte:267`, prop `errorMsg={markError}` `:519` ; finding R6-10 de la + P6) — et c'est pourquoi ils ne + renvoient pas à un choix de compte que ce dialogue n'offre pas (finding R1 = F1 de la P3) ; ceux de la + fiche fournisseur sont dans la page — **aucun changement de code**. Le test Vitest (§ *Tests*) ne fige que + le **rendu** de la prop `errorMsg` par le dialogue, pas la lecture de `err.message` par les parents + (montage trop lourd pour ce qu'il prouverait) : c'est dit, pas surestimé. +10. **AC10 — Documentation des intégrateurs** (`docs/api-external.md`). Le § 10 *Gestion des + erreurs* (`:397-415`) **n'est pas touché** : il ne porte que des codes génériques + (`UNAUTHENTICATED`, `API_KEY_*`, `VALIDATION_ERROR`, `NOT_FOUND`), et la convention du guide est la + **table de refus par route** (`CREDIT_NOTE_INVOICE_SETTLED` n'y figure pas non plus). Les sites, + tranchés (findings R2 = F2 de la P3, choix C-15-6-26) : + - **Solde du reste** — table « Refus / Code / Statut » de `POST /api/v1/invoices/{id}/write-off` + (`:274-283`) : une ligne « Un compte désigné pour la nature, le reste d'arrondi ou la TVA due est + le compte débiteurs de la facture | `SETTLEMENT_COUNTERPARTY_IS_CLAIM_ACCOUNT` | `400`, + `details.role` (`write_off_nature`, `rounding`, `vat_payable`) ». + - **Règlement client** — `POST /api/v1/invoices/{id}/settlements` n'a ni section ni table (le + guide ne le cite qu'en renvoi, `:257`, `:272`) : une phrase « Refus de `POST + /api/v1/invoices/{id}/settlements` (enregistrer un règlement) » ajoutée sous le § *Lister et + annuler les règlements d'une facture* (`:253`), **hors** de la table de ce § (`:259-268`), qui est + celle de l'**annulation** — sans quoi un intégrateur la lirait comme un refus d'annulation (finding + F6 de la P4) ; elle nomme la route, le code, `400` et les `details` (`claim`, `role` : + `counterparty` ou `rounding`). + - **Règlement fournisseur et lots** — `POST /supplier-invoices/{id}/pay` n'est cité qu'en renvoi + (`:287`) et le guide n'a **aucune** section sur les lots (`grep -n "payment-batch" + docs/api-external.md` ne rend **rien** ; la mention de `:287` dit « lot de paiement ») : une + phrase « Refus de `POST /api/v1/supplier-invoices/{id}/pay` (régler une facture fournisseur) » + sous le § *Annuler le règlement d'une facture fournisseur* (`:285`), à côté de ce renvoi, posée + **avant** la ligne « Refus : » de l'**annulation** (`:291`, `SUPPLIER_INVOICE_NOT_PAID`…) et + libellée par sa route, pour qu'un intégrateur ne la lise pas comme un refus d'annulation — même + consigne que côté client (finding F7 de la P5). Elle nomme la route du paiement (`400`), puis les deux + routes de lot (ouvertes aux clés : `comptable_routes`, `crates/kesh-api/src/lib.rs:496-505`) : + `POST /api/v1/payment-batches` (item `failed[]`, avec `SUPPLIER_INVOICE_PURCHASE_ENTRY_MALFORMED`) + et `POST /api/v1/payment-batches/{id}/confirm` (`400`, `details.paymentBatchId` et + `details.supplierInvoiceId`). Pas de section neuve. + - **Rapprochement** — dans la **phrase** des refus par proposition du § *Accepter des propositions + de rapprochement* (`:307`, qui cite en prose `RECONCILIATION_OVERPAYMENT` et + `RECONCILIATION_INVOICE_NOT_ELIGIBLE` ; le § n'a pas de liste), avec `details.role` + (`counterparty`, `rounding`). +11. **AC11 — Manuels** (`docs/manual/fr/user-manual.tex`, et `admin-manual.tex` en dernier tiret) : + - § *Enregistrer et annuler un règlement* (`\label{sec:reglement-client}`, `:1045`) : « ou + espèces et **tout autre compte** » (`:1048`, jumeau du « libre » fournisseur, présent au PDF + aplati — finding F5 de la P4) devient « ou espèces et un autre compte de contrepartie » ; après « Kesh + passe l'écriture *Débit contrepartie / Crédit débiteurs* » (`:1049`), une phrase : la contrepartie + ne peut pas être le compte débiteurs de la facture — l'écriture serait nulle et le reste dû + baisserait sans que rien ne bouge au grand livre ; Kesh ne le propose pas et le refuse ; de même un + compte bancaire lié au compte débiteurs. **Deux cas, deux remèdes** (finding F1 de la P3) : si le + compte en cause est celui que vous **choisissez** (compte interne, compte bancaire), choisissez-en + un autre ou reliez le compte bancaire à son propre compte de banque ; si c'est un compte + **désigné dans les réglages** — compte d'arrondi, compte d'une nature de solde, compte de TVA due + (AC3 bis) —, un administrateur doit en désigner un autre dans *Paramètres → Facturation*. La + seconde phrase est écrite + au paragraphe **Solder le reste** du même § (`:1064-1072`, qui renvoie déjà à la section + *Solde du reste* des paramètres). + - § *Régler une facture fournisseur* (`:1295-1305`) : le même énoncé pour le compte créanciers ; + et « choisissez un compte de contrepartie **libre** » (présent au PDF aplati) est **corrigé** — + « libre » devient faux. + - § *Paiements par fichier pain.001* (`\label{sec:paiements-fournisseurs}`, `:1346-1360`) : une + facture dont le compte créanciers est le compte lié au compte bancaire du lot n'entre pas dans le + lot (motif affiché) ; si le compte bancaire a été relié depuis, la confirmation est refusée — et + le manuel dit les **deux issues** : relier de nouveau le compte bancaire à son propre compte de + banque puis confirmer, ou annuler le lot et régler la facture depuis sa fiche (le fichier + pain.001 a pu être exécuté par la banque : c'est alors la première). + - § rapprochement (puce *Accepter*, `:1525`) : la proposition échoue avec son libellé si le compte + bancaire est lié au compte débiteurs (renvoi à la configuration, que la 15-6c ferme), **ou** si + le compte d'arrondi désigné est le compte débiteurs (renvoi à *Paramètres → Facturation*) — les + deux cas du libellé de l'AC7. ⚠️ La + **15-5c** réécrit cette section : écrire sur sa version si elle est mergée, sinon rebase attendu. + - § balance âgée (`:1754`, finding F3 de la P5) : la liste des écritures qui écartent le total + général du solde du compte clients cite « règlement dont la contrepartie est ce compte + lui-même » comme un cas ordinaire — après la story, ce geste est **refusé**. La phrase devient + « règlement **enregistré avant la version 0.13.0** dont la contrepartie était ce compte + lui-même », ou le cas rejoint les « données antérieures » que la même phrase cite déjà. Le reste + de la phrase (« changement du compte clients dans les réglages », que la 15-6a relit sans le + réécrire ; « paiement d'une facture fournisseur compensé sur ce compte », que la story n'interdit + pas — le compte soldé y est 2000, non 1100) **ne change pas**. + - **Manuel d'administration** (`docs/manual/fr/admin-manual.tex`, § des réglages de facturation ; + finding F6-1 de la P6 — l'AC n'y portait aucun verdict, et la 15-6c écrivait que la 15-6b n'y + touche pas) : le remède des trois clés « compte désigné » renvoie l'**administrateur** à + *Paramètres → Facturation*, et c'est ce manuel qu'il lit. Verdict, ligne par ligne : + `:2027` (*Compte de différences d'arrondi* : « charge ou produit, actif et imputable ») — + **inchangée**, elle reste vraie (l'exigence de type est celle de la désignation) ; `:2029` + (*Comptes du solde du reste*) — **une phrase ajoutée** : un solde du reste est refusé, sans rien + écrire, si le compte de la nature, le compte de différences d'arrondi ou le compte de TVA due + est le **compte débiteurs de la facture** (cas d'un compte débiteurs changé de type depuis) ; + désigner alors un autre compte ici ; `:2033` (paiement avec écart) — la liste « réglage vide, + ou compte archivé, rendu non imputable ou changé de type depuis » gagne « **ou devenu le compte + débiteurs de la facture** ». La phrase de frontière de la 15-6c (son AC9) est alignée dans la + même remédiation. + PDF régénérés (`make fr`) et contrôlés **aplatis** (`pdftotext … | tr '\n' ' ' | tr -s ' '`), une + phrase neuve au moins par section — `:1754` et les deux phrases du manuel d'administration compris. +12. **AC12 — CHANGELOG** sous `## [0.13.0] — Non publié`, `### Corrigé`, renvoi à + [#474](https://github.com/guycorbaz/kesh/issues/474). **Propriétaire de la section** : la première + story de la version mergée la crée en tête des versions si elle est absente ; les suivantes y + ajoutent leur entrée. + +## Tasks / Subtasks + +- [ ] **T0 — Rebase** sur `main` après le merge de la 15-6a et des 15-5a à 15-5e ; + **refaire tous les numéros de ligne** de la fiche sur `HEAD` ; vérifier où la 15-5a a placé + `ACCOUNT_NOT_POSTABLE` dans `settle_invoice` et `pay_in_tx` (AC3, AC4) ; **relever l'ordre des + lectures** de `settle_invoice` et de `write_off_invoice` sur `HEAD` et vérifier que l'AC3 bis le suit + — sinon l'AC3 bis suit le code et le doc-comment le dit ; constater que les routes du règlement, du + solde, du paiement fournisseur et de la confirmation d'un lot sont rejouées par la 15-5e (un refus + métier de cette story n'est pas un 1213 : il n'est jamais rejoué) ; relire + le commentaire de la 15-5b dans `accept_one_invoice` (AC5), tester l'existence de + `failed-proposal-label.ts` (AC7), relever `sitesTotal` (AC8). Les choix **C36, C52, C54, C55** + que la fiche cite vivent au registre de la branche des 15-5 (dépôt principal) : absents de cette + branche avant le rebase, ils y arrivent avec lui (finding R6-11 de la P6). +- [ ] **T1 — Variante, rôle, contexte de lot, code, mapping, i18n** (AC1, AC2). +- [ ] **T2 — Helper commun puis gardes serveur** : `ensure_not_claim_account`, + `claim_account_refusal` et `claim_account_refusal_details` dans `invoice_settlements.rs` (AC1), **avant** les sites qui les + appellent ; gardes (AC3, AC3 bis, AC4, AC5), lecteur sœur fournisseur (AC4). Aucun site ne compare + ni ne construit le refus ni ne lit le numéro par lui-même. **Critère par le nom seul, sur une liste + fermée** (findings R5-3 et F2 de la P5, choix C-15-6-33 — le critère de la P4, un `grep` mono-ligne + sur un littéral à champs, rendait vert par construction le littéral que rustfmt coupe sur plusieurs + lignes, et tout champ contenant un point) : + `grep -rnF "SettlementCounterpartyIsClaimAccount" crates/*/src | grep -vE ':[0-9]+:\s*//'` + (occurrences de **code**, commentaires et doc-comments exclus) ne rend des lignes **que** dans ces + cinq fichiers, chacune pour une raison nommée : + - `kesh-db/src/errors.rs` — la définition et le bras d'`error_code()` (et leurs tests unitaires) ; + - `kesh-db/src/repositories/invoice_settlements.rs` — `claim_account_refusal`, **seule** + construction de la variante (et ses tests) ; + - `kesh-db/src/repositories/payment_batches.rs` — l'interception de `confirm_batch`, qui reconstruit + le refus avec `batch: Some`, et la conversion en `PaymentBatchFailedItem` à la création (AC6) ; + - `kesh-api/src/errors.rs` — le mapping HTTP (AC2) et ses tests ; + - `kesh-api/src/routes/reconciliation.rs` — la conversion en `FailedProposal` (AC5, AC3 bis). + Une ligne dans un **autre** fichier est un site non résolu : il est ramené au helper ou ajouté à la + liste avec sa raison, au Dev Agent Record. Dans les cinq fichiers, chaque occurrence est **lue** + (pas seulement comptée) : une construction hors de `claim_account_refusal` et de l'interception de + `confirm_batch` est un défaut. Et `grep -rnF "SELECT number FROM accounts WHERE id = ? AND company_id = ?" crates/*/src` + ne rend, pour cette story, que `claim_account_refusal` (chaîne écrite sur une ligne — une requête + coupée par `\` y échapperait : le helper l'écrit d'un tenant, et le doc-comment le dit). +- [ ] **T3 — Lots de paiement** (AC6), serveur (création, écriture d'achat malformée, confirmation + contextualisée) et libellés. +- [ ] **T4 — Libellé `failed[]` du rapprochement** (AC7) ou signalement. +- [ ] **T5 — Fonctions d'écran et écrans** (AC8, AC9). +- [ ] **T6 — Tests** (§ *Tests*) — chaque test de garde **rougit d'abord** sur le code non corrigé, + **sauf** les tests de non-régression 3 et 9, verts avant comme après. +- [ ] **T7 — Documentation** : `docs/api-external.md` (AC10), manuel + PDF (AC11), CHANGELOG (AC12). +- [ ] **T8 — Inventaire fermé** (§ *Inventaire*) : rejouer les commandes ; tout site neuf est traité + ou écrit en angle mort avec sa raison. +- [ ] **T9 — Gates** : backend complet (dépôts `kesh-db` touchés : pas de ciblage), frontend complet + (`check`, `lint-i18n-ownership`, `test:unit` — dont la borne `sitesTotal` relevée, AC8 —, `build`), + E2E complet au dernier commit de code (D7). + Le verdict de lecture sur les E2E existants (§ *Tests*, « E2E existants ») est recopié au Dev Agent + Record, puis confronté au run : un rouge sur une des specs nommées s'examine d'abord contre ce + verdict. + +## Inventaire — les sites qui écrivent `D X / C X` avec un compte choisi par le client + +Règle « Inventorier les sites NON RÉSOLUS » : l'ensemble clos est celui des **écrivains d'une +écriture de règlement ou de rapprochement à deux comptes dont l'un est désigné par l'utilisateur ou par +la configuration**. Commandes de recompte : + +```sh +grep -rn "journal_entries::create_in_tx" crates/kesh-db/src crates/kesh-api/src +grep -rn "pay_in_tx\|settle_invoice(" crates/kesh-db/src crates/kesh-api/src +grep -rn "rounding_account_for_write\|write_off_account_for_write\|vat_payable_account_for_write" crates/kesh-db/src crates/kesh-api/src +grep -rn "active && a.postable\|a.active && a.postable" frontend/src +grep -rn "listBankAccounts(" frontend/src +``` + +| site | compte soldé | contrepartie | verdict | +|---|---|---|---| +| `settle_invoice` (règlement client, deux modes) | créance (vente) | banque / compte interne | **AC3** | +| `settle_invoice`, compte d'arrondi du règlement | créance (vente) | compte d'arrondi | **AC3 bis** | +| `write_off_invoice` (solde du reste) | créance (vente) | nature, reste d'arrondi, TVA due | **AC3 bis** (le type ne suffit pas : changement de type confirmé) | +| `pay_in_tx` (règlement fournisseur, deux modes) | dette (achat) | banque / compte interne | **AC4** | +| `create_batch` / `confirm_batch` (lots) | dette (achat) | banque du lot | **AC6** | +| `accept_one_invoice` (rapprochement d'une facture) | créance (vente) | banque ; arrondi (c-bis) | **AC5**, **AC3 bis** | +| `post_manual`, `accept_one_rule` | compte de banque | compte choisi | **15-6d** (#524) | +| `accept_one_split`, `post_split` | compte de banque | comptes choisis | déjà gardés (`:1941-1950`, `:3462-3475`) | +| `credit_notes.rs:548` `create_credit_note` | créance (vente) | produit, TVA, arrondi | **15-6a** pour la créance et l'arrondi (lus sur la vente) ; TVA due lue dans les réglages courants : **angle mort, #525** ; produit de repli : **angle mort, #525** (commentaire de l'orchestrateur du 2026-10-08) (§ *Le défaut*) | +| validation de facture (`invoices.rs:2255`) | — (**crée** la créance) | produit `Revenue`, arrondi | créance des réglages sans type contrôlé : **angle mort, #537** (§ *Le défaut*) | +| validation d'achat (`supplier_invoices.rs:372`) | — (**crée** la dette) | charge | hors classe (aucun compte soldé) ; même forme possible que #537 côté dette (2000 retypé en charge) — à confronter en T8 à la garde à l'usage de la 15-5d, tracé par **#537** (commentaire du 2026-10-08, « Même forme côté dette ») | +| `opening_complement.rs:641` (soldes de départ) | — | lignes saisies, `enforce_postable = true` | hors classe : chaque ligne est choisie par l'utilisateur, aucune créance n'est soldée par le geste | +| saisie manuelle d'écriture (non rendue par les commandes : elle passe par `journal_entries::create`, ajoutée par raisonnement) | — | lignes libres | hors classe : l'utilisateur écrit chaque ligne | + +Écrans rendus par les commandes frontend : + +| écran | verdict | +|---|---| +| `SettleInvoiceDialog.svelte:105`, `supplier-invoices/[id]/+page.svelte:84` | **AC8** | +| `payment-batches/+page.svelte:53` (création de lot, comptes bancaires) | non filtré, **choix écrit** (C-15-6-19) : refus par facture lisible ; § *Angles morts* | +| `settings/invoicing/+page.svelte:65-77` (réglages) | **15-6c** (désignation) | +| `supplier-invoices/+page.svelte:129`, `supplier-invoices/import/+page.svelte:83` (charges d'une facture fournisseur) | hors classe : aucun compte soldé n'est choisi | +| `bank-accounts/+page.svelte`, `BankAccountJournalLinkForm.svelte` (lien des comptes bancaires) | **15-6c** | +| `ReconciliationProposals.svelte` / `ManualMatchModal.svelte` | **15-6d** | +| `routes/(app)/+page.svelte:58` (tuile des comptes bancaires du tableau de bord, rendue par `listBankAccounts(`) | hors classe : affichage seul, aucun règlement (finding F6-2 de la P6) | +| `features/accounts/account-validity.ts:20` (rendu par `active && a.postable`) | hors classe : doc-comment, aucun menu (finding F6-2 de la P6) | + +## Décompte des modules — signal de découpage (D5) + +Finding F5 de la P2 : la P1 a ajouté les lots sans recompter. Recompté le 2026-10-08 (et de nouveau à +la P5, finding R5-5), **au barème de la règle** (« modules métier de premier niveau ») : `kesh-db` +règlement client (`invoice_settlements_write.rs`, solde du reste compris), `kesh-db` helper commun +(`invoice_settlements.rs` : trois fonctions publiques et les types `ClaimAccountClash`, +`ClaimSubject`, `DesignatedRole` — les deux premiers omis jusqu'à la P4, le troisième jusqu'à la P6), `kesh-db` règlement fournisseur +(`supplier_invoices.rs`), `kesh-db` lots (`payment_batches.rs`), `kesh-api` rapprochement +(`routes/reconciliation.rs`), `frontend` factures (`features/invoices`), `frontend` factures +fournisseurs (`routes/supplier-invoices`), `frontend` lots (`features/payment-batches`), `frontend` +comptes (`features/accounts/account-options.ts`) — **neuf** (et non huit : le helper commun manquait +au décompte ; les clés plates de la P5 n'ajoutent pas `kesh-i18n/src/loader.rs`), plus la plomberie +d'erreur et d'i18n +(`kesh-db/errors`, `kesh-api/errors`, `kesh-i18n`) et, si la 15-5c est mergée, une ligne dans +`features/reconciliation`. **Au barème du geste** (patron de la 15-5a : l'écran suit le geste qu'il +sert) : règlement client, règlement fournisseur, lots, rapprochement, fonctions d'écran partagées — +**cinq**. **Signal déclaré au Project Lead** ; **pas de découpage** (choix C-15-6-20) : les gestes ne +sont pas indépendants — une variante, un `ClaimSide`, un lecteur sœur partagé par `pay_in_tx` et les +lots, trois fonctions d'écran —, et les lots ne font qu'appeler la garde de `pay_in_tx` (AC4) ; les +séparer laisserait une story sans sa garde ou dupliquerait la plomberie. Aucun défaut de la P2 ne +recycle un défaut de la P1. + +**P3 (signal D5 levé de nouveau, déclaré au Project Lead)** : sévérité P2 → P3 MEDIUM → MEDIUM. Deux +des cinq MEDIUM distincts (R1 = F1 : remède faux du refus de l'AC3 bis ; R2 = F2 : table de refus du +solde oubliée) **naissent de la remédiation P2** — l'AC3 bis ajoutée sans son message ni sa +documentation : c'est le motif « recyclage » de l'amendement D5, mais **contenu** — une seule règle +(AC3 bis), un seul module de plus (aucun : le dialogue de solde est lu, pas modifié), corrigé ici par +un discriminant et une clé. Le troisième (F3, DRY) est d'origine. **Pas de découpage** (choix +C-15-6-26) ; la remédiation P3 change une règle métier (le message suit le rôle) : la passe suivante +doit être **complète**, pas ciblée. + +**P4 (signal D5 levé une troisième fois, déclaré au Project Lead)** : MEDIUM → MEDIUM ; F1 **recycle** +le discriminant posé en P3 (compte d'arrondi du solde mal classé), contenu à une ligne de classement et +un test ; F2 et F3 sont d'origine. Aucun module de plus (F2 traité en angle mort, #537 et #525). **Pas +de découpage** (C-15-6-30) ; un troisième recyclage sur l'AC3 bis en P5 déciderait de la sortir en +story propre. + +**P5 (signal D5 levé par la sévérité, déclaré au Project Lead)** : MEDIUM → MEDIUM (3 et 3, R + F ; +F2 = R5-3). **Le critère « troisième recyclage sur l'AC3 bis » n'est pas atteint** : le seul MEDIUM qui +touche l'AC3 bis (F1, son ordre des refus) **ne vient pas d'un patch** — il naît d'un **changement +extérieur**, la création de la 15-5e (C52), qui réordonnait alors `settle_invoice` et +`write_off_invoice`, après la P4 ; sa réécriture par C54 (pendant la remédiation) a retiré ce +réordonnancement, et l'AC3 bis garde l'ordre des lectures du code. Les deux autres recyclent des patches de passes antérieures +**hors** de l'AC3 bis : R5-1, le sélecteur Fluent de l'AC2 (posé en P3, réécrit en P4), remplacé par +trois clés plates ; F2 = R5-3, le critère de T2 réécrit en P4, remplacé par un critère par nom sur liste +fermée. F3 (manuel `:1754`) est d'origine. Aucun module de plus (le décompte passe de huit à neuf par +une omission corrigée, pas par un ajout). **Pas de découpage** (C-15-6-33). + +**P6 (clôture — signal de dispersion déclaré, non découpé)** : 0 au-dessus de LOW ; le signal de +**sévérité** n'est pas levé. Le finding F6-7 constate en revanche que le **critère de dispersion** de +l'amendement D5 (« la dispersion dans plus de cinq modules ») est **rempli au barème de la règle** : +**neuf** modules métier de premier niveau, plus la plomberie d'erreur et d'i18n, et `admin-manual.tex` +(documentation, hors décompte) ajouté par F6-1. Le chiffre de **cinq** ne vaut qu'au « barème du +geste », que la règle ne prévoit pas : il n'est **plus** invoqué comme justification. **Déclaré au +Project Lead, non découpé** (choix C-15-6-34), pour une raison écrite qui ne dépend pas du barème : la +fiche est **validée** (six passes, sévérité retombée à 0 au-dessus de LOW), ses gestes partagent une +variante, un sujet typé, un lecteur sœur et trois fonctions d'écran, et un découpage à ce stade +rouvrirait la validation de deux fiches pour séparer ce que la conception lie — le risque que la règle +de découpage veut prévenir (non-convergence) ne s'est pas réalisé. L'arbitrage revient à Guy en revue +de fin d'epic. + +## Tests + +Backend, `#[sqlx::test(migrations = "./test-schema")]` : + +1. `crates/kesh-db/tests/invoice_settlement.rs` — **compte interne = créance** : refus + `SettlementCounterpartyIsClaimAccount { claim: Receivable, role: Counterparty, account_number: Some("1100"), batch: None, .. }` ; + aucune écriture neuve, aucune ligne `invoice_settlements`, `version` et `paid_at` inchangés. +2. Même fichier — **virement, compte bancaire lié au 1100** (posé par `UPDATE bank_accounts SET + journal_account_id = …` : le test ne doit pas dépendre de la 15-6c) : même refus. +3. Même fichier — un règlement ordinaire (banque 1020, puis compte interne caisse) reste accepté + (non-régression de l'ordre et du montant). Le seed `seed_accounting_company` n'a pas de 1020 + (`test_fixtures.rs:129-133` : 1000, 1100, 2000, 3000, 4000) : le créer comme + `invoice_settlement.rs:182-185`. +4. Même fichier — **montant excessif sur 1100** : le refus est celui de la contrepartie, **pas** le + trop-perçu (ordre AC3 ; le test 1 n'a pas de montant excessif et ne le fige pas). +5. `crates/kesh-db/tests/supplier_invoices_repository.rs` — compte interne = 2000 : `claim: Payable`, + `role: Counterparty`, + facture toujours `open`, aucune écriture. +6. Même fichier — virement sur un compte bancaire lié au 2000 : même refus. ⚠️ La fixture existante + (`:215`) lie un compte bancaire au **1100** pour payer des factures fournisseurs : elle reste + valide (le compte soldé est 2000) ; ne pas la changer. +7. `crates/kesh-api/tests/reconciliation_e2e.rs` — **un lot de deux propositions, deux créances + différentes** : la facture A validée avec la créance sur 1100 ; puis un compte d'actif `1101` créé + et désigné comme débiteurs ; la facture B validée (créance sur 1101) ; le compte bancaire du lot lié + au 1100 (SQL). `accept_batch` (un seul `bank_account_id` pour tout le lot, `:1013`) : HTTP 200, B + acceptée, A dans `failed[]` avec le code et les **cinq** clés de `details` (`bankAccountId`, + `accountId`, `accountNumber`, `claim`, `role = "counterparty"`) ; la transaction bancaire + de A reste en attente. +8. Même fichier — proposition sur A **d'un montant supérieur au reste** : `failed[]` porte + `SETTLEMENT_COUNTERPARTY_IS_CLAIM_ACCOUNT`, pas `RECONCILIATION_OVERPAYMENT` (ordre AC5). La + transaction bancaire porte la **référence de la facture** (patron du test existant de + `RECONCILIATION_OVERPAYMENT`, `two_centimes_over_a_half_centime_invoice_is_an_overpayment`, + `reconciliation_e2e.rs:4869`), pour que son **score reste positif** : l'étape 7bis + (`RECONCILIATION_SCORE_TOO_LOW` si `score.total <= 0.0`, `reconciliation.rs:1344-1354`) passe + **avant** les étapes (a) à (c), et un score nul rendrait ce refus-là avant comme après la story, + sans rien figer de l'ordre AC5 (finding F6-4 de la P6). +9. `crates/kesh-db/tests/invoice_settlement.rs` — **ordre** : compte interne 1100 **archivé** → + `INACTIVE_OR_INVALID_ACCOUNTS` (l'ancien refus prime). +10. `crates/kesh-db/tests/invoice_write_off.rs` — **solde du reste, nature = créance** (AC3 bis) : + après validation, 1100 change de type par SQL (`account_type = 'Expense'`, `role = NULL` — le + chemin `confirm_retype` sans la route) et est désigné compte d'escompte en SQL ; solder le reste en + escompte → `SettlementCounterpartyIsClaimAccount { claim: Receivable, role: WriteOffNature, .. }`, + rien d'écrit. +10 bis. Même fichier — **solde du reste, reste d'arrondi = créance** (findings R1 = F1 de la P4) : + même changement de type, 1100 désigné compte **d'arrondi** (et non d'escompte, qui reste un compte + de charge distinct) ; solder en **escompte** un reste à quatre décimales (montage de + `a_four_decimal_remainder_sends_its_sub_centime_to_the_rounding_account`, `:898`) → + `SettlementCounterpartyIsClaimAccount { role: Rounding, .. }` — **pas** `WriteOffNature` : le rôle + suit la colonne de réglage lue, pas la nature du geste ; rien d'écrit. +10 ter. Même fichier — **ordre des refus du solde** (AC3 bis, finding F6 de la P5) : une facture + **avec TVA** validée d'abord (la validation exige le réglage de TVA due, `test_fixtures.rs:150-151`) ; + **après validation** — finding R6-9 de la P6 —, même changement de type, 1100 désigné compte + d'**escompte**, **et** réglage de TVA due vidé (`default_vat_payable_account_id = NULL`) ; solder en + escompte → `SettlementCounterpartyIsClaimAccount { role: WriteOffNature, .. }`, **pas** + `ConfigurationRequired("default_vat_payable_account_id")` : la nature est comparée aussitôt la + créance lue, avant la lecture de la TVA due ; rien d'écrit. +11. `crates/kesh-db/tests/invoice_settlement.rs` (finding R5-7 de la P5 : le fichier du règlement, non + celui du solde) — **arrondi du règlement = créance** (AC3 bis). Ce fichier n'a pas de montage + d'arrondi : il reprend celui de `crates/kesh-db/tests/invoice_amount_due_parity.rs` + (`seeded_with_rounding`, `:72`, qui désigne le compte d'arrondi par `designate_rounding_account`) + et une facture au **reste brut hors centime** (patron `raw_due_invoice`, + `crates/kesh-api/tests/invoice_echeancier_e2e.rs:986`) — + **arrondi du règlement = créance** + (AC3 bis) : même changement de type, 1100 désigné compte d'arrondi ; un paiement au centime de + cette facture (`SettlesWithRounding`) par **compte interne** (caisse 1000) → même refus, + `role: Rounding`, rien d'écrit (le compte interne, valide, est contrôlé avant, AC3 bis). +12. `crates/kesh-api/tests/reconciliation_e2e.rs` — **arrondi (c-bis) = créance** (AC3 bis) : patron + `half_centime_invoice_and_tx` (`:4705`) et `designate_rounding_account` (`:4681`), le compte + d'arrondi désigné étant la créance retypée → `failed[]` `SETTLEMENT_COUNTERPARTY_IS_CLAIM_ACCOUNT`, + HTTP 200, `details.role = "rounding"` et **pas** de `details.bankAccountId` (AC3 bis). +13. `crates/kesh-db/tests/payment_batches_repository.rs` — **création** : deux factures fournisseurs, + A achetée avec la dette sur 2000, B après changement du réglage créanciers vers un `2001` ; compte + bancaire source lié au 2000 : A dans `failed[]` avec le code et ses `details`, B dans le lot ; avec + A seule, **aucun** lot créé. `details` = les cinq clés de l'AC6 (`role = "counterparty"`), et + `accountNumber = "2000"` (lu à l'échec par le helper, AC6). +14. Même fichier — **création, écriture d'achat sans ligne de crédit** (en-tête d'écriture sans + ligne, désigné par `UPDATE supplier_invoices SET purchase_journal_entry_id = …`) : la facture dans + `failed[]` avec `SUPPLIER_INVOICE_PURCHASE_ENTRY_MALFORMED` et ses `details`, les autres du lot + retenues. +15. Même fichier — **confirmation, puis remède** : lot créé sur un compte bancaire lié au 1020 (créé + comme au test 3), puis relié au 2000 en SQL ; `confirm_batch` → `SettlementCounterpartyIsClaimAccount + { claim: Payable, role: Counterparty, batch: Some(..), .. }` portant l'id du lot, de la facture + **et son numéro** (`supplier_invoice_number`, relu par `confirm_batch`, AC6), lot toujours + `generated`, aucun règlement écrit ; puis relié de nouveau au 1020 → la confirmation **passe**. + (La fixture `:100` lie au 1100 : inchangée, même raison qu'au test 6.) +16. Test HTTP `POST /api/v1/invoices/{id}/settlements` (fichier existant qui l'appelle, + `crates/kesh-api/tests/invoice_echeancier_e2e.rs`) : 400, code, `details.claim = "receivable"`, + message **fr**. ⚠️ Kesh n'a **pas** de négociation par `Accept-Language` : la langue des messages + est **globale au processus** (`init_error_i18n`, `crates/kesh-api/src/errors.rs:23-26`), comme le + dit `docs/api-external.md` § 10. Le test de parité de `kesh-i18n` (`parity_between_locales`, + `crates/kesh-i18n/src/loader.rs:719`) ne contrôle que la **présence** des clés dans les quatre + locales, **pas leur traduction** — une copie du français passerait : la traduction se relit en + revue. +16 bis. Même fichier — test HTTP `POST /api/v1/invoices/{id}/write-off` (le fichier l'appelle déjà) : + compte de la nature `discount` = la créance retypée (montage du test 10) → **400**, + `SETTLEMENT_COUNTERPARTY_IS_CLAIM_ACCOUNT`, `details.role = "write_off_nature"`, message **fr** + contenant « nature de solde » et « Paramètres → Facturation » et **ne** contenant **pas** « banque, + caisse » — il fige la clé plate `error-settlement-write-off-account-is-receivable` de l'AC2 + (findings R1 = F1 de la P3, R5-1 de la P5), que le test 16 ne voit pas. + +Frontend (Vitest) : + +17. `frontend/src/lib/features/accounts/account-options.test.ts` (existant) : les trois fonctions — le + rôle donné écarté, les comptes bancaires liés écartés, les autres gardés, les comptes sans rôle + intacts ; un compte bancaire lié à un compte de rôle `Receivable` **non imputable** est écarté (ids + calculés avant le filtre `active && postable`). +18. `SettleInvoiceDialog.test.ts` : 1100 (rôle `Receivable`) absent du menu « Compte interne » ; un + compte bancaire lié au 1100 absent du menu des virements, la présélection tombe sur un autre ; + `accounts` fourni **après** l'ouverture et une saisie de montant : le montant saisi est **gardé** + (AC8, effet distinct) ; seul + compte bancaire lié au 1100 → le message de liste vide ; **aucun** compte bancaire → **pas** de + message de liste vide (rien n'a été écarté, AC8 — finding F3 de la P4) ; la prop `errorMsg` + s'affiche (AC9 — rendu seulement). +19. `supplier-invoices/[id]/supplier-settlement-page.test.ts` : 2000 absent, banque liée au 2000 + absente, liste vide écrite ; aucun compte bancaire lié → pas de message de liste vide. +20. `frontend/src/lib/features/payment-batches/payment-batch-helpers.test.ts` : les libellés des deux + codes neufs (AC6), le test « mappe les six codes » devenu « huit ». + +Soit **23 tests nommés** (19 backend — 1 à 16, 10 bis, 10 ter et 16 bis —, 4 fichiers Vitest étendus) : tous neufs, +sauf le test existant « mappe les six codes » de `payment-batch-helpers.test.ts:61`, **renommé et +étendu** à huit codes (test 20 ; ses six assertions existantes restent) — à recompter aux deux bornes +avant de les déclarer. Si la 15-5c est mergée, `failed-proposal-label.test.ts` reçoit en plus les +deux cas de l'AC7 (non compté : conditionnel). + +**E2E existants — verdict de lecture** (finding F7 de la P3 ; à confronter au run de T9) : +- `frontend/tests/e2e/bank-account-journal-link.spec.ts:105-119` lie le premier compte bancaire + (TEST_IBAN) au **1100**, compte débiteurs du préréglage (`test_fixtures.rs:129-147`). La 15-6b ne + touche pas la liaison (c'est la 15-6c, C-15-6-15) : la spec reste verte ici. ⚠️ **Le filtre par + rôle de l'AC8 est inerte en E2E** (finding R4 de la P4) : le seed (`seed_accounting_company`, + appelé par `test_endpoints.rs:184`, `:197`) insère les comptes **sans rôle** + (`test_fixtures.rs:129-139`, `INSERT INTO accounts (company_id, number, name, account_type)` ; + aucun `SET role` ni `'Receivable'` dans le seed) : `accountIdsWithRole(…, 'Receivable')` y rend un + ensemble vide, rien ne disparaît des menus, rien ne se décale. Seul le refus **serveur** (qui + compare la créance lue sur la vente, sans rôle) s'appliquerait si une spec réglait une facture + client par le compte bancaire lié au 1100 — aucune ne le fait. +- Les specs qui en dépendent n'en souffrent pas, par lecture : `invoices_echeancier.spec.ts:143-146` + règle par **compte interne**, `selectOption({ index: 1 })` — le menu n'est pas filtré en E2E (seed + sans rôle), l'index 1 reste le même compte qu'avant la story ; `invoices.spec.ts:318-321` fait de + même (même menu, même index, même raison — finding R5-6 de la P5) ; `reminders.spec.ts:104-112` règle + par API sur un compte d'actif `startsWith('10')` (liquidités, jamais 1100) ; + `invoice-write-off.spec.ts:70-85` — **seule** spec qui traverse le chemin de l'AC3 bis — désigne + l'escompte sur 4000 et règle par API sur 1000 : nature 4000, TVA due 2000 du seed + (`test_fixtures.rs:164-166`), aucune n'est la créance 1100 ; `invoices_echeancier.spec.ts:227-228` et + `invoices-settlement-cancel.spec.ts:103-104` règlent par API sur `cash` (1000) ; + `reconciliation-cancel.spec.ts:60-83` relie le compte bancaire au 1000 avant d'accepter ; + `reconciliation-amount-due.spec.ts` n'accepte rien ; `reconciliation.spec.ts` ne joue pas + l'acceptation ; `payment-batches.spec.ts:51-81` et `supplier-invoices.spec.ts` paient des dettes + du **2000** avec un compte bancaire lié à un compte liquide, ou un compte interne de l'actif + (`supplier-invoices.spec.ts:77`, premier compte `Asset` actif — 1000, pas 2000). +- **Aucune rupture attendue** — par l'inertie du filtre en E2E, non par un décalage sans effet ; aucune + spec n'est modifiée par cette story, et **aucun E2E n'exerce l'AC8** (angle mort écrit, § *Angles + morts*). *(Le test « un compte d'actif désigné comme compte d'arrondi est refusé » de la P1 est +**retiré** : il figeait la garantie du « résolu par le type », que l'AC3 bis remplace.)* + +## Dev Notes + +### Ce qui doit être préservé + +- L'**ordre** des refus existants (AC3–AC6) — la 15-5e n'en change aucun (son AC7) — et le **verrou** `FOR UPDATE` + posé sur le compte bancaire ou le compte interne à la résolution de la contrepartie — la garde neuve compare des identifiants déjà lus, elle ne + relit rien sauf le numéro du compte (à l'échec) et, à la confirmation d'un lot, le numéro de la + facture fournisseur (`supplier_invoice_number`, AC6). +- Le **refus du trop-perçu** (du règlement ; étape (c) du rapprochement) vient **après** : un + montant excessif sur 1100 rend le refus de contrepartie, pas `overpayment` — choix délibéré (la + contrepartie est le défaut de fond) ; les tests 4 et 8 le figent. +- `enforce_postable = false` de ces flux : inchangé. +- Le pattern batch des lots (`PaymentBatchFailedItem`) : une facture refusée n'empêche pas les autres. + +### Pièges + +- **Ne pas** réutiliser `DbError::InvalidInput` : code générique `INVALID_INPUT`, sans `details` + (choix C-15-6-3). **Ne pas** réutiliser `ACCOUNT_NOT_POSTABLE` (15-5a) : 1100 est imputable. +- La 15-5a insère `ACCOUNT_NOT_POSTABLE` à la lecture du compte interne de `settle_invoice` et de `pay_in_tx` ; la 15-5b + pose dans `accept_one_invoice` un commentaire de classement que l'AC5 amende ; la **15-5c** pose les + libellés de `failed[]` (#492) et réécrit le manuel du rapprochement — le rebase peut demander de + fusionner la liste de codes : c'est l'AC7. T0 refait les numéros de ligne. +- `kesh-i18n` : un test vérifie la **présence** des clés dans les quatre locales (pas leur + traduction) ; un autre (`no_new_select_expression_reaches_the_frontend_dictionary`, `loader.rs:482`) + refuse tout **sélecteur** Fluent neuf — d'où les clés plates (AC2). Ajouter les clés neuves aux + quatre fichiers dans le même commit — **six** serveur (AC2 : trois par côté et contexte, trois pour + un compte désigné dans les réglages, une par rôle), **quatre** écran + (deux de lots, AC6 ; deux de liste vide, AC8), **deux** de plus si la 15-5c est mergée (libellé + `failed[]` du rapprochement à deux cas, AC7). +- **Un code, deux remèdes** (C-15-6-26) : ne pas créer un second code pour le compte désigné — le + défaut est le même (`D X / C X`), un intégrateur le traite d'un seul bras ; c'est `details.role` + qui porte la différence, et le message qui la dit à l'utilisateur. +- **Recompter** les tests ajoutés aux deux bornes avant de les déclarer (§ *Recompter ses propres + comptes rendus*). + +### Angles morts assumés + +- Le filtre d'écran est par **rôle**, la garde serveur par **compte de l'écriture de vente** : si le + réglage débiteurs a été déplacé hors du compte de rôle `Receivable`, l'écran peut proposer un compte + que le serveur refusera (avec un message clair) — jamais l'inverse (écriture fausse). Choix C-15-6-4. +- **Comptes archivés** : absents des listes que reçoivent les écrans ; un compte bancaire lié à un + compte archivé reste proposé, et le serveur refuse toute écriture sur un compte archivé (C-15-6-17). +- **Écran de création d'un lot** (`payment-batches/+page.svelte:53`) : il propose un compte bancaire + lié au compte créanciers ; le refus arrive par facture, libellé, avant tout fichier (AC6). Le + filtrer demanderait de charger le plan comptable sur cet écran (C-15-6-19). +- **Validation d'une facture et avoir** (finding F2 de la P4) : créance retypée lue à la validation + (**#537**), TVA due de l'avoir lue dans les réglages courants (**#525**), produit de repli de + l'avoir de même (**#525** aussi, commentaire de l'orchestrateur du 2026-10-08) ; validation d'achat, + même forme côté dette (**#537**, commentaire du 2026-10-08) — § *Le défaut*, inventaire. Hors classe + de la story (aucun ne solde un compte), et deux modules de plus. +- **Le filtre d'écran n'est exercé par aucun E2E** : le seed E2E n'a pas de rôle (§ *Tests*, « E2E + existants ») ; l'AC8 n'est figé que par Vitest (tests 17-19). +- **Données antérieures** : des règlements `D 1100 / C 1100` ou `D 2000 / C 2000` déjà écrits par une + instance v0.12 restent tels quels, sans détection. Acceptable : le produit ne tient pas encore de + comptabilité réelle (`CLAUDE.md` § *Project Overview*) ; à réexaminer si une instance en a écrit. + +### Références + +- [Source: crates/kesh-db/src/repositories/invoice_settlements_write.rs:43-200, :356-540] +- [Source: crates/kesh-db/src/repositories/accounts.rs:497-515 (changement de type confirmé)] +- [Source: crates/kesh-db/src/repositories/supplier_invoices.rs:554-700] +- [Source: crates/kesh-db/src/repositories/payment_batches.rs:68-140, :226-300, :304-380, :431] +- [Source: crates/kesh-api/src/routes/reconciliation.rs:1013, :1175, :1378-1500] +- [Source: crates/kesh-db/src/errors.rs:515-532, :742-790 ; crates/kesh-api/src/errors.rs:23-26, :2823-2846] +- [Source: crates/kesh-db/src/repositories/company_invoice_settings.rs:397-447, :454-477 (TVA due, sans type), :484-507] +- [Source: frontend/src/lib/features/invoices/SettleInvoiceDialog.svelte:89, :105, :208 ; + frontend/src/routes/(app)/supplier-invoices/[id]/+page.svelte:80-84 ; + frontend/src/routes/(app)/invoices/[id]/+page.svelte:509, :554, :582-585, :1577 ; frontend/src/routes/(app)/invoices/due-dates/+page.svelte:102, :265-268 ; + frontend/src/lib/features/payment-batches/payment-batch-helpers.ts:78 ; + frontend/src/routes/(app)/payment-batches/[id]/+page.svelte:40-50 ; frontend/scripts/lint-i18n-ownership.js:164-187] +- [Source: docs/manual/fr/user-manual.tex:1045-1052, :1064-1072, :1295-1305, :1346-1360, :1525 ; + docs/api-external.md:253-283, :285-287, :307, :397-415] +- [Source: frontend/tests/e2e/bank-account-journal-link.spec.ts:105-119, invoices_echeancier.spec.ts:143-146 ; + crates/kesh-db/src/test_fixtures.rs:129-139 (seed sans rôle) ; crates/kesh-api/src/lib.rs:207-210 (réglages, Admin)] +- [Source: crates/kesh-db/src/repositories/company_invoice_settings.rs:408, :438-441 (même colonne d'arrondi) ; + crates/kesh-db/src/repositories/invoices.rs:2008-2010 ; crates/kesh-db/src/repositories/credit_notes.rs:205, :511 ; + frontend/src/routes/(app)/settings/invoicing/+page.svelte:29 ; issues #525, #537 ; C36 (15-5d), C-15-6-30] +- [Source: issue #474 ; `epic-15-choix-autonomes.md` C-15-6-1, C-15-6-3, C-15-6-4, C-15-6-9 à C-15-6-12, C-15-6-14, C-15-6-17 à C-15-6-20, C-15-6-26, C-15-6-30, C-15-6-33, C-15-6-34 ; C36, C52, C54, C55 au registre de la branche des 15-5 (arrivent au rebase, T0)] +- [Source: docs/manual/fr/admin-manual.tex:2027, :2029, :2033 (AC11, verdict) ; frontend/src/lib/features/invoices/SettleInvoiceDialog.svelte:82-91 (effet de réinitialisation) ; crates/kesh-api/src/errors.rs:52-59, :88-92 (repli `t_args` formaté)] + +## Dev Agent Record + +### Agent Model Used + +### Debug Log References + +### Completion Notes List + +### File List + +## Change Log + +- 2026-10-08 — Spécification initiale (bmad-create-story, en autonomie). Statut `ready-for-dev`. +- 2026-10-08 — **Validation P1** (Sonnet 4.6, lentilles F et R ; prompt + `15-6b-validate-prompt-p1.md`). F : 2 HIGH, 5 MEDIUM, 2 LOW ; R : 2 HIGH, 5 MEDIUM, 2 LOW (R3 = F1, + R4 = F2, R5 = F3). Appliqués : + - **HIGH F1 (= R3 MEDIUM)** — #524 : sorti vers une sous-story **15-6d** (le finding F4 de découpage + la rendait nécessaire), avec le même refus que le flux ventilé ; C-15-6-6 révisé par C-15-6-9 ; + l'angle mort « voisin hors périmètre » est retiré de cette fiche. + - **HIGH F2 (= R4 MEDIUM)** — `confirm_batch`, troisième appelant de `pay_in_tx` : refus **à la + création du lot**, par facture, avant tout fichier pain.001, et garde maintenue à la confirmation + (AC6, tests 11–12, manuel, api-external). Choix C-15-6-10. + - **HIGH R1** — test 5 irréalisable (un seul compte bancaire par lot) : refait avec deux factures à + créances différentes (test 7). + - **HIGH R2** — test « fr et de par `Accept-Language` » irréalisable (locale globale, + `init_error_i18n`) : test fr + parité des clés (test 13). + - **MEDIUM F3 = R5** — #492 est en **15-5c**, pas en 15-5b : dépendance, AC7, pièges corrigés ; + registre révisé (C-15-6-12). + - **MEDIUM F4** — découpage (plus de cinq modules avec #524 et les lots) : 15-6d créée. + - **MEDIUM F5** — ordre vis-à-vis de `BANK_ACCOUNT_NOT_CONFIGURED`, `INVOICE_SALE_ENTRY_MALFORMED` + et `RECONCILIATION_OVERPAYMENT` écrit (AC5) et testé (test 8). + - **MEDIUM F6** — `docs/api-external.md` (AC10). + - **MEDIUM F7** — liste de comptes bancaires vide écrite à l'écran (AC8, tests 15–16). + - **MEDIUM R6** — « le test 1 fige le trop-perçu » était faux : test 4 distinct. + - **MEDIUM R7** — DRY : la garde client compare l'id du lecteur de la 15-6a ; côté fournisseur, la + requête diffère (crédit + TTC) et devient un lecteur sœur partagé par `pay_in_tx` et la création + de lot (C-15-6-11). + - **LOW F8** — propriétaire unique de la section `[0.13.0]` (AC12) ; lignes de la fiche fournisseur + corrigées (`:82-83`). **LOW F9** — T7 « grep de formes » remplacé par un inventaire fermé des + sites (§ *Inventaire*, T8). **LOW R8** — décomptes refaits. **LOW R9** — ids calculés sur la liste + complète (AC8, test 14) ; route du règlement client nommée ; AC9 « vérifié, aucun changement » + + test ; lignes de la fiche fournisseur. + - Signature des fonctions d'écran fixée pour être partagée avec la 15-6c (C-15-6-14, finding F2 de + la 15-6c). + - Décompte après passe : **12 AC, 9 tâches, 17 tests** (13 backend, 4 fichiers Vitest). +- 2026-10-08 — **Validation P2** (Opus 5.5, lentilles R et F ; prompt `15-6b-validate-prompt-p2.md`). + R : 0 CRITICAL, 0 HIGH, 3 MEDIUM, 9 LOW ; F : 0 CRITICAL, 0 HIGH, 5 MEDIUM, 10 LOW (R-1 = F1, + R-2.1 = F9, R-6 = F7, R-7 = F10, R-9 ⊃ F12, R-10 = F13, R-11 = F8). Tous appliqués, décisions de + l'orchestrateur comprises : + - **MEDIUM R-1 = F1** — les écrans ne reçoivent pas les archivés : l'AC8 y **renonce** (un compte + archivé ne peut recevoir aucune écriture, la garde serveur suffit) ; les ids se calculent avant le + filtre `active && postable` (cas non imputable, test 17). Choix C-15-6-17. + - **MEDIUM F2** — clés renommées pour `lint-i18n-ownership` (`invoices-settle-…`), sans allonger + `KNOWN_VIOLATIONS` ; clé fournisseur alignée (`supplier-invoices-pay-…`). Choix C-15-6-17. + - **MEDIUM F3** — « résolu par le type » ne tient pas (changement de type confirmé) : les comptes + d'arrondi et de solde du reste sont **comparés par identifiant** à la créance (AC3 bis, tests + 10–12). Choix C-15-6-18, qui révise C-15-6-3. + - **MEDIUM F4** — lot bloqué à la confirmation : message propre, `details` (lot, facture) et manuel + disent les deux issues (relier de nouveau, ou annuler le lot) ; test 15 vérifie le remède. Le refus + à la création reste la défense principale. Choix C-15-6-19. + - **MEDIUM F5** — modules recomptés et écrits (§ *Décompte des modules*) : huit au barème des + fichiers, cinq au barème du geste ; **signal D5 déclaré au Project Lead**, pas de découpage + (gestes dépendants). Choix C-15-6-20. + - **MEDIUM R-2** — `ACCOUNT_NOT_POSTABLE` (15-5a) dans l'ordre des refus (AC3, AC4) ; tâche T0 de + rebase ; commentaire de la 15-5b dans `accept_one_invoice` amendé (AC5). + - **MEDIUM R-3** — le filtre de `ManualMatchModal` relève de la 15-6d (en-tête, inventaire), qui le + tranche sans conditionnel. + - **LOW** — R-4 : `:83-84` ; R-5 : `:120`, `usable_designated_account` `:484-507` ; R-6 = F7 : + fichier de la confirmation et « vérifié, aucun changement » ; R-7 = F10 : signature de + `validate_invoice_for_batch`, écriture d'achat malformée → `failed[]` (test 14) ; R-8 : six → huit + clés ; R-9 = F12 : décompte des clés, parité = présence ; R-10 = F13 : test du dialogue = rendu de + la prop, dit ; R-11 = F8 : inventaire complété par ses propres commandes (backend et écrans) ; + R-12 : 1020 créé, tests verts d'avance exclus du « rougit d'abord » ; F6 : écran de création de + lot non filtré, choix écrit ; F11 : « contrepartie libre » corrigé au manuel ; F14 : lots dans la + table du § 10 seulement, routes nommées ; F15 : données antérieures en angle mort. + - **Tests renumérotés** (10–12 neufs pour l'AC3 bis, 14 neuf, l'ancien 10 retiré) : les numéros + cités dans l'entrée P1 ci-dessus visent l'**ancienne** numérotation. + - Décompte après passe : **13 AC (AC1–AC12 et AC3 bis), 10 tâches (T0–T9), 20 tests** (16 backend, + 4 fichiers Vitest). +- 2026-10-08 — **Validation P3** (lentilles R et F en **Sonnet** — prompt `15-6b-validate-prompt-p3.md` ; + remédiation par Opus 5.5). R : 0 CRITICAL, 0 HIGH, 2 MEDIUM, 5 LOW ; F : 0 CRITICAL, 0 HIGH, + 3 MEDIUM, 4 LOW (F8 noté par la lentille, non compté comme défaut). R1 = F1, R2 = F2. Tous appliqués, + décisions de l'orchestrateur comprises (choix C-15-6-26) : + - **MEDIUM R1 = F1** — un compte venu des **réglages** (arrondi, nature, TVA due) n'appelle pas le + remède d'une contrepartie choisie : discriminant `role` (`SettlementAccountRole`) dans la variante + et en `details.role` ; quatrième clé serveur `error-settlement-designated-account-is-receivable` + qui renvoie à *Paramètres → Facturation* (sélecteur Fluent sur `$role`) ; libellé `failed[]` du + rapprochement à deux cas (AC7) ; manuel à deux cas (AC11) ; `details` de (c-bis) fixés sans + `bankAccountId` ; dialogue de solde (`invoices/[id]/+page.svelte:509`) couvert par l'AC9 ; test 16 + bis (HTTP du solde) neuf. Un seul code conservé. + - **MEDIUM R2 = F2** — `docs/api-external.md` : ligne à la table du solde (`:274-283`) ; une phrase + « Refus » sous le § des règlements client (`:253`) et sous celui du règlement fournisseur (`:285`, + lots compris) ; le § 10, générique, n'est plus visé (AC10). + - **MEDIUM F3** — helper commun dans `invoice_settlements` : **comparaison** pure + (`ensure_not_claim_account`, empruntée par la 15-6d) séparée de la **construction** du refus + (`claim_account_refusal`, numéro lu à l'échec seulement) et des `details` + (`claim_account_refusal_details`, trois consommateurs) ; T2 l'ordonne en premier. + - **LOW** — F4 : `vat_payable_account_for_write` ne contrôle aucun type (`company_invoice_settings.rs:454-480`), + la comparaison est sa seule garde (§ *Le défaut*, AC3 bis) ; F5 : `supplier_invoice_number` relu par + `confirm_batch` (une `SELECT`), pas de champ de plus ; R7 : `accountNumber` de la création de lot lu + à l'échec par le helper ; F6 : textes fr des quatre clés d'écran (et des deux libellés de l'AC7) ; + F7 : verdict écrit sur les E2E existants, `bank-account-journal-link.spec` compris (§ *Tests*, T9) ; + R3 : `:432` → `:433`, `:110` → `:114`, `:247-249` → `:251-253` ; R4 : « six » → « huit » aux quatre + sites (jeton grepé) ; R5 : patron `CREDIT_NOTE_INVOICE_SETTLED` borné à la forme de `details` + (409, `t(`) ; R6 : relève du registre — porté par C-15-6-26, qui dit C-15-6-3 révisé (pas de + réécriture d'entrée). + - Propagation : `grep` des jetons `six`, `trois** clés`, `:432`, `:247-249`, `lecture neuve`, + « patron de CREDIT_NOTE » sur la fiche — résidus restants légitimes (Change Logs P1/P2, sites + « six » nommés pour être corrigés). + - Signal D5 : **levé** (MEDIUM → MEDIUM ; deux MEDIUM nés de la remédiation P2, contenus dans + l'AC3 bis) ; déclaré au Project Lead, pas de découpage (§ *Décompte des modules*). La P4 doit être + complète (règle métier touchée). + - Trend : P1 4 HIGH / 10 MEDIUM / 4 LOW (F + R) → P2 0 HIGH / 8 MEDIUM / 19 LOW → P3 0 HIGH / + 5 MEDIUM / 9 LOW (dédoublonnés : 3 MEDIUM distincts). + - Décompte après passe : **13 AC (AC1–AC12 et AC3 bis), 10 tâches (T0–T9), 21 tests** (17 backend + dont 16 bis neuf, 4 fichiers Vitest ; 20 neufs, 1 existant renommé et étendu). +- 2026-10-08 — **Validation P4** (Opus 5.5, lentilles R et F ; prompt `15-6b-validate-prompt-p4.md` ; + remédiation Opus 5.5). R : 0 CRITICAL, 0 HIGH, 2 MEDIUM, 6 LOW ; F : 0 CRITICAL, 0 HIGH, 3 MEDIUM, + 4 LOW (R1 = F1, R5 ⊂ F6). Tous appliqués, décisions de l'orchestrateur comprises (choix C-15-6-30) : + - **MEDIUM R1 = F1** — le compte du reste d'arrondi (5 bis) et celui de la nature `rounding` lisent + `default_rounding_account_id`, la colonne du compte d'arrondi du règlement : ils portent désormais + `role: rounding` ; `write_off_nature` ne vaut que pour `discount`, `bank_fees`, `bad_debt` (les trois + champs de la section *Solde du reste*). Le rôle suit la colonne lue, pas la route (doc-comment). + AC1, AC3 bis, AC9, AC10, AC11 ; test **10 bis** neuf (escompte à reste fractionnaire, compte + d'arrondi = créance → `rounding`). + - **MEDIUM R2** — critère de T2 réécrit : il vise la **construction** de la variante (littéral avec + champs) et la **lecture du numéro** (`grep -rn`, `-r` compris) ; les déstructurations attendues + sont énumérées hors du critère. + - **MEDIUM F2** — « résolu par le type » était faux pour la validation et l'avoir : remplacé par des + angles morts écrits — créance retypée à la validation, **#537** (ouverte par l'orchestrateur) ; TVA + due et produit de repli de l'avoir lus dans les réglages courants, **#525** (§ *Le défaut*, + inventaire, § *Angles morts*). Pas de garde neuve : deux modules de plus, hors de la classe + « règlement ». La validation d'achat (même forme côté dette) est **signalée à l'orchestrateur**. + - **MEDIUM F3** — le message de liste vide n'apparaît que si la liste filtrée est vide **et** qu'au + moins un compte bancaire a été écarté par le filtre ; une société sans compte bancaire (installation + neuve) garde le comportement actuel (AC8, tests 18 et 19). + - **LOW** — R3 : sélecteur Fluent écrit sur plusieurs lignes, repli Rust par rôle (AC2) ; R4 : verdict + E2E corrigé — le filtre par rôle est **inerte** en E2E (seed sans rôle), aucun E2E n'exerce l'AC8 + (angle mort écrit) ; R5 : `grep "payment-batch"` ne rend rien ; R6 : Dev Notes — numéro de la + facture relu à la confirmation d'un lot ; R7 : erreur SQL de la lecture du numéro à la création d'un + lot propagée (échec infra) ; R8 : transaction de `confirm_batch` ouverte `:311`, montages des tests + `:352`/`:898` (des tests, pas des helpers), `ACCOUNT_NOT_POSTABLE` fournisseur renvoyé à T0, borne + `sitesTotal` de la 15-5c à recompter (AC7) ; F4 : « un administrateur doit désigner » (C36) aux + messages, au libellé `failed[]` de l'AC7 et au manuel ; F5 : « espèces et tout autre compte » + (`user-manual.tex:1047`) corrigé avec le « libre » ; F6 : sites de l'AC10 (phrase « Refus » hors de + la table d'annulation, phrase et non liste au § du rapprochement) ; F7 : ordre des refus quand + plusieurs comptes d'écart sont la créance (nature, reste d'arrondi, TVA due). + - Propagation : jetons `write_off_nature`, `WriteOffNature`, `désignez`, `résolu par le type`, + `tout autre compte`, `payment-batch` grepés sur la fiche — résidus restants légitimes (Change Logs + P2/P3, citations du finding, test 16 bis dont la nature `discount` reste `write_off_nature`). + - **Signal D5 — levé, déclaré au Project Lead, pas de découpage.** Sévérité F : P3 3 MEDIUM → P4 + 3 MEDIUM (égale). **F1 est un recyclage** : le discriminant `role` posé par la remédiation P3 pour + donner le bon remède classait mal le compte d'arrondi du solde — un défaut né du correctif + précédent, la forme que l'amendement D5 retient comme motif de découpage. Il ne découpe pas ici, + pour trois raisons écrites : (1) le recyclage est **contenu** au même discriminant — une ligne de + classement et un test, aucun module touché de plus ; (2) les deux autres MEDIUM ne recyclent rien + (F2 date de la spécification initiale, F3 de la P1) ; (3) F2 est traité en angle mort et non en + garde, précisément pour ne pas ajouter les sixième et septième modules qui déclencheraient le + critère de dispersion. Le recyclage de la P3 (deux MEDIUM nés de la remédiation P2) et celui-ci + portent sur la même règle (AC3 bis) : si la P5 en trouve un troisième, le découpage de l'AC3 bis en + story propre est la mesure indiquée. + - Trend : P1 4 HIGH / 10 MEDIUM / 4 LOW (F + R) → P2 0 HIGH / 8 MEDIUM / 19 LOW → P3 0 HIGH / + 5 MEDIUM / 9 LOW → P4 0 HIGH / 5 MEDIUM / 10 LOW (dédoublonnés : 4 MEDIUM distincts). + - Décompte après passe : **13 AC (AC1–AC12 et AC3 bis), 10 tâches (T0–T9), 22 tests** (18 backend + dont 10 bis et 16 bis, 4 fichiers Vitest ; 21 neufs, 1 existant renommé et étendu). +- 2026-10-08 — **Validation P5** (Sonnet, lentilles R et F ; prompt `15-6b-validate-prompt-p5.md` ; + remédiation Opus 5.5). R : 0 CRITICAL, 0 HIGH, 3 MEDIUM, 5 LOW ; F : 0 CRITICAL, 0 HIGH, 3 MEDIUM, + 5 LOW (R5-3 = F2). Tous appliqués, décision structurante de l'orchestrateur comprise (choix + **C-15-6-33**) — **une seule règle d'ordre des verrous pour l'epic, celle de la 15-5e** : + - **MEDIUM F1** — la fiche ignorait la **15-5e** : dépendances « après 15-5a à 15-5e » (en-tête, + T0) ; étapes citées **par leur nom**. La 15-5e, **réécrite par C54** pendant cette remédiation + (correction de consigne de l'orchestrateur), ne réordonne plus `settle_invoice` ni + `write_off_invoice` : elle **rejoue** leurs routes (et celles du paiement fournisseur et de la + confirmation d'un lot) et réécrit le commentaire « 5 bis ». L'ordre des refus de l'AC3 bis reste + celui des lectures du code — **nature, puis reste d'arrondi, puis TVA due** au solde ; + **contrepartie, puis trop-perçu, puis compte d'arrondi** au règlement —, désormais énoncé comme + règle (chaque comparaison suit sa lecture). + - **MEDIUM R5-1** — le sélecteur Fluent `$role` aurait rougi + `no_new_select_expression_reaches_the_frontend_dictionary` (`loader.rs:482`) : **trois clés + plates**, une par rôle désigné (comme la 15-5c) ; le repli Rust par `match` disparaît ; six clés + serveur au lieu de quatre. + - **MEDIUM R5-2** — la borne `sitesTotal` **existe** (1868) : relevée pour **tous** les sites neufs, + comptés (+4, +6 avec la 15-5c), ventilation recomptée sur l'état rebasé (AC8, T0, T9). + - **MEDIUM R5-3 = F2** — le critère de T2 était muet sur un littéral que rustfmt coupe : critère par + le **nom seul** (`grep -rnF`, commentaires exclus) sur une **liste fermée** de cinq fichiers, chaque + occurrence lue. + - **MEDIUM F3** — `user-manual.tex:1754` (balance âgée) citait le règlement sur le compte débiteurs + comme un cas ordinaire : ajouté à l'AC11, reformulé en données antérieures, contrôlé au PDF aplati. + - **LOW** — R5-4 : le **produit de repli** de l'avoir n'est **pas** couvert par #525 (vérifié par + `gh api` sur le corps et les commentaires) : écrit « non tracé », **signalé à l'orchestrateur** ; + R5-5 : modules recomptés, **neuf** (le helper commun `invoice_settlements.rs` manquait) ; R5-6 : + verdict E2E complété (`invoices.spec.ts:318-321`, `reminders.spec.ts:104-112`, + `invoice-write-off.spec.ts:70-85`) ; R5-7 : test 11 placé dans `invoice_settlement.rs`, montage + d'arrondi nommé, `:180-183` → `:182-185` ; R5-8 : colonne « issues » de la fiche d'index complétée + (angles morts #537, #525) ; F4 : `:1047` → `:1048` ; F5 : `Payable` + rôle désigné rendu + irreprésentable par un sujet `ClaimSubject` au helper de construction ; F6 : chaque comparaison suit + sa lecture, une nature égale à la créance précède le `CONFIGURATION_REQUIRED` de la TVA due — test + **10 ter** neuf ; F7 : phrase « Refus » du paiement fournisseur posée avant la ligne de refus de + l'annulation, libellée par sa route ; F8 : pour mémoire (registre non réécrit ; la fiche d'index dit + les révisions de C-15-6-3). + - Propagation : jetons `15-5a, 15-5b, 15-5c et 15-5d`, `designated-account-is-receivable`, + `sélecteur`, `$role`, `(4bis)`, `(5 bis)`, `étape (3)`, `étape (4)`, `huit`, `:1047`, `180-183`, + `#525` grepés sur la fiche — résidus restants légitimes (Change Logs P1-P4 ; étapes de `pay_in_tx`, + que la 15-5e ne touche pas). + - Signal D5 : § *Décompte des modules*, « P5 » — levé par la sévérité, **non** déclenché : F1 vient + d'un changement extérieur (la 15-5e), non d'un patch ; les deux recyclages (R5-1, F2) sont hors de + l'AC3 bis. + - Trend : P1 4 HIGH / 10 MEDIUM / 4 LOW (F + R) → P2 0 HIGH / 8 MEDIUM / 19 LOW → P3 0 HIGH / + 5 MEDIUM / 9 LOW → P4 0 HIGH / 5 MEDIUM / 10 LOW → P5 0 HIGH / 6 MEDIUM / 10 LOW (dédoublonnés : + 5 MEDIUM distincts). La remédiation P5 change l'ordre des refus et les clés i18n : une **P6 + complète** (Opus, rotation D6) est indiquée. + - Décompte après passe : **13 AC (AC1–AC12 et AC3 bis), 10 tâches (T0–T9), 23 tests** (19 backend + dont 10 bis, 10 ter et 16 bis, 4 fichiers Vitest ; 22 neufs, 1 existant renommé et étendu). +- 2026-10-08 — **Validation P6 — clôture** (Opus 5.5, lentilles R et F ; prompt + `15-6b-validate-prompt-p6.md` ; rapports `target/gate-logs/15-6b-p6-R.md`, `15-6b-p6-F.md` ; + remédiation Opus 5.5). R : 0 CRITICAL, 0 HIGH, 0 MEDIUM, 11 LOW ; F : 0 CRITICAL, 0 HIGH, 0 MEDIUM, + 7 LOW (R6-4 = F6-6 ; F6-7 est une observation). **0 au-dessus de LOW : la boucle s'arrête** (§ *Review + Iteration Rule*). Tous les LOW appliqués (choix C-15-6-34) : + - R6-1 : test 11 sans renvoi à la « concurrence 1 » de la 15-5e, retirée par C55 (le helper + `raw_due_invoice` existe toujours, `invoice_echeancier_e2e.rs:986`). + - R6-2, R6-3 : le **produit de repli** de l'avoir est tracé par **#525**, la **validation d'achat** + par **#537** (commentaires de l'orchestrateur du 2026-10-08) — « non tracé » / « signalé » + corrigés au § *Le défaut*, à l'inventaire et aux angles morts (le Change Log P5 reste historique). + - R6-4 = F6-6 : la règle de placement de l'AC3 bis dit « suit la lecture de son compte **et** celle + de la créance » — la nature est lue avant la créance (`:433`, puis `:434-445`) ; aucun + réordonnancement. + - R6-5 : repli de `t_args` construit par `format!` (patron `errors.rs:88-92`). + - R6-6 : fiche d'index — « un message par rôle (six clés) ». + - R6-7 : `:409` → `:408`, `:454-480` → `:454-477`, table de l'annulation `:262-271` → `:259-268`. + - R6-8 : `DesignatedRole` placé dans `invoice_settlements.rs` avec `ClaimSubject`, et cité au décompte. + - R6-9 : test 10 ter — TVA due vidée **après** validation. + - R6-10 : AC9 — le message `rounding` arrive aussi par le dialogue de **règlement**. + - R6-11 : C-15-6-30, C-15-6-33, C-15-6-34 aux Références ; C36, C52, C54, C55 arrivent au rebase (T0). + - F6-1 : **manuel d'administration** — verdict ligne par ligne (`:2027` inchangée ; une phrase à + `:2029` ; « ou devenu le compte débiteurs de la facture » à `:2033`) ; frontière de la 15-6c alignée. + - F6-2 : deux lignes « hors classe » à la table des écrans (`routes/(app)/+page.svelte:58`, + `account-validity.ts:20`). + - F6-3 : présélection dans un **effet distinct**, qui n'efface pas la saisie en cours ; test 18 étendu. + - F6-4 : test 8 — transaction à référence de facture, score positif (étape 7bis avant (a)-(c)) ; AC5 : + tous les refus antérieurs gardent la priorité. + - F6-5 : clés de lot ajoutées par le mapping HTTP seul, hors de `claim_account_refusal_details`. + - F6-7 : décompte au barème de la règle (neuf modules) écrit et déclaré (§ *Décompte des modules*, P6). + - Propagation : jetons `non tracé`, `signalé à l'orchestrateur`, `aucune issue`, `concurrence 1`, + `aussitôt`, `:409`, `454-480`, `262-271`, `DesignatedRole`, `deux messages` grepés sur les fiches + 15-6 — résidus restants légitimes (Change Logs P4/P5, citations des findings) ; `:558` de la 15-6a + corrigé dans le même commit. + - **Signal D5 — dispersion déclarée au Project Lead, non découpée** : neuf modules métier au barème + de la règle, plus de cinq ; le « barème du geste » n'est plus invoqué. Raison écrite (§ *Décompte + des modules*, P6) : fiche validée et convergée, gestes liés par une variante, un sujet typé, un + lecteur sœur et trois fonctions d'écran ; le risque visé par la règle (non-convergence) ne s'est + pas réalisé. Arbitrage de Guy en revue de fin d'epic. + - **Trend complet** : P1 (Sonnet) 4 HIGH / 10 MEDIUM / 4 LOW (F + R) → P2 (Opus) 0 HIGH / 8 MEDIUM / + 19 LOW → P3 (Sonnet) 0 HIGH / 5 MEDIUM / 9 LOW → P4 (Opus) 0 HIGH / 5 MEDIUM / 10 LOW → P5 (Sonnet) + 0 HIGH / 6 MEDIUM / 10 LOW → **P6 (Opus) 0 HIGH / 0 MEDIUM / 18 LOW** (17 distincts). Aucun + CRITICAL en six passes. Remédiations : Opus 5.5 (P2 à P6). Reclassements : aucun MEDIUM+ reclassé + en dette ; la validation et l'avoir (F2 de la P4) sont des angles morts **tracés** (#537, #525). + - Décompte après passe : **13 AC (AC1–AC12 et AC3 bis), 10 tâches (T0–T9), 23 tests** (19 backend + dont 10 bis, 10 ter et 16 bis, 4 fichiers Vitest ; 22 neufs, 1 existant renommé et étendu) — + inchangé (le test 18 gagne un cas, sans test neuf). Statut : `ready-for-dev`. diff --git a/_bmad-output/implementation-artifacts/15-6b-validate-prompt-p1.md b/_bmad-output/implementation-artifacts/15-6b-validate-prompt-p1.md new file mode 100644 index 00000000..589846d6 --- /dev/null +++ b/_bmad-output/implementation-artifacts/15-6b-validate-prompt-p1.md @@ -0,0 +1,34 @@ +# Prompt — validation P1 de la spec, Story 15-6b + +*Versionné le 2026-10-08. Deux lentilles (Sonnet), contexte frais chacune, en lecture seule. Rotation (décision D6 de la +rétro 25) : passes complètes Sonnet ↔ Opus ; Haiku réservé à une passe ciblée de fin de boucle.* + +Dépôt `/home/gcorbaz/devel/kesh-15-6`, branche `story/15-6-creance-juste-avoir-reglement`. **Fiche** : `_bmad-output/implementation-artifacts/15-6b-contrepartie-distincte-de-la-creance.md`. C'est la première passe : la fiche n'a pas encore été remédiée. +Issues, par `gh api repos/guycorbaz/kesh/issues/N` (et `/comments`) — `gh issue view` échoue ici : 474, 524. +Choix autonomes : `_bmad-output/implementation-artifacts/epic-15-choix-autonomes.md`. Règles : `CLAUDE.md`. Fiche d'index 15-6, sœurs 15-6a et 15-6c. ⚠️ L'issue #524 (rapprochement manuel et acceptation par règle acceptent le compte de la banque comme contrepartie) a été ouverte APRÈS l'écriture de la fiche : l'orchestrateur décide qu'elle entre dans la 15-6b — signale ce qu'il faut ajouter. La 15-5b (dépôt principal, `/home/gcorbaz/devel/kesh/_bmad-output/implementation-artifacts/15-5b-gardes-surfaces-neuves.md`, lecture seulement) touche les mêmes flux de rapprochement : vérifie la frontière. +Aucun code de la story n'est encore écrit. + +## Lentilles + +- **R — Auditeur d'acceptation** : applique `.claude/skills/bmad-create-story/checklist.md`. Chaque phrase des issues couverte par un AC testable, chaque AC par une tâche, chaque tâche par un test ; implémentable sans deviner (fonctions, fichiers, codes d'erreur, clés i18n 4 locales, replis Svelte) ; faits cités revérifiés au code (`grep -nF`) ; recompte des AC et tâches. +- **F — Full-scope adversary** : la conception elle-même. Les chemins de code qui écrivent ou lisent ce que la story + change (inventorier les sites NON résolus, pas énumérer ceux qui marchent) ; transactions, verrous, ordre des + contrôles ; tests existants qui changeront de sens ; frontend qui lit les codes ou champs touchés (`grep -rn` dans + `frontend/src`) ; i18n 4 locales et replis ; le **manuel** (`docs/manual/fr/*.tex` **et le PDF aplati** : + `pdftotext docs/manual/fr/user-manual.pdf - | tr '\n' ' ' | tr -s ' '` vers `target/gate-logs/`) ; + `docs/api-external.md` ; CHANGELOG ; la règle de découpage (§ « Règle de splitting préventif », amendement D5). + +## Ce que tu rends + +Findings numérotés avec sévérité (CRITICAL/HIGH/MEDIUM/LOW), `fichier:ligne` (fiche et code), **preuve** (commande et +sortie, ou code cité relu), correction proposée. Pour tout finding affirmant qu'un code est absent ou présent : la +sortie d'un `grep -nF`. ⛔ **La liste des axes exercés ET non exercés** — un « 0 finding » sans elle ne compte pas. +Rapport compact. + +## Interdits + +⛔ N'écris aucun fichier du dépôt hors `target/gate-logs/` ; aucune commande qui écrit dans le dépôt ou dans une +base : `scripts/*` (dont `scripts/prepare-release.sh`), `make`, `latexmk`, `git commit`/`add`/`checkout`/`stash`, +`sqlx`, `cargo test`/`nextest`/`build`, `npm run`, `npx`, `gh issue create`/`comment`/`edit`, aucune requête SQL +d'écriture. Autorisés : lecture, `grep`, `sed -n`, `git log`/`show`/`diff`, `gh api` en lecture, `pdftotext` +vers `target/gate-logs/`. diff --git a/_bmad-output/implementation-artifacts/15-6b-validate-prompt-p2.md b/_bmad-output/implementation-artifacts/15-6b-validate-prompt-p2.md new file mode 100644 index 00000000..a156b414 --- /dev/null +++ b/_bmad-output/implementation-artifacts/15-6b-validate-prompt-p2.md @@ -0,0 +1,35 @@ +# Prompt — validation P2 de la spec, Story 15-6b + +*Versionné le 2026-10-08. Deux lentilles (Opus), contexte frais chacune, en lecture seule. Rotation (décision D6 de la +rétro 25) : passes complètes Sonnet ↔ Opus ; Haiku réservé à une passe ciblée de fin de boucle.* + +Dépôt `/home/gcorbaz/devel/kesh-15-6`, branche `story/15-6-creance-juste-avoir-reglement`. **Fiche** : `_bmad-output/implementation-artifacts/15-6b-contrepartie-distincte-de-la-creance.md`. La dernière remédiation est le commit `cb54ff72` (`git show cb54ff72`) — lis le Change Log de la fiche. +Issues, par `gh api repos/guycorbaz/kesh/issues/N` (et `/comments`) — `gh issue view` échoue ici : 474. +Choix autonomes : `_bmad-output/implementation-artifacts/epic-15-choix-autonomes.md`. Règles : `CLAUDE.md`. Fiche d'index 15-6 ; sœurs 15-6a/b/c/d. Les fiches 15-5a/b/c du dépôt principal (`/home/gcorbaz/devel/kesh/_bmad-output/implementation-artifacts/15-5*.md`, lecture seule) touchent des fichiers voisins et seront mergées AVANT 15-6b/c/d : vérifie la frontière. +Aucun code de la story n'est encore écrit. + +## Lentilles + +- **R — Regression hunter** : la dernière remédiation (`git show cb54ff72`) a-t-elle introduit des défauts ? Contradictions entre AC, tâches, Dev Notes et choix du registre ; frontière avec les fiches sœurs ; faits recopiés sans vérification (numéros de ligne, fonctions, codes, clés i18n) — revérifie-les au code (`grep -nF`) ; recompte des AC et tâches ; implémentable sans deviner ? +- **F — Full-scope adversary** : la conception elle-même. Les chemins de code qui écrivent ou lisent ce que la story + change (inventorier les sites NON résolus, pas énumérer ceux qui marchent) ; transactions, verrous, ordre des + contrôles ; tests existants qui changeront de sens ; frontend qui lit les codes ou champs touchés (`grep -rn` dans + `frontend/src`) ; i18n 4 locales et replis ; le **manuel** (`docs/manual/fr/*.tex` **et le PDF aplati** : + `pdftotext docs/manual/fr/user-manual.pdf - | tr '\n' ' ' | tr -s ' '` vers `target/gate-logs/`) ; + `docs/api-external.md` ; CHANGELOG ; la règle de découpage (§ « Règle de splitting préventif », amendement D5). + +## Ce que tu rends + +Findings numérotés avec sévérité (CRITICAL/HIGH/MEDIUM/LOW), `fichier:ligne` (fiche et code), **preuve** (commande et +sortie, ou code cité relu), correction proposée. Pour tout finding affirmant qu'un code est absent ou présent : la +sortie d'un `grep -nF`. ⛔ **La liste des axes exercés ET non exercés** — un « 0 finding » sans elle ne compte pas. +⛔ **Écris ton rapport complet** dans `target/gate-logs/15-6b-p2-.md` (R ou F ; autorisé), et rends +comme dernier message **seulement** : ce chemin, le bilan par sévérité, et une ligne par finding MEDIUM ou plus. + +## Interdits + +⛔ N'écris aucun fichier du dépôt hors `target/gate-logs/` ; aucune commande qui écrit dans le dépôt ou dans une +base : `scripts/*` (dont `scripts/prepare-release.sh`), `make`, `latexmk`, `git commit`/`add`/`checkout`/`stash`, +`sqlx`, `cargo test`/`nextest`/`build`, `npm run`, `npx`, `gh issue create`/`comment`/`edit`, aucune requête SQL +d'écriture. Autorisés : lecture, `grep`, `sed -n`, `git log`/`show`/`diff`, `gh api` en lecture, `pdftotext` +vers `target/gate-logs/`. diff --git a/_bmad-output/implementation-artifacts/15-6b-validate-prompt-p3.md b/_bmad-output/implementation-artifacts/15-6b-validate-prompt-p3.md new file mode 100644 index 00000000..4d1c0fcf --- /dev/null +++ b/_bmad-output/implementation-artifacts/15-6b-validate-prompt-p3.md @@ -0,0 +1,35 @@ +# Prompt — validation P3 de la spec, Story 15-6b + +*Versionné le 2026-10-08. Deux lentilles (Sonnet), contexte frais chacune, en lecture seule. Rotation (décision D6 de la +rétro 25) : passes complètes Sonnet ↔ Opus ; Haiku réservé à une passe ciblée de fin de boucle.* + +Dépôt `/home/gcorbaz/devel/kesh-15-6`, branche `story/15-6-creance-juste-avoir-reglement`. **Fiche** : `_bmad-output/implementation-artifacts/15-6b-contrepartie-distincte-de-la-creance.md`. La dernière remédiation est le commit `4964eeb4` (`git show 4964eeb4`) — lis le Change Log de la fiche. +Issues, par `gh api repos/guycorbaz/kesh/issues/N` (et `/comments`) — `gh issue view` échoue ici : 474. +Choix autonomes : `_bmad-output/implementation-artifacts/epic-15-choix-autonomes.md`. Règles : `CLAUDE.md`. Fiche d'index 15-6 ; sœurs 15-6a/b/c/d. Les fiches 15-5a/b/c/d du dépôt principal (`/home/gcorbaz/devel/kesh/_bmad-output/implementation-artifacts/15-5*.md`, lecture seule) seront mergées AVANT : vérifie la frontière. Axes : AC3 bis (TVA due du solde du reste), contexte de lot (SettlementBatchContext), code SUPPLIER_INVOICE_PURCHASE_ENTRY_MALFORMED. +Aucun code de la story n'est encore écrit. + +## Lentilles + +- **R — Regression hunter** : la dernière remédiation (`git show 4964eeb4`) a-t-elle introduit des défauts ? Contradictions entre AC, tâches, Dev Notes et choix du registre ; frontière avec les fiches sœurs ; faits recopiés sans vérification (numéros de ligne, fonctions, codes, clés i18n) — revérifie-les au code (`grep -nF`) ; recompte des AC et tâches ; implémentable sans deviner ? +- **F — Full-scope adversary** : la conception elle-même. Les chemins de code qui écrivent ou lisent ce que la story + change (inventorier les sites NON résolus, pas énumérer ceux qui marchent) ; transactions, verrous, ordre des + contrôles ; tests existants qui changeront de sens ; frontend qui lit les codes ou champs touchés (`grep -rn` dans + `frontend/src`) ; i18n 4 locales et replis ; le **manuel** (`docs/manual/fr/*.tex` **et le PDF aplati** : + `pdftotext docs/manual/fr/user-manual.pdf - | tr '\n' ' ' | tr -s ' '` vers `target/gate-logs/`) ; + `docs/api-external.md` ; CHANGELOG ; la règle de découpage (§ « Règle de splitting préventif », amendement D5). + +## Ce que tu rends + +Findings numérotés avec sévérité (CRITICAL/HIGH/MEDIUM/LOW), `fichier:ligne` (fiche et code), **preuve** (commande et +sortie, ou code cité relu), correction proposée. Pour tout finding affirmant qu'un code est absent ou présent : la +sortie d'un `grep -nF`. ⛔ **La liste des axes exercés ET non exercés** — un « 0 finding » sans elle ne compte pas. +⛔ **Écris ton rapport complet** dans `target/gate-logs/15-6b-p3-.md` (R ou F ; autorisé), et rends +comme dernier message **seulement** : ce chemin, le bilan par sévérité, et une ligne par finding MEDIUM ou plus. + +## Interdits + +⛔ N'écris aucun fichier du dépôt hors `target/gate-logs/` ; aucune commande qui écrit dans le dépôt ou dans une +base : `scripts/*` (dont `scripts/prepare-release.sh`), `make`, `latexmk`, `git commit`/`add`/`checkout`/`stash`, +`sqlx`, `cargo test`/`nextest`/`build`, `npm run`, `npx`, `gh issue create`/`comment`/`edit`, aucune requête SQL +d'écriture. Autorisés : lecture, `grep`, `sed -n`, `git log`/`show`/`diff`, `gh api` en lecture, `pdftotext` +vers `target/gate-logs/`. diff --git a/_bmad-output/implementation-artifacts/15-6b-validate-prompt-p4.md b/_bmad-output/implementation-artifacts/15-6b-validate-prompt-p4.md new file mode 100644 index 00000000..0cd92444 --- /dev/null +++ b/_bmad-output/implementation-artifacts/15-6b-validate-prompt-p4.md @@ -0,0 +1,35 @@ +# Prompt — validation P4 de la spec, Story 15-6b + +*Versionné le 2026-10-08. Deux lentilles (Opus), contexte frais chacune, en lecture seule. Rotation (décision D6 de la +rétro 25) : passes complètes Sonnet ↔ Opus ; Haiku réservé à une passe ciblée de fin de boucle.* + +Dépôt `/home/gcorbaz/devel/kesh-15-6`, branche `story/15-6-creance-juste-avoir-reglement`. **Fiche** : `_bmad-output/implementation-artifacts/15-6b-contrepartie-distincte-de-la-creance.md`. La dernière remédiation est le commit `89e8987a` (`git show 89e8987a`) — lis le Change Log de la fiche. +Issues, par `gh api repos/guycorbaz/kesh/issues/N` (et `/comments`) — `gh issue view` échoue ici : 474. +Choix autonomes : `_bmad-output/implementation-artifacts/epic-15-choix-autonomes.md`. Règles : `CLAUDE.md`. Fiche d'index 15-6 ; sœurs 15-6a/b/c/d ; les fiches 15-5a/b/c/d (dépôt principal, lecture seule) sont mergées AVANT. Axes : details.role et les deux familles de refus, ensure_not_claim_account (helper commun), api-external. +Aucun code de la story n'est encore écrit. + +## Lentilles + +- **R — Regression hunter** : la dernière remédiation (`git show 89e8987a`) a-t-elle introduit des défauts ? Contradictions entre AC, tâches, Dev Notes et choix du registre ; frontière avec les fiches sœurs ; faits recopiés sans vérification (numéros de ligne, fonctions, codes, clés i18n) — revérifie-les au code (`grep -nF`) ; recompte des AC et tâches ; implémentable sans deviner ? +- **F — Full-scope adversary** : la conception elle-même. Les chemins de code qui écrivent ou lisent ce que la story + change (inventorier les sites NON résolus, pas énumérer ceux qui marchent) ; transactions, verrous, ordre des + contrôles ; tests existants qui changeront de sens ; frontend qui lit les codes ou champs touchés (`grep -rn` dans + `frontend/src`) ; i18n 4 locales et replis ; le **manuel** (`docs/manual/fr/*.tex` **et le PDF aplati** : + `pdftotext docs/manual/fr/user-manual.pdf - | tr '\n' ' ' | tr -s ' '` vers `target/gate-logs/`) ; + `docs/api-external.md` ; CHANGELOG ; la règle de découpage (§ « Règle de splitting préventif », amendement D5). + +## Ce que tu rends + +Findings numérotés avec sévérité (CRITICAL/HIGH/MEDIUM/LOW), `fichier:ligne` (fiche et code), **preuve** (commande et +sortie, ou code cité relu), correction proposée. Pour tout finding affirmant qu'un code est absent ou présent : la +sortie d'un `grep -nF`. ⛔ **La liste des axes exercés ET non exercés** — un « 0 finding » sans elle ne compte pas. +⛔ **Écris ton rapport complet** dans `target/gate-logs/15-6b-p4-.md` (R ou F ; autorisé), et rends +comme dernier message **seulement** : ce chemin, le bilan par sévérité, et une ligne par finding MEDIUM ou plus. + +## Interdits + +⛔ N'écris aucun fichier du dépôt hors `target/gate-logs/` ; aucune commande qui écrit dans le dépôt ou dans une +base : `scripts/*` (dont `scripts/prepare-release.sh`), `make`, `latexmk`, `git commit`/`add`/`checkout`/`stash`, +`sqlx`, `cargo test`/`nextest`/`build`, `npm run`, `npx`, `gh issue create`/`comment`/`edit`, aucune requête SQL +d'écriture. Autorisés : lecture, `grep`, `sed -n`, `git log`/`show`/`diff`, `gh api` en lecture, `pdftotext` +vers `target/gate-logs/`. diff --git a/_bmad-output/implementation-artifacts/15-6b-validate-prompt-p5.md b/_bmad-output/implementation-artifacts/15-6b-validate-prompt-p5.md new file mode 100644 index 00000000..c580e9c1 --- /dev/null +++ b/_bmad-output/implementation-artifacts/15-6b-validate-prompt-p5.md @@ -0,0 +1,35 @@ +# Prompt — validation P5 de la spec, Story 15-6b + +*Versionné le 2026-10-08. Deux lentilles (Sonnet), contexte frais chacune, en lecture seule. Rotation (décision D6 de la +rétro 25) : passes complètes Sonnet ↔ Opus ; Haiku réservé à une passe ciblée de fin de boucle.* + +Dépôt `/home/gcorbaz/devel/kesh-15-6`, branche `story/15-6-creance-juste-avoir-reglement`. **Fiche** : `_bmad-output/implementation-artifacts/15-6b-contrepartie-distincte-de-la-creance.md`. La dernière remédiation est le commit `bc4986d2` (`git show bc4986d2`) — lis le Change Log de la fiche. +Issues, par `gh api repos/guycorbaz/kesh/issues/N` (et `/comments`) — `gh issue view` échoue ici : 474, 537, 525. +Choix autonomes : `_bmad-output/implementation-artifacts/epic-15-choix-autonomes.md`. Règles : `CLAUDE.md`. Fiche d'index 15-6 ; les fiches 15-5a/b/c/d/e (dépôt principal, lecture seule ; la 15-5a est MERGÉE sur main) passent avant. Axes : role rounding, critère de T2, message de liste vide conditionné. +Aucun code de la story n'est encore écrit. + +## Lentilles + +- **R — Regression hunter** : la dernière remédiation (`git show bc4986d2`) a-t-elle introduit des défauts ? Contradictions entre AC, tâches, Dev Notes et choix du registre ; frontière avec les fiches sœurs ; faits recopiés sans vérification (numéros de ligne, fonctions, codes, clés i18n) — revérifie-les au code (`grep -nF`) ; recompte des AC et tâches ; implémentable sans deviner ? +- **F — Full-scope adversary** : la conception elle-même. Les chemins de code qui écrivent ou lisent ce que la story + change (inventorier les sites NON résolus, pas énumérer ceux qui marchent) ; transactions, verrous, ordre des + contrôles ; tests existants qui changeront de sens ; frontend qui lit les codes ou champs touchés (`grep -rn` dans + `frontend/src`) ; i18n 4 locales et replis ; le **manuel** (`docs/manual/fr/*.tex` **et le PDF aplati** : + `pdftotext docs/manual/fr/user-manual.pdf - | tr '\n' ' ' | tr -s ' '` vers `target/gate-logs/`) ; + `docs/api-external.md` ; CHANGELOG ; la règle de découpage (§ « Règle de splitting préventif », amendement D5). + +## Ce que tu rends + +Findings numérotés avec sévérité (CRITICAL/HIGH/MEDIUM/LOW), `fichier:ligne` (fiche et code), **preuve** (commande et +sortie, ou code cité relu), correction proposée. Pour tout finding affirmant qu'un code est absent ou présent : la +sortie d'un `grep -nF`. ⛔ **La liste des axes exercés ET non exercés** — un « 0 finding » sans elle ne compte pas. +⛔ **Écris ton rapport complet** dans `target/gate-logs/15-6b-p5-.md` (R ou F ; autorisé), et rends +comme dernier message **seulement** : ce chemin, le bilan par sévérité, et une ligne par finding MEDIUM ou plus. + +## Interdits + +⛔ N'écris aucun fichier du dépôt hors `target/gate-logs/` ; aucune commande qui écrit dans le dépôt ou dans une +base : `scripts/*` (dont `scripts/prepare-release.sh`), `make`, `latexmk`, `git commit`/`add`/`checkout`/`stash`, +`sqlx`, `cargo test`/`nextest`/`build`, `npm run`, `npx`, `gh issue create`/`comment`/`edit`, aucune requête SQL +d'écriture. Autorisés : lecture, `grep`, `sed -n`, `git log`/`show`/`diff`, `gh api` en lecture, `pdftotext` +vers `target/gate-logs/`. diff --git a/_bmad-output/implementation-artifacts/15-6b-validate-prompt-p6.md b/_bmad-output/implementation-artifacts/15-6b-validate-prompt-p6.md new file mode 100644 index 00000000..9ad814e1 --- /dev/null +++ b/_bmad-output/implementation-artifacts/15-6b-validate-prompt-p6.md @@ -0,0 +1,35 @@ +# Prompt — validation P6 de la spec, Story 15-6b + +*Versionné le 2026-10-08. Deux lentilles (Opus), contexte frais chacune, en lecture seule. Rotation (décision D6 de la +rétro 25) : passes complètes Sonnet ↔ Opus ; Haiku réservé à une passe ciblée de fin de boucle.* + +Dépôt `/home/gcorbaz/devel/kesh-15-6`, branche `story/15-6-creance-juste-avoir-reglement`. **Fiche** : `_bmad-output/implementation-artifacts/15-6b-contrepartie-distincte-de-la-creance.md`. La dernière remédiation est le commit `e871cd58` (`git show e871cd58`) — lis le Change Log de la fiche. +Issues, par `gh api repos/guycorbaz/kesh/issues/N` (et `/comments`) — `gh issue view` échoue ici : 474, 537, 525. +Choix autonomes : `_bmad-output/implementation-artifacts/epic-15-choix-autonomes.md`. Règles : `CLAUDE.md`. Index 15-6 ; dépend des 15-5a à 15-5e (dépôt principal, lecture seule : la 15-5e a été RÉÉCRITE selon C54 — rejeu sur interblocage, b92f3319). 15-5a mergée sur main. +Aucun code de la story n'est encore écrit. + +## Lentilles + +- **R — Regression hunter** : la dernière remédiation (`git show e871cd58`) a-t-elle introduit des défauts ? Contradictions entre AC, tâches, Dev Notes et choix du registre ; frontière avec les fiches sœurs ; faits recopiés sans vérification (numéros de ligne, fonctions, codes, clés i18n) — revérifie-les au code (`grep -nF`) ; recompte des AC et tâches ; implémentable sans deviner ? +- **F — Full-scope adversary** : la conception elle-même. Les chemins de code qui écrivent ou lisent ce que la story + change (inventorier les sites NON résolus, pas énumérer ceux qui marchent) ; transactions, verrous, ordre des + contrôles ; tests existants qui changeront de sens ; frontend qui lit les codes ou champs touchés (`grep -rn` dans + `frontend/src`) ; i18n 4 locales et replis ; le **manuel** (`docs/manual/fr/*.tex` **et le PDF aplati** : + `pdftotext docs/manual/fr/user-manual.pdf - | tr '\n' ' ' | tr -s ' '` vers `target/gate-logs/`) ; + `docs/api-external.md` ; CHANGELOG ; la règle de découpage (§ « Règle de splitting préventif », amendement D5). + +## Ce que tu rends + +Findings numérotés avec sévérité (CRITICAL/HIGH/MEDIUM/LOW), `fichier:ligne` (fiche et code), **preuve** (commande et +sortie, ou code cité relu), correction proposée. Pour tout finding affirmant qu'un code est absent ou présent : la +sortie d'un `grep -nF`. ⛔ **La liste des axes exercés ET non exercés** — un « 0 finding » sans elle ne compte pas. +⛔ **Écris ton rapport complet** dans `target/gate-logs/15-6b-p6-.md` (R ou F ; autorisé), et rends +comme dernier message **seulement** : ce chemin, le bilan par sévérité, et une ligne par finding MEDIUM ou plus. + +## Interdits + +⛔ N'écris aucun fichier du dépôt hors `target/gate-logs/` ; aucune commande qui écrit dans le dépôt ou dans une +base : `scripts/*` (dont `scripts/prepare-release.sh`), `make`, `latexmk`, `git commit`/`add`/`checkout`/`stash`, +`sqlx`, `cargo test`/`nextest`/`build`, `npm run`, `npx`, `gh issue create`/`comment`/`edit`, aucune requête SQL +d'écriture. Autorisés : lecture, `grep`, `sed -n`, `git log`/`show`/`diff`, `gh api` en lecture, `pdftotext` +vers `target/gate-logs/`. diff --git a/_bmad-output/implementation-artifacts/15-6c-configuration-sans-ecriture-nulle.md b/_bmad-output/implementation-artifacts/15-6c-configuration-sans-ecriture-nulle.md new file mode 100644 index 00000000..cf687ce1 --- /dev/null +++ b/_bmad-output/implementation-artifacts/15-6c-configuration-sans-ecriture-nulle.md @@ -0,0 +1,631 @@ +# Story 15.6c : La configuration ne prépare plus l'écriture nulle + +## Status + +ready-for-dev + + + +**Issue** : `closes #474` (P1, avec la 15-6b) — mot-clé **dans la PR**. **Mère** : +`15-6-creance-juste-avoir-reglement.md`. + +⛔ **Ne commence qu'après le merge de la 15-5b**, qui modifie les **mêmes fonctions** : +`validate_journal_account_id` (`crates/kesh-api/src/routes/bank_accounts.rs:260`, paramètre +`require_postable` ajouté), `create_bank_account` (`:384`), `update_bank_account` (`:498`), +`patch_bank_account_journal_link` (`:717`), le dépôt `crates/kesh-db/src/repositories/bank_accounts.rs` +(`set_journal_account_id_for_company` `:265`, `update_for_company` `:356`, contrôle « dans la +transaction » de son choix C10), le PUT des réglages (`company_invoice_settings.rs`, ses AC10, AC11 et +AC19) et l'écran des comptes bancaires (son AC13 : `withCurrentAccount` sur les deux `` n'offrent ni le compte débiteurs ni le compte créanciers désignés, et + affichent un lien existant vers 1100 sans le perdre ; `getInvoiceSettings` en échec → menus non + filtrés, page affichée. +15. `frontend/src/routes/(app)/settings/invoicing/settings-invoicing-page.test.ts` (existant) : les + menus du compte débiteurs et du compte créanciers (15-5d) n'offrent pas un compte lié à un compte + bancaire, gardent la valeur en place, et les autres menus d'actif et de passif (TVA) sont intacts ; + `listBankAccounts` en échec → pas de filtre. + +E2E : `bank-account-journal-link.spec.ts` modifiée (inventaire ci-dessus). + +Soit **16 tests nommés** (13 backend — 1 à 12 et 12 bis —, 3 fichiers Vitest) et **1 spec E2E +modifiée** ; s'y ajoutent les **adaptations** des tests Vitest existants (§ *Tests existants qui +changent de sens*, quatre rendus et un mock), qui ne sont pas des tests neufs. + +## Dev Notes + +### Angles morts assumés + +- **Rôle sans réglage** : la garde suit les **réglages** (les comptes qu'utiliseront les ventes et + achats futurs), pas le rôle ; l'écran aussi (AC7). Un compte de rôle `Receivable` qui ne serait plus + le réglage débiteurs reste liable. +- **Données antérieures** : un lien fautif existant n'est pas défait (pas de migration de données — le + produit ne tient pas encore de comptabilité réelle, `CLAUDE.md` § *Project Overview*) ; il est refusé + à l'usage (15-6b) et signalé à la modification. +- **Import d'une sauvegarde** (`.keshbackup`) : il restaure tables et liens sans repasser par ces + routes ; un couple fautif présent dans la sauvegarde revient tel quel. Filet : la garde à l'usage de + la 15-6b. +- **Création des réglages à l'onboarding** (`company_invoice_settings::insert_with_defaults_in_tx`, + désignation par rôle) : elle ne consulte pas les comptes bancaires. Un compte bancaire déjà lié au + compte de rôle `Receivable` à ce moment n'est pas détecté ; même filet. + +### Latence — ce que les verrous ajoutent, sans le présenter comme un défaut + +- Tout geste bancaire **vers un compte** (création, remplacement, lien) prend un S sur la ligne des + réglages ; il **attend** donc toute transaction qui tient cette ligne en X : PUT des réglages, mais + aussi validation de facture, avoir et facture fournisseur (`get_or_create_default_in_tx`, + `FOR UPDATE`, `invoices.rs:2006`, `credit_notes.rs:361`, `supplier_invoices.rs:359`) — le temps + d'une transaction. Une dé-liaison n'attend pas (AC2). +- Le lecteur bancaire du PUT pose des verrous d'intervalle (AC2) : une création de compte bancaire + concurrente peut attendre le PUT. Raisonné au code, **non mesuré**. + +### Pièges + +- **Respecter l'ordre et l'emplacement de la 15-5b** : sa garde de postabilité et celle-ci ne doivent + pas se court-circuiter. Les deux vivent au même endroit — dans la transaction, après le contrôle de + version et, pour le lien, après son no-op ; sous la condition « nouvelle valeur ≠ + `existing.journal_account_id` » ; la postabilité d'abord, puis le compte de créance. +- ⛔ **`FOR SHARE` n'existe pas en MariaDB 10.11** : `LOCK IN SHARE MODE` (AC2, + `opening_complement.rs:38-39`). Le défaut a été écrit en P1 et attrapé en P2 ; un test de + sérialisation (11, 12, 12 bis) le révélerait en 500 dès sa première exécution. +- **Attendre un verrou, c'est le prouver** : `attendre_une_requete_en_cours` (tests 11, 12, 12 bis), + jamais un délai fixe. +- **Respecter l'ordre des verrous** (AC2) : réglages, puis comptes bancaires. Lire les réglages + **après** le `FOR UPDATE` de la ligne bancaire réintroduirait un interblocage entre les deux gestes. +- `default_receivable_account_id` / `default_payable_account_id` peuvent être `NULL` : pas de refus + dans ce cas. + +### Références + +- [Source: crates/kesh-api/src/routes/bank_accounts.rs:258-294, :384-600, :717-770] +- [Source: crates/kesh-db/src/repositories/bank_accounts.rs:237-420, :579-598 (verrou sentinelle)] +- [Source: crates/kesh-api/src/routes/company_invoice_settings.rs:246-400 ; + crates/kesh-db/src/repositories/company_invoice_settings.rs:147-240] +- [Source: frontend/src/routes/(app)/bank-accounts/+page.svelte:77-85, :321-330, :437, :472-481 ; + frontend/src/lib/features/bank-accounts/BankAccountJournalLinkForm.svelte:44-60 ; + frontend/src/routes/(app)/settings/invoicing/+page.svelte:65, :85-111, :195-196] +- [Source: crates/kesh-db/src/test_fixtures.rs:80-172 ; frontend/tests/e2e/bank-account-journal-link.spec.ts:105-119] +- [Source: crates/kesh-db/src/repositories/opening_complement.rs:38-39, :502, :540 ; crates/kesh-api/src/lib.rs:190-343, :339-341, :664-671 ; + crates/kesh-api/tests/admin_pat_denied_e2e.rs:64 ; frontend/src/lib/features/bank-accounts/BankAccountList.svelte] +- [Source: docs/manual/fr/user-manual.tex:237-242 ; docs/manual/fr/admin-manual.tex:2013, :2017-2035 (insertion après :2033)] +- [Source: crates/kesh-db/src/test_fixtures.rs:560 (`attendre_une_requete_en_cours`) ; crates/kesh-db/tests/credit_notes_repository.rs:594-628 ; + docs/api-external.md § 7, § 10 ; frontend/src/routes/(app)/settings/invoicing/settings-invoicing-page.test.ts:27-32] +- [Source: `15-5b-gardes-surfaces-neuves.md` AC10–AC13, AC19, choix C3, C4, C5, C10, C12, C25, C26 ; + `15-5d-garde-usage-comptes-reglage.md` AC5, AC8, choix C34 (dépôt principal, branche + `story/15-5-gardes-postabilite-serveur`)] +- [Source: issue #474 ; `epic-15-choix-autonomes.md` C-15-6-1, C-15-6-5, C-15-6-13 à C-15-6-15, C-15-6-21, C-15-6-22, C-15-6-27] + +## Dev Agent Record + +### Agent Model Used + +### Debug Log References + +### Completion Notes List + +### File List + +## Change Log + +- 2026-10-08 — Spécification initiale (bmad-create-story, en autonomie). Statut `ready-for-dev`. +- 2026-10-08 — **Validation P1** (Sonnet 4.6, lentilles F et R ; prompt + `15-6c-validate-prompt-p1.md`). F : 2 HIGH, 4 MEDIUM, 3 LOW ; R : 1 HIGH, 5 MEDIUM, 4 LOW (R3 = F5, + R7 = F4). Appliqués : + - **HIGH F1** — contrôle des réglages hors transaction : déplacé **dans** `company_invoice_settings::update`, + contre `before` lu sous verrou ; ordre des erreurs écrit, route par route (AC5, AC6). + - **HIGH F2** — course entre les deux gestes : **fermée** par `FOR SHARE` *(graphie fausse, corrigée en P2 : `LOCK IN SHARE MODE`)* / `FOR UPDATE` et un ordre + de verrous unique (AC2, tests 11–12) ; l'angle mort est supprimé (C-15-6-13, qui révise + C-15-6-5). + - **HIGH R1** — AC6 périmé par l'AC13 de la 15-5b : l'écran vise désormais le filtre + `eligibleAccounts` de `BankAccountJournalLinkForm`, avant `withCurrentAccount` (AC7). + - **MEDIUM F3** — écran filtré sur les **ids désignés dans les réglages**, non sur le rôle (AC7). + - **MEDIUM F4 (= R7 LOW)** — l'écran des réglages ne propose plus en débiteurs un compte lié (AC8) ; + la lecture de `err.message` est vérifiée, la question retirée. + - **MEDIUM F5 = R3** — destinations du manuel fixées (AC9). + - **MEDIUM F6 = R6** — variantes `DbError` et fonctions de dépôt nommées, contrôle de création placé + dans la transaction de la route, lecteurs uniques (AC1–AC3). + - **MEDIUM R2** — signature des fonctions d'écran partagée avec la 15-6b (C-15-6-14). + - **MEDIUM R4** — tests existants inventoriés au seed ; la spec E2E qui liait au 1100 lie au 1000 + (C-15-6-15). + - **MEDIUM R5** — compte créanciers comparé à sa **valeur résolue** (AC19 de la 15-5b), test dédié + (test 8). + - **LOW F7** — propriétaire unique de la section `[0.13.0]` (AC11). **LOW F8** — angles morts + « import de sauvegarde » et « création des réglages à l'onboarding » écrits. **LOW F9** — tests + « réglage NULL » et « deux comptes bancaires, premier par id » (tests 4, 7) ; seed vérifié. + **LOW R8** — une clé par `claim` nommant le bon compte (AC1). **LOW R9** — refus typé et traduit + sur une route aux refus en français en dur : dit (AC1). **LOW R10** — fichier de test du PUT des + réglages fixé (neuf) ; `docs/api-external.md` (AC10). + - Décompte après passe : **11 AC, 9 tâches (T0–T8), 15 tests** (12 backend, 3 fichiers Vitest) + + 1 spec E2E modifiée. +- 2026-10-08 — **Validation P2** (Opus 5.5, lentilles R et F ; prompt `15-6c-validate-prompt-p2.md`). + R : 0 CRITICAL, 1 HIGH, 3 MEDIUM, 6 LOW ; F : 0 CRITICAL, 1 HIGH, 3 MEDIUM, 4 LOW (R2-1 = F1, + R2-2 = F4, R2-3 = F3, R2-8 = F5, R2-10 = F7). Tous appliqués, décisions de l'orchestrateur comprises : + - **HIGH R2-1 = F1** — `FOR SHARE` (né de la remédiation P1) est une erreur de syntaxe sur MariaDB + 10.11 : **`LOCK IN SHARE MODE`** dans les deux lecteurs, lecteur renommé + `claim_accounts_in_share_mode`, piège écrit ; valeur grepée sur les quatre fiches et le registre. + Choix C-15-6-21. + - **MEDIUM F2** — route des réglages : le **409 d'abord**, puis le refus (le client voit l'état + courant avant qu'on juge son changement) ; AC6, test 9 (→ 409). Choix C-15-6-21, qui révise + l'ordre de C-15-6-13. + - **MEDIUM R2-3 = F3** — `CLAIM_ACCOUNT_LINKED_TO_BANK_ACCOUNT` n'est pas destiné aux intégrateurs + (route fermée aux clés) : AC1 (destinataires), AC10 (un seul code au guide), test 8. Choix + C-15-6-21. + - **MEDIUM R2-2 = F4** — tests 6 et 14 **étendent** les fichiers créés par la 15-5b, pas de doublon ; + T0 vérifie leur existence. + - **MEDIUM R2-4** — appels directs au dépôt inventoriés (14 sites de test) et tranchés : + `ClaimAccounts::default()`. Choix C-15-6-22. + - **15-5d** (consigne de l'orchestrateur) : le compte créanciers devient corrigeable à l'écran ; son + menu est filtré (AC8), son refus atteignable par l'écran (AC5, test 8, test 15). + - **LOW** — R2-5 : références de fixture `:80-172` ; R2-6 : place exacte du contrôle (après la + version, après le no-op du seul lien) ; R2-7 : « premier verrou après la sentinelle », ordre global + `companies → réglages → bank_accounts` ; R2-8 = F5 : interblocage préexistant de l'`INSERT IGNORE` + écrit dans la note KF-004 réécrite, non promis comme résolu ; R2-9 : frontière 15-5b/15-5d/15-6a + au manuel d'administration, paragraphe à la fin du bloc LTVA qui porte déjà tous les réglages de + facturation ; R2-10 = F7 : échec de `getInvoiceSettings()` / `listBankAccounts()` → pas de filtre ; + F6 : `BankAccountList.svelte`, code mort, supprimé, prop obligatoire (C-15-6-22) ; F8 : voir + ci-dessous. + - **Signal de découpage (F8), déclaré au Project Lead** : la sévérité ne baisse pas (HIGH en P1, + HIGH en P2) et le HIGH de la P2 **naît de la remédiation P1** — c'est le cas de recyclage que + l'amendement D5 désigne comme déclencheur ; les modules dépassent cinq (`kesh-db` comptes bancaires + et réglages, `kesh-api` deux routes, `frontend` deux écrans). **Non découpée** (choix C-15-6-22) : + le défaut est une graphie de mot-clé, non une conception qui tourne en rond, et séparer les deux + sens casserait l'ordre de verrous qui les tient ensemble. **Arbitrage de Guy attendu.** + - Décompte après passe : **11 AC, 9 tâches (T0–T8), 15 tests** (12 backend, 3 fichiers Vitest) + + 1 spec E2E modifiée ; un fichier supprimé (`BankAccountList.svelte`). +- 2026-10-08 — **Validation P3** (Sonnet, lentilles R et F ; remédiation Opus 5.5 ; prompt + `15-6c-validate-prompt-p3.md`). R : 0 CRITICAL, 0 HIGH, 1 MEDIUM, 5 LOW ; F : 0 CRITICAL, 0 HIGH, + 2 MEDIUM, 6 LOW (R3-2 = F3, R3-6 = F6). Tous appliqués, décisions de l'orchestrateur comprises + (choix C-15-6-27) : + - **MEDIUM F1** — le `FOR UPDATE` de `before`, clé de la sérialisation côté réglages, n'était épinglé + par aucun test (11 et 12 passent avec un `SELECT` simple) : **test 12 bis** neuf (transaction tenue + en S par `claim_accounts_in_share_mode`, le PUT attend au `FOR UPDATE`), qui rougit par mutation ; + le test 12 dit ce qu'il ne prouve pas. + - **MEDIUM F2** — attente des tests 11, 12, 12 bis prouvée par `attendre_une_requete_en_cours` + (`test_fixtures.rs:560`), motifs écrits ; plus de délai fixe. + - **MEDIUM R3-1** — paragraphe du manuel d'administration placé **après** `:2033` (« Quand un + paiement… »), avant `:2035` ; borne `:2017-2033` (AC9, Références). **LOW F5** — il présente + d'abord les comptes débiteurs et créanciers, absents du manuel d'administration. + - **LOW R3-2 = F3** — `$account` (numéro, repli `#`), `$bank` (`bank_name`) ; numéro lu après + décision du refus, sans verrou ; cas `None` (AC1). + - **LOW F4** — tests Vitest existants inventoriés : quatre rendus de + `BankAccountJournalLinkForm.test.ts` reçoivent `claimAccountIds: new Set()`, mock de + `bank-accounts.api` ajouté à `settings-invoicing-page.test.ts` ; aucun ne change de sens. + - **LOW R3-6 = F6** — portée réelle du verrou (next-key / intervalle, attente possible d'un `INSERT` + concurrent, non mesurée) ; latence des gestes bancaires derrière les validations de pièces écrite + (Dev Notes, *Latence*). + - **LOW F7** — dé-liaison `null` (et création/remplacement sans compte) : ni lecture des réglages ni + verrou S, `ClaimAccounts::default()` (AC2, AC4). + - **LOW F8** — `docs/api-external.md` : ligne *Comptes bancaires* au § 7, code au § 10 (AC10, T7). + - **LOW R3-3** — les sept fonctions de l'AC6 nommées. **LOW R3-4** — « 14 sites » daté, avant le + merge de la 15-5b. **LOW R3-5** — chemin du dépôt préfixé pour la sentinelle (`:588`/`:592`). + - **Trend** : P1 3 HIGH, 9 MEDIUM → P2 2 HIGH, 6 MEDIUM → P3 0 HIGH, 3 MEDIUM. **Signal de + découpage (D5)** : non levé à cette passe — la sévérité baisse (HIGH → MEDIUM) et les trois MEDIUM + sont distincts de ceux de la P2 ; aucun ne naît d'une remédiation (F1 et F2 visent des tests écrits + dès la P1, R3-1 une borne du manuel écrite en P2 mais fausse dès son écriture, non recyclée). Le + signal de la P2 (modules > 5) reste déclaré, l'arbitrage de Guy toujours attendu. + - Décompte après passe : **11 AC, 9 tâches (T0–T8), 16 tests** (13 backend dont le 12 bis neuf, + 3 fichiers Vitest) + 1 spec E2E modifiée + adaptations de deux fichiers Vitest existants ; un + fichier supprimé (`BankAccountList.svelte`). +- 2026-10-08 — **Validation P4 — passe ciblée, clôture de la validation** (Haiku 4.5, une lentille + « chasseur de régressions » braquée sur la seule remédiation P3, diff aplati + `git diff 4964eeb4 89e8987a` sur cette fiche ; prompt versionné `15-6c-validate-prompt-p4-ciblee.md`). + **0 CRITICAL, 0 HIGH, 0 MEDIUM, 0 LOW.** Axes déclarés exercés : cohérence du commentaire d'en-tête, + arguments des messages et source du numéro, portée du verrou (next-key), exemption de la dé-liaison, + les sept fonctions de l'AC6 (recomptées), place du paragraphe du manuel d'administration, ligne du + § 7 du guide, tâches T6/T7, inventaire daté et adaptations Vitest, test 12 bis (distinct du 12, motif + d'attente, mutation), recompte des tests (16). Axes déclarés **non** exercés (hors d'une passe + ciblée sur une fiche) : compilation, rougissement des tests, code des routes et du dépôt, rendu + frontend, numéros de ligne après rebase, E2E — renvoyés à T0, T6 et T8. + **« 0 » vérifié par l'orchestrateur, comme un finding** (règle « Une passe qui ne déclare pas ses axes + ne compte pas ») : le test 12 bis existe dans la fiche ; `attendre_une_requete_en_cours` existe à + `crates/kesh-db/src/test_fixtures.rs:560`. Haiku est employé ici en passe **ciblée**, conformément à + la décision D6 (rétrospective de l'Epic 25) ; la remédiation relue ne touchait aucune ligne de code de + production (fiche seule) : la boucle peut se clore. + - **Trend complet** (F + R par passe) : P1 (Sonnet 4.6, lentilles F et R) 3 HIGH, 9 MEDIUM, 7 LOW → + P2 (Opus 5.5, lentilles R et F) 2 HIGH, 6 MEDIUM, 10 LOW → P3 (Sonnet, lentilles R et F ; + remédiation Opus 5.5) 0 HIGH, 3 MEDIUM, 11 LOW → P4 ciblée (Haiku 4.5) 0. Aucun CRITICAL sur + l'ensemble. Modèles : Sonnet → Opus → Sonnet pour les passes complètes, Haiku pour la passe ciblée + de fin de boucle (D6). + - **Reclassements** : aucun finding MEDIUM+ reclassé en dette. **Signal de découpage** : levé en P2 + (modules > 5, HIGH né de la remédiation P1), non découpé (C-15-6-22), **arbitrage de Guy toujours + attendu** ; non relevé en P3 ni en P4. + - **Validation close** : 0 au-dessus de LOW. Décompte final : **11 AC, 9 tâches (T0–T8), 16 tests** + (13 backend, 3 fichiers Vitest) + 1 spec E2E modifiée + adaptations de deux fichiers Vitest + existants ; un fichier supprimé (`BankAccountList.svelte`). diff --git a/_bmad-output/implementation-artifacts/15-6c-validate-prompt-p1.md b/_bmad-output/implementation-artifacts/15-6c-validate-prompt-p1.md new file mode 100644 index 00000000..867afe5c --- /dev/null +++ b/_bmad-output/implementation-artifacts/15-6c-validate-prompt-p1.md @@ -0,0 +1,34 @@ +# Prompt — validation P1 de la spec, Story 15-6c + +*Versionné le 2026-10-08. Deux lentilles (Sonnet), contexte frais chacune, en lecture seule. Rotation (décision D6 de la +rétro 25) : passes complètes Sonnet ↔ Opus ; Haiku réservé à une passe ciblée de fin de boucle.* + +Dépôt `/home/gcorbaz/devel/kesh-15-6`, branche `story/15-6-creance-juste-avoir-reglement`. **Fiche** : `_bmad-output/implementation-artifacts/15-6c-configuration-sans-ecriture-nulle.md`. C'est la première passe : la fiche n'a pas encore été remédiée. +Issues, par `gh api repos/guycorbaz/kesh/issues/N` (et `/comments`) — `gh issue view` échoue ici : 474. +Choix autonomes : `_bmad-output/implementation-artifacts/epic-15-choix-autonomes.md`. Règles : `CLAUDE.md`. Fiche d'index 15-6, sœurs 15-6a et 15-6b. La 15-5b (dépôt principal, `/home/gcorbaz/devel/kesh/_bmad-output/implementation-artifacts/15-5b-gardes-surfaces-neuves.md`, lecture seulement) modifie les mêmes fonctions de `bank_accounts.rs` et des réglages : vérifie la frontière et la dépendance. +Aucun code de la story n'est encore écrit. + +## Lentilles + +- **R — Auditeur d'acceptation** : applique `.claude/skills/bmad-create-story/checklist.md`. Chaque phrase des issues couverte par un AC testable, chaque AC par une tâche, chaque tâche par un test ; implémentable sans deviner (fonctions, fichiers, codes d'erreur, clés i18n 4 locales, replis Svelte) ; faits cités revérifiés au code (`grep -nF`) ; recompte des AC et tâches. +- **F — Full-scope adversary** : la conception elle-même. Les chemins de code qui écrivent ou lisent ce que la story + change (inventorier les sites NON résolus, pas énumérer ceux qui marchent) ; transactions, verrous, ordre des + contrôles ; tests existants qui changeront de sens ; frontend qui lit les codes ou champs touchés (`grep -rn` dans + `frontend/src`) ; i18n 4 locales et replis ; le **manuel** (`docs/manual/fr/*.tex` **et le PDF aplati** : + `pdftotext docs/manual/fr/user-manual.pdf - | tr '\n' ' ' | tr -s ' '` vers `target/gate-logs/`) ; + `docs/api-external.md` ; CHANGELOG ; la règle de découpage (§ « Règle de splitting préventif », amendement D5). + +## Ce que tu rends + +Findings numérotés avec sévérité (CRITICAL/HIGH/MEDIUM/LOW), `fichier:ligne` (fiche et code), **preuve** (commande et +sortie, ou code cité relu), correction proposée. Pour tout finding affirmant qu'un code est absent ou présent : la +sortie d'un `grep -nF`. ⛔ **La liste des axes exercés ET non exercés** — un « 0 finding » sans elle ne compte pas. +Rapport compact. + +## Interdits + +⛔ N'écris aucun fichier du dépôt hors `target/gate-logs/` ; aucune commande qui écrit dans le dépôt ou dans une +base : `scripts/*` (dont `scripts/prepare-release.sh`), `make`, `latexmk`, `git commit`/`add`/`checkout`/`stash`, +`sqlx`, `cargo test`/`nextest`/`build`, `npm run`, `npx`, `gh issue create`/`comment`/`edit`, aucune requête SQL +d'écriture. Autorisés : lecture, `grep`, `sed -n`, `git log`/`show`/`diff`, `gh api` en lecture, `pdftotext` +vers `target/gate-logs/`. diff --git a/_bmad-output/implementation-artifacts/15-6c-validate-prompt-p2.md b/_bmad-output/implementation-artifacts/15-6c-validate-prompt-p2.md new file mode 100644 index 00000000..fdb59357 --- /dev/null +++ b/_bmad-output/implementation-artifacts/15-6c-validate-prompt-p2.md @@ -0,0 +1,35 @@ +# Prompt — validation P2 de la spec, Story 15-6c + +*Versionné le 2026-10-08. Deux lentilles (Opus), contexte frais chacune, en lecture seule. Rotation (décision D6 de la +rétro 25) : passes complètes Sonnet ↔ Opus ; Haiku réservé à une passe ciblée de fin de boucle.* + +Dépôt `/home/gcorbaz/devel/kesh-15-6`, branche `story/15-6-creance-juste-avoir-reglement`. **Fiche** : `_bmad-output/implementation-artifacts/15-6c-configuration-sans-ecriture-nulle.md`. La dernière remédiation est le commit `cb54ff72` (`git show cb54ff72`) — lis le Change Log de la fiche. +Issues, par `gh api repos/guycorbaz/kesh/issues/N` (et `/comments`) — `gh issue view` échoue ici : 474. +Choix autonomes : `_bmad-output/implementation-artifacts/epic-15-choix-autonomes.md`. Règles : `CLAUDE.md`. Fiche d'index 15-6 ; sœurs 15-6a/b/c/d. Les fiches 15-5a/b/c du dépôt principal (`/home/gcorbaz/devel/kesh/_bmad-output/implementation-artifacts/15-5*.md`, lecture seule) touchent des fichiers voisins et seront mergées AVANT 15-6b/c/d : vérifie la frontière. Axe prioritaire : la sérialisation par verrous (C-15-6-13) — ordre d'acquisition, interblocages possibles avec les autres chemins qui verrouillent les réglages ou les comptes bancaires. +Aucun code de la story n'est encore écrit. + +## Lentilles + +- **R — Regression hunter** : la dernière remédiation (`git show cb54ff72`) a-t-elle introduit des défauts ? Contradictions entre AC, tâches, Dev Notes et choix du registre ; frontière avec les fiches sœurs ; faits recopiés sans vérification (numéros de ligne, fonctions, codes, clés i18n) — revérifie-les au code (`grep -nF`) ; recompte des AC et tâches ; implémentable sans deviner ? +- **F — Full-scope adversary** : la conception elle-même. Les chemins de code qui écrivent ou lisent ce que la story + change (inventorier les sites NON résolus, pas énumérer ceux qui marchent) ; transactions, verrous, ordre des + contrôles ; tests existants qui changeront de sens ; frontend qui lit les codes ou champs touchés (`grep -rn` dans + `frontend/src`) ; i18n 4 locales et replis ; le **manuel** (`docs/manual/fr/*.tex` **et le PDF aplati** : + `pdftotext docs/manual/fr/user-manual.pdf - | tr '\n' ' ' | tr -s ' '` vers `target/gate-logs/`) ; + `docs/api-external.md` ; CHANGELOG ; la règle de découpage (§ « Règle de splitting préventif », amendement D5). + +## Ce que tu rends + +Findings numérotés avec sévérité (CRITICAL/HIGH/MEDIUM/LOW), `fichier:ligne` (fiche et code), **preuve** (commande et +sortie, ou code cité relu), correction proposée. Pour tout finding affirmant qu'un code est absent ou présent : la +sortie d'un `grep -nF`. ⛔ **La liste des axes exercés ET non exercés** — un « 0 finding » sans elle ne compte pas. +⛔ **Écris ton rapport complet** dans `target/gate-logs/15-6c-p2-.md` (R ou F ; autorisé), et rends +comme dernier message **seulement** : ce chemin, le bilan par sévérité, et une ligne par finding MEDIUM ou plus. + +## Interdits + +⛔ N'écris aucun fichier du dépôt hors `target/gate-logs/` ; aucune commande qui écrit dans le dépôt ou dans une +base : `scripts/*` (dont `scripts/prepare-release.sh`), `make`, `latexmk`, `git commit`/`add`/`checkout`/`stash`, +`sqlx`, `cargo test`/`nextest`/`build`, `npm run`, `npx`, `gh issue create`/`comment`/`edit`, aucune requête SQL +d'écriture. Autorisés : lecture, `grep`, `sed -n`, `git log`/`show`/`diff`, `gh api` en lecture, `pdftotext` +vers `target/gate-logs/`. diff --git a/_bmad-output/implementation-artifacts/15-6c-validate-prompt-p3.md b/_bmad-output/implementation-artifacts/15-6c-validate-prompt-p3.md new file mode 100644 index 00000000..167c3178 --- /dev/null +++ b/_bmad-output/implementation-artifacts/15-6c-validate-prompt-p3.md @@ -0,0 +1,35 @@ +# Prompt — validation P3 de la spec, Story 15-6c + +*Versionné le 2026-10-08. Deux lentilles (Sonnet), contexte frais chacune, en lecture seule. Rotation (décision D6 de la +rétro 25) : passes complètes Sonnet ↔ Opus ; Haiku réservé à une passe ciblée de fin de boucle.* + +Dépôt `/home/gcorbaz/devel/kesh-15-6`, branche `story/15-6-creance-juste-avoir-reglement`. **Fiche** : `_bmad-output/implementation-artifacts/15-6c-configuration-sans-ecriture-nulle.md`. La dernière remédiation est le commit `4964eeb4` (`git show 4964eeb4`) — lis le Change Log de la fiche. +Issues, par `gh api repos/guycorbaz/kesh/issues/N` (et `/comments`) — `gh issue view` échoue ici : 474. +Choix autonomes : `_bmad-output/implementation-artifacts/epic-15-choix-autonomes.md`. Règles : `CLAUDE.md`. Fiche d'index 15-6 ; sœurs 15-6a/b/c/d. Les fiches 15-5a/b/c/d du dépôt principal (`/home/gcorbaz/devel/kesh/_bmad-output/implementation-artifacts/15-5*.md`, lecture seule) seront mergées AVANT : vérifie la frontière. Axes : LOCK IN SHARE MODE et ordre des verrous, 409 avant le refus sur les réglages, suppression de BankAccountList.svelte. +Aucun code de la story n'est encore écrit. + +## Lentilles + +- **R — Regression hunter** : la dernière remédiation (`git show 4964eeb4`) a-t-elle introduit des défauts ? Contradictions entre AC, tâches, Dev Notes et choix du registre ; frontière avec les fiches sœurs ; faits recopiés sans vérification (numéros de ligne, fonctions, codes, clés i18n) — revérifie-les au code (`grep -nF`) ; recompte des AC et tâches ; implémentable sans deviner ? +- **F — Full-scope adversary** : la conception elle-même. Les chemins de code qui écrivent ou lisent ce que la story + change (inventorier les sites NON résolus, pas énumérer ceux qui marchent) ; transactions, verrous, ordre des + contrôles ; tests existants qui changeront de sens ; frontend qui lit les codes ou champs touchés (`grep -rn` dans + `frontend/src`) ; i18n 4 locales et replis ; le **manuel** (`docs/manual/fr/*.tex` **et le PDF aplati** : + `pdftotext docs/manual/fr/user-manual.pdf - | tr '\n' ' ' | tr -s ' '` vers `target/gate-logs/`) ; + `docs/api-external.md` ; CHANGELOG ; la règle de découpage (§ « Règle de splitting préventif », amendement D5). + +## Ce que tu rends + +Findings numérotés avec sévérité (CRITICAL/HIGH/MEDIUM/LOW), `fichier:ligne` (fiche et code), **preuve** (commande et +sortie, ou code cité relu), correction proposée. Pour tout finding affirmant qu'un code est absent ou présent : la +sortie d'un `grep -nF`. ⛔ **La liste des axes exercés ET non exercés** — un « 0 finding » sans elle ne compte pas. +⛔ **Écris ton rapport complet** dans `target/gate-logs/15-6c-p3-.md` (R ou F ; autorisé), et rends +comme dernier message **seulement** : ce chemin, le bilan par sévérité, et une ligne par finding MEDIUM ou plus. + +## Interdits + +⛔ N'écris aucun fichier du dépôt hors `target/gate-logs/` ; aucune commande qui écrit dans le dépôt ou dans une +base : `scripts/*` (dont `scripts/prepare-release.sh`), `make`, `latexmk`, `git commit`/`add`/`checkout`/`stash`, +`sqlx`, `cargo test`/`nextest`/`build`, `npm run`, `npx`, `gh issue create`/`comment`/`edit`, aucune requête SQL +d'écriture. Autorisés : lecture, `grep`, `sed -n`, `git log`/`show`/`diff`, `gh api` en lecture, `pdftotext` +vers `target/gate-logs/`. diff --git a/_bmad-output/implementation-artifacts/15-6c-validate-prompt-p4-ciblee.md b/_bmad-output/implementation-artifacts/15-6c-validate-prompt-p4-ciblee.md new file mode 100644 index 00000000..eb7520c7 --- /dev/null +++ b/_bmad-output/implementation-artifacts/15-6c-validate-prompt-p4-ciblee.md @@ -0,0 +1,15 @@ +# Prompt — validation P4 CIBLÉE de la spec, Story 15-6c + +*Versionné le 2026-10-08. Une lentille (Haiku), contexte frais, lecture seule. Passe ciblée (CLAUDE.md § « La passe ciblée ») : la P3 n'a laissé que des MEDIUM de tests manquants, remédiés par le commit `89e8987a`, qui ne touche que des fiches. Haiku est réservé à ce type de passe (décision D6).* + +Worktree `/home/gcorbaz/devel/kesh-15-6`, branche `story/15-6-creance-juste-avoir-reglement`. Fiche : `_bmad-output/implementation-artifacts/15-6c-configuration-sans-ecriture-nulle.md`. + +## Lentille unique — Regression hunter sur la dernière remédiation + +Lis `git diff 4964eeb4 89e8987a -- _bmad-output/implementation-artifacts/15-6c-configuration-sans-ecriture-nulle.md` (un seul diff aplati), puis la fiche entière. Pour chaque hunk : contradiction introduite (AC, tâches, Dev Notes, registre C-15-6-*) ? fait faux (vérifie au code par `grep -nF`) ? test qui ne prouverait rien (le test 12 bis mord-il sur le `FOR UPDATE` de `before` ? `attendre_une_requete_en_cours` est-il employé avec un motif qui existe ?) ? décompte faux (recompte AC, tâches, tests) ? + +⛔ Toute affirmation de présence ou d'absence : la sortie d'un `grep -nF`, copiée. ⛔ Un « 0 » sans la liste des axes exercés ET non exercés ne compte pas. + +Rapport complet dans `target/gate-logs/15-6c-p4-ciblee.md`. Dernier message : le chemin, le bilan, une ligne par MEDIUM+. + +⛔ Interdits : n'écris rien hors `target/gate-logs/` ; aucune commande qui écrit, compile ou exécute (scripts/*, make, latexmk, git commit/add/checkout/stash, sqlx, cargo, npm, npx, gh issue create/comment/edit, SQL d'écriture). Autorisés : lecture, grep, sed -n, git log/show/diff, gh api en lecture. diff --git a/_bmad-output/implementation-artifacts/15-6d-contrepartie-distincte-de-la-banque.md b/_bmad-output/implementation-artifacts/15-6d-contrepartie-distincte-de-la-banque.md new file mode 100644 index 00000000..f25eab3f --- /dev/null +++ b/_bmad-output/implementation-artifacts/15-6d-contrepartie-distincte-de-la-banque.md @@ -0,0 +1,485 @@ +# Story 15.6d : Un rapprochement ne prend pas le compte de la banque pour contrepartie + +## Status + +ready-for-dev + + + +**Issue** : `closes #524` (P1) — mot-clé **dans la PR** (merge en squash). **Mère** : +`15-6-creance-juste-avoir-reglement.md`. +**Après la 15-5b** (rebase : elle modifie les mêmes fonctions — `ACCOUNT_NOT_POSTABLE` sur +`post_manual`, son AC1, et sur `accept_one_rule`, son AC4 ; l'ensemble passé à `first_matching_rule` +dans `get_proposals`, son AC5). **Aucune dépendance d'ordre des refus** : le refus de cette story +précède le sien (AC1, AC2, choix C-15-6-23). Son passage du manuel s'écrit sur la section du +rapprochement que la **15-5c** réécrit : après la 15-5c de préférence, sinon rebase attendu. La +**15-5c** modifie aussi les deux fichiers d'écran de l'AC4 — `ReconciliationProposals.svelte` (son +libellé de `failed[]`) et `ReconciliationProposals.test.ts` (trois tests ajoutés, sa T3) — : rebase de +forme attendu sur eux. Sa traduction des codes de `failed[]` porte l'AC6. +**Après la 15-6b** pour un seul emprunt : la **comparaison** de deux comptes de son helper commun +(`invoice_settlements`, `pub fn ensure_not_claim_account(account_id: i64, claim_account_id: i64) -> Result<(), ClaimAccountClash>`, +fonction **pure**, sans connexion ni `async`, telle que la fiche 15-6b la pose — T0 reprend le nom réel), choix +C-15-6-28 ; **pas** sa construction de refus (AC3). Autres fonctions de `reconciliation.rs` que les +15-6a/b/c ; filtre d'écran local, sans emprunt à `account-options.ts`. + +## Story + +En tant que **comptable qui rapproche ses relevés**, +je veux que **Kesh refuse un rapprochement manuel ou par règle dont la contrepartie est le compte même +de la banque**, +afin qu'**une transaction bancaire ne soit jamais marquée rapprochée par une écriture `D banque / C +banque`, nulle au grand livre**. + +## Le défaut, établi au code + +- Le rapprochement **ventilé** refuse déjà une contrepartie égale au compte comptable du compte + bancaire, sur ses deux chemins : + - `accept_one_split` (`crates/kesh-api/src/routes/reconciliation.rs:1829`, garde `:1941-1950`) : + `FailedProposal { error_code: "VALIDATION_ERROR", details: { "reason": "counterparty_equals_bank_ledger" } }` ; + - `post_split` (`:3362`, garde `:3462-3475`) : `AppError::Validation("splits[{idx}].counterpartyAccountId ne peut pas être le compte ledger banque")`, + **400**, message en français en dur. +- Le rapprochement **manuel** (`post_manual`, `:2942` ; compte de banque `:2989`, contrepartie + `:3028`) et l'acceptation **par règle** (`accept_one_rule`, `:2207` ; compte de banque `:2235`, + contrepartie `:2287`) **ne comparent jamais** les deux comptes ; + `build_journal_entry_for_counterparty` (`crates/kesh-reconciliation/src/manual.rs:67`) non plus. +- Une règle n'est attachée à aucun compte bancaire (l'entité `reconciliation_rule` ne porte pas de + `bank_account_id`), et sa création par l'API ne restreint pas le type du compte : seule + l'acceptation connaît le compte de banque en jeu. +- **Contrôle du compte de banque actif** : `post_manual` (étape 2 bis, `:3004-3017`, 412), + `accept_one_split` (« Step c », `:1912-1938`, `failed[]` `BANK_ACCOUNT_NOT_CONFIGURED`) et + `post_split` (« Step 4bis », `:3446-3460`, 412) le font **avant** leur garde d'égalité ; + `accept_one_rule` **ne le fait pas** (étapes 1, 2, 4, 5, 6 — `:2217-2318`) : un compte de banque + archivé y atteint l'écriture, dont la garde `active` (`journal_entries.rs:98`) refuse. +- **Écrans** : `ManualMatchModal.svelte:65-69` ne propose que les classes 5/6/7. Le serveur impose le + **type** du compte de banque — actif ou passif (`validate_journal_account_id`, + `crates/kesh-api/src/routes/bank_accounts.rs:260-294`) — mais **pas sa classe** : aucun des trois + plans livrés (`crates/kesh-core/assets/charts/*.json`) n'a d'actif ou de passif en classe 5 à 7, et + le commentaire de `post_split` (`:3465-3467`) le dit (« en UX normal pas de collision possible ») ; + la collision suppose un compte d'actif ou de passif créé par l'utilisateur en classe 5 à 7. + `RuleFormModal.svelte:44-50` ne propose **en création** que charges et produits ; en **édition**, le + compte en place de la règle est réintroduit (`withCurrentAccount`, `:55-57`, + `frontend/src/lib/features/accounts/account-options.ts:58`, #271), même s'il n'est pas éligible. + +## Acceptance Criteria + +1. **AC1 — Rapprochement manuel** (`post_manual`) : **juste après** l'étape 2 bis (compte de banque + résolu et actif, `:2989-3017`), **avant** l'étape 3 (contrepartie : 404 `ACCOUNT_NOT_FOUND`, puis + `ACCOUNT_NOT_POSTABLE` de la 15-5b) : `body.counterparty_account_id == bank_ledger_account_id` → + **le même refus que `post_split`** (choix C-15-6-9) : + `AppError::Validation(…)`, 400 `VALIDATION_ERROR` — même **code** ; le **texte** suit le champ en + cause : `post_split` écrit aujourd'hui `splits[{idx}].counterpartyAccountId ne peut pas être le + compte ledger banque` (`reconciliation.rs:3471`), `post_manual` écrira `counterpartyAccountId ne + peut pas être le compte ledger banque`, par la fonction unique de l'AC3, qui prend le chemin du champ + en paramètre (finding F3-2 de la P3). **Même place que le flux ventilé** (choix C-15-6-23) : `accept_one_split` et + `post_split` comparent **après** le contrôle d'activité du compte de banque (« Step c » + `:1912-1938`, « Step 4bis » `:3446-3460`) et **avant** la validation des comptes de contrepartie + (`:1941-1950` puis « Step d » ; `:3462-3475` puis « Step 5 batch validation ») — si bien qu'un + compte de banque devenu non imputable pris pour contrepartie rend partout **ce** refus, et non + `ACCOUNT_NOT_POSTABLE` (qui inviterait à agir sur la mauvaise chose). Le 404 ne pourrait de toute façon pas précéder utilement : égale au + compte de banque, la contrepartie existe et est active (étape 2 bis). **Avant l'étape 4** + (`find_strictly_pending…`) aussi : une contrepartie égale au compte de banque sur une transaction + **déjà rapprochée** rend donc **400 `VALIDATION_ERROR`**, pas 404 + `RECONCILIATION_TRANSACTION_NOT_PENDING` — choix écrit (le défaut de la requête prime l'état de la + transaction), testé (test 3). Rien n'est écrit : aucune écriture, transaction bancaire toujours + `pending`, aucun audit. **Ordre** écrit dans le doc-comment : forme (étape 0) → 404 compte bancaire + / 412 `BANK_ACCOUNT_NOT_CONFIGURED` (étapes 1 à 2 bis, existantes) → **400 `VALIDATION_ERROR`** → + 404 `ACCOUNT_NOT_FOUND` → 400 `ACCOUNT_NOT_POSTABLE` (15-5b) → 404 transaction non en attente → la + suite inchangée. +2. **AC2 — Acceptation par règle** (`accept_one_rule`). + - **Contrôle du compte de banque actif, ajouté** (choix C-15-6-28) : juste après l'étape 1, la + même étape que la « Step c » d'`accept_one_split` (`:1912-1938`) — `SELECT active FROM accounts + WHERE id = ? AND company_id = ?` dans la transaction ; erreur SQL → `DATABASE_ERROR` + (`details.message`) ; compte absent ou archivé → `FailedProposal { error_code: + "BANK_ACCOUNT_NOT_CONFIGURED", details: { "bankAccountId" } }`, forme de l'étape 1 et + d'`accept_one_split`. Sans lui, un même défaut — compte de banque archivé — rendrait sur ce seul + chemin `VALIDATION_ERROR` (règle sur ce compte) ou le refus de la garde `active` de l'écriture + (toute autre règle), quand les trois autres rendent `BANK_ACCOUNT_NOT_CONFIGURED`. **Changement + délibéré** d'un refus existant : le code rendu aujourd'hui pour une règle ordinaire sur un compte + de banque archivé est relevé en T0 et écrit au Change Log ; aucun test existant ne l'exerce + (`grep -n "BANK_ACCOUNT_NOT_CONFIGURED" crates/kesh-api/tests/reconciliation_rules_e2e.rs` → `:1360` + seul, compte bancaire **non lié**, étape 1, inchangé). **Atteignable depuis l'écran** (finding R1 de + la P3) : `get_proposals` ne contrôle que l'archivage du **compte bancaire** (`ba_check.archived`, + `reconciliation.rs:476`), jamais l'activité du compte comptable lié, et l'archivage d'un compte du + plan n'est pas refusé quand un compte bancaire y est lié ; un compte de banque archivé **avant** + l'ouverture de l'écran laisse donc proposer une règle ordinaire, dont l'acceptation tombe dans + cette étape — ce que l'utilisateur d'écran voit (libellé de la 15-5c, AC6). Aucun contrôle n'est + ajouté à `get_proposals` (périmètre) : angle mort écrit (§ *Angles morts*). + - **Garde d'égalité** : **après** l'étape 4 (concordance de la + contrepartie de la proposition avec celle de la règle, `RECONCILIATION_RULE_MISMATCH`) et **avant** + l'étape 5 (contrepartie active, puis `ACCOUNT_NOT_POSTABLE` de la 15-5b, son AC4) : égalité de la + contrepartie de la règle avec le compte de banque (lu à l'étape 1, dans la transaction) → + `FailedProposal { error_code: "VALIDATION_ERROR", details: { "reason": "counterparty_equals_bank_ledger" } }` + — exactement la forme de `accept_one_split`. HTTP 200, la proposition dans `failed[]`, les autres + du lot traitées normalement (§ *Pattern batch* du `CLAUDE.md`) ; la transaction bancaire reste en + attente, aucune écriture, aucun audit pour elle. **Ordre** : étape 1 (compte de banque : + `DATABASE_ERROR`, `BANK_ACCOUNT_NOT_CONFIGURED`, `BANK_ACCOUNT_NOT_FOUND`) → **compte de banque + actif** (`DATABASE_ERROR`, `BANK_ACCOUNT_NOT_CONFIGURED`) → étape 2 + (`RECONCILIATION_RULE_NOT_FOUND`) → étape 4 (`RECONCILIATION_RULE_MISMATCH`) → **`VALIDATION_ERROR`** + → étape 5 (`ACCOUNT_NOT_FOUND`, `ACCOUNT_NOT_POSTABLE`) → étape 6. Écrit dans le doc-comment. + **AC2 bis — Une règle sur le compte de banque n'est plus proposée.** `get_proposals` lit déjà le + compte bancaire (`ba_check`, `reconciliation.rs:469-478`, qui porte `journal_account_id`) et passe + à `first_matching_rule` (`:634-638` ; `kesh-reconciliation/src/rules.rs:85-95`, qui rend la + **première** règle concordante) l'ensemble `active_account_ids` (`:556-557`). Le compte de banque en + est **retiré** (s'il est configuré) : une règle dont la contrepartie est ce compte n'est plus + proposée — l'acceptation la refuserait toujours — et **ne masque plus** une règle suivante valable + pour la même transaction. Patron de l'AC5 de la 15-5b, qui retire du même ensemble les comptes non + imputables : les deux retraits se composent, à écrire sur l'état rebasé. Le commentaire que la 15-5b + pose au site d'appel pour dire ce que contient l'ensemble est **mis à jour** : « actifs, imputables, + hors compte de la banque ». Le doc-comment de `first_matching_rule` (`kesh-reconciliation/src/rules.rs:72-75`, + « non archivé ») reste tel quel : `kesh-reconciliation` est hors périmètre, comme pour la 15-5b — le + site d'appel fait foi. La garde de l'AC2 reste le filet d'un client d'API ou d'une règle modifiée + entre la proposition et l'acceptation. +3. **AC3 — Le choix de la forme est écrit.** Les deux refus reprennent ceux du flux ventilé + **existant** — forme **et place** — pour qu'un même défaut rende la même réponse sur les **quatre** + chemins (manuel, ventilé direct, ventilé par lot, règle) : compte de banque **actif** pris pour + contrepartie → `VALIDATION_ERROR` ; devenu **non imputable** → `VALIDATION_ERROR`, pas + `ACCOUNT_NOT_POSTABLE` ; **archivé** → `BANK_ACCOUNT_NOT_CONFIGURED` (412 sur les routes directes, + `failed[]` sur le lot — sur la règle grâce à l'étape ajoutée par l'AC2). La **comparaison** emploie + celle du helper commun de la 15-6b (en-tête) ; la **construction** du refus reste locale à + `reconciliation.rs`, **une** fonction pour les deux `FailedProposal` (`accept_one_split`, + `accept_one_rule`) et **une** pour les deux `AppError::Validation` (`post_split`, `post_manual`), + les deux sites ventilés existants y étant ramenés. Signature de la seconde (indicative) : + `fn counterparty_is_bank_ledger_error(field: &str) -> AppError`, qui rend + `AppError::Validation(format!("{field} ne peut pas être le compte ledger banque"))` — + `post_split` lui passe `splits[{idx}].counterpartyAccountId`, `post_manual` `counterpartyAccountId`. + Les tests 1 et 8 n'assertent que le **code** `VALIDATION_ERROR`, pas le message (en dur, non + traduit). **Limite assumée** (héritée + de `post_split`, non introduite ici) : le message du 400 manuel est en français en dur, non + traduit ; le code `VALIDATION_ERROR` et, pour le lot, `details.reason` sont les champs stables. Les + doc-comments des deux gardes renvoient l'une à l'autre et au flux ventilé. +4. **AC4 — L'écran du rapprochement manuel ne propose pas le compte de la banque** (choix C-15-6-23). + - **Résolution** dans `ReconciliationProposals.svelte` (qui reçoit `bankAccountId`, et dont la page + n'a pas de test) : `listBankAccounts()` (`frontend/src/lib/features/bank-accounts/bank-accounts.api.ts:96`, + comptes non archivés) **une fois, au montage**. **Pas de garde de génération** : la page remonte le + composant à chaque changement de compte bancaire (`{#key selectedId}`, + `frontend/src/routes/(app)/reconciliation/+page.svelte:70-72`), si bien qu'une réponse ne peut pas + arriver pour un autre `bankAccountId` que celui du montage (choix C-15-6-28). Le doc-comment de + l'appel le dit, et dit que le retrait du `{#key}` exigerait la garde. Le `journalAccountId` du + compte sélectionné est passé à `ManualMatchModal` (prop neuve `bankLedgerAccountId: number | null`, + `null` par défaut). + - **Repli** : pendant le chargement, en cas d'échec de l'appel (erreur ignorée, sans message — + comme le chargement des comptes du même composant, `:75-90`), si le compte bancaire est absent de + la liste (archivé) ou non lié (`journalAccountId` nul) → `null` : **aucun filtrage**, la garde + serveur (AC1) tranche. + - **Filtre** dans `ManualMatchModal` : **local, une condition** dans `filteredAccounts` (`:65-69`), + `a.id !== bankLedgerAccountId` — **sans conditionnel sur la 15-6b** et sans emprunt à + `account-options.ts` (un retrait d'un seul id ne justifie pas la fonction d'ensemble ; rien ne le + remplacera). Le filtre de classes 5/6/7 reste. + - *(La page lit déjà `/api/v1/companies/current`, dont `bankAccounts` porte `journalAccountId` + — `crates/kesh-api/src/routes/companies.rs:315-323` — mais son type local ne le déclare pas, et la + page n'a aucun test : résoudre dans le composant garde le câblage testable dans son fichier de + test existant.)* + - **Écran de ventilation** (`TransactionSplitModal.svelte:71-72`, même filtre 5/6/7) : **non + filtré**, angle mort écrit (§ *Angles morts*) — le serveur y refuse déjà. +5. **AC5 — L'écran des règles, figé.** `RuleFormModal.svelte:44-50` exclut le compte de banque par le + type (charges et produits seulement) **en création**. En **édition**, `withCurrentAccount` (`:55-57`, + #271) réintroduit le compte en place de la règle même non éligible : une règle créée par l'API sur le + compte de banque, ouverte en édition, le propose et l'enregistre tel quel. **Aucun changement de + code** : la réintroduction est voulue (#271 — sinon le champ s'afficherait vide sur une règle + complète) ; une telle règle n'est plus proposée (AC2 bis) et elle est refusée à l'acceptation (AC2). + Un commentaire dit les deux cas et renvoie à la garde serveur. +6. **AC6 — Libellé dans `failed[]`.** `VALIDATION_ERROR` fait partie des codes que la **15-5c** + traduit (`failed-proposal-label.ts`, sa liste de 26 codes) : rien à ajouter si elle est mergée. Le + libellé est **générique** — la 15-5c le relève sur un message existant ; au 2026-10-08, + `error-validation` dit « Erreur de validation » (`crates/kesh-i18n/locales/fr-CH/messages.ftl:36`) + — et `details.reason` n'est pas affiché : l'utilisateur n'apprend pas que la contrepartie est le + compte de banque. **Assumé** : depuis l'AC2 bis, l'écran ne propose plus une telle règle ; ce + libellé n'est atteint que par un client d'API ou une règle modifiée entre la proposition et + l'acceptation. Le dev relit le libellé effectif de la 15-5c et l'écrit au Dev Agent Record. Si la + 15-5c n'est pas mergée : le signaler, sans second mécanisme. +7. **AC7 — Documentation des intégrateurs** (`docs/api-external.md`) : au § *Accepter des propositions + de rapprochement* (`:303-307`), `VALIDATION_ERROR` avec `details.reason = "counterparty_equals_bank_ledger"` + pour une proposition par règle (et par ventilation, déjà vrai et non documenté), et + `BANK_ACCOUNT_NOT_CONFIGURED` (`details.bankAccountId`) pour une proposition par règle dont le compte + de banque est archivé (AC2) ; une demi-phrase dit qu'une règle visant le compte de la banque n'est + pas proposée (AC2 bis). **Rapprochement manuel et ventilation directe** : la **15-5b**, mergée + avant, ajoute au même § une phrase disant que ces deux routes refusent une contrepartie non + imputable en `400 ACCOUNT_NOT_POSTABLE`, « après le `404 ACCOUNT_NOT_FOUND` » + (`15-5b-gardes-surfaces-neuves.md:323-330`, dépôt principal). Cette phrase est **complétée** : les + deux routes refusent une contrepartie égale au compte de la banque en `400 VALIDATION_ERROR` (message + seul, sans `details.reason`), **avant** le 404 et le non-imputable. T0 refait + `grep -n "reconciliation/manual\|reconciliation/split" docs/api-external.md` sur l'état rebasé + (vide au 2026-10-08, avant la 15-5b) ; s'il est encore vide, la 15-5b n'est pas mergée : le + signaler, sans écrire sa phrase à sa place. +8. **AC8 — Manuel** (`docs/manual/fr/user-manual.tex`) : une phrase qui vaut pour les **trois** + flux — rapprochement manuel (`:1536-1545`), éclatement (`:1546-1560`) et règles d'affectation + (`:1561-1586`) — : la contrepartie d'un rapprochement ne peut pas être le compte comptable du compte + bancaire lui-même (l'écriture serait nulle) ; Kesh **le refuse** dans les trois cas — seule + affirmation sans réserve — et, **en règle générale**, ne le propose pas dans le rapprochement manuel + ni ne propose une règle qui le vise (finding F3-4 de la P3 : la fiche connaît des cas où il est + proposé — échec du chargement des comptes bancaires, compte non lié, plan atypique, écran de + ventilation jamais filtré ; le manuel ne promet pas une protection partielle comme totale). *(La section *Éclatement* ne + dit aujourd'hui rien de ce refus : ne pas écrire « comme pour un éclatement ».)* La section du + rapprochement manuel est inexacte et la **15-5c** la réécrit : écrire **sur sa version**. PDF + régénéré et contrôlé **aplati**. +9. **AC9 — CHANGELOG** sous `## [0.13.0] — Non publié`, `### Corrigé`, renvoi à + [#524](https://github.com/guycorbaz/kesh/issues/524) ; et `### Modifié` (finding F3-5 de la P3) : + l'acceptation d'une proposition par règle dont le compte de banque est archivé rend désormais + `BANK_ACCOUNT_NOT_CONFIGURED` (`details.bankAccountId`) dans `failed[]` — changement délibéré d'un + refus existant (AC2), visible d'un intégrateur ; le code d'avant, relevé en T0, y est nommé. La + section `[0.13.0]` est absente au 2026-10-08 (`grep -n "^## \[" CHANGELOG.md` → premier `0.12.1`) : + T0 le revérifie après rebase. **Propriétaire de la section** : la première + story de la version mergée la crée en tête des versions si elle est absente ; les suivantes y + ajoutent leur entrée. + +## Tasks / Subtasks + +- [ ] **T0 — Rebase après la 15-5b et la 15-6b** (et la 15-5c si mergée — elle touche + `ReconciliationProposals.svelte` et `ReconciliationProposals.test.ts`) ; relire les numéros de ligne + de `post_manual`, `accept_one_rule`, `accept_one_split`, `post_split` et `get_proposals` ; vérifier + où la 15-5b a placé `ACCOUNT_NOT_POSTABLE` (le refus de cette story le précède), comment elle a réduit + `active_account_ids` (AC2 bis s'y compose), où elle a posé le helper `set_account_not_postable` (le + réutiliser, ou le dupliquer dans chaque binaire de test qui l'emploie : tests 2, 5, 8, 9) ; reprendre + le nom réel du helper de comparaison de la 15-6b ; relever le code que rend aujourd'hui une règle + ordinaire sur un compte de banque archivé (AC2) ; refaire le grep de `docs/api-external.md` (AC7). +- [ ] **T1 — Gardes serveur** (AC1, AC2 dont l'étape du compte de banque actif, AC3 et ses deux + fonctions de refus) et proposition (AC2 bis, commentaire du site d'appel). +- [ ] **T2 — Écrans** (AC4, AC5) — dont le mock de `bank-accounts.api` dans + `ReconciliationProposals.test.ts` ; libellé (AC6) ou signalement. +- [ ] **T3 — Tests** (§ *Tests*), chaque test d'une garde neuve **rougit d'abord** ; les témoins 8 + et 9 (gardes ventilées existantes) passent avant **et** après. +- [ ] **T4 — Documentation** : `docs/api-external.md` (AC7), manuel + PDF (AC8), CHANGELOG (AC9). +- [ ] **T5 — Gates** : backend complet, frontend complet, E2E complet au dernier commit de code (D7). + +## Tests + +Backend : + +1. `crates/kesh-api/tests/reconciliation_manual_e2e.rs` — `POST /reconciliation/manual` avec + `counterpartyAccountId` = compte lié du compte bancaire → 400 `VALIDATION_ERROR` ; **aucune + écriture**, transaction bancaire toujours `pending`, **aucun audit** (patron + `post_manual_emits_audit_log_pair`, `:1217`). +2. Même fichier — **ordre** : le compte de banque rendu **non imputable** (`postable = FALSE`, toujours + actif — `set_account_not_postable` de la 15-5b, UPDATE direct, `version + 1`, sur le patron de + `products_revenue_account_e2e.rs:254-260`) et pris pour contrepartie rend **ce refus** + (`VALIDATION_ERROR`), **pas** `ACCOUNT_NOT_POSTABLE` (AC1, même place que le flux ventilé). *(Un + compte de banque **archivé** n'atteint pas cette garde : l'étape 2 bis rend 412 + `BANK_ACCOUNT_NOT_CONFIGURED` avant — de même sur les trois autres chemins, test 7 pour la règle.)* +3. Même fichier — **transaction déjà rapprochée** et contrepartie = compte de banque → 400 + `VALIDATION_ERROR`, pas 404 `RECONCILIATION_TRANSACTION_NOT_PENDING` (AC1). +4. `crates/kesh-api/tests/reconciliation_rules_e2e.rs` — une règle dont la contrepartie est le compte + lié (créée par l'API — **avant** tout passage en non imputable : après la 15-5b, AC7, la création + contrôle `postable` — ou en SQL). Depuis l'AC2 bis, `GET /reconciliation/proposals` ne rend plus + cette proposition : le test **construit** le corps (`type: "rule"`, `bankTransactionId`, `ruleId`, + `counterpartyAccountId`), patron `post_accept_rule` (`:1255`). `POST /reconciliation/accept` d'un + lot de deux propositions, l'une par cette règle, l'autre valide → HTTP 200, l'une acceptée, l'autre + dans `failed[]` avec `VALIDATION_ERROR` et `details.reason = "counterparty_equals_bank_ledger"` ; sa + transaction reste en attente, **aucune écriture ni audit** pour elle. +5. Même fichier — **ordre côté règle** : la même règle, **créée d'abord**, puis le compte de banque + rendu non imputable ; proposition construite comme au test 4 → `failed[]` `VALIDATION_ERROR` / + `counterparty_equals_bank_ledger`, **pas** `ACCOUNT_NOT_POSTABLE` (AC2). +6. Même fichier (ou celui des propositions, à repérer en T0) — **proposition** (AC2 bis) : deux règles + concordant avec la même transaction, la **première** sur le compte de banque, la seconde valable → + `GET` des propositions : la proposition porte la **seconde** ; avec la première seule, aucune + proposition par règle. +7. Même fichier — **compte de banque archivé, chemin par règle** (AC2, étape ajoutée) : le compte lié + du compte bancaire archivé en SQL (`UPDATE accounts SET active = FALSE`) **après** la création des + deux règles par l'API — la création refuse un compte archivé (`validate_counterparty_account`, + `reconciliation_rules.rs:197-221`, 404 `ACCOUNT_NOT_FOUND`) : règles d'abord, archivage ensuite + (findings R2 = F3-3 de la P3) ; deux propositions + construites, l'une par une règle ordinaire (contrepartie 6510), l'autre par la règle sur le compte + de banque → les deux dans `failed[]` en `BANK_ACCOUNT_NOT_CONFIGURED` avec + `details.bankAccountId`, ni `VALIDATION_ERROR` ni le refus de l'écriture ; rien d'écrit. *(Rougit + sur le code actuel.)* +8. `crates/kesh-api/tests/reconciliation_split_e2e.rs` — **témoin de l'ordre ventilé direct** : + `POST /reconciliation/split`, une ligne dont la contrepartie est le compte de banque rendu non + imputable → 400 `VALIDATION_ERROR`, pas `ACCOUNT_NOT_POSTABLE` ; aucune écriture. Fige l'ordre + « égalité avant postabilité » sur lequel repose C-15-6-23 (garde `:3462-3475`, aucun test + aujourd'hui : `grep -rnF "compte ledger banque" crates/kesh-api/tests` vide). +9. `crates/kesh-api/tests/reconciliation_e2e.rs` — **témoin de l'ordre ventilé par lot** : acceptation + d'une proposition `type: "split"` (patron `:2579`) dont une ligne vise le compte de banque rendu non + imputable → `failed[]` `VALIDATION_ERROR` / `details.reason = "counterparty_equals_bank_ledger"`, pas + `ACCOUNT_NOT_POSTABLE` ; aucune écriture (garde `:1941-1950`, aucun test aujourd'hui : + `grep -rnF "counterparty_equals_bank_ledger" crates/kesh-api/tests` vide). + +Frontend (Vitest) : + +10. `frontend/src/lib/features/reconciliation/ManualMatchModal.test.ts` (existant ; ses montages + passent la prop neuve) : le compte passé en `bankLedgerAccountId` n'est pas proposé, même numéroté + en classe 5/6/7 ; les autres le sont ; `null` → aucun filtrage. +11. `frontend/src/lib/features/reconciliation/ReconciliationProposals.test.ts` (existant) : **mock de + `$lib/features/bank-accounts/bank-accounts.api` ajouté** (les tests existants ne mockent que + `reconciliation.api`, `accounts.api` et l'i18n, `:20`, `:29` — sans lui, l'appel neuf partirait sur + l'`apiClient` réel) ; la modale ouverte ne propose pas le `journalAccountId` du compte bancaire + monté ; `listBankAccounts` en échec → compte non écarté, aucune erreur affichée. +12. `frontend/src/lib/features/reconciliation/rules/RuleFormModal.test.ts` (existant) : **en création**, + un compte d'actif n'est pas proposé (fige l'AC5). + +Soit **12 tests nommés** (9 backend dont 2 témoins, 3 fichiers Vitest). + +## Dev Notes + +### Ce qui doit être préservé + +- L'ordre des refus existants de `post_manual` et `accept_one_rule` ; le refus neuf s'y **insère** + avant la validation de la contrepartie, comme dans le flux ventilé — il ne déplace aucun refus + existant, **hors** le changement délibéré de l'AC2 (compte de banque archivé sur le chemin par règle, + désormais `BANK_ACCOUNT_NOT_CONFIGURED` ; finding R3 de la P3). +- La forme des refus du flux ventilé : ne pas les « améliorer » ici (un code dédié, une traduction) — + ce serait diverger des deux chemins existants ; si un code dédié devient souhaitable, il vaudra pour + les quatre chemins et sera une story à part. Le helper de la 15-6b n'est emprunté que pour la + comparaison : sa construction de refus (variante nommée d'un compte de créance ou de dette) n'est pas + celle-ci (AC3). +- **Aucun verrou à ajouter** pour l'égalité : sur les quatre chemins, la valeur comparée est **celle qui + construit l'écriture** — `post_manual` lit `bank_ledger_account_id` (`:2989`) et passe la même + variable à `build_journal_entry_for_counterparty` (`:3143-3147`) ; les chemins de lot font de même + dans la transaction. Un relien concurrent du compte bancaire peut faire imputer l'**ancien** compte + de banque (dette préexistante, hors #524), jamais produire une écriture `D x / C x`. Le + `with_account_lock` des acceptations est un `GET_LOCK` consultatif + (`crates/kesh-reconciliation/src/mutex.rs:66-90`) que les routes des comptes bancaires ne prennent + pas : ce n'est pas lui qui rend l'égalité sûre. + +### Angles morts assumés + +- La **création** d'une règle sur un compte qui est, ou deviendra, le compte de banque d'un compte + bancaire n'est pas refusée : une règle n'est pas attachée à un compte bancaire. Elle n'est plus + **proposée** (AC2 bis) et elle est refusée à l'acceptation (AC2), seul moment où le couple existe. +- **Écran de ventilation** (`TransactionSplitModal.svelte:71-72`) : il propose encore le compte de + banque s'il est numéroté en classe 5/6/7 (plan atypique) ; le serveur le refuse déjà (400 / + `failed[]`, gardes `:1941-1950`, `:3462-3475`). Filtre d'écran non étendu (choix C-15-6-23). +- **Message du repli d'écran** : quand le filtre ne s'applique pas (échec de `listBankAccounts`, + compte non lié, plan atypique), `ManualMatchModal.svelte:126-131` affiche `e.message` du 400 — en + français en dur dans les quatre langues (limite héritée de `post_split`, AC3). Pas de clé i18n ici + (§ *Ce qui doit être préservé*). +- **Édition d'une règle sur le compte de banque** (AC5) : l'écran la propose et l'enregistre ; elle + n'est ni proposée ni acceptée. +- **Compte de banque archivé avant l'ouverture de l'écran** (AC2) : `get_proposals` ne contrôle pas + l'activité du compte lié ; une règle ordinaire est proposée et son acceptation rend + `BANK_ACCOUNT_NOT_CONFIGURED`. Pas de contrôle ajouté à la proposition. +- **Câblage de l'écran vu seulement sous mock** (finding F3-6 de la P3) : le test 11 mocke + `listBankAccounts` ; `frontend/tests/e2e/reconciliation-manual.spec.ts` est un smoke sans fixtures ; + le passage `journalAccountId` API → prop → filtre n'est jamais exercé de bout en bout. Risque faible + (le parseur du champ est couvert, `bank-accounts.api.test.ts:37`, `:120`), assumé. + +### Références + +- [Source: crates/kesh-api/src/routes/reconciliation.rs:469-478, :556-557, :634-638, :1829, :1912-1950, :2207-2330, :2942-3060, :3143-3147, :3362, :3446-3475 ; + crates/kesh-reconciliation/src/rules.rs:72-95 ; crates/kesh-reconciliation/src/mutex.rs:66-90 ; + crates/kesh-api/src/routes/companies.rs:315-323 ; crates/kesh-api/src/routes/bank_accounts.rs:260-294] +- [Source: crates/kesh-reconciliation/src/manual.rs:67] +- [Source: frontend/src/lib/features/reconciliation/ManualMatchModal.svelte:65-69, :126-131, :157-163 ; + frontend/src/lib/features/reconciliation/ReconciliationProposals.svelte:26-34, :75-90, :330-348 ; + frontend/src/lib/features/reconciliation/ReconciliationProposals.test.ts:20, :29 ; + frontend/src/routes/(app)/reconciliation/+page.svelte:70-72 ; + frontend/src/lib/features/reconciliation/TransactionSplitModal.svelte:71-72 ; + frontend/src/lib/features/reconciliation/rules/RuleFormModal.svelte:44-57 ; + frontend/src/lib/features/accounts/account-options.ts:58-74] +- [Source: crates/kesh-api/tests/reconciliation_rules_e2e.rs:1255, :1360 ; crates/kesh-api/tests/reconciliation_e2e.rs:2579 ; + crates/kesh-api/tests/products_revenue_account_e2e.rs:254-260] +- [Source: docs/manual/fr/user-manual.tex:1536-1586 ; docs/api-external.md:303-307 ; crates/kesh-i18n/locales/fr-CH/messages.ftl:36] +- [Source: `15-5b-gardes-surfaces-neuves.md` AC1, AC4, AC5, AC7, `:323-330`, T6 ; `15-5c-rapprochement-libelles-et-manuel.md` + AC1, T3 (dépôt principal) ; `15-6b-contrepartie-distincte-de-la-creance.md` (helper commun)] +- [Source: issue #524 ; `epic-15-choix-autonomes.md` C-15-6-6, C-15-6-9, C-15-6-23, C-15-6-28] + +## Dev Agent Record + +### Agent Model Used + +### Debug Log References + +### Completion Notes List + +### File List + +## Change Log + +- 2026-10-08 — Création à la validation P1 de la 15-6b (findings F1 = R3 et F4), en autonomie : + #524 sort de la 15-6b, qui dépassait cinq modules avec lui. Choix C-15-6-9. **9 AC, 6 tâches + (T0–T5), 5 tests.** Statut `ready-for-dev`. Validation : à lancer. +- 2026-10-08 — **Validation P1** (Opus 5.5, lentilles R et F ; prompt `15-6d-validate-prompt-p1.md`). + R : 0 CRITICAL, 0 HIGH, 3 MEDIUM, 3 LOW ; F : 0 CRITICAL, 0 HIGH, 3 MEDIUM, 4 LOW (R-1 = F3, + R-2 ⊃ F5, R-3 ≈ F4, R-5 = F7). Tous appliqués, décisions de l'orchestrateur comprises : + - **MEDIUM F1** — l'égalité avec le compte de banque est testée **avant** le 404 et + `ACCOUNT_NOT_POSTABLE`, comme le flux ventilé : même réponse sur tous les chemins, plus de + dépendance d'ordre envers la 15-5b ; AC1, AC2, AC3, test 2 inversé. Choix C-15-6-23, qui révise + l'ordre de C-15-6-9. + - **MEDIUM F2** — `get_proposals` ne propose plus une règle sur le compte de banque, qui masquait une + règle suivante (AC2 bis, test 6). + - **MEDIUM R-1 = F3** — câblage `ReconciliationProposals` → `ManualMatchModal` spécifié (effet + dépendant de `bankAccountId`, garde de génération) et testé (test 8), mock de `bank-accounts.api` + dans le fichier existant, repli écrit (chargement, échec, compte non lié ou archivé → aucun + filtrage). + - **MEDIUM R-3** — filtre local d'une condition, sans conditionnel sur la 15-6b (AC4). + - **MEDIUM R-2 (⊃ F5)** — « aucune écriture / aucun audit » asserté (tests 1, 4) ; ordre côté règle + testé (test 5) ; transaction déjà rapprochée → 400, écrit et testé (AC1, test 3). + - **LOW** — R-4 : phrase du manuel pour les trois flux, ligne des règles, « comme pour un + éclatement » retiré ; R-5 = F7 : guide sans section manuel/ventilation → « rien à ajouter » ; + R-6 : libellé générique dit et assumé (AC6) ; F4 : écran de ventilation non filtré, angle mort + écrit ; F6 : course résiduelle de `post_manual` écrite. + - Signal de découpage : sans objet (lentille F : `kesh-api` rapprochement, `frontend` + rapprochement, docs — sous le seuil ; aucun défaut recyclé, première passe). + - Décompte après passe : **10 AC (AC1–AC9 et AC2 bis), 6 tâches (T0–T5), 9 tests** (6 backend, + 3 fichiers Vitest). +- 2026-10-08 — **Validation P2** (Opus 5.5, lentilles R et F ; prompt `15-6d-validate-prompt-p2.md`). + R : 0 CRITICAL, 0 HIGH, 1 MEDIUM, 5 LOW ; F : 0 CRITICAL, 0 HIGH, 2 MEDIUM, 7 LOW (R-1 = F-4, + R-2 = F-3, R-4 = F-7). Tous appliqués, décisions de l'orchestrateur comprises : + - **MEDIUM R-1 (= F-4 LOW)** — `accept_one_rule` reçoit le contrôle du compte de banque actif (étape + de la « Step c » d'`accept_one_split`), **avant** la garde d'égalité : même réponse sur les quatre + chemins, y compris compte de banque archivé (AC2, AC3) ; AC1 rectifié (le flux ventilé compare + **après** ce contrôle, avant la validation des contreparties) ; test 7 neuf. La voie « angle mort » + proposée par F-4 est écartée. Choix C-15-6-28. + - **MEDIUM F-2** — les deux gardes ventilées, sur lesquelles repose l'ordre de C-15-6-23, sont figées + par deux témoins (tests 8 et 9, `post_split` et `accept_one_split`). + - **MEDIUM F-1** — AC7 et T0 : le grep de `docs/api-external.md` est refait sur l'état rebasé ; la + phrase que la 15-5b ajoute sur les routes manuelle et ventilée est complétée par le refus de cette + story, qui précède le 404. *(Corrige la ligne R-5 = F7 de la P1 : « rien à ajouter » n'était vrai + qu'avant la 15-5b.)* + - **LOW** — F-3 = R-2 : angle mort « course résiduelle » retiré ; la valeur comparée est celle qui + construit l'écriture, aucun verrou à ajouter (Dev Notes) ; F-6 : garde de génération et sous-cas + « changement de `bankAccountId` » retirés, la page remonte le composant (`{#key}`) ; R-3 : + `withCurrentAccount` en édition dit tel quel (AC5, test 12 « en création », angle mort) ; R-4 = + F-7 : helper `set_account_not_postable` décrit sans sous-compte, emplacement repéré en T0 ; R-5 : + tests 4 et 5 construisent leur proposition, règle créée avant le passage en non imputable, + parenthèse sur la création retirée ; R-6 : la 15-5c citée en en-tête et en T0 pour les deux + fichiers d'écran ; F-5 : prémisse corrigée (type imposé, classe non) ; F-8 : commentaire du site + d'appel de la 15-5b mis à jour, doc-comment de `rules.rs` laissé (choix écrit), demi-phrase du + guide sur la règle non proposée ; F-9 : message du repli d'écran en français, angle mort écrit. + - **Helper commun** (décision de l'orchestrateur, finding F3 de la P3 de la 15-6b) : la 15-6d emprunte + la **comparaison** du helper que la 15-6b pose dans `invoice_settlements` (nom indicatif + `ensure_not_claim_account`) ; la construction du refus reste celle du flux ventilé, ramenée à deux + fonctions locales (AC3). Ordre : après la 15-6b. Choix C-15-6-28. + - Signal de découpage (amendement D5) : **non levé**. Sévérité P1 → P2 : MEDIUM → MEDIUM, mais les + défauts sont **distincts** de ceux de la P1 et ne viennent pas de sa remédiation, sauf F-1, qui + prolonge une affirmation de la P1 sans la recycler ; modules : `kesh-api` (rapprochement), frontend + (rapprochement, règles), docs, tests `kesh-api` — sous le seuil. Déclaré au Project Lead par ce + Change Log. + - **Renumérotation** : les tests Vitest 7, 8, 9 de la P1 deviennent 10, 11, 12 (les numéros cités par + l'entrée P1 ci-dessus sont ceux d'alors). + - Décompte après passe : **10 AC (AC1–AC9 et AC2 bis), 6 tâches (T0–T5), 12 tests** (9 backend dont + 2 témoins, 3 fichiers Vitest). +- 2026-10-08 — **Validation P3 — clôture de la validation** (Sonnet, lentilles R et F ; prompt + `15-6d-validate-prompt-p3.md` ; remédiation Opus 5.5). R : 0 CRITICAL, 0 HIGH, 0 MEDIUM, 4 LOW ; F : + 0 CRITICAL, 0 HIGH, 0 MEDIUM, 6 LOW (R2 = F3-3). Axes déclarés exercés par les deux lentilles + (numéros de ligne revérifiés au code, inventaire des appelants du constructeur d'écriture et de + `active_account_ids`, ordre des contrôles sur les quatre chemins, verrous, tests existants, frontend, + i18n, manuel `.tex` et PDF aplati, guide, CHANGELOG, règle de découpage, registre) et non exercés + (aucune exécution ; fiches 15-5b/15-5c non rebasées). Tous les LOW appliqués (choix C-15-6-31) : + - R1 : la nouvelle étape de l'AC2 est **atteignable depuis l'écran** (compte lié archivé avant + l'ouverture ; `get_proposals` ne le contrôle pas) — phrase corrigée, angle mort écrit, aucun + contrôle ajouté ; + - R2 = F3-3 : test 7 — règles créées d'abord, compte lié archivé ensuite ; + - R3 : Dev Notes — « ne déplace aucun refus » sauf le changement délibéré de l'AC2 ; + - R4 : bandeau — C-15-6-28 parmi les choix propres ; + - F3-1 : le registre ne se réécrit pas ; C-15-6-31 dit que C-15-6-28 **révise** C-15-6-23 (garde de + génération, course résiduelle) ; + - F3-2 : texte du refus selon le champ, signature de la fonction de refus (AC1, AC3) ; tests 1 et 8 + sur le code seul ; + - F3-4 : le manuel n'affirme sans réserve que le refus (AC8) ; + - F3-5 : `### Modifié` au CHANGELOG pour le changement délibéré de l'AC2 ; section `[0.13.0]` à + revérifier en T0 (AC9) ; + - F3-6 : câblage de l'écran vu seulement sous mock, angle mort écrit. + - Propagation : jetons `Atteignable seulement`, `ne déplace aucun refus`, `Kesh ne le propose pas`, + `ne peut pas être le compte ledger banque` grepés sur la fiche — aucun résidu hors citations. + - **Trend complet** (F + R) : P1 (Opus 5.5, lentilles R et F) 0 HIGH, 6 MEDIUM, 7 LOW → P2 (Opus 5.5, + lentilles R et F) 0 HIGH, 3 MEDIUM, 12 LOW → P3 (Sonnet, lentilles R et F) 0 HIGH, 0 MEDIUM, + 10 LOW. Aucun CRITICAL ni HIGH sur l'ensemble. Reclassements : aucun. Signal de découpage : jamais + levé (sous le seuil de cinq modules, défauts distincts d'une passe à l'autre). + - **Validation close** : 0 au-dessus de LOW. Décompte final : **10 AC (AC1–AC9 et AC2 bis), + 6 tâches (T0–T5), 12 tests** (9 backend dont 2 témoins, 3 fichiers Vitest) — inchangé par la P3. diff --git a/_bmad-output/implementation-artifacts/15-6d-validate-prompt-p1.md b/_bmad-output/implementation-artifacts/15-6d-validate-prompt-p1.md new file mode 100644 index 00000000..14ba0d2b --- /dev/null +++ b/_bmad-output/implementation-artifacts/15-6d-validate-prompt-p1.md @@ -0,0 +1,35 @@ +# Prompt — validation P1 de la spec, Story 15-6d + +*Versionné le 2026-10-08. Deux lentilles (Sonnet), contexte frais chacune, en lecture seule. Rotation (décision D6 de la +rétro 25) : passes complètes Sonnet ↔ Opus ; Haiku réservé à une passe ciblée de fin de boucle.* + +Dépôt `/home/gcorbaz/devel/kesh-15-6`, branche `story/15-6-creance-juste-avoir-reglement`. **Fiche** : `_bmad-output/implementation-artifacts/15-6d-contrepartie-distincte-de-la-banque.md`. C'est la première passe : la fiche n'a pas encore été remédiée. +Issues, par `gh api repos/guycorbaz/kesh/issues/N` (et `/comments`) — `gh issue view` échoue ici : 524. +Choix autonomes : `_bmad-output/implementation-artifacts/epic-15-choix-autonomes.md`. Règles : `CLAUDE.md`. Fiche d'index 15-6 ; sœurs 15-6a/b/c/d. Les fiches 15-5a/b/c du dépôt principal (`/home/gcorbaz/devel/kesh/_bmad-output/implementation-artifacts/15-5*.md`, lecture seule) touchent des fichiers voisins et seront mergées AVANT 15-6b/c/d : vérifie la frontière. Elle est née du découpage de la 15-6b en P1 (commit cb54ff72). +Aucun code de la story n'est encore écrit. + +## Lentilles + +- **R — Auditeur d'acceptation** : applique `.claude/skills/bmad-create-story/checklist.md`. Chaque phrase des issues couverte par un AC testable, chaque AC par une tâche, chaque tâche par un test ; implémentable sans deviner (fonctions, fichiers, codes d'erreur, clés i18n 4 locales, replis Svelte) ; faits cités revérifiés au code (`grep -nF`) ; recompte des AC et tâches. +- **F — Full-scope adversary** : la conception elle-même. Les chemins de code qui écrivent ou lisent ce que la story + change (inventorier les sites NON résolus, pas énumérer ceux qui marchent) ; transactions, verrous, ordre des + contrôles ; tests existants qui changeront de sens ; frontend qui lit les codes ou champs touchés (`grep -rn` dans + `frontend/src`) ; i18n 4 locales et replis ; le **manuel** (`docs/manual/fr/*.tex` **et le PDF aplati** : + `pdftotext docs/manual/fr/user-manual.pdf - | tr '\n' ' ' | tr -s ' '` vers `target/gate-logs/`) ; + `docs/api-external.md` ; CHANGELOG ; la règle de découpage (§ « Règle de splitting préventif », amendement D5). + +## Ce que tu rends + +Findings numérotés avec sévérité (CRITICAL/HIGH/MEDIUM/LOW), `fichier:ligne` (fiche et code), **preuve** (commande et +sortie, ou code cité relu), correction proposée. Pour tout finding affirmant qu'un code est absent ou présent : la +sortie d'un `grep -nF`. ⛔ **La liste des axes exercés ET non exercés** — un « 0 finding » sans elle ne compte pas. +⛔ **Écris ton rapport complet** dans `target/gate-logs/15-6d-p1-.md` (R ou F ; autorisé), et rends +comme dernier message **seulement** : ce chemin, le bilan par sévérité, et une ligne par finding MEDIUM ou plus. + +## Interdits + +⛔ N'écris aucun fichier du dépôt hors `target/gate-logs/` ; aucune commande qui écrit dans le dépôt ou dans une +base : `scripts/*` (dont `scripts/prepare-release.sh`), `make`, `latexmk`, `git commit`/`add`/`checkout`/`stash`, +`sqlx`, `cargo test`/`nextest`/`build`, `npm run`, `npx`, `gh issue create`/`comment`/`edit`, aucune requête SQL +d'écriture. Autorisés : lecture, `grep`, `sed -n`, `git log`/`show`/`diff`, `gh api` en lecture, `pdftotext` +vers `target/gate-logs/`. diff --git a/_bmad-output/implementation-artifacts/15-6d-validate-prompt-p2.md b/_bmad-output/implementation-artifacts/15-6d-validate-prompt-p2.md new file mode 100644 index 00000000..abd779fa --- /dev/null +++ b/_bmad-output/implementation-artifacts/15-6d-validate-prompt-p2.md @@ -0,0 +1,35 @@ +# Prompt — validation P2 de la spec, Story 15-6d + +*Versionné le 2026-10-08. Deux lentilles (Opus), contexte frais chacune, en lecture seule. Rotation (décision D6 de la +rétro 25) : passes complètes Sonnet ↔ Opus ; Haiku réservé à une passe ciblée de fin de boucle.* + +Dépôt `/home/gcorbaz/devel/kesh-15-6`, branche `story/15-6-creance-juste-avoir-reglement`. **Fiche** : `_bmad-output/implementation-artifacts/15-6d-contrepartie-distincte-de-la-banque.md`. La dernière remédiation est le commit `4964eeb4` (`git show 4964eeb4`) — lis le Change Log de la fiche. +Issues, par `gh api repos/guycorbaz/kesh/issues/N` (et `/comments`) — `gh issue view` échoue ici : 524. +Choix autonomes : `_bmad-output/implementation-artifacts/epic-15-choix-autonomes.md`. Règles : `CLAUDE.md`. Fiche d'index 15-6 ; sœurs 15-6a/b/c/d. Les fiches 15-5a/b/c/d du dépôt principal (`/home/gcorbaz/devel/kesh/_bmad-output/implementation-artifacts/15-5*.md`, lecture seule) seront mergées AVANT : vérifie la frontière. +Aucun code de la story n'est encore écrit. + +## Lentilles + +- **R — Regression hunter** : la dernière remédiation (`git show 4964eeb4`) a-t-elle introduit des défauts ? Contradictions entre AC, tâches, Dev Notes et choix du registre ; frontière avec les fiches sœurs ; faits recopiés sans vérification (numéros de ligne, fonctions, codes, clés i18n) — revérifie-les au code (`grep -nF`) ; recompte des AC et tâches ; implémentable sans deviner ? +- **F — Full-scope adversary** : la conception elle-même. Les chemins de code qui écrivent ou lisent ce que la story + change (inventorier les sites NON résolus, pas énumérer ceux qui marchent) ; transactions, verrous, ordre des + contrôles ; tests existants qui changeront de sens ; frontend qui lit les codes ou champs touchés (`grep -rn` dans + `frontend/src`) ; i18n 4 locales et replis ; le **manuel** (`docs/manual/fr/*.tex` **et le PDF aplati** : + `pdftotext docs/manual/fr/user-manual.pdf - | tr '\n' ' ' | tr -s ' '` vers `target/gate-logs/`) ; + `docs/api-external.md` ; CHANGELOG ; la règle de découpage (§ « Règle de splitting préventif », amendement D5). + +## Ce que tu rends + +Findings numérotés avec sévérité (CRITICAL/HIGH/MEDIUM/LOW), `fichier:ligne` (fiche et code), **preuve** (commande et +sortie, ou code cité relu), correction proposée. Pour tout finding affirmant qu'un code est absent ou présent : la +sortie d'un `grep -nF`. ⛔ **La liste des axes exercés ET non exercés** — un « 0 finding » sans elle ne compte pas. +⛔ **Écris ton rapport complet** dans `target/gate-logs/15-6d-p2-.md` (R ou F ; autorisé), et rends +comme dernier message **seulement** : ce chemin, le bilan par sévérité, et une ligne par finding MEDIUM ou plus. + +## Interdits + +⛔ N'écris aucun fichier du dépôt hors `target/gate-logs/` ; aucune commande qui écrit dans le dépôt ou dans une +base : `scripts/*` (dont `scripts/prepare-release.sh`), `make`, `latexmk`, `git commit`/`add`/`checkout`/`stash`, +`sqlx`, `cargo test`/`nextest`/`build`, `npm run`, `npx`, `gh issue create`/`comment`/`edit`, aucune requête SQL +d'écriture. Autorisés : lecture, `grep`, `sed -n`, `git log`/`show`/`diff`, `gh api` en lecture, `pdftotext` +vers `target/gate-logs/`. diff --git a/_bmad-output/implementation-artifacts/15-6d-validate-prompt-p3.md b/_bmad-output/implementation-artifacts/15-6d-validate-prompt-p3.md new file mode 100644 index 00000000..9b350133 --- /dev/null +++ b/_bmad-output/implementation-artifacts/15-6d-validate-prompt-p3.md @@ -0,0 +1,35 @@ +# Prompt — validation P3 de la spec, Story 15-6d + +*Versionné le 2026-10-08. Deux lentilles (Sonnet), contexte frais chacune, en lecture seule. Rotation (décision D6 de la +rétro 25) : passes complètes Sonnet ↔ Opus ; Haiku réservé à une passe ciblée de fin de boucle.* + +Dépôt `/home/gcorbaz/devel/kesh-15-6`, branche `story/15-6-creance-juste-avoir-reglement`. **Fiche** : `_bmad-output/implementation-artifacts/15-6d-contrepartie-distincte-de-la-banque.md`. La dernière remédiation est le commit `89e8987a` (`git show 89e8987a`) — lis le Change Log de la fiche. +Issues, par `gh api repos/guycorbaz/kesh/issues/N` (et `/comments`) — `gh issue view` échoue ici : 524. +Choix autonomes : `_bmad-output/implementation-artifacts/epic-15-choix-autonomes.md`. Règles : `CLAUDE.md`. Fiche d'index 15-6 ; sœurs 15-6a/b/c/d ; les fiches 15-5a/b/c/d (dépôt principal, lecture seule) sont mergées AVANT. Elle passe désormais APRÈS la 15-6b, dont elle emprunte la comparaison. +Aucun code de la story n'est encore écrit. + +## Lentilles + +- **R — Regression hunter** : la dernière remédiation (`git show 89e8987a`) a-t-elle introduit des défauts ? Contradictions entre AC, tâches, Dev Notes et choix du registre ; frontière avec les fiches sœurs ; faits recopiés sans vérification (numéros de ligne, fonctions, codes, clés i18n) — revérifie-les au code (`grep -nF`) ; recompte des AC et tâches ; implémentable sans deviner ? +- **F — Full-scope adversary** : la conception elle-même. Les chemins de code qui écrivent ou lisent ce que la story + change (inventorier les sites NON résolus, pas énumérer ceux qui marchent) ; transactions, verrous, ordre des + contrôles ; tests existants qui changeront de sens ; frontend qui lit les codes ou champs touchés (`grep -rn` dans + `frontend/src`) ; i18n 4 locales et replis ; le **manuel** (`docs/manual/fr/*.tex` **et le PDF aplati** : + `pdftotext docs/manual/fr/user-manual.pdf - | tr '\n' ' ' | tr -s ' '` vers `target/gate-logs/`) ; + `docs/api-external.md` ; CHANGELOG ; la règle de découpage (§ « Règle de splitting préventif », amendement D5). + +## Ce que tu rends + +Findings numérotés avec sévérité (CRITICAL/HIGH/MEDIUM/LOW), `fichier:ligne` (fiche et code), **preuve** (commande et +sortie, ou code cité relu), correction proposée. Pour tout finding affirmant qu'un code est absent ou présent : la +sortie d'un `grep -nF`. ⛔ **La liste des axes exercés ET non exercés** — un « 0 finding » sans elle ne compte pas. +⛔ **Écris ton rapport complet** dans `target/gate-logs/15-6d-p3-.md` (R ou F ; autorisé), et rends +comme dernier message **seulement** : ce chemin, le bilan par sévérité, et une ligne par finding MEDIUM ou plus. + +## Interdits + +⛔ N'écris aucun fichier du dépôt hors `target/gate-logs/` ; aucune commande qui écrit dans le dépôt ou dans une +base : `scripts/*` (dont `scripts/prepare-release.sh`), `make`, `latexmk`, `git commit`/`add`/`checkout`/`stash`, +`sqlx`, `cargo test`/`nextest`/`build`, `npm run`, `npx`, `gh issue create`/`comment`/`edit`, aucune requête SQL +d'écriture. Autorisés : lecture, `grep`, `sed -n`, `git log`/`show`/`diff`, `gh api` en lecture, `pdftotext` +vers `target/gate-logs/`. diff --git a/_bmad-output/implementation-artifacts/epic-15-choix-autonomes.md b/_bmad-output/implementation-artifacts/epic-15-choix-autonomes.md index 385c4dd4..4f6ce705 100644 --- a/_bmad-output/implementation-artifacts/epic-15-choix-autonomes.md +++ b/_bmad-output/implementation-artifacts/epic-15-choix-autonomes.md @@ -3614,3 +3614,752 @@ l'import (#458–#461). côtés (il aurait omis l'apport de l'autre). - **Réversible** : oui (rebase ; branche poussée). +## C-15-6-1 — 15-6 : découpée d'emblée en trois (a, b, c) + +- **Contexte** : #473 et #474 réunis touchent `kesh-db` (avoir, règlement client, règlement + fournisseur, comptes bancaires, erreurs), `kesh-api` (rapprochement, comptes bancaires, réglages de + facturation, erreurs), `kesh-i18n`, trois écrans et le manuel : bien plus de cinq modules — le + critère de périmètre de la § *Règle de splitting préventif* est franchi avant toute validation. +- **Retenu** : trois sous-stories, comptées en modules métier (la plomberie d'erreur et d'i18n + suit le geste qu'elle sert, comme pour la 15-5a) : + - **15-6a** — l'avoir crédite la créance de la vente (#473) : avoir, règlement, rapprochement + (3 modules) ; elle pose le lecteur partagé du compte de créance. + - **15-6b** — un règlement ne vise pas le compte qu'il solde (#474, cœur) : règlement client, + règlement fournisseur, rapprochement, et les deux écrans de règlement (5 modules). + - **15-6c** — la configuration ne prépare pas l'écriture nulle (#474, voisin) : compte comptable + d'un compte bancaire, réglages de facturation, écran des comptes bancaires (3 modules). +- **Ordre** : a → b (mêmes fichiers) ; c **après le merge de la 15-5b** (mêmes routes + `bank_accounts.rs` et `company_invoice_settings.rs`) ; b de préférence après la 15-5b aussi (le + libellé du nouveau code dans `failed[]` passe par l'écran de #492 que la 15-5b pose). +- **Écartées** : une story unique (règle franchie) ; deux stories « serveur / écrans » (le filtre + d'écran se séparerait de la garde qu'il reflète, et la story serveur dépasserait encore cinq + modules). +- **Réversible** : oui, tant qu'aucune n'est développée. + +## C-15-6-2 — 15-6a : l'avoir lit la créance sur l'écriture de vente ; arrondi et TVA restent aux réglages + +- **Contexte** : l'avoir lit dans les réglages du moment trois comptes que la vente a déjà + mouvementés : créance (#473), compte de différences d'arrondi (même défaut, non signalé), TVA due. + Le compte de produit est déjà recopié par ligne (16-1a, D5). +- **Retenu** : la créance se lit sur l'écriture de vente par un **lecteur partagé** + (`invoice_settlements::sale_receivable_account`) qui remplace aussi les trois copies de la même + requête (règlement, solde du reste, rapprochement). L'avoir ne dépend plus du réglage + `default_receivable_account_id`. +- **Laissés, en angles morts écrits dans la fiche** : + - la **TVA due** reste débitée sur le compte **courant** — c'est la convention écrite du solde du + reste (doc de `write_off_invoice` : « comme l'avoir ») et la changer toucherait le décompte TVA ; + - le **compte d'arrondi** reste lu dans les réglages : le lire sur la vente casserait le refus + nommé de #486 (`a_credit_note_is_refused_when_the_rounding_account_was_archived`, dont le message + renvoie aux réglages) et demanderait un refus neuf « réactivez tel compte » ; l'écart en jeu est + d'au plus 2,5 centimes par facture. **Issue à ouvrir** par l'orchestrateur ; + - le repli sur le produit par défaut des lignes sans compte (D-B2), inchangé. +- **Écartée** : construire l'avoir en miroir ligne à ligne de l'écriture de vente (exact par + construction, mais refonte de la génération, de la garde des comptes archivés de la 16-1a et de + leurs tests, hors de la mesure d'un P1). +- **Réversible** : oui. + +## C-15-6-3 — 15-6b : un refus dédié, sur les deux modes de règlement, après les contrôles existants + +- **Retenu** : nouvelle variante `DbError::SettlementCounterpartyIsClaimAccount`, code stable + `SETTLEMENT_COUNTERPARTY_IS_CLAIM_ACCOUNT` (HTTP 400), `details = { accountId, accountNumber, + claim: "receivable" | "payable" }`, message 4 locales (deux clés : client, fournisseur). Elle + s'applique au **virement** autant qu'au **compte interne** (un compte bancaire lié au 1100 produit la + même écriture nulle), au règlement client, au règlement fournisseur et à l'acceptation d'un + rapprochement de facture (`failed[]`, même code). Elle vient **après** les refus existants (404, + configuration, inactif/non imputable) : leur ordre ne change pas. +- **Résolus par le type, sans garde neuve** : compte d'arrondi et comptes des natures de solde, + revérifiés « charge ou produit » au moment d'écrire — jamais 1100 ni 2000. +- **Écartées** : `InvalidInput("…")` (code générique `INVALID_INPUT`, pas de `details`, contraire à + la consigne) ; `ACCOUNT_NOT_POSTABLE` de la 15-5a (autre motif : 1100 est imputable). +- **Réversible** : oui. + +## C-15-6-4 — 15-6b : le filtre d'écran se fait par rôle, sans champ d'API nouveau + +- **Contexte** : l'écran ne connaît pas le compte de créance d'une facture ; l'échéancier ouvre le + même dialogue depuis une liste. +- **Retenu** : le dialogue de règlement client écarte les comptes de **rôle `Receivable`** et les + comptes bancaires liés à un tel compte ; la fiche fournisseur, ceux de rôle `Payable`. Les rôles + sont des singletons par société (`uq_accounts_company_singleton_role`) et les réglages en sont + dérivés. La garde serveur reste exacte (compte de l'écriture de vente) ; l'écart possible — réglage + changé depuis — se solde par un refus clair, non par une écriture fausse. +- **Écartée** : exposer `receivableAccountId` sur la fiche et sur les lignes de l'échéancier (deux + DTO, une sous-requête de plus sur une liste tenue à parité par `invoice_amount_due_parity.rs`). +- **Réversible** : oui. + +## C-15-6-5 — 15-6c : la configuration refuse le couple, dans les deux sens, avec exemption « inchangé » + +- **Retenu** : le compte comptable d'un compte bancaire ne peut être ni le compte débiteurs ni le + compte créanciers **des réglages** ; symétriquement, les réglages refusent un compte débiteurs ou + créanciers lié à un compte bancaire non archivé. Exemption quand la valeur ne change pas (patron + C4/C10 de la 15-5b) ; contrôle des routes bancaires dans la transaction, comme la 15-5b. Deux + codes : `BANK_ACCOUNT_LEDGER_IS_CLAIM_ACCOUNT`, `CLAIM_ACCOUNT_LINKED_TO_BANK_ACCOUNT`. +- **Assumé** : la course entre les deux gestes (deux administrateurs, deux tables) n'est pas + verrouillée ; la garde à l'usage de la 15-6b reste le filet. Écrit dans la fiche. +- **Écartée** : ne garder que l'usage (la configuration fautive resterait offerte par l'écran, et + chaque rapprochement échouerait ensuite un par un). +- **Réversible** : oui. + +## C-15-6-6 — Voisin relevé, hors périmètre : la contrepartie égale au compte de banque + +- **Constat (lu au code, non exécuté)** : le rapprochement **ventilé** refuse une contrepartie égale + au compte de banque (`reconciliation.rs:1941-1950`, `:3462-3474`) ; le rapprochement **manuel** + (`post_manual`, `:2942`) et l'acceptation **par règle** (`accept_one_rule`, `:2207`) ne le font pas, + et `build_journal_entry_for_counterparty` (`kesh-reconciliation/src/manual.rs:67`) non plus — même + écriture nulle `D banque / C banque`. +- **Retenu** : hors de #474 (le compte soldé n'est pas une créance) ; signalé à l'orchestrateur pour + une issue, non traité ici. +- **Réversible** : sans objet. + +## C-15-6-7 — 15-6a absorbe #523 : l'arrondi de l'avoir se lit sur la vente ; un compte archivé est refusé par `create_in_tx` (révise C-15-6-2) + +- **Contexte** : validation P1 de la 15-6a (findings F1 = R1, HIGH). C-15-6-2 laissait le compte + d'arrondi de l'avoir aux réglages pour ne pas casser le refus nommé de #486 ; l'orchestrateur a + ouvert #523 et décidé de le traiter dans la 15-6a (`closes #473, closes #523`). +- **Retenu** : un second lecteur de l'écriture de vente, sur sa **dernière** ligne + (`ORDER BY jel.id DESC LIMIT 1`), **recoupée** avec l'arrondi figé sur la facture (sens et valeur + absolue), sinon `DbError::Invariant` ; appelé seulement si l'arrondi ≠ 0. Compte de la vente archivé + depuis : **voie (b)** — `journal_entries::create_in_tx` refuse en `INACTIVE_OR_INVALID_ACCOUNTS`, + comme pour la créance. Le test `invoices_validate_vat.rs:846` change d'assertion, délibérément, et + la fiche l'écrit comme tel. Trois tests neufs : réglage redésigné, réglage vidé, recoupement. +- **Écartées** : (a) garder `RoundingAccountNotConfigured { Issuance }` en vérifiant le compte de la + vente — son message renvoie aux paramètres, remède **faux** puisque l'avoir ne les lit plus ; (c) une + variante neuve nommant le compte de la vente — un refus de plus pour un cas que le refus générique + existant couvre déjà, comme pour la créance ; réutiliser `usable_designated_account` — contraire à la + doctrine « mêmes comptes que l'origine, seule l'inactivité bloque ». +- **Réversible** : oui, tant que la 15-6a n'est pas développée. + +## C-15-6-8 — 15-6a : la TVA due de l'avoir reste un angle mort, tracé par #525 ; le manuel dit la limite + +- **Contexte** : finding F3 (MEDIUM) de la 15-6a — l'avoir débite la TVA due sur le compte **courant** + des réglages ; même classe que #473. L'orchestrateur a ouvert **#525** (P1, jalon de la TVA, report + assumé). +- **Retenu** : la fiche cite #525 comme angle mort tracé. Le manuel **ne présente pas** l'écriture + comme juste : il dit que la TVA de l'avoir suit le compte de TVA due actuellement désigné, et que + changer ce réglage entre une facture et son avoir est une limite connue de cette version. +- **Écartée** : le traiter dans la 15-6a (toucherait le décompte TVA et la convention partagée avec le + solde du reste, `vat_payable_account_for_write` — c'est l'objet du jalon de la TVA). +- **Réversible** : oui. + +## C-15-6-9 — #524 devient la sous-story 15-6d ; même refus que le flux ventilé existant (révise C-15-6-6) + +- **Contexte** : validation P1 de la 15-6b — finding F1 (HIGH) : #524 doit être traité ; finding F4 + (MEDIUM) : avec lui et les lots de paiement, la 15-6b dépasse cinq modules. C-15-6-6 le laissait + hors périmètre ; **il est retiré** par la présente entrée. +- **Retenu** : sous-story **15-6d-contrepartie-distincte-de-la-banque** (`closes #524`). + `post_manual` et `accept_one_rule` refusent une contrepartie égale au compte de la banque **avec le + même refus que le flux ventilé existant** : manuel → `AppError::Validation` comme `post_split` (400, + message français en dur — limite assumée de ce flux existant, écrite comme telle) ; règle → + `FailedProposal VALIDATION_ERROR`, `details.reason = "counterparty_equals_bank_ledger"`, HTTP 200, + comme `accept_one_split`. Ordre : 404, puis `ACCOUNT_NOT_POSTABLE` (15-5b), puis ce refus. L'écran du + rapprochement manuel ne propose pas le compte de la banque (test Vitest). Dépend de la 15-5b. +- **Écartées** : un code dédié et traduit (divergerait des deux chemins ventilés existants ; s'il + devient souhaitable, il vaudra pour les quatre chemins, story à part) ; garder #524 dans la 15-6b + (règle de découpage franchie). +- **Réversible** : oui, tant qu'elle n'est pas développée. + +## C-15-6-10 — 15-6b : les lots de paiement refusent le compte créanciers à la création, et gardent le refus à la confirmation + +- **Contexte** : findings F2 (HIGH) / R4 de la 15-6b — `confirm_batch` est le troisième appelant de + `pay_in_tx`. Avec la seule garde de `pay_in_tx`, un compte bancaire lié au compte créanciers produit + un lot **et son fichier pain.001**, puis un lot inconfirmable — alors que le fichier est peut-être + déjà à la banque. +- **Retenu** : `create_batch` compare, **par facture**, la dette de l'écriture d'achat au compte lié du + compte bancaire source **avant** de produire le fichier : égalité → la facture va dans `failed[]` du + lot (`SETTLEMENT_COUNTERPARTY_IS_CLAIM_ACCOUNT`, `details` avec `claim: "payable"`), patron batch ; + aucune facture retenue → aucun lot, aucun fichier. `confirm_batch` garde le refus de `pay_in_tx` + (le compte bancaire a pu être relié entre-temps) : 400, lot toujours `generated`. Libellé à l'écran + des lots, manuel (§ pain.001), `docs/api-external.md`. +- **Écartées** : refuser le lot entier en erreur globale (contraire au patron batch : une facture + fautive ne doit pas bloquer les autres) ; ne garder que la confirmation (le défaut même du finding). +- **Réversible** : oui. + +## C-15-6-11 — 15-6b : les gardes réutilisent les lecteurs ; côté fournisseur, un lecteur sœur + +- **Contexte** : finding R7 (MEDIUM) de la 15-6b — la dépendance au lecteur de la 15-6a était déclarée + mais inemployée. +- **Retenu** : côté client (`settle_invoice`, `accept_one_invoice`), la garde compare l'identifiant que + le lecteur `sale_receivable_account` de la 15-6a a **déjà** rendu — aucune requête neuve. Côté + fournisseur, la requête est **différente** (ligne de **crédit** de l'écriture d'achat **et** le TTC, + `supplier_invoices.rs:582-593`) : elle est extraite en lecteur sœur `purchase_payable_line`, partagé + par `pay_in_tx` et la création de lot (C-15-6-10). +- **Écartée** : faire lire la dette par le lecteur de la créance (autre sens, autre montant). +- **Réversible** : oui. + +## C-15-6-12 — #492 relève de la 15-5c, non de la 15-5b (révise l'ordre de C-15-6-1) + +- **Contexte** : findings F3 = R5 de la 15-6b. C-15-6-1 disait « le libellé du nouveau code dans + `failed[]` passe par l'écran de #492 que la 15-5b pose » ; depuis C15, #492 est dans la **15-5c** + (`frontend/src/lib/features/reconciliation/failed-proposal-label.ts`). +- **Retenu** : la 15-6b est « de préférence après la 15-5c » ; son développeur ajoute le code au module + si la 15-5c est mergée, sinon il le signale (pas de second mécanisme). Fiches 15-6 et 15-6b corrigées. +- **Réversible** : sans objet (correction d'une référence). + +## C-15-6-13 — 15-6c : contrôles dans la transaction, et les deux gestes se sérialisent (révise C-15-6-5) + +- **Contexte** : findings F1 et F2 (HIGH) de la 15-6c — le contrôle des réglages se faisait hors + transaction, contre une valeur lue sans verrou ; la course entre les deux gestes était déclarée en + angle mort alors qu'elle se ferme à bon marché. +- **Retenu** : deux lecteurs uniques — `company_invoice_settings::claim_accounts_for_share` (`FOR + SHARE` sur la ligne des réglages) et `bank_accounts::first_active_bank_account_linked_to` (`FOR + SHARE`, premier compte bancaire non archivé par `id`) — et un **ordre de verrous unique** : la ligne + des réglages d'abord, les comptes bancaires ensuite. Côté compte bancaire, la lecture des réglages + est la première de la transaction (après le verrou sentinelle, avant tout `FOR UPDATE` de ligne + bancaire) ; côté réglages, `company_invoice_settings::update` lit `before` en `FOR UPDATE`, compare + contre lui (exemption « inchangé »), puis interroge les comptes bancaires. **L'angle mort « course » + est supprimé.** Variantes nommées : `DbError::BankAccountLedgerIsClaimAccount`, + `DbError::ClaimAccountLinkedToBankAccount`. **Ordre des erreurs** : routes bancaires — celui de C10 + (le refus neuf en dernier, **après** le 409 de version et le non-imputable) ; route des réglages — + forme → refus → 409, tous les 400 de cette route précédant déjà le 409. +- **Écartées** : contrôle dans le handler (valeur lue hors verrou, F1) ; verrou sentinelle `companies` + sur les quatre gestes (aurait sérialisé aussi, mais le PATCH et le PUT des réglages ne le prennent + pas, et la sentinelle sérialise bien plus que ces deux gestes) ; lire les réglages **après** la + ligne bancaire (ordre inverse → interblocage entre les deux gestes). +- **Réversible** : oui, tant qu'elle n'est pas développée. + +## C-15-6-14 — 15-6b/15-6c : des fonctions d'écran par ensemble d'identifiants ; la 15-6b les nourrit par le rôle, la 15-6c par les réglages (précise C-15-6-4) + +- **Contexte** : finding F3 (MEDIUM) de la 15-6c — un écran filtré par **rôle** et un serveur qui + refuse selon les **réglages** divergent ; finding R2 (MEDIUM) — la signature du helper de la 15-6b + (un rôle) ne servait pas la 15-6c (deux comptes, et pas par rôle). +- **Retenu** : trois fonctions pures dans `account-options.ts`, posées par la 15-6b : + `accountIdsWithRole`, `withoutAccountIds`, `bankAccountsNotLinkedTo`, et des identifiants calculés + sur la liste **complète** des comptes. La 15-6b les nourrit par le rôle (C-15-6-4 maintenu : le + dialogue de règlement ne connaît pas la créance de la facture) ; la 15-6c par les identifiants + **désignés dans les réglages** (`getInvoiceSettings`, lisible par tout rôle), comme son serveur, et + l'écran des réglages par les comptes liés à un compte bancaire non archivé. +- **Écartée** : un helper par story (deux filtres voisins dans le même module, qui dériveraient). +- **Réversible** : oui. + +## C-15-6-15 — 15-6c : la spec E2E qui liait un compte bancaire au 1100 le lie au 1000 + +- **Contexte** : finding R4 (MEDIUM) de la 15-6c. Vérifié au code : le préréglage E2E `with-company` + (`seed_accounting_company`, `test_fixtures.rs:88-107`) désigne **1100 « Banque CI »** comme compte + débiteurs ; `bank-account-journal-link.spec.ts:105-119` lie un compte bancaire au 1100 par l'écran. + Après la 15-6c, le menu ne le propose plus et le serveur le refuse. Le seed de dev + (`scripts/seed-dev-db.sql`) ne crée pas de réglages : non concerné. +- **Retenu** : la spec lie au **1000 « Caisse CI »** (actif, imputable, non désigné comme créance). +- **Écartée** : changer la fixture partagée (désigner un autre compte débiteurs) — elle sert des + 204 appels dans 41 fichiers (`grep -rho "seed_accounting_company(" crates/`, relevé le 2026-10-08), + dont beaucoup supposent la créance sur 1100. +- **Réversible** : oui. + +## C-15-6-16 — 15-6a : les comptes lus sur la vente sont verrouillés ; un compte archivé est refusé par le refus nommé de la contre-passation (révise la voie (b) de C-15-6-7) + +- **Contexte** : validation P2 de la 15-6a — R-2 = F-1 (MEDIUM) : remplacer `rounding_account_for_write` + par un lecteur sans verrou retirait le `FOR UPDATE` que #486 posait sur le compte d'arrondi ; la garde + `active` de `create_in_tx` est une lecture simple, qui sous REPEATABLE READ ne voit pas un archivage + validé entre-temps. R-3 = F-2 (MEDIUM) : la voie (b) rendait un refus anonyme + (`INACTIVE_OR_INVALID_ACCOUNTS`), que la 6 ter de la même fonction avait justement écarté pour les + comptes de produit. Décisions de l'orchestrateur : fermer la course, nommer le compte par la forme + la plus simple. +- **Retenu** : après les deux lecteurs et la garde 6 ter, `create_credit_note` verrouille en une + instruction les lignes `accounts` de la créance et de l'arrondi de la vente + (`SELECT id, number, active … ORDER BY id FOR UPDATE`, **sans** filtre `active`) et lit l'état actif + **dans** cette lecture verrouillante. Un compte archivé → `DbError::ReversalAccountsArchived` + (400 `ACCOUNT_ARCHIVED`, `details.rejected[]`, clé `journal-entries-reverse-account-archived` + existante) : l'avoir est une contre-passation, c'est le refus que la contre-passation rend pour le + même cas. Aucune variante, aucun code, aucune clé neuve. La garde de `create_in_tx` reste, en filet. + Ordre des verrous : facture → règlements → avoir existant → comptes (celui de `validate_invoice`). + Test neuf de la course (attente puis refus nommé). Manuel : `keshwarning` au § *Avoirs*, + avertissement du plan comptable étendu, phrase du manuel d'administration sur l'arrondi. +- **Concilie** deux consignes : « le refus reste celui de `create_in_tx` » (pas de refus neuf — tenu : + aucun code neuf) et « nommer le compte » (tenu par le refus nommé existant). Lire `active` par la + garde de `create_in_tx` après le verrou n'aurait pas suffi : sa lecture simple rend l'instantané. +- **Écartées** : une variante neuve propre à l'avoir (un refus de plus pour un cas que la + contre-passation nomme déjà) ; garder `INACTIVE_OR_INVALID_ACCOUNTS` anonyme et le compenser au + manuel (l'utilisateur ne saurait pas quel compte réactiver) ; assumer la course en dette LOW (un site + aujourd'hui verrouillé l'aurait perdu). +- **Conséquence assumée** : deux avoirs simultanés sur des factures de même compte débiteurs se + sérialisent sur la ligne de ce compte, le temps d'une transaction. Le code du refus change pour + l'arrondi (`ROUNDING_ACCOUNT_NOT_CONFIGURED` → `ACCOUNT_ARCHIVED`) : l'esprit de la contrainte de + #523 (refus nommé) est tenu, sa lettre non — à commenter sur #523. +- **Réversible** : oui, tant que la 15-6a n'est pas développée. + +## C-15-6-17 — 15-6b : les écrans renoncent aux comptes archivés ; clés d'écran conformes au lint + +- **Contexte** : validation P2 de la 15-6b — R-1 = F1 (MEDIUM) : l'AC8 promettait d'écarter un compte + bancaire lié à un compte de rôle `Receivable` **archivé**, mais les trois écrans chargent le plan sans + les archivés ; F2 (MEDIUM) : la clé `invoice-settle-no-eligible-bank-account` faisait rougir + `lint-i18n-ownership` dans `src/lib/features/invoices/`. +- **Retenu** : l'AC8 **renonce** aux archivés — un compte archivé ne peut recevoir aucune écriture (la + garde `active` de `create_in_tx` est inconditionnelle), la garde serveur suffit ; les ids se + calculent avant le filtre `active && postable` (cas non imputable, qui lui arrive à l'écran). Clés + renommées `invoices-settle-no-eligible-bank-account` et `supplier-invoices-pay-no-eligible-bank-account` + (alignée sur ses voisines). +- **Écartées** : charger `fetchAccounts(true)` dans trois écrans pour un cas que le serveur refuse + déjà ; allonger `KNOWN_VIOLATIONS` (dette #30) au lieu de nommer la clé correctement. +- **Réversible** : oui. + +## C-15-6-18 — 15-6b : les comptes d'écart sont comparés à la créance par identifiant (révise le « résolu par le type » de C-15-6-3) + +- **Contexte** : finding F3 (MEDIUM) de la 15-6b — le « résolu par le type » reposait sur un invariant + qu'un geste ultérieur défait : le type d'un compte porteur d'écritures se change avec confirmation + (`accounts.rs:497-512`, `confirm_retype`), et le 1100 devenu `Expense` porte toujours la créance des + ventes antérieures ; `write_off_invoice` écrirait `D 1100 / C 1100`, l'arrondi du règlement de même. +- **Retenu** : le même refus `SETTLEMENT_COUNTERPARTY_IS_CLAIM_ACCOUNT`, par comparaison + d'identifiants avec la créance lue sur la vente, garde le compte d'arrondi de `settle_invoice` (4bis) + et de `accept_one_invoice` (c-bis) et les comptes de `write_off_invoice` (nature, reste d'arrondi, TVA + due) — AC3 bis, trois tests. Le contrôle de type existant reste. Le test qui figeait le « résolu par + le type » est retiré. +- **Écartée** : écrire le chemin du changement de type en angle mort avec une issue (trois `if` sur des + identifiants déjà lus coûtent moins qu'une dette tracée). +- **Réversible** : oui. + +## C-15-6-19 — 15-6b : lots — refus contextualisé à la confirmation, écriture d'achat malformée en `failed[]`, écran de création non filtré + +- **Contexte** : findings F4 (MEDIUM), R-7 = F10 et F6 (LOW) de la 15-6b. Le refus à la confirmation + d'un lot disait « par un autre compte » alors que l'utilisateur n'en choisit aucun, et que le fichier + pain.001 a pu être exécuté par la banque ; le cas « écriture d'achat sans ligne de crédit » à la + création n'était pas dit ; l'écran de création d'un lot propose un compte bancaire lié au compte + créanciers. +- **Retenu** : la variante porte un contexte de lot optionnel (`SettlementBatchContext`), rempli par + `confirm_batch` ; clé propre `error-settlement-counterparty-is-payable-in-batch` qui dit les deux + issues (relier de nouveau le compte bancaire puis confirmer, ou annuler le lot et régler depuis la + fiche), `details.paymentBatchId` / `supplierInvoiceId`, manuel ; test du remède. Le refus à la + création reste la défense principale. À la création, une écriture d'achat sans ligne de crédit est + une donnée de **cette** facture : item `failed[]` `SUPPLIER_INVOICE_PURCHASE_ENTRY_MALFORMED` + (patron `INVOICE_SALE_ENTRY_MALFORMED`, § *Pattern batch*), libellé à l'écran des lots. L'écran de + création n'est **pas** filtré : il ne charge pas le plan comptable, et le refus y arrive par facture, + libellé, avant tout fichier — angle mort écrit. +- **Écartées** : le message du règlement unitaire à la confirmation (remède faux) ; une erreur globale + (500) pour l'écriture d'achat malformée (contraire au patron batch) ; charger le plan comptable sur + l'écran de création pour un filtre que le serveur double. +- **Réversible** : oui. + +## C-15-6-20 — 15-6b : décompte des modules refait, signal D5 déclaré, pas de découpage + +- **Contexte** : finding F5 (MEDIUM) de la 15-6b — la P1 avait ajouté les lots sans recompter. +- **Retenu** : décompte écrit dans la fiche — huit modules au barème des fichiers, cinq au barème du + geste (patron de la 15-5a). Signal **déclaré au Project Lead** ; pas de découpage : les gestes ne sont + pas indépendants (une variante, un `ClaimSide`, un lecteur sœur partagé par `pay_in_tx` et les lots, + trois fonctions d'écran ; les lots n'appellent que la garde de `pay_in_tx`), et aucun défaut de la P2 + ne recycle un défaut de la P1. +- **Écartée** : sortir les lots en sous-story (elle naîtrait sans sa garde, ou dupliquerait la + plomberie d'erreur et d'i18n). +- **Réversible** : oui — un découpage reste possible tant que la story n'est pas développée, sur + arbitrage de Guy. + +## C-15-6-21 — 15-6c : `LOCK IN SHARE MODE` ; route des réglages, le 409 d'abord ; son code n'est pas pour les intégrateurs (révise C-15-6-13) + +- **Contexte** : validation P2 de la 15-6c — R2-1 = F1 (HIGH) : `FOR SHARE`, écrit par C-15-6-13, est + une erreur de syntaxe (1064) sur MariaDB 10.11 ; F2 (MEDIUM) : le refus des réglages avant le 409 + jugeait un changement contre un état que le client n'avait pas vu ; R2-3 = F3 (MEDIUM) : la route des + réglages est fermée aux clés d'API. +- **Retenu** : `LOCK IN SHARE MODE` dans les deux lecteurs (graphie du dépôt, + `opening_complement.rs:38-39`), lecteur renommé `claim_accounts_in_share_mode` ; route des réglages : + forme → 409 version → 400 `CLAIM_ACCOUNT_LINKED_TO_BANK_ACCOUNT` → no-op ; `docs/api-external.md` ne + documente que `BANK_ACCOUNT_LEDGER_IS_CLAIM_ACCOUNT` (routes bancaires, ouvertes aux clés). La graphie + `FOR SHARE` de C-15-6-13 est **fausse** ; cette entrée la corrige (l'entrée d'origine n'est pas + réécrite). +- **Écartées** : garder le refus avant le 409 « parce que les autres 400 de la route le précèdent » + (ceux-là ne dépendent que du corps) ; lister le second code au guide comme « interne » (le guide + documente l'API par clé : un code qu'elle ne peut pas rendre n'y a pas sa place). +- **Réversible** : oui. + +## C-15-6-22 — 15-6c : appels directs au dépôt, fichiers de test de la 15-5b, code mort, signal D5 + +- **Contexte** : validation P2 de la 15-6c — R2-4 (MEDIUM) : 14 sites de test appellent directement les + deux fonctions du dépôt dont la signature change ; R2-2 = F4 (MEDIUM) : deux fichiers de test + doublaient ceux que crée la 15-5b ; F6 (LOW) : `BankAccountList.svelte`, second appelant du formulaire + de lien, n'est importé nulle part ; F8 : signal de découpage (le HIGH de la P2 naît de la remédiation + P1). Consigne de l'orchestrateur : tenir compte de la 15-5d, qui expose `defaultPayableAccountId` à + l'écran des réglages. +- **Retenu** : les appels directs passent `&ClaimAccounts::default()` (la garde n'est l'objet d'aucun + d'eux ; aucun test ne change de sens) ; les tests 6 et 14 étendent `company_invoice_settings_postable_e2e.rs` + et `bank-accounts/+page.test.ts` de la 15-5b ; `BankAccountList.svelte` est supprimé, la prop + `claimAccountIds` obligatoire ; le menu créanciers de la 15-5d est filtré comme celui des débiteurs. + Signal D5 **déclaré au Project Lead**, pas de découpage : le défaut est une graphie de mot-clé, non + une conception qui tourne en rond, et séparer les deux sens casserait l'ordre de verrous qui les + tient ensemble. Références de fixture de C-15-6-15 (`test_fixtures.rs:88-107`) **fausses** : lire + `:80-172`, désignation `:157-172`. +- **Écartées** : passer les réglages réels de la fixture aux appels directs (ferait rougir un test qui + lie au 1100, sans rapport avec son objet) ; prop optionnelle (laisserait le composant mort non + filtré, en silence). +- **Réversible** : oui ; la suppression de `BankAccountList.svelte` se défait par l'historique. + +## C-15-6-23 — 15-6d : l'égalité avec le compte de banque testée d'abord ; règle sur ce compte plus proposée ; filtre d'écran local (révise l'ordre de C-15-6-9) + +- **Contexte** : validation P1 de la 15-6d — F1 (MEDIUM) : la fiche plaçait le refus après le 404 et + `ACCOUNT_NOT_POSTABLE`, à l'inverse du flux ventilé qu'elle prétendait reproduire ; F2 (MEDIUM) : + `get_proposals` proposait une règle sur le compte de banque, que l'acceptation refuserait toujours, + et qui masquait une règle suivante ; R-1 = F3, R-3 (MEDIUM) : câblage de l'écran non testé, filtre + conditionné à la 15-6b. Décisions de l'orchestrateur. +- **Retenu** : `post_manual` compare juste après l'étape 2 bis, `accept_one_rule` juste après l'étape 4 + — avant 404 et non imputable, et avant la recherche de la transaction en attente (une transaction + déjà rapprochée rend 400) ; même réponse sur tous les chemins. `get_proposals` retire le compte de + banque de l'ensemble passé à `first_matching_rule` (patron 15-5b AC5). Écran : `ReconciliationProposals` + résout le compte de banque par `listBankAccounts()` (effet dépendant de `bankAccountId`, garde de + génération ; repli `null` = aucun filtrage), testé dans son fichier existant (mock de + `bank-accounts.api`) ; filtre **local** d'une condition dans `ManualMatchModal`. L'écran de + ventilation reste non filtré (angle mort écrit). Course résiduelle de `post_manual` écrite. +- **Écartées** : garder l'ordre « après la 15-5b » (réponse différente selon le chemin, message + trompeur) ; faire passer le compte de banque par la page (`/companies/current` le porte, mais la page + n'a pas de test : le câblage resterait non vérifié) ; `withoutAccountIds` de la 15-6b (dépendance pour + un seul id). +- **Réversible** : oui, tant qu'elle n'est pas développée. + +## C-15-6-24 — 15-6a : comptes de la vente verrouillés EN PARTAGE et AVANT l'exercice (rectifie C-15-6-16) + +- **Contexte** : validation P3 de la 15-6a — R3-1 (HIGH, né de la remédiation P2) : C-15-6-16 plaçait + le verrou des comptes de la vente (créance, arrondi) **après** `fiscal_years` et la séquence, et + affirmait « c'est l'ordre de `validate_invoice` » ; c'est l'inverse de l'ordre canonique + (`invoices.rs:1916-1925` : `invoices` → `accounts` (1 bis) → `fiscal_years` → séquences → + écriture), et la 15-5d verrouille la créance dans `validate_invoice` avant l'exercice : cycle sur la + ligne 1100 entre chaque validation et chaque avoir. F-1 (MEDIUM) : un `FOR UPDATE` (X) sur la + créance ouvrait un cycle neuf avec `accept_one_invoice`, qui tient S sur 1100 (clé étrangère des + lignes insérées) avant de demander X sur la facture, que l'avoir tient. F-8 : la « conséquence + assumée » (deux avoirs se sérialisent sur la ligne du compte) était fausse. Décision de + l'orchestrateur. +- **Retenu** : les deux lecteurs sont appelés juste après les réglages (étape (3)) ; les comptes de la + vente sont verrouillés en une instruction `… ORDER BY id LOCK IN SHARE MODE`, **avant** l'exercice + (4) et la séquence (5) — ordre : facture → règlements → avoir existant → réglages → comptes de la + vente (S) → exercice → séquence → écriture, conforme à la validation, au règlement et à la 15-5d. Un + S suffit à faire attendre `accounts::archive` (X) et rend l'état courant de `active` ; S + S ne + bloque ni le rapprochement ni un second avoir. Ordre des refus qui en découle : compte de la vente + archivé avant `FISCAL_YEAR_INVALID`, `creditNoteTotalZero` et `CREDIT_NOTE_REVENUE_ACCOUNT_ARCHIVED` + (test 14). Test de course sur `attendre_une_requete_en_cours` (motif `LOCK IN SHARE MODE`). Ce que + le verrou ne couvre pas — comptes de produit (6 ter) et TVA due, lus sans verrou — est écrit comme + angle mort assumé, préexistant. +- **Rectification de C-15-6-16** (entrée non réécrite) : ses phrases « ordre des verrous : … comptes + de la vente. C'est celui de `validate_invoice` » et « deux avoirs simultanés … se sérialisent sur la + ligne de ce compte » sont **fausses** ; le mode `FOR UPDATE` qu'elle prescrivait est remplacé par + `LOCK IN SHARE MODE`. Le reste de C-15-6-16 (comptes verrouillés sans filtre `active`, état lu sous + verrou, refus nommé) tient, le refus étant précisé par C-15-6-25. +- **Écartées** : garder `FOR UPDATE` en le déclarant risque connu (cycle avec le rapprochement sur le + compte le plus mouvementé du grand livre) ; garder la place après l'exercice en écrivant l'inversion + comme angle mort (cycle avec chaque validation dès la 15-5d) ; joindre les comptes de produit et de + TVA due au même verrou (exige de faire lire la 6 ter sous verrou ; hors sujet de #473/#523). +- **Réversible** : oui, tant qu'elle n'est pas développée. + +## C-15-6-25 — 15-6a : un seul vocabulaire de refus pour l'avoir — variante propre, code `ACCOUNT_ARCHIVED`, libellé « Impossible d'émettre l'avoir » (révise C-15-6-16) + +- **Contexte** : validation P3 de la 15-6a — F-9 (LOW) : sur `POST /api/v1/credit-notes`, un compte + de produit archivé rend `CREDIT_NOTE_REVENUE_ACCOUNT_ARCHIVED` (« Impossible d'émettre l'avoir — + … ») et un compte de la vente archivé rendait, par C-15-6-16, `ACCOUNT_ARCHIVED` avec le message de + la contre-passation (« Impossible de contre-passer — … ») : deux vocabulaires pour un même geste. + L'orchestrateur demande de trancher entre étendre `CREDIT_NOTE_REVENUE_ACCOUNT_ARCHIVED` et un + libellé « Impossible d'émettre l'avoir » pour `ACCOUNT_ARCHIVED` sur cette route. +- **Retenu** : la seconde voie. Variante `DbError::CreditNoteAccountsArchived(Vec)`, + **même code** `ACCOUNT_ARCHIVED`, même `details.rejected[]` (`accountId`, `accountNumber`) que la + contre-passation ; clé neuve `credit-note-account-archived` dans les quatre locales — fr : + « Impossible d'émettre l'avoir — compte(s) archivé(s) : { $detail }. Réactivez le ou les comptes + concernés. » Le manuel et le CHANGELOG disent les deux codes de la route (par ligne / par compte de + la vente). Révise le « aucune variante, aucun code, aucune clé neuve » de C-15-6-16 : aucun **code** + neuf, une variante et une clé. +- **Écartées** : étendre `CREDIT_NOTE_REVENUE_ACCOUNT_ARCHIVED` (sa structure `RejectedRevenueAccount` + désigne une **ligne de facture** ou le produit par défaut, et son nom dit « produit » : y loger la + créance mentirait au lecteur de l'API et exigerait un sujet neuf dans `format_rejected_revenue_accounts`) ; + garder le message de la contre-passation (« contre-passer » face au bouton « Créer un avoir », + incohérent avec le refus voisin de la même route). +- **Réversible** : oui, tant qu'elle n'est pas développée ; après, retirer la variante ne change pas le + code rendu. + +## C-15-6-26 — 15-6b : un code, un rôle, deux remèdes ; tables de refus du guide ; comparaison séparée de la construction du refus (révise C-15-6-18 ; C-15-6-3 révisé par C-15-6-18 et ce choix) + +- **Contexte** : validation P3 de la 15-6b (lentilles R et F, Sonnet ; remédiation Opus 5.5). + R1 = F1 (MEDIUM) : l'AC3 bis ajoutée en P2 (C-15-6-18) rendait, pour un compte d'arrondi, de nature + ou de TVA due **désigné dans les réglages**, le message du règlement unitaire, qui renvoie à « un + autre compte (banque, caisse…) » alors que l'utilisateur n'a choisi aucun compte — cas du dialogue + de solde, qui affiche `err.message` (`invoices/[id]/+page.svelte:509`). R2 = F2 (MEDIUM) : l'AC10 + visait le § 10 générique de `docs/api-external.md` et oubliait la table des refus du solde + (`:274-283`). F3 (MEDIUM) : six à sept sites répétaient comparaison, lecture du numéro et + construction du refus. Le corps de C-15-6-3 dit encore « deux clés » et « résolu par le type » pour + les comptes d'écart : révisé par C-15-6-18 puis par ce choix, non réécrit. Décisions de + l'orchestrateur. +- **Retenu** : (a) un seul code `SETTLEMENT_COUNTERPARTY_IS_CLAIM_ACCOUNT`, discriminant + `role: SettlementAccountRole` dans la variante et en `details.role` (`counterparty`, `rounding`, + `write_off_nature`, `vat_payable`) ; quatrième clé `error-settlement-designated-account-is-receivable` + (sélecteur Fluent sur `$role`) qui renvoie à *Paramètres → Facturation* ; deux cas dans le libellé + `failed[]` du rapprochement et au manuel ; `claim: Payable` ne se combine qu'avec `counterparty`. + (b) Guide : une ligne dans la table du solde, une phrase « Refus » sous le § des règlements client et + sous celui du règlement fournisseur (qui couvre les deux routes de lot) ; le § 10 n'est pas touché. + (c) Helper commun dans `invoice_settlements.rs` : `ensure_not_claim_account` (comparaison **pure**, + seule partie empruntée par la 15-6d), `claim_account_refusal` (lit le numéro à l'échec seulement), + `claim_account_refusal_details` (mapping HTTP, `FailedProposal`, `PaymentBatchFailedItem`). + (d) `confirm_batch` relit `supplier_invoice_number` par une `SELECT` ; la variante ne gagne pas de + champ. (e) Signal D5 levé (MEDIUM → MEDIUM, deux défauts nés de la remédiation P2, confinés à + l'AC3 bis), déclaré, sans découpage ; la P4 est une passe complète. +- **Écartées** : un second code pour le compte désigné (même défaut `D X / C X`, un intégrateur le + traite d'un seul bras ; la différence de remède est une information) ; un message unique reformulé + (vague pour les deux cas) ; inscrire le code au § 10 (rompt la convention) ; un helper qui compare et + construit en une fonction (inempruntable par la 15-6d, dont le refus est le `VALIDATION_ERROR` du flux + ventilé) ; ajouter `supplier_invoice_number` à `pay_in_tx` ou à la variante. +- **Réversible** : oui avant le développement ; après, `details.role` et la quatrième clé sont des + ajouts compatibles, retirer `role` romprait un contrat d'API documenté. + +## C-15-6-27 — 15-6c : le `FOR UPDATE` de `before` testé, attente prouvée, pas de verrou pour une dé-liaison, place du manuel, routes bancaires au guide + +- **Contexte** : validation P3 de la 15-6c (lentilles R et F, Sonnet ; remédiation Opus 5.5) — 3 + MEDIUM, 11 LOW. F1 : le `FOR UPDATE` de `before` dans `company_invoice_settings::update` ferme une + course, mais aucun test ne l'épinglait (les tests 11 et 12 passent avec un `SELECT` simple). F2 : les + attentes des tests étaient bornées à 300 ms. R3-1 : le paragraphe du manuel d'administration était + placé juste avant un paragraphe sans en-tête, qui en serait devenu la suite. F8 : les routes + `/bank-accounts` ne figurent nulle part dans `docs/api-external.md`. Décisions de l'orchestrateur. +- **Retenu** : test 12 bis — une transaction tenue pose S sur les réglages par + `claim_accounts_in_share_mode`, le PUT doit attendre au `FOR UPDATE` (rougit si on le retire) ; + tests 11, 12 et 12 bis prouvent l'attente par `attendre_une_requete_en_cours`, jamais par un délai ; + une cible `NULL` (dé-liaison, création ou remplacement sans compte) ne lit pas les réglages et ne pose + aucun verrou S (`ClaimAccounts::default()`) ; `$account` = numéro du compte lu sans verrou après la + décision du refus, repli `#` ; `$bank` = `bank_name` ; paragraphe du manuel d'administration + après `:2033` (borne `:2017-2033`), qui présente d'abord les comptes débiteurs et créanciers puis la + règle réciproque ; `docs/api-external.md` : une ligne *Comptes bancaires* au § 7 et le code au § 10, + sans section neuve ; portée réelle des verrous dite (next-key et intervalle, attente transitoire + possible, non mesurée) ; tests Vitest existants adaptés (`BankAccountJournalLinkForm.test.ts`, + `settings-invoicing-page.test.ts`), aucun ne change de sens. +- **Écartées** : garder le délai de 300 ms (passe à vide sur machine chargée) ; verrouiller aussi pour + une dé-liaison (attente inutile, le contrôle ne joue jamais sur `NULL`) ; insérer le paragraphe après + *Montant minimum* (`:2031`) ; documenter toute la ressource bancaire au guide (hors périmètre) ; ne + rien ajouter au § 7 (le § 10 renverrait à des routes que le guide ne présente pas). +- **Réversible** : oui, la fiche n'est pas codée. + +## C-15-6-28 — 15-6d : contrôle du compte de banque actif sur le chemin par règle ; gardes ventilées figées ; pas de garde de génération ; comparaison empruntée au helper de la 15-6b (complète C-15-6-23) + +- **Contexte** : validation P2 de la 15-6d (lentilles R et F, Opus ; remédiation Opus 5.5) — R-1 + (MEDIUM) = F-4 (LOW) : `accept_one_rule` ne contrôle pas l'activité du compte de banque, que les + trois autres chemins contrôlent avant leur garde d'égalité ; « même réponse sur tous les chemins » + était faux pour un compte de banque archivé. F-2 (MEDIUM) : l'ordre « égalité avant postabilité » de + C-15-6-23 reposait sur deux gardes ventilées qu'aucun test n'exerce. F-6 (LOW) : la garde de + génération protégeait un cas que la page ne produit pas (`{#key selectedId}`). F3 de la P3 de la + 15-6b : helper commun dans `invoice_settlements`. Décisions de l'orchestrateur. +- **Retenu** : `accept_one_rule` gagne, juste après l'étape 1, l'étape « compte de banque actif » + d'`accept_one_split` (`reconciliation.rs:1912-1938` ; `DATABASE_ERROR`, + `BANK_ACCOUNT_NOT_CONFIGURED` + `details.bankAccountId`), avant la garde d'égalité — même réponse + sur les quatre chemins (actif → `VALIDATION_ERROR`, non imputable → `VALIDATION_ERROR`, archivé → + `BANK_ACCOUNT_NOT_CONFIGURED`) ; changement délibéré d'un refus existant, relevé en T0, testé + (test 7). Deux témoins figent les gardes ventilées (`post_split`, `accept_one_split`). Écran : + `listBankAccounts()` une fois au montage, sans garde de génération (le `{#key}` de la page remonte + le composant ; son retrait exigerait la garde, dit au doc-comment). Backend : la **comparaison** + emprunte `ensure_not_claim_account` (fonction pure de la 15-6b, C-15-6-26) ; la **construction** du + refus reste celle du flux ventilé, ramenée à deux fonctions locales. La 15-6d passe donc après la + 15-6b. L'angle mort « course résiduelle » est retiré : la valeur comparée est celle qui construit + l'écriture, aucun verrou à ajouter. +- **Écartées** : écrire la divergence du chemin par règle comme angle mort (un même défaut rendrait + trois réponses) ; garder la garde de génération « par cohérence avec `loadGen` » (justification + fausse, test d'un cas impossible) ; emprunter aussi la construction de refus de la 15-6b (changerait + la forme `VALIDATION_ERROR` fixée par C-15-6-9 / C-15-6-23) ; `withoutAccountIds` à l'écran (écarté + par C-15-6-23). +- **Réversible** : oui, tant qu'elle n'est pas développée. + +## C-15-6-29 — 15-6a : le verrou partagé couvre tous les comptes que l'avoir écrit ; rejeu de la route sur interblocage ; ligne absente → `Invariant` (rectifie C-15-6-24 ; étend C-15-6-25) + +- **Contexte** : validation P4 de la 15-6a (lentilles R et F, Opus ; remédiation Opus 5.5). F-1 + (MEDIUM) : la TVA due et les comptes de produit demandaient leur S **après** l'exercice, face au X + que le solde du reste y prend **avant** le sien — cycle préexistant que la 15-5d décrit et ferme pour + la validation, non pour l'avoir. R4-1 ≈ F-2 (MEDIUM) : l'analyse de C-15-6-24 (« S + S ne bloque ni + le rapprochement ni un second avoir ») était fausse pour l'arrondi — le rapprochement prend X sur + l'arrondi et sur l'exercice avant son `UPDATE invoices`, et le cycle avoir ↔ rapprochement de la + même facture préexiste par l'exercice (**#536**, ouverte par l'orchestrateur) ; la route de l'avoir + ne rejoue pas un 1213 (500). F-3 (LOW) : cycle à trois neuf par un X en attente. R4-9 = F-5 (LOW) : + ligne absente du résultat du verrou non spécifiée. Décisions de l'orchestrateur. +- **Retenu** : (1) le `LOCK IN SHARE MODE`, pris avant l'exercice, en **une** requête `ORDER BY id`, + couvre la créance et l'arrondi de la vente, la TVA due des réglages (si l'avoir porte de la TVA et + que le réglage est posé) et les comptes de produit effectifs (repli D-B2 compris) — règle écrite : + un compte qu'un autre flux tient en X avant l'exercice se verrouille avant l'exercice ; (2) la 6 ter + lit `active` dans le résultat du verrou (l'instantané REPEATABLE READ, établi au snapshot des lignes, + précède le verrou), en gardant son code, son message par ligne et sa place ; (3) un compte de TVA + due archivé rend `CreditNoteAccountsArchived` (`ACCOUNT_ARCHIVED` nommé) au lieu de + `INACTIVE_OR_INVALID_ACCOUNTS` anonyme — même remède que la créance et l'arrondi ; (4) un id que le + verrou ne rend pas → `DbError::Invariant` ; (5) la route `POST /api/v1/credit-notes` enveloppe le + dépôt dans `retry_with` (`is_deadlock_error`), patron de `write_off_invoice_handler`, figé par un + test qui provoque un interblocage déterministe dont l'avoir est la victime ; (6) trois cycles + résiduels inscrits à l'inventaire (avoir ↔ rapprochement, #536 ; avoir ↔ solde du reste par ordres + opposés entre deux comptes ; X en attente), tous détectés par InnoDB et rejoués. L'angle mort « un + compte lu sans verrou pendant l'avoir » disparaît. C-15-6-24 reste vrai sur le mode (S) et la place + (avant l'exercice) ; sa justification « S + S ne bloque pas le rapprochement » est rectifiée ici. +- **Écartées** : garder le verrou à la créance et à l'arrondi et inscrire le cycle de la TVA due et + des produits en angle mort (la 15-5d a fermé la même forme pour la validation ; l'avoir serait le + dernier chemin à l'ouvrir) ; un refus `ACCOUNT_ARCHIVED` pour les produits aussi (changerait le + refus par ligne de la 6 ter, figé par ses tests et son message) ; laisser la ligne absente au filet + anonyme de `create_in_tx` ; laisser la route sans rejeu (un 500 opaque sur « Créer un avoir » quand + l'avoir est la victime). +- **Réversible** : oui, tant qu'elle n'est pas développée. + +## C-15-6-30 — 15-6b : le compte d'arrondi porte le rôle `rounding` quel que soit le geste ; validation et avoir en angles morts écrits ; message de liste vide conditionné + +- **Contexte** : validation P4 de la 15-6b (lentilles R et F, Opus ; remédiation Opus 5.5). R1 = F1 + (MEDIUM) : le reste d'arrondi du solde (5 bis) et la nature `rounding` étaient classés + `write_off_nature`, alors qu'ils lisent `default_rounding_account_id`, la colonne du compte d'arrondi + du règlement — le message renvoyait au champ d'escompte (recyclage du discriminant posé en P3, + C-15-6-26). F2 (MEDIUM) : « résolu par le type » faux pour la validation (créance des réglages sans + type contrôlé) et pour l'avoir (TVA due, produit de repli). F3 (MEDIUM) : le message de liste vide + accusait le compte débiteurs dans une société sans compte bancaire. F4 (LOW) : « désignez » s'adresse + à un Comptable sans accès aux réglages. Décisions de l'orchestrateur. +- **Retenu** : `role: rounding` pour tout compte lu dans `default_rounding_account_id` (règlement, + rapprochement, reste d'arrondi et nature `rounding` du solde) ; `write_off_nature` pour `discount`, + `bank_fees`, `bad_debt` seulement ; le rôle suit la colonne lue, pas la route ; ordre des refus au + solde : nature, reste d'arrondi, TVA due. Angles morts écrits, sans garde neuve : créance retypée à + la validation (**#537**, ouverte par l'orchestrateur), TVA due et produit de repli de l'avoir + (#525). Message de liste vide seulement si au moins un compte bancaire a été écarté par le filtre. + Messages : « un administrateur doit désigner », comme C36. Sélecteur Fluent multi-lignes, repli Rust + par rôle. **Signal D5 levé (recyclage de F1), pas de découpage** : recyclage contenu au même + discriminant (une ligne de classement, un test), F2/F3 d'origine, aucun module de plus ; un troisième + recyclage sur l'AC3 bis en P5 en déciderait la sortie en story propre. +- **Écartées** : un rôle distinct « reste d'arrondi » (deux valeurs pour une même colonne, un + intégrateur verrait deux rôles pour un même compte désigné) ; garder la validation et l'avoir + (sixième et septième modules, hors de la classe « règlement ») ; un second message pour « aucun + compte bancaire » (aucun état vide n'existe aujourd'hui, le comportement actuel suffit) ; + « désignez » avec une branche d'écran par rôle (écartée par C36). +- **Réversible** : oui, tant qu'elle n'est pas développée. + +## C-15-6-31 — 15-6d : clôture de la validation ; C-15-6-28 révise C-15-6-23 (garde de génération, course résiduelle) ; texte du refus selon le champ + +- **Contexte** : validation P3 de la 15-6d (lentilles R et F, Sonnet ; remédiation Opus 5.5) — 10 LOW, + 0 au-dessus. F3-1 : C-15-6-23 « Retenu » dit toujours « effet dépendant de `bankAccountId`, garde de + génération » et « course résiduelle de `post_manual` écrite », que C-15-6-28 retire en se disant + « complète » ; le registre ne se réécrit pas. F3-2 : « le même refus que `post_split` » n'est pas le + même texte (`splits[{idx}].…`). R1 : l'étape ajoutée à `accept_one_rule` est atteignable depuis + l'écran. +- **Retenu** : C-15-6-28 **révise** C-15-6-23 sur deux points — plus de garde de génération à l'écran, + plus d'angle mort « course résiduelle » — ; le reste de C-15-6-23 tient. Une fonction de refus + `counterparty_is_bank_ledger_error(field)` pour `post_split` et `post_manual`, même code, texte selon + le champ ; tests sur le code seul. Compte de banque archivé avant l'ouverture de l'écran : angle mort + écrit, aucun contrôle ajouté à `get_proposals`. Manuel : le refus seul est affirmé sans réserve. + CHANGELOG : `### Modifié` pour le changement délibéré de l'AC2. Validation close. +- **Écartées** : réécrire C-15-6-23 (interdit par la consigne du registre) ; un message identique sur + les deux routes (`post_manual` n'a pas d'index de ligne) ; contrôler l'activité du compte lié dans + `get_proposals` (hors périmètre, la garde d'acceptation suffit). +- **Réversible** : oui, tant qu'elle n'est pas développée. + +## C-15-6-32 — 15-6a : alignement sur la 15-5e réécrite par C54 — le rejeu de la route vient de la 15-5e, le verrou reste pour la seule course de lecture, aucun réordonnancement, cycles nommés sans prétention d'absence ; helper de verrou de liste partagé avec la 15-5d ; « émet de la TVA » à source unique (révise C-15-6-29 sur le rejeu et les cycles) + +- **Contexte** : validation P5 de la 15-6a (lentilles R et F, Sonnet ; remédiation Opus 5.5). R5-1 = + F5-1 (MEDIUM) : la fiche ignorait la 15-5e (C52). R5-2 (MEDIUM) et F5-3 (LOW) : cycles non + inscrits (avoir ↔ solde du reste par TVA due ↔ arrondi ; avoir ↔ règlement par compte interne). + F5-5 (LOW) : deux helpers jumeaux (15-5d en X, 15-6a en S). R5-4 = F5-2 (LOW) : condition « l'avoir + émet de la TVA » calculée après le verrou. Consigne initiale de l'orchestrateur : « une seule règle + d'ordre des verrous pour l'epic, celle de la 15-5e », avec « l'arrondi d'abord ». **Correction de + consigne reçue pendant la remédiation** : la 15-5e est réécrite selon **C54** (la défense contre + l'interblocage est le rejeu ; la 15-5e rejoue toutes les routes d'écriture, avoir compris, et ne pose + plus « l'arrondi d'abord », C55) ; ne pas réordonner l'avoir pour fermer des cycles, garder les + verrous qui ferment une course de lecture, retirer toute affirmation d'absence de cycle. +- **Retenu** : (1) dépendances « après 15-5a à 15-5e » ; (2) l'AC7 ne réécrit ni le doc-comment + canonique, ni le « 5 bis », ni le doc-comment de la route, que la 15-5e réécrit ou pose ; elle ne + corrige qu'une phrase qui décrirait l'ancien ordre de l'avoir, s'il y en a une ; (3) la route + `POST /api/v1/credit-notes` est rejouée **par la 15-5e** — la 15-6a ne spécifie plus de `retry_with` + (révise C-15-6-29) ; son test 15 fige que ce rejeu couvre l'attente neuve créée par le verrou ; + (4) le verrou `LOCK IN SHARE MODE` des comptes écrits reste **en une instruction `ORDER BY id`**, + avant l'exercice, pour la seule **course de lecture** (archivage concurrent, `active` lu frais) ; + aucun réordonnancement ; (5) les cycles connus — #536 (rejeu, C57), solde du reste (nature + commune, ou TVA due ↔ arrondi), règlement par compte interne, X en attente — sont **nommés, non + exhaustifs**, couverts par le rejeu ; plus d'« ensemble clos » ; (6) **un seul** helper de verrou + d'une liste de comptes, paramétré par le mode, créé par la première story mergée (a priori la + 15-5d), réutilisé par la 15-6a ; `EXPLAIN` et test d'identifiant étranger comme la 15-5d (C51) ; + (7) agrégation de la TVA de l'avoir extraite en fonction pure partagée par le générateur et le + calcul des ids ; refus `ConfigurationRequired(TVA)` laissé à sa place ; (8) une ligne pour l'avoir + au Pattern 5, dans la forme que la 15-5e lui donne. +- **Écartées** : « l'arrondi de la vente d'abord » (demandé par la consigne initiale, retiré par la + correction : un réordonnancement pour fermer un cycle, contraire à C54/C55) ; un « ensemble clos » + de cycles (affirmation d'absence, contraire à C54) ; garder un `retry_with` propre à la route (doublon + de la 15-5e) ; un second helper en S (DRY) ; avancer le générateur avant le verrou (déplacerait le + refus de TVA devant `FISCAL_YEAR_INVALID`) ; dupliquer `total_vat > 0`. +- **Réversible** : oui, tant qu'elle n'est pas développée. **À répercuter hors du worktree** (par + l'orchestrateur) : la fiche 15-5d peut dire que son helper prend un mode. + +## C-15-6-33 — 15-6b : alignement sur la 15-5e réécrite par C54 (aucun ordre ne change ; chaque comparaison suit sa lecture) ; trois clés plates ; critère de T2 par nom sur liste fermée ; sujet de refus typé (révise C-15-6-26 et C-15-6-30 sur la forme des messages) + +- **Contexte** : validation P5 de la 15-6b (lentilles R et F, Sonnet ; remédiation Opus 5.5). F1 + (MEDIUM) : la 15-5e (C52) réordonnait `settle_invoice` et `write_off_invoice` ; sa réécriture par + C54, reçue en cours de remédiation, retire ce réordonnancement et rejoue les routes. R5-1 (MEDIUM) : + le sélecteur Fluent `$role` rougit un test de `kesh-i18n` et se résout mal côté frontend. R5-2 + (MEDIUM) : la borne `sitesTotal` existe déjà. R5-3 = F2 (MEDIUM) : le critère de T2 (P4) ne voyait + pas un littéral coupé par rustfmt. F3 (MEDIUM) : `user-manual.tex:1754`. F5, F6, F7, R5-4 à R5-8 (LOW). +- **Retenu** : (1) dépendances « après 15-5a à 15-5e », étapes citées par leur nom ; (2) **chaque + comparaison suit aussitôt la lecture de son compte**, dans l'ordre des lectures du code, que la + 15-5e ne change pas : au solde, nature → reste d'arrondi → TVA due ; au règlement, contrepartie → + trop-perçu → compte d'arrondi ; une nature égale à la créance précède les refus de configuration + des comptes lus après elle (test 10 ter) ; (3) trois clés plates + (`error-settlement-{rounding,write-off,vat-payable}-account-is-receivable`), un rôle une clé, sans + `match` de repli ; (4) `sitesTotal` relevé pour tous les sites neufs (+4, +6 avec la 15-5c), recompté + sur l'état rebasé ; (5) critère de T2 : `grep -rnF` du nom, commentaires exclus, sur une liste fermée + de cinq fichiers, chaque occurrence lue ; (6) helper de construction à sujet typé + (`ClaimSubject::{Counterparty(ClaimSide), Designated(DesignatedRole)}`) ; (7) `:1754` au manuel ; + phrase « Refus » du paiement fournisseur avant la ligne d'annulation ; (8) produit de repli de l'avoir + écrit « non tracé » (#525 ne le couvre pas), signalé à l'orchestrateur. **Signal D5 non déclenché** : + F1 vient d'un changement extérieur (la 15-5e), les recyclages (R5-1, F2) sont hors de l'AC3 bis. +- **Écartées** : suivre l'ordre « l'arrondi d'abord » de la première version de la 15-5e (retiré par + C54/C55) ; inscrire la clé à sélecteur à `SELECTEURS_RESOLUS_COTE_SERVEUR` (ajoute `loader.rs` et un + repli Rust par `match` ; la 15-5c a choisi les clés plates pour la même raison) ; toutes les + comparaisons après la dernière lecture (ordre des refus dépendant de refus de configuration + étrangers au défaut) ; un critère `rg -U` multi-lignes (plus fragile qu'une liste fermée) ; un + `debug_assert!` pour `Payable` + rôle désigné (muet en production). +- **Réversible** : oui, tant qu'elle n'est pas développée. + +## C-15-6-34 — 15-6b : clôture de la validation à la P6 ; produit de repli et validation d'achat tracés (#525, #537) ; règle de placement « après son compte et la créance » ; manuel d'administration ; dispersion déclarée sans découpage + +- **Contexte** : validation P6 de la 15-6b (Opus, lentilles R et F) : 0 au-dessus de LOW (R 11 LOW, + F 7 LOW, R6-4 = F6-6). Après la P5, l'orchestrateur a tracé le produit de repli de l'avoir sur #525 + et la forme côté dette de la validation d'achat sur #537 (commentaires du 2026-10-08) ; la fiche les + disait « non tracé » / « signalé ». F6-1 : le manuel d'administration n'avait aucun verdict. F6-7 : + le critère de dispersion de l'amendement D5 est rempli au barème de la règle (neuf modules). +- **Retenu** : (1) **validation close** (0 > LOW, § *Review Iteration Rule*) ; tous les LOW appliqués ; + (2) « tracé par #525 / #537 » aux trois sites de la fiche et dans la 15-6a ; (3) règle de placement : + chaque comparaison suit la lecture de son compte **et** celle de la créance (la nature est lue avant + la créance au solde) — aucun réordonnancement (C55) ; (4) manuel d'administration : `:2027` inchangée, + une phrase à `:2029`, une condition à `:2033`, frontière de la 15-6c alignée ; (5) présélection du + dialogue de règlement dans un effet distinct qui n'efface pas la saisie ; (6) clés de lot de + `details` ajoutées par le mapping HTTP seul ; (7) `DesignatedRole` avec `ClaimSubject` dans + `invoice_settlements.rs` ; (8) **dispersion déclarée au Project Lead, non découpée** : le « barème du + geste » n'est plus invoqué ; raison — fiche convergée en six passes, gestes liés par une variante, un + sujet typé, un lecteur sœur et trois fonctions d'écran ; le risque visé (non-convergence) ne s'est + pas réalisé ; arbitrage de Guy en revue de fin d'epic. +- **Écartées** : une P7 (aucun finding > LOW ; la boucle s'arrête) ; un angle mort écrit pour le + manuel d'administration au lieu de deux phrases (le remède du message envoie l'administrateur à ce + manuel) ; découper la story à ce stade (rouvrirait deux validations pour séparer ce que la + conception lie) ; reconstruire le `default` de `t_args` par un `match` (le `format!` du patron + `errors.rs:88-92` suffit). +- **Réversible** : oui, tant qu'elle n'est pas développée ; le découpage reste ouvert à l'arbitrage + de Guy. + +## C-15-6-35 — 15-6a : un seul contrat pour le helper de verrou de liste (mode, `pub`, ne refuse rien, type nommé distinct de `LockedAccount`) ; critère DRY à exception écrite ; partenaires réels du cycle (iv) ; dépendance ferme à la 15-5d + +- **Contexte** : validation P6 de la 15-6a (Opus, lentilles R et F) : 4 MEDIUM distincts — F6-1 = R6-1 + (deux contrats pour la fonction de verrou), R6-2 (critère DRY faux : `opening_complement.rs:518-537`), + R6-3 (doctrine retirée restée aux Dev Notes et en T0), F6-2 = R6-4 (validation impossible comme + partenaire du cycle (iv)) ; trois recyclent la remédiation P5. +- **Retenu** : (1) la puce « Un seul helper » est le **seul** contrat : paramétré par le mode, à + l'emplacement de la 15-5d, `id, number, active, postable`, ne refuse rien (« ligne absente » et + partage chez `create_credit_note`), **`pub`** (test 17 d'intégration), type de ligne nommé et public + **distinct** de `opening_complement::LockedAccount` (p. ex. `LockedAccountState`), ou celui de la + 15-5d s'il existe ; le paragraphe « Refus nommé » y renvoie ; (2) critère DRY : aucune autre requête + de verrou d'une liste d'ids de comptes **hors** `opening_complement.rs:518-537`, exception écrite + (forme distincte, hors périmètre) ; (3) Dev Notes et T0 : plus de « règle de l'AC6 » ni de + conformité à vérifier — le verrou avant l'exercice sert la course de lecture, la défense est le + rejeu (C54) ; (4) cycle (iv) : partenaires = flux qui prennent l'exercice sans la ligne des réglages + (saisie manuelle, règlement, solde du reste, rapprochement) ; validation et saisie fournisseur + exclues ; (5) la 15-5d devient une dépendance **ferme** (la story attend) ; (6) test 17 : issue + attendue rouge d'après la mesure du dépôt, puis `owned_account_ids`, sauf adoption par la 15-5d ; + (7) **signal D5 levé** (MEDIUM → MEDIUM, recyclage de la P5) et dispersion (six modules métier, plus + la plomberie) **déclarés**, pas de découpage : recyclage contenu au texte de l'AC6, sans règle métier + ni module ; P7 complète (Sonnet) due. +- **Écartées** : réutiliser `opening_complement::LockedAccount` (privé, autres colonnes, flux hors + epic) ou lui prendre son nom (homonymie) ; refondre `create_opening_complement` sur le helper + (changement sans motif d'un flux livré) ; restreindre le critère DRY aux seuls flux de facturation + (moins décidable qu'une exception nommée) ; garder la branche « si la 15-5d n'est pas mergée » + (deux chemins à spécifier pour un ordre d'epic déjà fixé) ; découper l'AC6 (le défaut est une + propagation incomplète, que le grep du symptôme traite). +- **Réversible** : oui, tant qu'elle n'est pas développée. **À faire par l'orchestrateur** : + commentaire sur #536 (l'avoir est couvert par le rejeu de la 15-5e ; la 15-6a ne ferme aucun cycle — + finding F6-6). + +## C-15-6-36 — 15-6a : un seul type de ligne pour le helper de verrou (révise C-15-6-35 (1)) ; C-15-6-32 (6) et C-15-6-29 marqués révisés ; manuel : le rôle repris avant la réactivation + +- **Contexte** : validation P7 de la 15-6a (Sonnet, lentilles R et F) : 1 MEDIUM (R7-1), 10 LOW. R7-1 : la fiche + (AC6) disait « la 15-6a prend le sien » si la 15-5d a nommé le type, le registre (C-15-6-35 (1)) « ou celui de + la 15-5d s'il existe » ; deux consignes qui s'excluent, la première produisant deux types publics de même forme. +- **Retenu** : (1) **un seul type, jamais deux** : si la 15-5d a nommé **et exposé (`pub`)** le type de ligne de + son helper, la 15-6a l'emploie ; sinon elle le rend public sous le nom qu'il porte (ou `LockedAccountState` + s'il n'en a pas). **Cette entrée révise C-15-6-35 (1)** (« type nommé et public distinct de `LockedAccount`, + ou celui de la 15-5d s'il existe »), la fiche (AC6) étant alignée ; (2) **C-15-6-32 (6)** (« créé par la + première story mergée, a priori la 15-5d ») est **révisé** : la 15-5d est une dépendance ferme (C-15-6-35 (5)) ; + **C-15-6-29** (« angle mort : cycle avec chaque validation dès la 15-5d ») est **révisé** : la validation + n'est pas partenaire du cycle (iv) (C-15-6-35 (4)) ; (3) le cycle (iv) ne cite `invoices.rs:2006` et `:2172` + que pour la validation ; l'ordre de la saisie fournisseur est relevé par T0 ; (4) test 17 : appel direct du + helper avec des ids explicites, sans `UPDATE` des réglages ; tests 2 et 7 : « par SQL direct » ; (5) le + `keshwarning` du manuel dit de retirer d'abord le rôle *Créances clients* du nouveau compte si nécessaire avant + de réactiver (`user-manual.tex:358-359`) ; (6) numéros de ligne relevés de `credit_notes.rs` et + `opening_complement.rs` corrigés (T0 les refait de toute façon). +- **Écartées** : deux types (un par story) ; remplacer le type de la 15-5d par celui de la 15-6a (change le retour + d'un helper d'une autre story). +- **Réversible** : oui, tant qu'elle n'est pas développée. **Pour le message de PR** (F7-6) : `closes #523` + renvoie au commentaire du 2026-10-08 sur #523 — la contrainte « sans casser le refus nommé de #486 » est levée + en esprit, pas à la lettre (l'assertion de `invoices_validate_vat.rs:846` change). From 6ef5662de96069e2f5fc656903147a3d0aa9ba0f Mon Sep 17 00:00:00 2001 From: Guy Corbaz Date: Thu, 8 Oct 2026 23:27:56 +0200 Subject: [PATCH 02/10] =?UTF-8?q?docs(15-6a):=20T0=20=E2=80=94=20alignemen?= =?UTF-8?q?t=20sur=20le=20livr=C3=A9=20;=20story=20en=20attente=20du=20mer?= =?UTF-8?q?ge=20de=20la=2015-5d=20(refs=20#473,=20refs=20#523)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Route de l'avoir déjà rejouée (15-5e2) ; helper de verrou de liste de la 15-5d en partagé seul, owned_account_ids adopté, type privé — présent sur sa seule branche, non mergée : dépendance ferme, développement suspendu. Relevés au Dev Agent Record, écarts au Change Log, choix C-15-6a-1 ; lignes 15-6* reportées au sprint-status. Co-Authored-By: Claude Opus 5.5 --- ...6a-avoir-credite-la-creance-de-la-vente.md | 99 ++++++++++++++++++- .../epic-15-choix-autonomes.md | 23 +++++ .../sprint-status.yaml | 7 +- 3 files changed, 127 insertions(+), 2 deletions(-) diff --git a/_bmad-output/implementation-artifacts/15-6a-avoir-credite-la-creance-de-la-vente.md b/_bmad-output/implementation-artifacts/15-6a-avoir-credite-la-creance-de-la-vente.md index 1283962f..d28da364 100644 --- a/_bmad-output/implementation-artifacts/15-6a-avoir-credite-la-creance-de-la-vente.md +++ b/_bmad-output/implementation-artifacts/15-6a-avoir-credite-la-creance-de-la-vente.md @@ -518,7 +518,7 @@ créance ni de l'arrondi. ## Tasks / Subtasks -- [ ] **T0 — Rebase et recompte** avant de coder : rebase sur `main` après le merge des 15-5a à 15-5e ; +- [x] **T0 — Rebase et recompte** *(fait le 2026-10-08 sur `9cb5083b` — Change Log « Alignement sur le livré (T0) » ; la story **attend le merge de la 15-5d**, C-15-6a-1)* avant de coder : rebase sur `main` après le merge des 15-5a à 15-5e ; **refaire tous les numéros de ligne** cités par cette fiche sur `HEAD` ; **lire** le doc-comment canonique de `validate_invoice`, le commentaire « 5 bis » et le Pattern 5 tels que la 15-5e (et la 15-5d) les laissent (AC7, AC9) ; **constater** que la route `POST /api/v1/credit-notes` est rejouée @@ -911,6 +911,65 @@ ligne et sa place (finding F6-5 de la P6) : `credit_note_uses_materialized_accou ### Completion Notes List +**T0 — relevés (2026-10-08, sur `origin/main` `9cb5083b` ; branche de la 15-5d lue à `5624ca78`, non mergée).** + +- **Rejeu de la route — constaté.** `crates/kesh-api/src/routes/credit_notes.rs:187` (`create_credit_note`) + appelle `kesh_db::retry::retry_on_deadlock("credit_notes::create", …)` (`:193`), doc-comment « Rejouée sur + interblocage (Story 15-5e2, #536) » (`:184-186`) ; registre `crates/kesh-api/tests/audit_route_registry.rs:220` + : `("post", "credit_notes::create_credit_note", Traced, Rejouee)`. Le rejeu vient de la **15-5e2** (la fiche + écrit « 15-5e » : la 15-5e a été découpée en 15-5e1/15-5e2, C61). `is_deadlock_error` : `crates/kesh-db/src/retry.rs:115` ; + `retry_on_deadlock` : `:150`. Fichier des tests de rejeu : `crates/kesh-api/tests/rejeu_interblocage_e2e.rs` (existe). +- **Helper de verrou de liste — forme sur la branche de la 15-5d** (`company_invoice_settings.rs`, ~`:595-740` à + `5624ca78`) : `lock_designated_accounts_in_tx(conn, company_id, ids) -> Result`, + **`pub(in crate::repositories)`** ; **déjà en partagé** (`LOCK IN SHARE MODE`, choix C87 de la 15-5d — il n'y a + plus d'appelant `FOR UPDATE`) ; **déjà** le patron `owned_account_ids` (C88 : lecture non verrouillante des ids + de la société, puis verrou sur ces seuls ids, `company_id` gardé en défense) ; plan épinglé + `FORCE INDEX (PRIMARY)` ; ids triés et dédoublonnés, `ORDER BY id` ; ne refuse rien. Type de ligne : + `LockedDesignatedAccount { id, number, active, postable }`, **privé**, enveloppé dans le newtype + `DesignatedAccountsSnapshot(Vec<…>)`. Son doc-comment liste « l'avoir, qui relit la créance et la TVA due dans + les réglages du moment sans ce contrôle (C35) » parmi « ce qui n'est pas contrôlé ici » (AC7 : à réécrire). + **Absent de `HEAD`** (`grep -rn "lock_designated_accounts_in_tx" crates/` vide). +- **Ordres de verrous relevés** (relevé, sans exigence de conformité, R6-3) : + validation `invoices.rs:1921-1957` (doc-comment canonique réécrit par la 15-5e1 : facture → réglages + `FOR UPDATE` → arrondi `FOR UPDATE` → produit en partagé par clé étrangère → exercice `:2205` → séquence → + écriture) ; la 15-5d y insère, sur sa branche, le S de la créance et de la TVA due **après** l'arrondi et + **avant** l'exercice (`invoice_settlements_write.rs` « 5 bis » de sa branche) ; + règlement client `invoice_settlements_write.rs:48` : facture, compte de contrepartie `FOR UPDATE` + (`:122`, `:159`), arrondi (`:200`), exercice (`:211`) ; + solde du reste `:367` : nature (étape 4), arrondi (`:474-499`), TVA due, exercice (`:509`) — commentaire + « 5 bis » `:474-486` ; + saisie fournisseur `supplier_invoices.rs` : réglages `:376` **avant** l'exercice `:429` (le cycle (iv) l'exclut à + juste titre — R7-5 tranché). +- **Commentaire de classement de la 15-5b** dans `accept_one_invoice` : c'est le **doc-comment** de la fonction + (`reconciliation.rs:1216-1222`, « la créance est lue **sur l'écriture de vente** de la facture »), sans numéro + de ligne ni requête citée ; il reste vrai après l'AC2. Le bloc remplacé est `(b)` `:1464-1501`. +- **Textes de la 15-5e lus** : doc-comment canonique de `validate_invoice` (ne mentionne pas l'avoir) ; « 5 bis » + (ne mentionne pas l'avoir) ; Pattern 5 `docs/MULTI-TENANT-SCOPING-PATTERNS.md:285-335` — **aucune ligne pour + l'avoir**, et les lignes des flux comptables y sont désormais **par renvoi au doc-comment canonique** (« the + order is written there only », C68). → aucune phrase décrivant l'ancien ordre de l'avoir (AC7 : « rien »). +- **Manuel** (lu en entier, `HEAD` et branche 15-5d) : sur `HEAD`, `user-manual.tex:382` (4) dit « un compte + désigné … devenu non imputable … reste utilisé par les écritures automatiques (validation d'une facture, + **avoir**, facture fournisseur) ». Sur la branche 15-5d, `:380` (« Deux exceptions, voulues : l'**avoir** relit + la créance et la TVA due dans les réglages au moment de l'avoir, sans les contrôler ») et `:928` (§ « Un compte + des réglages devenu non imputable » : « L'avoir, lui, n'est pas soumis à ce contrôle … même si sa créance ou sa + TVA due est devenue non imputable ») — la phrase sur la **créance** deviendra fausse (AC8). `admin-manual.tex` + : rien sur l'avoir et les réglages hors `:2035` (« un avoir le reprend et l'annule »), qui reste vrai. + Numéros actuels : `sec:avoirs` `:1226`, puce « revient à zéro » `:1232`, `keshwarning` produit archivé `:1249`, + `sec:plan-comptable-archivage` `:361` (avertissement `:364`), arrondi `:924`, balance âgée `:1836` ; + `admin-manual.tex:2035`. +- **Tests** : `credit_notes_repository.rs` compte **9** `#[sqlx::test]` sur `HEAD` (`:101` … `:593`, inchangé) ; + **la 15-5d n'y ajoute rien** : son test de C35 est `credit_note_is_exempt_from_the_guard`, dans + `crates/kesh-db/tests/invoices_validate_vat.rs` (`~:1395-1416` sur sa branche, module des tests de la 15-5d) ; + le test 13 se ré-ancre là. Son test `foreign_account_is_never_locked_and_is_refused` (même fichier) mesure déjà + l'identifiant étranger : le test 17 sera **vert d'emblée** (cas prévu par la fiche). +- **Inventaire recompté** (commandes de la fiche) : deux écrivains neufs depuis `1920381e` — + `journal_entries::update` (15-8a, `INSERT INTO journal_entry_lines` `journal_entries.rs:1480`) et + `journal_entries::delete_in_tx` (15-8b, `:1648`). **Hors classe** (lignes saisies ; suppression) ; tous deux + refusent une écriture **possédée par une pièce** (`journal_entries.rs:45`, `:1163`, motifs `:1955-1965`), donc + l'écriture de vente d'une facture validée reste **gelée** — la prémisse des deux lecteurs tient. Ils + s'ajoutent aux partenaires possibles du cycle (iv) (ils tiennent l'exercice sans la ligne des réglages) — liste + déjà déclarée non exhaustive. Aucun autre site neuf ne relit créance, dette ou arrondi. + ### File List ## Change Log @@ -1178,3 +1237,41 @@ ligne et sa place (finding F6-5 de la P6) : `credit_note_uses_materialized_accou - **Suite** : la **P8** (dernière passe du plafond de 8) sera une passe **ciblée** sur ce commit, la remédiation P7 ne touchant que la fiche et le registre (aucun code de production). - Décompte après passe : **9 AC, 7 tâches (T0–T6), 18 tests** (16 neufs, 2 modifiés : 9 et 13) — inchangé. + +- 2026-10-08 — **Alignement sur le livré (T0)**, sur `origin/main` `9cb5083b` (15-5a, 15-5b, 15-5c, 15-5e1, + 15-5e2, 15-8a, 15-8b mergées ; **15-5d non mergée**, branche lue à `5624ca78`). Relevés au Dev Agent Record. + Écarts (C-15-6a-1) : + 1. **15-5d non mergée — dépendance ferme : la story attend.** Le helper de verrou de liste + (`lock_designated_accounts_in_tx`) et le test de C35 n'existent que sur sa branche. La fiche dit « si l'une + n'est pas mergée au moment de T0, la story attend » (finding R6-10) et C-15-6-35 a écarté la branche « si la + 15-5d n'est pas mergée » : **développement suspendu après le T0**. + 2. **Helper déjà en partagé, sans mode** (C87 de la 15-5d) : le paramètre `AccountLockMode` de l'AC6 n'a plus + d'objet — aucun appelant exclusif. L'AC6 prévoyait d'ajouter le mode « si T0 la trouve sans paramètre » pour + que la 15-5d passe `Exclusive` ; elle passerait `Share` : la 15-6a **emploie le helper tel quel**, sans mode. + Forme, non règle. + 3. **Type de ligne privé** (`LockedDesignatedAccount`, dans le newtype `DesignatedAccountsSnapshot`), helper + `pub(in crate::repositories)` : cas prévu (AC6, C-15-6-36) — la 15-6a les rend **`pub`** sous ces noms (le + test 17 l'appelle de `tests/`), sans second type. + 4. **`owned_account_ids` déjà adopté** (C88 de la 15-5d) : test 17 vert d'emblée (cas prévu par la fiche) ; + plan épinglé `FORCE INDEX (PRIMARY)` — l'`EXPLAIN` de l'AC6 se relève sur ce plan. + 5. **Test 13 ailleurs** : le test de C35 est dans `invoices_validate_vat.rs`, pas dans + `credit_notes_repository.rs` (qui reste à 9 tests) ; il se ré-ancre là. + 6. **Rejeu de la route par la 15-5e2** (et non « la 15-5e », découpée — C61) : constaté, rien à poser. + 7. **Pattern 5 par renvoi** (C68 de la 15-5e2) : la ligne de l'avoir (AC9) prendra la forme des autres flux + comptables — renvoi au doc-comment « Ordre des locks » de `create_credit_note` —, non la séquence recopiée. + 8. **Deux écrivains neufs** (15-8a `update`, 15-8b `delete_in_tx`) : hors classe, et gardés par la pièce — les + lignes de l'écriture de vente restent gelées. + 9. **Numéros de ligne** : ceux de `credit_notes.rs` ont peu bougé (`:362-364` créance, `:365-367` produit, + `:429-440` ids de la 6 ter, `:442-446` filtre `active = TRUE`, `:449-498` refus, `:507-512` appel du + générateur, `:525` arrondi, `:609-611` commentaire « inverse exact ») ; ailleurs : `invoices.rs` doc de + `generate_invoice_journal_lines_rounded` `:1870-1884`, validation `:1971`, créance `:2041-2042`, arrondi + `Issuance` `:2101`, exercice `:2205` ; copies de la requête de créance `invoice_settlements_write.rs:104-115` + et `:445-456`, `reconciliation.rs:1475-1501` ; rapprochement : arrondi `:1537`, exercice `:1570`, + `UPDATE invoices` `:1753` ; `fiscal_years.rs:550-555` ; `errors.rs` (kesh-db) `ArchivedAccount` `:403`, + `ReversalAccountsArchived` `:846-856`, `RoundingContext::Issuance` `:972`, code `:1010`, `map_db_error` + `:1048` ; `kesh-api/src/errors.rs` bras `ReversalAccountsArchived` `:3018-3050`, bras + `CreditNoteRevenueAccountsArchived` `:3254`, son test `:3814` ; `accounts::archive` `:646` ; + `archived_accounts_in_tx` `journal_entries.rs:2380` ; `test_fixtures.rs:560` (délai `:586`) ; + `fr-CH/messages.ftl:304` (`credit-note-revenue-account-archived`), `:289` (`…-issuance`). + **Aucun écart ne change une règle métier ni un AC sur le fond** (2, 3, 4, 5, 7 sont des cas que la fiche + prévoyait ou des formes) ; le seul bloquant est le 1. diff --git a/_bmad-output/implementation-artifacts/epic-15-choix-autonomes.md b/_bmad-output/implementation-artifacts/epic-15-choix-autonomes.md index 4f6ce705..87a8b73b 100644 --- a/_bmad-output/implementation-artifacts/epic-15-choix-autonomes.md +++ b/_bmad-output/implementation-artifacts/epic-15-choix-autonomes.md @@ -4363,3 +4363,26 @@ l'import (#458–#461). - **Réversible** : oui, tant qu'elle n'est pas développée. **Pour le message de PR** (F7-6) : `closes #523` renvoie au commentaire du 2026-10-08 sur #523 — la contrainte « sans casser le refus nommé de #486 » est levée en esprit, pas à la lettre (l'assertion de `invoices_validate_vat.rs:846` change). + +## C-15-6a-1 — 15-6a : T0 fait, développement suspendu jusqu'au merge de la 15-5d ; helper employé tel quel (partagé, sans mode), rendu `pub` sans second type + +- **Contexte** : T0 de la 15-6a sur `origin/main` `9cb5083b`. La 15-5d (helper `lock_designated_accounts_in_tx`, + test de C35) est développée et revue (branche `story/15-5d-garde-usage-comptes-reglage`, `5624ca78`) mais **non + mergée**. La fiche en fait une dépendance **ferme** (« si l'une n'est pas mergée au moment de T0, la story + attend », R6-10 ; C-15-6-35 a écarté la branche « si la 15-5d n'est pas mergée »). Sur la branche de la 15-5d, + le helper est déjà en **partagé seul** (C87), déjà sur le patron `owned_account_ids` (C88), `FORCE INDEX + (PRIMARY)`, `pub(in crate::repositories)`, type de ligne privé `LockedDesignatedAccount` dans le newtype + `DesignatedAccountsSnapshot` ; son test de C35 est dans `invoices_validate_vat.rs`. +- **Retenu** : (1) **arrêt après le T0**, relevés et écarts consignés (Change Log de la fiche, « Alignement sur le + livré (T0) ») ; (2) une fois la 15-5d mergée, la 15-6a **emploie le helper tel quel, sans paramètre de mode** + (aucun appelant exclusif ne reste ; un mode à une seule valeur serait du code mort) et rend `pub` la fonction, + `LockedDesignatedAccount` et l'accès aux lignes de `DesignatedAccountsSnapshot` sous leurs noms (C-15-6-36 : + un seul type) ; (3) le test 13 se ré-ancre dans `invoices_validate_vat.rs`, le test 17 est attendu vert + d'emblée ; (4) la ligne Pattern 5 de l'avoir prend la forme « par renvoi » (C68). +- **Écartées** : fusionner la branche de la 15-5d dans celle de la 15-6a (les consignes interdisent tout merge ; + et la résolution du conflit 15-5d ↔ 15-5e2 sur `invoice_settlements_write.rs` reviendrait à qui merge la 15-5d) ; + écrire un helper jumeau (contraire au critère DRY de l'AC6, conflit certain au merge) ; développer T1/T2 seuls + (story livrée en deux temps, sans motif — T1/T2 ne prennent qu'une fraction du travail). +- **Réversible** : oui. **À faire par l'orchestrateur** : merger la 15-5d, puis relancer le développement de la + 15-6a sur `main` à jour (le T0 n'aura à refaire que les numéros de ligne des fichiers que la 15-5d touche : + `company_invoice_settings.rs`, `invoices.rs`, `invoice_settlements_write.rs`, `invoices_validate_vat.rs`, manuels). diff --git a/_bmad-output/implementation-artifacts/sprint-status.yaml b/_bmad-output/implementation-artifacts/sprint-status.yaml index 544d6323..f6247aa0 100644 --- a/_bmad-output/implementation-artifacts/sprint-status.yaml +++ b/_bmad-output/implementation-artifacts/sprint-status.yaml @@ -1,3 +1,4 @@ +# last_updated: 2026-10-08 (27) (numéroté (19) sur sa branche, renuméroté (24) au rebase sur 5e4bec50, puis (27) au rebase sur de285ea8) (15-6a : T0 fait sur 9cb5083b — route de l'avoir déjà rejouée (15-5e2), helper de verrou de la 15-5d en partagé seul et non mergé → story en attente de la 15-5d ; C-15-6a-1) # last_updated: 2026-10-09 (26) (15-11b intégrée sur ec745d0c (15-7a1) — registre par union ; gates sur l'état rebasé : backend 2912/2912, Vitest 1091/1091, E2E 245 / 7 KF-029 + 2 KF-045 avant 12:00 UTC) # last_updated: 2026-10-09 (25) (15-11b done — rebasée sur 5e4bec50 (15-5d) ; revue de code close : P1 Sonnet ×3 1 MEDIUM / 14 LOW → P2 ciblée Haiku 0 ; gates sur l'état rebasé : backend 2902/2902, Vitest 1091/1091, E2E 247 / 7 KF-029 ; choix C-15-11b-4) # last_updated: 2026-10-09 (24) (15-11b développée → review — env_nonempty seule lecture, test (L) lexical ; gates 2877/2877, E2E 247 / 7 KF-029 ; C-15-11b-1, C-15-11b-2) @@ -338,7 +339,11 @@ development_status: 15-5e-ordre-des-verrous-reglements: split # 2026-10-08 DÉCOUPÉE après la validation P3 (Sonnet ×2 : R 7 LOW, F 2 MEDIUM / 7 LOW ; F3-2 — la dérogation de découpage ne reposait pas sur l'exception de la règle) en 15-5e1 (socle) + 15-5e2 (rollout), choix C61. Corps vidé (version complète au commit 94f1365e) ; Change Logs P1–P3 conservés (trend : P1 1 HIGH / 6 MEDIUM → réécriture C54 → P2 6 MEDIUM distincts → P3 2 MEDIUM). Historique : créée par le découpage de la 15-5d (C52, C53), réécrite selon C54 (rejeu sur interblocage des flux d'écriture), validée P2 (C58–C60). 15-5e1-socle-rejeu: done # 2026-10-08 REVUE DE CODE CLOSE (P1 Sonnet ×3 : 3 MEDIUM, 9 LOW, remédiés en 1dc41152 → P2 ciblée Haiku : 0, vérifiée par l'orchestrateur). DEV FAITE (gate backend 2793/2793, 4 ignorés ; frontend 979/979 ; E2E 240 passés / 7 KF-029 attendus ; R3-7 mesurée VRAIE sur 10.11.16 — issue à ouvrir ; dix mutations rouges ; choix C-15-5e1-1..3). — VALIDATION CLOSE (P7 ciblée Haiku : 0, vérifiée par l'orchestrateur). closes #463 #491, refs #536 #429. DÉPEND de 15-5a ET 15-5b (mergées, f289414e) ; passe AVANT 15-5e2 et 15-5d. Story-zéro (C61) : inventaire fermé des routes qui écrivent au journal (21 + 4 exemptées) gravé dans une seconde colonne d'audit_route_registry.rs (statut transitoire ARejouer, partition 8/13/4/90 = 115 ; volet (c) par analyse syn du corps du handler — C63, C70) ; enveloppes nommées kesh_db::retry::retry_on_deadlock et kesh_api::retry::retry_app_on_deadlock / is_app_deadlock, nom d'opération en champ du warn! de retry_with (C62) ; règlement client (#491), annulation de règlement (#463), validation (#536) et saisie fournisseur (C66) rejoués, tests « route victime » sur une vraie 1213 (tests 2-5) + test du prédicat et de l'enveloppe AppError (test 1), registre (test 6) ; doc-comments canoniques de validate_invoice et de supplier_invoices::create_in_tx, avance des réglages de la saisie fournisseur (R3-7 hypothèse forte, mesurée en T0 — C70) ; enregistrement des réglages de facturation rejoué (test 7, SansEcritureAuJournal au registre — C70), « 5 bis », module retry.rs (ce que la 15-5d attend — C65, C66) ; CHANGELOG Corrigé + Modifié (C64, C69). Dérogation de découpage : 11 modules, signal D5 déclaré (C67, révisé par C70). 7 AC, 6 tâches, 7 tests. Validation : P4 Opus ×2 (3 MEDIUM, 19 LOW) remédiée ; P5 Sonnet ×2 (1 MEDIUM — recyclage du volet (c), signal D5, changement de méthode —, 11 LOW) remédiée ; P6 Opus ×2 (1 MEDIUM — F6-1, le test 7 pouvait passer sans rejeu hors MariaDB 10.11, recyclage de la remédiation P5, signal D5 —, 10 LOW distincts) remédiée : témoin du rejeu (warn! de kesh_db::retry capté) aux tests 2-5 et 7, cycle du test 7 formé en T0 (C74) ; EN VALIDATION : passe ciblée P7 (Haiku) à lancer sur la remédiation P6. 15-5e2-rejeu-des-autres-flux: done # 2026-10-08 DÉVELOPPÉE (commit 6125d50b, sur ec675288 qui porte la 15-8b) : 12 routes rejouées (9 DbError, 3 AppError « une tentative »), 6 sites retry_with migrés, post_accept seul en retry_with ; registre 22/0/4/89, volet (c bis) ; test 8 (post_manual victime) vert, cycle formé en T0 sur 10.11.16 ; commentaires d'ordre, Pattern 5, api-external § 10, CHANGELOG, manuels #484 ; gates au commit de code — backend 2837/2837 (4 ignorés), frontend 1086/1086, E2E 246 / 7 KF-029 + 1 pollution (sidebar :75 vert seul) ; choix C-15-5e2-1..6. Revue de code à lancer (lentille adversariale sur les trois extractions). Avant : — closes #536 #484, refs #463 #491. DÉPEND de 15-5e1 ; ordre libre avec 15-5d, sûr aussi pour la documentation (C65, C68). Rollout (C61) : les 17 autres routes Rejouee (13 à rejouer — 10 DbError, 3 AppError —, 3 sites retry_with migrés vers les enveloppes, post_accept inchangée ; la saisie fournisseur est passée à la 15-5e1, C66), revue fichier par fichier + lentille adversariale sur les trois extractions ; registre sans ARejouer (21/4/90), retry_with restreint à post_accept ; mentions retry_with d'opening_complement.rs (C69) ; commentaires d'ordre restants (journal_entries étapes 0 et 0-bis, accept_one_rule, accept_one_split, post_manual, post_split, fiscal_years.rs:489-502), inventaire au symptôme insensible à la casse (205 lignes / 42 fichiers sur f289414e) ; Pattern 5 (lignes par renvoi aux doc-comments canoniques, ordre du lot, Required et When to Use réécrits) ; api-external § 10 ; CHANGELOG (texte final complet) ; manuels #484 (SERIALIZABLE, innodb_deadlock_detect), make admin user, brochure non commitée (C64). 6 AC, 6 tâches. Validation : P4 Opus ×2 (5 MEDIUM dont 4 distincts et d'origine, signal D5 déclaré, pas de découpage ; 15 LOW) remédiée ; P5 Sonnet ×2 (0 au-dessus de LOW, 9 LOW) remédiée — VALIDATION CLOSE (trend P1 1 HIGH/6 MED → P2 6 MED → P3 2 MED → découpage → P4 5 MED → P5 0) ; développement après le merge de la 15-5e1. - 15-6-creance-juste-avoir-reglement: backlog # 2026-10-08 — #473, #474 (P1) + 15-6-creance-juste-avoir-reglement: split # 2026-10-08 SPÉCIFIÉE et DÉCOUPÉE d'emblée (règle de splitting, plus de cinq modules — choix C-15-6-1) en 15-6a + 15-6b + 15-6c ; 15-6d ajoutée à la validation P1 (C-15-6-9). Fiche d'index. #473, #474, #523, #524 (P1) + 15-6a-avoir-credite-la-creance-de-la-vente: ready-for-dev # 2026-10-08 T0 FAIT (alignement sur le livré, C-15-6a-1) — EN ATTENTE du merge de la 15-5d (dépendance ferme : helper lock_designated_accounts_in_tx et test de C35 sur sa branche seule) ; aucun écart de règle métier. Avant : 2026-10-08 — closes #473, closes #523. Créance ET arrondi lus sur l'écriture de vente (lecteurs partagés ; compte archivé → INACTIVE_OR_INVALID_ACCOUNTS, test :846 modifié délibérément) ; P2 : comptes de la vente verrouillés, archivé → ACCOUNT_ARCHIVED nommé (C-15-6-16) ; TVA due en angle mort tracé #525 (C-15-6-8) ; P3 : verrou LOCK IN SHARE MODE avant l'exercice (C-15-6-24), variante CreditNoteAccountsArchived, code ACCOUNT_ARCHIVED, libellé « Impossible d'émettre l'avoir » (C-15-6-25), signal D5 déclaré. P4 : verrou partagé étendu à tous les comptes écrits (TVA due, produits), rejeu retry_with de POST /credit-notes, cycle avoir ↔ rapprochement tracé #536, ligne absente → Invariant (C-15-6-29). P5 : APRÈS 15-5a à 15-5e ; rejeu de la route porté par la 15-5e (C54), verrou gardé pour la course de lecture seule, aucun réordonnancement, cycles nommés ; helper de verrou de liste partagé avec 15-5d ; TVA à source unique (C-15-6-32). Validations P1 à P5 appliquées ; P6 complète à lancer. + 15-6b-contrepartie-distincte-de-la-creance: ready-for-dev # 2026-10-08 — refs #474. DÉPEND de 15-6a ; de préférence après 15-5c (#492, C-15-6-12). Refus SETTLEMENT_COUNTERPARTY_IS_CLAIM_ACCOUNT : règlement client/fournisseur, deux modes, rapprochement (failed[]), lots de paiement à la création et à la confirmation (C-15-6-10) ; lecteur sœur fournisseur (C-15-6-11) ; P2 : comptes d'écart comparés à la créance (C-15-6-18), lot bloqué contextualisé (C-15-6-19), signal D5 déclaré sans découpage (C-15-6-20) ; P3 : details.role et clé distincte pour le compte désigné, helper commun emprunté par 15-6d (C-15-6-26), signal D5 déclaré ; P4 : rôle rounding du compte d'arrondi quel que soit le geste, validation et avoir en angles morts (#537, #525), message de liste vide conditionné, « un administrateur doit » (C-15-6-30), signal D5 déclaré (recyclage de F1, pas de découpage). P5 : APRÈS 15-5a à 15-5e ; trois clés plates, chaque comparaison suit sa lecture, critère de T2 par nom sur liste fermée, sitesTotal relevé, user-manual :1754 (C-15-6-33) ; signal D5 levé, non déclenché. Validations P1 à P5 appliquées ; P6 complète à lancer. + 15-6c-configuration-sans-ecriture-nulle: ready-for-dev # 2026-10-08 — closes #474. APRÈS le merge de 15-5b et de 15-6b. Compte bancaire ≠ débiteurs/créanciers, et réciproquement ; exemption « inchangé » ; contrôles dans la transaction, deux gestes sérialisés (C-15-6-13) ; écrans filtrés sur les réglages (C-15-6-14) ; P2 : LOCK IN SHARE MODE, 409 avant le refus des réglages (C-15-6-21), tests étendus de 15-5b, APRÈS 15-5d aussi (C-15-6-22), signal D5 déclaré sans découpage ; P3 : test 12 bis du FOR UPDATE de before, attentes prouvées, pas de verrou pour une dé-liaison (C-15-6-27). Validations P1, P2 et P3 appliquées ; P4 ciblée (Haiku) : 0, vérifié — VALIDATION CLOSE. + 15-6d-contrepartie-distincte-de-la-banque: ready-for-dev # 2026-10-08 — closes #524. Créée à la validation P1 de 15-6b (C-15-6-9). Rebase APRÈS 15-5b ; de préférence après 15-5c (manuel du rapprochement). post_manual et accept_one_rule refusent la contrepartie égale au compte de la banque, même refus et même place que le flux ventilé ; règle sur ce compte plus proposée (C-15-6-23) ; P2 : compte de banque actif sur le chemin par règle, gardes ventilées figées, APRÈS 15-6b (comparaison empruntée, C-15-6-28) ; P3 : 10 LOW appliqués (C-15-6-31). Validations P1, P2 et P3 appliquées — VALIDATION CLOSE. 15-7-trace-onboarding: split # 2026-10-08 DÉCOUPÉE en 15-7a (production) + 15-7b (démo et remise à zéro) après la validation P1 (1 HIGH, 8 MEDIUM, 10 LOW distincts ; règle de splitting franchie, findings R7/F-5) — choix C-15-7-8. Corps vidé ; Change Log de P1 conservé. SPÉCIFIÉE le 2026-10-08 (choix C-15-7-1 à 7). 15-7a-trace-installation-production: split # 2026-10-08 DÉCOUPÉE à la validation P2 (5 MEDIUM, 15 LOW distincts ; R2-3/F-1 : neuf modules) en 15-7a1 + 15-7a2 — choix C-15-7-19. Corps vidé. Née du découpage de la 15-7 (C-15-7-8). 15-7a1-socle-transactions-onboarding: done # 2026-10-09 — REVUE P1 CLOSE (Sonnet ×3, 0 > LOW, 8 LOW : 2 commentaires + 3 tests ; E-1/E-3 → 15-7a2, E-2 → 15-7b1 ; C-15-7a1-3) ; rebasée sur 8f9811d8, gate 2867/2867, E2E 8 échecs attendus. DÉVELOPPÉE (a76a6dbc) : gate complet 2844/2844, E2E 7 échecs KF-029 attendus, 10 mutations rouges ; C-15-7a1-1, C-15-7a1-2. — refs #434. Socle kesh-db SANS changement de comportement ni d'audit : variantes _in_tx (accounts, bank_accounts, company_invoice_settings — fin du MIRROR —, vat_rates ligne à ligne), companies::clear_stub_in_tx, onboarding::lock_state_in_tx (LOCK_SQL). Six modules (story-zéro, signal D5 déclaré). Validation P1 appliquée (2 MEDIUM) ; P2 appliquée (1 HIGH recyclé, 2 MEDIUM : test 8 complété par finalize, séquence [user.created, fiscal_year.created], SQL du verrou — C-15-7-30) ; P3 appliquée (1 MEDIUM : graphie du squash, fichier accounts_repository.rs, pub LOCK_SQL — C-15-7-33) ; P4 ciblée (Haiku) : 0, vérifiée par l'orchestrateur ; **validation CLOSE**. From d697cc1e9178f033a5234d752b0ef70a3035ebb1 Mon Sep 17 00:00:00 2001 From: Guy Corbaz Date: Fri, 9 Oct 2026 00:51:15 +0200 Subject: [PATCH 03/10] =?UTF-8?q?docs(15-6a):=20T0=20repris=20apr=C3=A8s?= =?UTF-8?q?=20le=20merge=20de=20la=2015-5d=20=E2=80=94=20helper=20confirm?= =?UTF-8?q?=C3=A9,=20d=C3=A9veloppement=20repris=20(refs=20#473,=20refs=20?= =?UTF-8?q?#523)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- ...6a-avoir-credite-la-creance-de-la-vente.md | 34 ++++++++++++++++++- .../epic-15-choix-autonomes.md | 16 +++++++++ .../sprint-status.yaml | 2 +- 3 files changed, 50 insertions(+), 2 deletions(-) diff --git a/_bmad-output/implementation-artifacts/15-6a-avoir-credite-la-creance-de-la-vente.md b/_bmad-output/implementation-artifacts/15-6a-avoir-credite-la-creance-de-la-vente.md index d28da364..350c0b80 100644 --- a/_bmad-output/implementation-artifacts/15-6a-avoir-credite-la-creance-de-la-vente.md +++ b/_bmad-output/implementation-artifacts/15-6a-avoir-credite-la-creance-de-la-vente.md @@ -2,7 +2,7 @@ ## Status -ready-for-dev +in-progress zU1Q!YSLu9J_qNbnFSA2u3@x6&VKD(~-hGHc*EZJa^ETVSmIwYW z)(E9P=2J`nZ?7biQkw}sJdq$8vpH?bftJf@jaAO~aMRElhs=qvPJ{%t+wN%!fy4c) z(v9}pTz8+3&`KrJTe?bgFdN`LWXRofbKErc&~x3w=+X_Lzwwu=UsY;WRL0UQKJT)- z-*3k*+?Z9Y#Ao=w99wPpd1iCp-#7wAxm9LYkJJCceuf_ld_U=H=+bKcPx;B2n&1AP zQXU8A|51K+Yfn1Tw*E)?`2jg}H*LDSM5}yUMEy_ZRNN4!m1uns+H_>=r8? zYqguex+sT2m;*`J{n?QwmI^SHa+PALWMkN5h*TS?I&OtOZjP|h`Mz%r4DJ1Dm8D}y zc~Eb+p0xWV{EqM4hYfiA=X=dCJDK})ky7Q@ zwbO)<|F!n8rITx21FAO>OQXM_)w2$-|M&U>jltz@xmCyo{Bp9gs@aM0e0LV0@9X)E zFryn2?8fnMbGUpCt3L(H`L_kP7<)UMN3X)6>uvIpjPmX zh<4h$M1OZofByhM@2$uh5FJUX{)_uPl&Kg^B-Fo%YthP|8%>0>iZ@6lE<1L)g3Q zWc`-8!y0y!VSMCU8AHM%&Zi`lE2@`;-mrH}&wjPz3}Y@)i;_@`qZqso1A&df$*A!O z#n$QQn~JY8bQ3!Z6{FJHPC3ZU#26d4KU4cMC8oF)PF12eM?kS^aI#x@hAF(-OSNwS zt7;)ISR_Ea`9aMklq4g&F~jaGX91UHQnF1xA~uB300vs9LQ3?(e3SHyJ#CQAyK*G5 zM_l2O`RE(1EHih$2cl#ZwgFU|`G_0OoEbItEga7H#MI^lQYDh+@F^Z8ITQE+6MJ{GKQ962>~&Zm7Z z^F9udMX5F+=U@xU9G7jcVNLWzT2Y-Z$x8o+!j?~#S;5*vaM8B0J6npTB%%6{unGq( zhV?hQrE>v1OL=FAY9X|>Au9saN=h6K3RC?ts3>g`8HqptNXfF)R52`2u3C)H(;eM* zQ3c?v9JeQ$g+TUjY)b}1aNRn_2#6YMMD>r7aE&GGNwUrUT982noQu!yH+g$4h%wM3 zac;m-QPYmxHV8#WkEIVTQWJl9g`^d_kz*qfIfGJ^(t2CC(i-zesdnU_Ebtm8{UUGJ zgVuMXd-Ymy?~|jJYxb}cMQ?oD=*;a%`7l7f1B4P7TQ5Q8?E1ICFcrg%6* zt#t5NL79H^%P39RMz$nY52AUN0o40UL}F{`$-!1P#wHF4ab=1Ngg64JMHclJtKR^n zqY3ch&A1Fj0wrSuzc_ubVtf<3idA#&(Opbzuyq$v@M9?JM&eOHKN^RuZCAhPdN-95 zX;Y*$2c+@OS034wiP~=(1eU74aB4Hn_p{pN?yybiDx%nP+xTtjHZoK(JFpY+2@Ozf0R- zdqkJ+A57r{RhyGFN&UtPoRiA?5v4kt6dgm5o={ysltCya?Lv%kZkt@hAhjryGu!#7RfrfPBU98Ix{Qx@3E7NVFfWc?fGBwm%LtU% zx$p&MCMx;*Jf}Gi@%rTgSgwFE$W@4UGaM!_z%o+nxqUQlGr1Sn+K%!2v<)7kDPpL2 z{xby`7lW%-J)V%|AB4SMQ+WB%$Y{sN^82PSmr7DOwrf3aLUI9>%KD@0YgL{rdZ}?j zOSNVqhQIa^oN6%^V#_fi_)@*!3E!9`gzRvNgqBCvR2PWn+A*wiR-0>@^?!5kcCqaBD#uauenOdA&@97A-Y z--S1;a}|wZ*hK9_kb7FwEMB&r=g}>XYqtf|Va8!!Htj3YcDuI9xwme(IBL>b)s@*@ z23LATYAa75URTzN|A_3#D?+fQ>M+sMe(hIVspkdt{lz9|QXY`_w;?ZypfWJGpl|>F z)?5&;7|33o16sJatHF#kyZ@s-Y*xN6){~>}?~&13(QHHT3AA7f%1Gt7knMPo*06`U ztOAHHuV$}UZI9&X?$6*y)dR64frB*?6(fhe{i2t8sxqC~*igWR+3sg8o-i2Mr*o#? zTU>L!uTxrJiWyB@^Q<=NRay?5u$6g9awV85dBL#}VlxHVjRq)EinD(} z7q91E<}5A~Url!gTqrFZzcfCe_LcVuaSzQX&qK+6>1+H`o&kv7qiCM2*v@N8LxAzF zV)6+T_~HK%a3u2DTXp1Cp*^T(I#xUR~itvRbhWqJe+biRdB4!a_(-Xf0SXpn9|91zrwbSg!5K$$5G zHVyMI{Zat8VR+g23HdKzJeh0G>>IJ_pEC(O2HEmzF$U6edwhfWkC{)u)*H4o(K&ed zox^ViSFfwoE#v}U8r9cL94cqKy+7DKaO%rdb|{z;JCaW&yQ+Mse1o3 zdolXj#hssP?!T{!y?13CyP=4!d+)C^^wRE}D%%4>zRv)Y0;#zIE{o5IUAfqQ_BYyu zk~<>{ckP9oii~t`)mx@~oimy7L~|608&{j+0O;il^EQ-}V~C&AtK`kcdEXuQYzR^6 zm6xOBO0SQ_>mbo>RNkUBXr`53K{}A#6bb2|p%U$p+I;j!t|U}8M(M$&?HQgOcNVORz6+M%{JhMq3EP%&{vd(H*6tV~3u{z|$p{^4nIyc=2 z!`UWweq&S);ND#C9HClnuMHBL)Ah?P32F%bNyK61Vc^FziH@}Eu2d!h>cQ9a!qmdw zY12v0RA4Y@ey2oF>RfI$RHKiGvgMdx_csUhf@PSpW3gVK4sTOI#>yRaKZNO}H6bEO zTTFgSt|{949XC0V3F9{}fk~F~xIV1?K3ba9+hbs25XUx=(bOhJWO5)eu(a&wPic2f z3>O;enf~_q@$zpbQ#s~+#5B5|@+m} z#hr_Edl(c7=n?@garK_9EoQMVg>C0aXb96NsXmONYfb~bq26K+L!^r@;j*x6Rf1~_ z?iv2Vf*TeLyelcs50mL&!YZc&7Bca1HYg>5d`|xN zy+{NvfH5*&;<8^TpDc(JV{jZ8_bX38oSbW6k5nATC5AOztEak5J4-d&0I*&}uSXtVa+|07nQdes&i(jUPo%aGH!>G(77>m|!lbG9eBAJIJUMV>F#Dq&5!G9L=Fh=G)opXAa zZJRo%huhGx55_muDb8VR8YFX8wVy6s%EU7=O5PLy_5xbhBUey3wu@o{FJ|a5(D|`r zL!Wf-4o}#!$C9AGMS3ALGlo%aEB}}({W|z7kU$N(+>o+6 zTVgg@CaVQIN~pkcGO0`&klr$FH@H`yQzQ~qs)<*)u&73=6X7!a>{%TgNU-?OE)L{n z8dw9O4@s^MV0_l4&g+B)vx7mK=oTC^_L&d+P&{c?(ScqZRHzlkSie}LuSv4e2=~N_ zTj!_l!l%tcyF0clE4xA^a^07kW8Yue-Y1kMuP(qK=C8Uf#x8FCKoRG`5!quz**ub_jjKiRZt9RGc- zGSA(Kt@(#Mz#U2tFg;n1rgcLM(wmuqohDp_9(!` z_-R%=U>X+A1Ejk@h9Z=w#<*j(YIKFxc}xF&t=r28pPktXz|kn``E1Jaa;#<`DYkAb zHJMs!wSRf$le|&9u9)U$EKXl2%P(FtF4uO}nuDf=pBFa8FSQ%_atCrPk%wfU zxS7ESqh0nY${n8%xwP+gZN(&czF=IrlS>IGB+agj+fi0vZTwAV1MeUFxH$qAtdGO~ zN!#b)wSnQ+#T(-tHMSdND;qV*RRj4E5ZxC$rzgD&_=TJ5uq^!*Nuq48RjlX>PCSif zOPDYWS#z~MBJiwo9xvk(S0AIH9QW* z%Zp3^&`f5x-yS>g)qzb4BmYzyc(%P3Nr@uEFE@+fNj~c5nx>c}Jg3;}GE`yTAWfcq zL)knX)pA|7L9(4?$eKtdy)Bzg9bBV75SqW!ZktgdNU4zQ;JT`}rTrX_&r{_-@ibI; zzc9E@9pnZ&zQGa$)ojDQ3YWL~Gs`+edGFH)upTkA(*gdBpng4DkEC>L7Eqs8&whRWs_1#`yvfvj)VknZLBD>2)Z~OcG==UFLPRij zF`NZzN)1jlr=FQ5l|UIrBdATHF)_ZbqVAm`WH)hGj2zqktl_m^|c8C0s>i zcVPyzG%p?C0d-`h&p2!+sl8`_m}R;hx(N6s8n?t^wj=CudslAqaA4cD+zOTa>I!o- zksK)`m;1{H%J!FQU8$sp!o+F<&B@vVe9850vNo0YUuZ%*?EbbHYnK|ZDo@s#2>R^@ z3UE^fj3I~i*u(X%dKu1+c7J$1cK!eb(7#6iFQN&l03~m&%K=S?1@mS)yG)g<03`;a zqyV4!Ulx$-zk+@+77i}<*18JNHU#ot0`V~N>vS2Y_;t6XpY&E3|Hxbd^=vvzZoXu3 zfSh$9IzoQ^rod5G)3RvEYb2gL?FqcNi3NXMc1*6aokMot6XI23>k+f(}$z)=5LHU8@{AaAu<0rf=! z{rc6DdUNs*FbZjC^WR|*M%L8(7+|ti5ABb%I)c*cS$g+|vJ6}yMJ}E(6rWRB zH?{mfcMMSc`aA#ctGH5uH9^S%tpBG~;u>!*9qH+@+p~g`d(Sn z7FU0OZcy!MQ_=bD?k(K+DL!p51G#&)kHei^81i>C(E=dinXu|@a`U{i_q}|Itev&n zC+jLJI$I}XeDdjKe>9P)JS(za-7Dtq&#x$tyQhl)Cg7b=;o9?W>%nmiqggk4vow5E zZ`A$e{#JD`klflPZnUiCK3a_n>msEr@LXJ8jr3W8?k~fS%T4XLQyCDDFG&MQvsf_C zE#z{hC?CMcmw!vxuFl&RMlL4o*)R*u31JxHDcL4{IuDiu5$hCbr)EUzhc};(4 zt`r`n9-0_>GZ}fl;C69Le%oNVw1C?FVbo)`uCNI;U=w1$%tTfzB9b%+5rH@v zqt+c?k+EXFM;kt^wxK8^9+s$dTGY*WtKm%t*O`7c zS%R?BE$p-y%C2l8}Oy- z+p*jCLo%q{S-v~zIuUyg#qU}@J|XCZjiqP(N!Z(Al*I>C8~UOXj}r03si3(A(JpH2 zNi!9-HyF?G+f|!<6t*E6{!GGVG-|-b;}Zn(*>qs8NS^pYYB2lQ98CQ09a0%8EdbE% z-?FI)5bqg6N{dE338t9Zb>ZgN*xRxZ(trQTmecq%WQ>IG7{x+NGj7MDv{^0qVJdHL}|2v@MFt@PH} zT^bralJ2~Dgu{-+{3^L{uQ)CLxBpT4TlX2i5QS}XH#<45GkF5m^{9(D4hC~J?#F{o zKMX(aDcn8$ns*0x-FO)Bxd#}<<}xLL>lEjR@ELXso=UzfhJ3PFN88OUK7BWm`*tavl2ZFI+AWVpGBfh*5uyW*fzy59?<}$ouyxrgQ4q@`79su^Z?_qst z?W66P49_I=anr66h6(McW{$X(uMUih;dIEM2uUEa?4{!8UpX^Q8jO3N=8+z{j!+sl z@?)Vf(2J(5aeGz+qttdm=WpEiDEtPj?v2Qz)%GPmHU%D4HJ)cJJg=!2p{RCq zIcbSdeRfl)e)qunf#>hr?pX>F_g2=`p_`7HAe&marm_3!Ya6WEkW7qMcz>YnpIEj= zWa3>uv14sA@P{fjNA8yB8l|x59B~IXyCz3m#(aWa#Wi@x{&uMwMFAp#YEN_B>_&4j zTMlQf);ca`d0kxYl)$V~*4I4|Po?XlLs>74X=n1mPRThk$x6V>AsWFGg8;mYjB$|h z7jU<9I!4AwV=o4$Dxo_Ptgs%6jd7z`xd`2cTMp)+5y`w@enAA}P2Dx4h#H*-7pbic z&|Oe?Q+V&PIBfN&xB$7|Evngy_5KO+m;E`Xo8*~T*D0oVj_2;2yUUqw&fXT`e2;}r zIgqs+^#nhbcD7(om#C~+h>V>WO{hf;BIWuZ-pIjfp8*nB`VUv0_U=j zrv#i1BjXquRM-VxGe%9H((zdfdLOg6Z9N}@Rg!0#$ZMrp9{>|qN<4dllkcf<2QiVse z>7Sf|;iOc)5dbVgY?>N|U=q#wEyvBo=hjsvlx8_0-0~`5R8sY9JejV75V)==c*jthHfE2j~8 zV`#eCvc?}(Df;&m1OyRXvqB5`Rm}i=lFIC8P-wPreZa0u9h?Crxn6TjGgONT2b@km>|85J37Dppn-Q2?|J%%adx#+#Tnb*PSTi63ju@J>pwy56e zRLKLRW7V-eEVD(CV8dz^rHc=*LCDjeEMhfmUn9SZXZWb@x<#(Ay@Grf79u7B|IWun zQeToO^i~ej0Pcg>)SV#SFSj|Wp?~I%hW~V0^;)vI@Ja8NE<2 z=jXIA07y5CRU*a1tGQQJW}T&_XmjWHPA(js*bCzz$lzb}nnuYjK=sG2N6C+!f z>yYdJ-kyK5P~9r7sfz7%t0Tn&*L4%ykdB?q#>sS!!LPeO8wi&ftXLd(X%Q$;Ocb9+ zlZaHPp~Z`Gwv7-z8&+M!|JjE@Jh zYmc_#4@RX-U3_d{8ehPtp|M-k{UH7!>5954cbJTeI=d6QxSBP+dqIdgD~cK`$&38K(315?UnPvORj!L?|P`UyBQ8rXt^k+QI~Q8 zkl71!Gi)oKRbYYkPtZpedcUp{_egHL%mrr&OBQ2uBj=?lH|x`29^z7z{yOs)TaNo+ zG-J#-ZIDaupEN78vZ~yMJI*a=iFw_Z7rCt)1JmV%c@kW=hk~@!&V$-~l_}h9fH}?F zGo1Yj3Md9*-hI_Y3SN=Ca()l($P&_Hgk-O+@Ek2x|ChkP%zPIP^Byai)#v1q<;|Sg zA?cGPf}vnPxZW9<(C5J8lV2;PQ3m$1xnNiQLoBF%UrVg1^Urh zC)n&7D#L20@j|yoTT+0ME5OpA;T&Gm%E*rXNo9|UfgDed&FV8x;5(E%ghG+XXSx`FYnp~4v zV^nX83f4Nuwe(AiSS(EjFi37+VvT2LgR(E9TIPFVSUtTM8+nHAjuDrKMNC_A z{A2^gsuvwnRcp7>1e1uPKVFSCuzc~g(r5Jiw@{SdqJDFg`hf5cTP19&ZKv(`Qhh6) z^M>syrcJn?LYB6BwA~?Ar0^z0s<$WuP-h*3aN9;WJJs4{KRk_P=u{1*90kF$md$+|zVb{X&7j%OSW}`5;WdnoT44if_GiQT>7+WPV$Ycw8_xTU}kX zT_oCF_4{IV^A?`i5#1HL`eu%&h}kSW7IA5_%flz77k` zW^ST1jnR+%;3s^vq657=cf#S@i$nVxhw;~1>MHQe5_Wg>W*-|Nbta4di-4aecjLcX z+O#@$P;yXa7AB@PRw_^)I0!Hky&WET{WN#>pG|f^K8x@Fn%xNfG-Kjm;7D6%2ju`x z;z&c`0IlTgHVTUO*&&YBZpLo|kAbPO5GQ&#WdhVbRzG^q&Ms1Ud6^vP5V&YWyR}{% zr)%5X#ykwBeRUjMv^{fxwgLgDiM3YDcAiO#-=0c{Y&FV;Sy262(T}Jb$EhptYs&lm z>(fMPT%zx?>LA0HvDKP?RawLn45BC5l`c>DgP1)`xSc1@=i-brcA{cc2=mcIMWL25 z{CeIq--YtSo5ZA8s3w-}6|9ooym>H)*m!hv`Xk^!=Qb5aJ)^QJwvkRpO1S?HuvdE&VG0643%7Nmo`Bdg|(!&OIljVu5_R z<}?%=N~$r7A5WC6?GxFF_Yx3)Pf1>P{GQT~sb9;MTBzK0P>Tk{dO|%<J_BNDvJb zkt#8{V;2B;A;)t?HWd0gXow%0j=yWjxU0`Yu5!8^-F3!$kJ^!K6dj0*DCQf8ae*5z zmNde_`jyF^%!S$cH7pN*uJx+Zy-sLOMV*gdBI1K~*z#$~^VyNZw2-ULiLVN4dFSBW z<{18C?w-poX~_l*ou{^3`qPRe6hQ5&qB{rJzKsqLN@B=6m<2aQS3@Ni1`8TU)Cf^u zP8F75NLhj9qIVHDY!oeSjBj>TLn311M+>Pyj5=^e$b8HCXJTm+t4D$Rd-;+H;H@V7 zT0V6s6iF9=HvF?SnGsrB*_#U&R&Cv~oGFGDDlG^TePfpr{4)VUQ_05lnMa~QF){$l#TREF4t+zUQEwJ#Fd&r&+xnW~a03+@qW6DQ^QrXg~v>pVwKtets44Pd&UX~9Xp05dI`;G7frlsmJ(?r9-PdthrzJbn z;R4zA-WCUY8A)f|nkR&FoG)IsGpkWp4dEjvVT`4I@iXwjeUYm%(p#g`TQRgH5ZPWI zCn0H|R{!k=Y<9^CiHX3z+<6lXZ<4bPkRR_qj;TECRba-Xbnx@Q7#GYu6q>`+N`h-s z`PBnZvPR~3Mq;@TH4vfIOV$<;>0@kiwNoT=Vwe!pH7Td=NgMQ|fwu~9*QJQHGjHxmff>K!{OA(;hsmDNv@BGXiF z?xmNgbuK3zxVravY;CMRcj2a+8}3@p_5!rhS;S; z`=jX>boI2(P4VmeDlm>D-@qejW`?gifX5OfB5Eu0rbaY)L(|2-x#C*t1$k}b(y&wp1Kqrf;R_8$h-21{;aBJ^ zoGNzKAwF1g>Ci6_q0o08LuN|3ZD6pZ2+C7~RFiioCa)RX3HL!cOC54KFR~*dU$D1U_PSgOcT^&~I6rV4q>;Qy#y0~3Hl^o{x9{VB4xmqb= zNqyFbf^=rJ7RfGIlk=cw-ijbNiPlweO{udgpL;#(1zy!^5zsL9qBpI?+s!wSSydkF zr=lolmF!@<+jxh}nh+R(OiA$O94v!Z_%3-Y?bEvM3M@+0t{cm62u;zOWskcZ_kZPY zSo0&AfBBR;qs{)r1=x4FGB6{#p;Fs=CFPf-PQTL}5b&NkdaTVLq?y*58h%uIxoc%4 zaPEg%q!I=)OqySj*_ex5OStsjuOc&U=B6!|8C>pj5bv;lBl&NnWeb7AF?IGGF^JJp zmCyo?SgZPa6`<7ykf-F}T{t!9!FkaEQt7ZYh$3HA&(IlwJ@nq3F0(#Je+PFOaKPMU-pT)c@a=?ggY!QPbZq<| z4aCC8)~2Nesscp{IsY1=cDqqw!tS=WDs$@|y<}3{)_3asMu+Q4=l5^&``3Pc+PpC+ zIuL78!wne-BNJmAq6sJlENNGs1Q>M;q*oz```+y$wS(~^NfZyTi~fGV&G-A}H&FI0 z5%vGZ3}6HKk!PH23~3H4AQ)}=4xpk4>6?KP(DS$Zb&pH)y9Nc0cO zS@!FUKU+-5JpcF9b)|F%jZ!ut(i zVPyZmV1pwKX?s#u#NG$$Pl(ZrwoRoaAqhlFd%=#H=lDmLL82%Pj@y?CC)M@q+>CmG zLb&|P>P^!we0|=n<@2V-iT*s*^@{As+(<1xpugCDalT8v9IyPtkl$m7%W7-6xzjmfky3ybiznxh+VA`?uEb45Wq|eX0=bra} z&t!)mi2@*f%Y?;>*i+L!4xB~!?xVxN{5qP4Zz2Tv+&(791Mm6D<(PhIuG-t`t~rJ8 zx-w*)vZY__qT9xvx&d29dN2!H8eHI8%^OGnVd2?`SpW(MG`lVV0aaT-iiy7%$$ z`E+e*_8;7_4~_&9!{5K7$X^xXnb<1G3sG!jK|%|RIFScki)|*_T8eX9Of3{4WLbg+ZRPV8OXPih{Q8+mOa)5Xgr#Rj}T@z zHVj&X&B346Wfpssc#8c-4*Om^0#SwR<~#fB>+sT>-R}b^yY#p<$P}P$=l2A;7pXZX zg`&d8@7_+R0v7{T7>80QPfTJxvznz9VzA*B?H0&QtEXgOM8(r$F0#2rO-Sg8BD2c^ z$U9k%TYiPR%m6t5ywRH%563#McTW1a4B#_W)x5;3gV+dR7g#r!3q#FNmQABB2CE5g z2g<_n6WIWMp%QE5{9S)vi!f*nGs@$7k;5WF-G?lg6Um#&VZG*JF!WcF=r3O<3B7MG z)Ax4^J71-5f~rN5n8KZP6RII$WL;D035kSTO1O+zNbs)}U>JE$Ew7lSjYvZ$#XSic zxhC!G=#RfX*6>b|*8St>lW=~=%-@Z;*mh#^2Y~|6gNIW+2Am>j7*IWAMn)MX?5iz1IxN=t~l znPqX7p0+Fyci&;q-}*kS7jM;>oZ19$dXyQ`h_0FaaNV}gx}|>OA`^F)(~X7$?x7Qq zSSz6{m^m0?;c2VD5VypC#z=OXVTP=p%80t62KP2cVc}xBY}WPU1MMQ_}BgDpL z$4tIi;H2H*!uGEsx!;i3a7R6OLH+~a9iipn7ayOZ`ey<=8&RD0*Y?7W#YE@}xVn@$ zXkMeY&Piil!zB*FHhr=$MuBfwn-=9~I<0X6zo~X5NDZJM^=%D&C#=3THUP#E5h$6%Dpa zB{hz*vhHw-60rkzw4?THNMW&r4vYuW1F&Z?3Bef>!W9R`tMa`CUQBEYtplA6aD`EL z{va8L^c$dcKrtHjTQp+TmCI5b?9rDSm_HRK_k+D|n?vxC~bJ zaNNL)B5>X~_!~|xY7%fs4gOnGqrgQ?>F60aa;2gxp-mNu&Lr^2LgivUN~oCO94O4J zLJa(#tB^&EK%_{bICU|k_JKuIS$jlm=L|eRFmR!Eb7IEPr8-5Rw2X>$l<)`dtV{@U}kQ<)AZ&~w$6S@8*Pp6>3g7zy+IFN+~@L&UgMIP^WaPW`s4MGz+*>tku6q468 zp^!bG)EeE6k7QQp21ut?=(P1Ij)Fj{Vbz0*v}*Eo!>w3g#F^nioa4fWJ?2)|2ICT+ zBuwOhxA$a@$uP0zAx|bSv5>$C9MA(82aNYf@PQ0^gA$5B;}TnxL!(?0pg$r;jHial z)_8x`d7xdP@gV@%AFsw=7@%lU9UQn)mwUMmc=E{AppugFV#6C`7u4~|Oj}3DIu-?GS}zcF(60>p2{2>jz3DBhF~EcXlK7)(pc;T@j?7aoEmBb zb7Ml7WFVAUkt5V*;%R(S6*4e<2kk}2AINxw!be90!UR(S6~D~`VlRr_D8YtiBei-?l=gn^0(WS58l{6_8or?;*2Yq#$c1uTz-pSU$K$fCU0TK~RuP zuNc~h&>K^xZh9P>FiqI(fGIh#*&EK{YI6SmF%?y4Ew8^@DxI`ANYOyMNLYJKOxcsP zd4GrHwNVp=Vl!m4V?bvZq+mc|2&4lHz#1oIrA(;}ScLl}M~7kxMAVpI3g-HU)-&@^ zC6~*FVJCd3#`X3L&XXeOIp7&iU02Xy^=uVc@8x~Z$Jb-y@quXA&I35%+- zU(M6A*SBw_$4xbPar(-;Fu$19d%Zq>Ieqr|I(k}Ny_n_i=<#;#;@s8M(Qg6dYU%R& zcK(BYp=SVU!xEBwwykdo(Y=_4%4v>Rm@Je3z6$6t_5m0W5C;#OSN4w31d4Jsqn|Qb zMrpnwwU4DDOhvRm9S>4A8=>?`NT<#oE``eQzI?wLz8pPluiFKQ4zcRj@OSyS_xSob zyY&uO83ohV*11`{fW=>3GpYjILK1s4kZ`}7d-wpo>pdFZXAkr%`u2WnHF>(ZJ?lOz zx_no3v-qhedEc++-vt!dd|k~Mbl)V6Ji$*8x673&&=#%Nke-TpU^3le` z9s7LtV#!)tbYQ5}vS?%RT7Vw1>s$Xc^W}r^IP`}UO>njWB~^vnldS_-!1BhJf-dV~ z52<$Xk-4UZxG)9*#Z%r;%)sivXUqzGhj=q?Zae7;3?3)s$Wh9 zL4fH==-uq&Qd4p#vFP7t>)sA$gA=f2Bwr_GnK79Ss9Pp+S*Lrriia;Uv0gu z1Yg}?VeChbBE#U#@h$-n+FHfdrXfl}8crueRWz2C&W|km0dd&p?+6EdP7XIMTeokq(l5R3N zQgnDAI*Xf7OsXA^W36f2*ToeZ5{BQgIn4&8Pj1;kw%HuqQv#*aaXnRp15Dbvf)q|V zH0*9(@t1}`Y)O0oUzW{wtAj{eXHWmcj+P;61a9W_0?g70kv=(Kf?4FxIzJZ|p62T3 zBndNHkRAR*DUC0z^XtdLht3Sb)mkH&Zo*)H|OThFPXdD{U!U9&7fe_6HCjR{=~ zMJ4!ZpqT9`=ehqcV-Eo`3w=W3lyQ zT~EY@b&cC)E%!uUxfFu`EiOG{T#r{8Qt3=??-C3^=nBO7&VRoI0wsR%a`}ByW&Zp! zzPWvU{Jgt*antwR`}KTZ%j@my>A0NRr+Z-bHSPBG{O;7$%lmQtZuD_z=H1%$I=TJx zeq?!l`R&-v*U#6piQC)t?%Jr$vvHkw-LAK*omF?|nr;_QMY3S}sD#2h^5lwad1WWr zgcJ}+IXX6H;@tIdT|Oa#m_;-J)6<)t98~jpPZ9X((6oMScSC1q&+c5PP`aYG|47q{ zD8K?Se{US#xM_F2ldId~=M=}X=ChgQ>cHvEo?5SWh>9)zYt>(sUG=Wf{hbbfih8CAtDJ_xviU&yo7*Z;hl+Bf}5yF&5U>C)718gsUi z)U6+LAJ7X4G5hp~Fy}X%@dyX~=j*xC{dR2jag2tYcjcCKwfz1Nqxn=t&(F*Dy8V`6 zk2i5*9s_u9F8cnve{baev7auIEUA{#Kf6 ze{0-pX?o*@KQ`W8_ZYxMwM@GtUQBRP&|6B@N_4qC5l(AGp?VHlo=4>hyd;kX4Rl@x z4GtUrWE^^f`FHTGk4|s5w_bf>z8BD~u%qkjF56Af_go~xo=R{m*Mes-sxpA_p912^ zsf2E{=>{ErO{~v+lBBh~KqrG}FuFsC9y{-c&&7NU%?B%?iSZ^in>K1=c%@@qMD5nX z1@(gF#aMTV_B6y>)9#baG?}uMd!eDdw6-}mX81Y5N5Ziamf|bw)E(Cjs1qPO(2jru z*T|F$)IB`aArG4ob;4@#kyohoL*m}pbSR9jzFf>GVyG{u%O_czTb&pT?NwSa(HDt* zVLN1WDL2$#_ct6;5FD#FEYCMyV^Enh6o8LfW4w7gX4L}`>0-ay_`NX06 z#0!-P^+)r}EsPRSSPmil62`#V1GAnGK*CfWjJrqVZO4A18|{f|a8ZwVsf#%Xxa6+1 zGlS`=d*-8Qra?-KbZ_gb>Y7x~vi%s=1INaJ-XR3}_GfmPtbg)xckB{&imuHsg+ilv z#+nO&c3B3$lf9xT?D=HwWks=)SM)0-w^Up5D~(slE6Y z$}c`2I3A1EHk)US+5jn~^*+7SJEvtnzA!lCmASrjCi2N|B2diG-%s=IB98hnzpK83 zZmFFc$ffehUtE7lz4O@e=W^&8MRj!0M=y)Uv`gVlt?yI2GQdUE83@$U_XYxC<-BUy zl;518t^q`&XN)_SekQlB+a;;^N$h55Kg7{WWHqe5CdjTY7={<{Dy44nj!dP?m>Vp5 zy1x^u_(QrRDQE<814X8~tS5T_g;8%MW_<{ER z)Ea@|jV58Htr6edz!;Xr9vn37uStwSC}(8;xQjtP1GOsx>@5Sper zP$wWnEuFf4ZbFqYzj(w$_3SNH9a{w;QP~o}RiOs4Q3mZ4@>~(PRf&NlZ>P%N^>RQV z-cu~~8^^nDp4S($e}<_{ODPw*)+7r#@K^RtS}erik>+2I5<4R?%?cRf!a&Ti;pPS5A8-&ri9L-R z7eBcsc7&>jN6i}0?CR49u7sUft%{#GDD{C`fvsnbP#t^Ypw@VZrD;*(i|s48!~9ETcI;lp)h41yFjX_dB>s-Hx$>i1H~w_9Y#iYl8D^7Z~u z4+0i?6E_S`ipd8{3rrGLAZP$+ama(XRgA?d8tON+_`Svsdl%+8gy^Ti4nH5}ctG>I z%)GfDJ!Ia#?8RVJ@ddQu4_oyFVF|(%yAv{e2#Y5PLA=-%$H#_avlpHrkY&ef>s0k zdMR%S?#!X`U$>n^nDu-%(O(0e7UMfqz9SDmn~-`^dq>7tW*>i;MOeIchS|9Q7L zCZX`$6H9>dKUrp{?X!7Nm>0!C-(;5#&$o&^Z>Ri_dD532;l5@C zf~Y10^)UjWXh_SQl@+BFGaH~Rs@XZDDk^4|;mR39e~lbpD($xYyJ~vrUK?6gS>IH+ z#Y6Mceim6@wFwT#P`iUUsGroRlb)8f@q_wiul?7O=BM@EneR3Ls(U%#6-;Zsqb5%t z^+UDeqn6anAy|UbgacSSFO$py;kYSgmsk1Jm#1C19lFtX^P>%0uZcJ#S9M;?5W{M? z*tRh^R{3b#2PY>xDR{yUO3pysQWM~}VG~Wcwu_IFmPKsxF=g^bDZ72w0W>(5P&+D_ z?%H};x;#X;pKv9B%&^Ar-Y*nR=4c2Oul3$B6u#zb_$=D~{`Y+%2eO-GX@uxXL`C^+ zx<(`nr~Ei3sSM93c^>L#vQ5p9x0hp?094FUA4+_uuKL?kn-kpcPg|Zm$*+9JB|5NN zX!z_vnQ?#0(`48aSWBrDRp__md`s3(Qz$J&mKvpO4UK8QEu+jh!>0>%LFttXwIPo( z&tEgH$h~GnlodMBoO}Nmqs&CZqBC?M+08TXK@W1SgqKo9p2HB2dN<3?4hb`oLP{() z;@rdnT3K8&3_({Zw$!sP9d`A?H=zJ z|bI=$MN+na;a z?b0d$&Nh%7eq1fdc5=s&@rwpjnBSR+yLb(^n!9&-EueqGEwN|qq~5E12JQQvTirCh za-f)>TiGd2CoT8+^J?>Lv`oiXeZ7pCF8+k%e%nZ|GU!U}Dy;>sFvwixIOfmYCA(F_ zy4)BzYv+s~PTZQGr0K4|4lX6)6~+j9)w-K0NTUYcl_-GUymzNg`RJK%GWkWZL(;Wb zU$wgOp}Y-momM>zlWH#r=Rs}l%EE*Fe410O`}cA}LMy&u8VfcqkvpoblB`1~$SDBA zBIZW;|HFGVvV!Jd=S)K^1Qh`={ohDuO1fYsX$4%!3!iAVzn~Cgd#L*J;}1|`mV0-% zuW<{D6lp+(zA{lJB;LczOW^fXoX;n^=&;C?Q|EQg_;mh)Lu7KEmp~~dZ<2_Y?@FP< zXGM6rp-ou(fSKG$?b;1fS#2^bCA|*+cn09kX%kFfT&RA4HK-3tvYYe|l@%<7 z9+;0M$RT!LcfKLe0!R>g%Vi1>k(hrx6k~deX*c*A%Ss)m0O9ZH>Mlt`L_!ILUx?T| z3Y3fv;Qn*z(qsbhe&yhh!^&`o6vq~yX1qw;9zVPl3jR$ z=3@%32C6leC?J>f43!H)bl4R_*)WIm6b+M#9YGK&>mUeT51bB_KgBgenNvKvy%uFi z9gV-P;CuwsAl#g{#OE?&=aI5YEL{xjD-j6}b6BLn-^4bPVejSmLd%L|YV>O!H-<54v`k1%uy@e{{eFQmT9u`m zF~l=ALvre@^_Vd~fD0ZSdVK(bFOetHz|bALANw!TQo0mUOsOTDjXx$L@33Q z8DLC{j2&I!-wjq)G~XGPCB2-}3 zf3%`r!_esB zJ2sMbf`0|V%P7+ihxTIVe(!l;WvWW>W-Fxcr*VnoS|6dv#3Y)y8vQHR6WC~Y#&8U1 zY-W(EdiWeb$5Ziz5BvnL{eX6{0Q8S!lJ&6r!-O5{xlWaO({pase3?74E%oqY2ns!1 zC&ClWAU`I3;z84h@*T~P)HUu@h*8u+3#$h86-voT=?tqf8GG!^r8w4Rw4x~flT3=^ z&2AP%^R^03dNV9z;&I%sC}BCY&sy5?7RJR-YHmln z1kDO_#kc!dw}i2AP8RvdB5JJm2ARP8PNx#-d&=j+2OYvv-ecq}VM*+h!3nMUf{N3e z4PPXr+*~7%62{1LH$Yqc4)F=JVwC-*4XUsCtm(yUZjP7lCCbc9Mc$@8C3U^bed`9X zQvt#9jmR^Mo6w0Q$k=jM_I+a7l)o#Fb<{_Cs;?phoo|Kw{`@K&tirCZLFePoEnwZt zZSu4=*7b;t8ePzoF_NtIh>{h89M0%j7tNlMW|iMJA*V4hZ-DG<>K)d*LSo31n}V<7<{_UrWJx{H*&$&+rRPTeQGF?Oi1~mU;Qa6I&xQ zPco1X^FFkXEWpxn&xhH4)cyz*!^LPWt!htFta3t^&E>-47r1ZI_A*WnUmD%POLvF8 zY{L_~v9n-~(Z$VZ=){p)M(@#d&VgQrofKcySkC(MaVzWU=QLfX<_g_U@>+ zW@+E-TKn!7oN-e?4EJ`&!^Mb>X{XGYUtUM=Z$oSTS>ev!-<~<8?+3P&wQvjgyN$!9 zmHfNO3Ey$*92ZrH@ChfK^dD6YwH9qW|2mCg&nVcQS@1Wl(yF~5TDvCwP^cSi3kpr` zcgE9CwjQ9+SksO4r$az& zJXv7c-mBwi((LZEjw8yp%iPJ-&1XB)2 zE8*7}O?;m%Z;!}i9X?Z>=(H@3PSD=+`0BH1G~m)B5cEviu<2&Vq$SJRBvc5~@2Hkp ze|-OPf+cDC|Ir#hq?jMAK?1_T#F5763-l9TVPW~dvZF^wyN;9{$?rscilqB-rd<@z9(8}Z%LTwpwcWkSZp>H? z&5pv-yEsh+EB$U3GvMyrgnwXY6BWUmi)YB=#C`(Yd%z6tGsZmnoIbl5eB%*&ehf0Y zlAn4z6KFJr1boVRj-E2&-YE*g6+lr~C;KV`ly{uU5DIn(s^xi|jLP>VOHB^Xzc3M? zC01S7!C7LbWj$BMf)Aj91VO9;p@2r;0uqU%&xEyrIhDhQ{l(m@C__2QNEkG}654?? zzwM70H{SvV;WNy!5&&mV(~3kAq<7`guM>+Emw@8M344rse2+~MO9%ELAI0lP8^Rp82O)RGjN0O&JiX(g#Nshfg190!qpiXY zaaD>&VW=ol4FG7uFsct1$Bu{dP{yI$@gMU^goX+VQwmgwOWQT3Vgr%pSkqe*@SLH*08lRR1oQo=0@=`>{cbPIrzvjp)6sD(t z4EF)0jQ)kA8EKf)7|AhL1n$!|g--AWLq@(J=EO(bumlu*Kv}_d1?5n$i2jn9yUkx? zS+e4)N2AcL_s{CLRw%grH6wQF94ICV)>?xb;Is#XZ9EVGDjGR;GK~ATR`MY~B1<4L zz>NG<-pLx}AJ{&5bP7-0P^q{91T~2YOlCTvXHDRKY$zN`v_yQ>D9QU!_@h+>6me(* zlcqsOXaOL2d5={kXUzCPCH4bZyV}%>fr-GKrtUtemoE;v<5wC4~hgQFqAC5 zE=E_Icaw#N9Vv^mD;v~wS&gx34^aTxBrQLCW8)|B_;h2q2B6urng<~oPc4&1y9??w zm~%@CZL7*H!Lc9CqHg7G4FYr4ml3C%vWs~NC=D=ZvwR6o%x?()D)R}?RU2gdZ$QJv zpe#dx-3Qot)v!B!$3w`PB_fYO`!Q5a917y^TLfN^8J5>GS}2lZc)xly@cP-XBdMnw zh7EWGNQhR;P+e{iYgkaIQC`NHsepg}L{pVQyhR}OZ&@A)9c;@i<8_XdMzo7{eVkvsCC8T^DCN9;PTT6)?;k z$oF0V!^i4n+45E0&A+=@zyJ$+Fl!?W>U9@E-`DeQ-+Lx&tyQZe?Vg$)n}PK*?6&qV zmnEFPh5<*ZJA`dkq3d*hYZ=J2aA&yMn#m#roD`#QBT<;;q z%bt;~;N$Xr;BAU&gSTQ$?@9;+LHhXFoz0#~b`?t~h-(fqIP`_bXUBq|ig6k5vCxWe z)yaXy!?K*ey+Q$kX%QQXH^#S63J$$GJ-II5y3K;+7;<6Hb5` z66?|og$xB_|38xG8gKQQ6(e)?>teL&?^~DCg;VY88zbkA%mK|Tc4~(6#uG6yQ8_F) z)HJspST>9wTiJZF9(JzTY(rJ@Sy$bjeC-)peJxfyHpkP38<$I_3v6wrsT`^7Wb{7A z%J#+KFfm|}-OlrVYdD6U^LD2*NG||%oKBsK*Qv4{^#0Kx5?75W4+rCEqu&W)1K}>} zq}HfGNF@xgB2~U|-dUi$t3x~Sx5&!sI~RM5G~;kZxGU-~A%^ydO&-|KZiKY>IVX$^ z%{0IJI2Pd&wD4uM*{@%BLW~B8o6UP|LH4PFf1g*m_w@nZKhH*`4L&cwy#1bD?D+uR z-{+11p8D8%sd-)iRS7O%L+T*1DrMZPe+j_f=8U8_>k99i?r=Bd=9z!&#&>HcW9-Ad zZ^Q@S{W5m*{qL)5_xpV@?)%LJ@b)?VvPSK zc`#RGzix`Vuwo7Q-cV75smRpIaffd_FO3yJC|F416&*;QzR)+2A)he=_n4Ua1AD9X z3ogR~s=N7OC>E1~+41TIv`J@rspn>LU>NBk9sXHf6tioi1R#7As4%d7JewhbM{c$$ zfsyW^Sk;XdyJTLm86>HMlWs^2vdU-NRl4Dk$dHTs#C}i!q8HCqD8Dz~R7KT|peU4` zpwYxG@vH_d{d?FX%JIj@O&z|`tDegK_p34*0SUKQXUQ<9ib>AXKD3{06y4w$RD&F} zDgke8v%_{w1R&YhVV5u+K7s4_Z+$Y2r67IVZa-Oe-L?H+;s+VhQRM8yx{W;N_2`}n zUBVU=2mK?LS#4>|B=2BPZC(`p7A7;vS{zg^6yXUc$+u?USV3*joYVrh$QfStEqnu9 z%{mu#YHTC+!zGk@YA2Uit?h-q#986(Ix@AJxhX=(wn|)rY23Qqd40ONIF>lifFOp$ z9A_-=CvWW3bk?dtK7433mto*vCkle{Xe6p8SrrxXXAr3pCRG7*v7^#w;d0irfKAYreFKO|@1Di#j%c{ga23*Y>JLlW> zdaB1vgNhDW3T(|D%WsYn$37FAV+XlQXItcPFDR*3_qdkb?HKOx;je=R7V7TGl__PN zrGXY`X3ESxqs%PVkJ&UYbfXfhL={=*Yf?60Dq@@v!QJvwj0+lf8una3DjbGSmA%Rz zDX&i`UYGKyZ^9M3);<|eX|)?1)p{kgY*MytL~AY}s-s|Oufc$sPZvTn*kUJzJJ4`m zAyG#er+^OblsvFtydX$q5iX#=@bn+`#A+EEQkx3`vKI84NyD9t4Mr^^<~qm2si<#9 zREjwc@!K>(aB52&PF(?j=4h^eDF(g0tNXqv!>evoSIlOwjLgL-3-7^L9oGX51;6P3 zdYio#Xfj{kh~VqjJYtuzMFsgm2|Hvtz;JK@VT6cJa>|ftB1aLhgS)9e{A#L0mDS$M z2#7Y0XD&z)auh!Bl;VW+&;#fB4Xn4*mO)#5jw>4Hlsq>c*#hYRIP*l>Ss8rZiizFO zv3A*cg30lQjX$%>%97nODG#b-LvG?8&;@w7N+HvKjL?6)QM^GI00k zQT_5fdoa9wdVW;{Y&F$2#Z7>;l38h|wX9-37aXYeK*Fgocj`)AO*WERBQTG4Fdmqo z7cD{^_~@eSM96Mxon9&3Fo?0+ee49M#O1giQKJJF$RS#l{;`Qzf>M?WRCpfdgNEXb z<#5iaXCRj`qbrt&j1r&G478`Tt{INFSrE*Tv`P&M<+4x*49jChyy?rG^yvMNWJQA@ zkF{(#%`f68o^_H54shOT#t9z}EUxseMvM;LKiA=`R(YK@8hzzS@@6AEC%TUtl z+m8=_b^drf@(Z-Uzsjc|%w^ye&*yLQn&Rc-#BL4bxr)gN8@|@YKdoDlOH99&ifELAR(hITQFl;u0NJ{vFF7jz!pts(E~~x{+ z#QVfrwzfVkgQt>4sM;xPXHbk=?v3%4>B6{}L&tzes&OnY+QH{nYJr*x?r{oAGb|q1 zLK;q=3Sk6{Ii!$oXa+)gQ2CYPaRpdrdf<2Q_}mACkYL(U}*ATDXZL+9YR zQp#o`)vDLU{>M)ek$+*}4aP>6$F=%2b2hI>1H&Gvlf2{9>Y*{(v_!`NeFfNN%FB zA6QI|48`KlC@~Tn>*Jmr|To9M0+sF`DTSl`Tbo{`i#Zq%$y2zJel5dO;1~zf=sq-6`Sbz*yi+ zdQNZxJtOtVsa-)Qlk~;Y#{TB89ZGvIMW0U=b2cM3 zCSPQW$P#UNig(eW{v+Y3fLmcHWt*&fd;>#g7lpmS)wH!&!R>Tj9&5WWF6Rk8Wd4+I zYDE?4v%&l&lDoSWY4jD0kH_>I`!K7@{Xq}Elz^hbP`}-8F1+4NgZFf;DAzPsX>`wq z8!*&y2pV$`=M%FSqA{`PFGQwUZ)bnI*DgHH7qysozCS)w9RIAE&AuP>)oDkjpY3oZ zpuHgVHIz_p-fS<`M00#(K0CpLW$|H5HEMiHsk2A+uu1pe^F=!N$tf!?>oX~VlUhla z5jC4$UA=nBBXb2}{Q-{o@yhht;<^mDD4BcCHsIPhZUu|(@P(JD@DiWM%ce4em^$}{ zZGt)d=w!y8N`SryqPsbKqF!Lcyj6HO+Se&Tv9Q?IF_HoCbgZcjn6_r{Ah_%R!zHOZ zmng#$UPL1X&4a*Ex8Aa^#t+tFQpAax4{KtHc+MY{6_5G7%JBg^)JH~jrB~?5IC3-Q zWt^++&LJC9&P==IYa-2`R_5Hwqye`FhQ|$VPae#pGX81=GEsR-f8C*iZ5C=spK?q2 zy7(B&=G6PYB28&{nNrR%)Zf)G;b&JYCRq-j+mTL9?W%$;I&&yxOsmN>l4FI=+Od?UZ@Nm!FMGHU&MHzu0|T@;pjS$$;*HtM8{W;Ipe9mx?FVcOG3%UuK6`y zu$7HsX)r{X%Hn4x3 z!Sqe1((BBNQS@cBDH76o9fB=xry9MKbWGuV^H1q_p8cy%d&lSR5#h z|5WvqI${4nr-+Y#$nTN&z`jHOk4)Ai-Vf^oozgu4iyZg$Yt!){tQ<5H=>JIg>5RZ4 zLcy?nXKAt+g&B7jsL78%n4yOXenes6TPm>P@ zTt5yJFs0^0#URC;8jc%^HAM&mj;CpZ8g2(31_b2#56vToW&Hpx{tsP(h3)@F^D4Bp z?Dhxae5b0mv<5Zw=TzqxgIW9*#qY_YjR3m({s}kVq|J6TmncML0Fzzvs%{+1XMIKH z75>xJ10udUDOF7Cdy3(!RL1Ukyme6GtPqiXJ7|Y5fhqNd&B;D67-1TW=5Wg zZXgh}gvGP5UMCF1LQoR!{8+;i*ixrz_WD!&416-BABkj{2wI|ohU&8$C>T<%BX(^XILW_*Uu zx$=i#QR5&ZtBm9J&}1Kse5lXNZkUxMsO}r@>;egIgygFeSnGgA`pg#2aIFZK~QM z0iV+Ht-4K-0iGoyNnYF(1t}x-pCRfroYFWr=LVaGTEV@ywH70YN5&qvFXGp<3GF_B zMJskULSngJ2IGL}8QO;gXZL#l=$rBB==U+E*?cqZ+@;8>4-N4QVbrfRB;qXES90N{4x}?vy`^qG1@D}4J=y9%y z=$1DvMad`_nW11wNAjS9UP`Ez=oz#KLyE64iYh@>*NwyilF!IwH6Esv=!Fj$DU(t| z6L(}9{VdZtGxi-z6B{-4n7IJ0|KaC|o&TRn4K*Y^^s%3a*INqK_c_XZ0Xhxi^DsrLLwNmlGA2e4i;pmz# zG0dn$g71#Ug z<$B+Qx?MKE&%1Mr*W2ag72{IVMV%YOwzI>h-P?O|;#b<6XuKFB=$FJC05|pU_q+r%(-rHwL7g?4`HR9m8Q?0e|HOFT_zDp^_KY zWkxF#SN_y}wNx_*#&s=3HahmE*Wv!AGndw}5*D6idVd6ZM3XEOn7Q50+(+w?EEM>~QS z6va}?@JSS8f@{c27D50P<^Nm<1U~KgLTo7^{G;}U#JY5o*Oj3 zN*biG88X)fD=uFw_390o%Z4~vurmXD-5loB`uEUeq8Aeu|BdVxWiQqk!TjWGWorxA z4$&WtZ9H-Cy01-4dT$tOPTU>8Q)OIE3LjKzm}eS*QCcosnV11gh&6D=&}FTCy?rqu zYXxj_Zfeg|a=Mnwnm0%x)H{5YF;@a#8rhsZ$7ckAaqfIUIad?h-fne8dTw4;QM!&{ zpINO57ZBMFVJ*`IyJ*tkRGa3m?axCrcR1CXIo0d$)@%;L8_)J_s%ionAZ?4HZcXmc z5Ihss!}x1O5C{F{LhIa`Q`6M!pK#@bxk3w?5ES`UUDuV>r2Fc8AEM&XmZA2x%9jS> zv0WH2pFl_<@?}H)S>a~$k1mA7zm4tJXPCN`Uij|R?1~&k&SJn&RWd)BM<`GMsaAT zZprP`IN^2S(u%#;+7R7+v92{Okv1@)2dcbzcw~L4!$kbb_?;G)%~FigIPxH}*PvIJ zvLTh;kD$OP+g8_=b51dvY2o~AE78prve~IqoOnzihdA5|`l9L0jS32E}UJ6x7hw#vih&wigbF%(l&__emwql$Iu| zWjTTNf|n~qdX-!PDb2 zr(Q%q^Fg0I zSozmGh~7&#(ySWq;Q_JMX-ATYtQW31CxL%-Jl129N6R6BFv%xSHA28|cQ?Vs|DH`0 zNl!0^hRpb{oaqW(=-9Fe4psKDkeG{imP?l`=w{?KY=vnM7!c?Xv{2#Tkdbh)8E7;V zN`5N{^x+yE}NmV&j=hhv&}vm(k##1S`9mt)uY@88w-V`=-D z+L}|X7`(c%y%;?BfA&9vrOxi~KLa;_hpngJEp+vNJBin@E=X{c*og`X!a|7uk8@pO zMi=8y+&funZ%+rIlG%rPSII7L8)QT)h{5Ly*=TMk$R5SLRv}TA-Ctf+$ySy%33Q=b zol*AaWJv-ez$h=v=8Q5mBwF(YkSN9!Nc@r5?@-+NYSO8A@^v7CLi|ebf0rfEykJ?J ziingEB73S}J|cUj;4UKjZ$Szq`>`!QqmMR`J6EH74U9nlL4dhJfHAdN`$tPA#ss15*v#N?Zoo%J}-v zo}F}`DNU@)aJ}Z?(J8}-0%$DEV0N0Z4FF<_g{j3vQN;z zfRWN4g=Tqz(toS5TiiKN_zd4ZEfr2$ot*c~I8-kUeB$tthDp2pZX95PGpjMsnJeY` z2_r@%C{S1ZF=DfxU8q#NYvJ^#Hksw%j|S^$B57)dhH@u@^co+F3W8+vrPguVmM>8} zs4X^j>{me1r4T;@{vsw)id3i|K2#xz4zOIrrot*TtasF+vQ0^YM|K99V_R=3eAWK% z()B>9g0%d)rC`bGdC_l?I=G%jce1eaNppy&*ZR7oTMNeKX!2@5b6soeM}G1$34{#b zTawKcAy_Eu5{_E6F@(&GS#e=eC;?HAW8&G^_vVstFX~Fk%+kam3#hoKWmT@f>A>qQ z)unSYSb3X2ZS?*6wsQ4smfU0UDzCuh7nHwaso6veL9DhX}4Ac6)f zDtAEBmB8Qw_41^DRou``S$0Hu) z*U7%^DOfKK3X3Dh+xR*3#iJ!WCtTRuVb4F^lt(#ZZ^0(<`#`~JZ zAx8GbjCY-fP;YL-A0&-CBoI3emEyK=XLdYrKigu zT5HIL64Cva+|`s^b9gZNJ+QxaN-RU3+Tz9in(!>VP#P_nQf(W;fbU0*X-?>^5H=;ZWX)er<)`sfXBM%V#+8X55lTz!3*XlV`3sc0K8H7(Fz{@k zK*%bV5B?BCeDy?@p!#DC<2*x6=x2Iy3(wrKo{_#hRSdb?bKrq95{+EGuB6`*@)vkE zb*8fjSj4RINaBVdR~~u3Hnx3@H~@EHGQEfBZ1ssMMhuot`QLPW-fr`C7ib+_+-) z(`U2ylswRs%hq_&Lj>>NxAPr3QkU1{d@>N-XU%|(x zjmTEe`v?(V&3ht-xQ%+Mo;q}<@efz$P#1}NO z#?;b8bT!?(nu`54HZ*pA)3=*ZW!KgA0=b<+n-Y)_xQvX6NqNYr*!W#PfqZ>;e)GgZ z{``8)Sr!AaeS13oIEvT!7J1IEgEyqhCuRnubIxa$NQ`8x7qdms|n^0p7`a>>vN88+S@%m3b%*yyE%s%Y5=Uk58uadqdV&4O`k1eF$C(D`m4X_Z z=LL%aI(YNNwWuc(sB@@OiuA-EwVq*?NhWqm_p$XbIUdZW1Mei{lYW9zi=1r5U zJsovSk$0AZSp%z!fZz9xkgVZD53!?o$8_sTO#YIcI46SIoYM)Mvdjq^Ghs3bYsZfb zCX&)g;8j3%)6s0fY|zSyXCki2yC~1Z%qk%v??-GhxlerPa=->T<9`n>keLo z5-pGEc4WqUMutClTr${qPaaSrS=RUhyT28oIBM4UDa5c(p)yj;{|GajWIw(@nGkF# z2715POby1pPr-PB#r_Ysq=utPIc9=mg=Jx3W&01e#Q(y=#Vl^&=;Zv3g`GX2r~B(i zHRAsx($$cxXSebly?gV4_GPKe@E+t!B#na!7B@I;Mb*$$zaxW-5zm9AJm&0to%|~Z z4X{$fpjA7|Jil3oOcsBp_%u%Tn|B9u8z_o(Y{^p+8XxHni@yu2?QaBK4P{Lf9ThQK z5D&yVqdyn0=B=n6`wT|NMLcBcT$$VruikB~1b!1<@EDP=K-i9Z`<;vvGsxRnZN_a|GXZ23QZ0_uZOu6Kf+ z03SA#9l+0QcB<__P@!I4SXjX0Z2=d{4L~xvxOvu)g-w2mvK*|dcfP@S1w_J?!mmK- zm&|FD2=a=IJ`1gpwN)TE3)5 z2S{B@7O4DbNT0Zz(vjsBv_x%%CbG!t-RC=}d}F}pI+#r(i*z-8V;A>wjQygnH~GcV z`FyHxDYNeON&|dR+Ld`V3T7Q}t;fvLH!aUk)gb6jUjQ^hNPusH235Bzj4px|(gNc{ z+pt-}Y(`dnvfe5%DPfSr^_y(eKD(Pwg(&f*CTS$UdRiX;xy)G}&I^fWoB29pyUvmD zJTab`7o!tP7*B5#7JKyO8R^ip_Z>9{&kbW*jz*@!41vc!L|mYOlo=`}5~TV=6_b-O za(3=4!&dd2CoF$^%9{gGfp$VeH{sK9n$G?3#2Fj@Z63~+-FjP!XI)3MEJnB_?KIby zp-7k55XB^7uZQ#5{Ic2E7Q!!8o~1u34rx&HjfzNOyAdePdem#FU07PZHm2d&Ad`Q-ySIvSV9LXC|-T0{Y18t7lGF;T_4fp zhXqw(28d~?LiKkxtF7b(9a;mAJn6{V7JRA5fd|2zA@vE-(Jdx|r3#n<*6 z@J)7L0|%Q&7LF&Bm?1RJ5Q1~o5ut-!tq&8IXUOdL4OeuI$BFGDU^@*-wImo{2#kzR z$eNBDv(j?4Z{GZyGMjw4Zy&p;eLmxlG0|L*7g$cd(K=;ZFiN$ND&Qb4UQTWs2iHyu zqj4K*_9V~mY1-Msan8F=ezIGy|D_8E;N(8aL-6^Fmyt{QWaRXvTkuP-{+AxyB}^>? zn#MuSCEQg5uQNa58Z6gArX^Jdc`hgMbzsWplZz!7FGKv0{A@eBxi|hLyk`S2kspi$ zy0{^>xY3uv9ettz5f;>42Yl2a3@{K=V%wZ35HU5>hy`(I+r0+baY9pmtNd=5dIyjh zsr;Ql*`7|W?-&T9>_Ant&5#*&3`98G;T`XOZ2p9|L#`buZBNV8x8Dq{zC))SWo6G; z*>`6c{H8zY26Oo5JxgZ1H>w$tYZrGdeAS-kqC2Y@*=?6(E#lc;fTTOn9sC(>7h`-d z-~CI>E+TJO@nyY67EeYUuut>L&a!m~zhUrmS_Q~D3Y)&H=XpH(qx<3h!4b9E#iq=m zCU<^+tK%HtP%>|z>|c|Vqm*HP*|JqUZ#yg|<4R7cTI5*O z0NcN%kfd&HnK7jW##L+Ju61Vcy@{%(TXK4unnvDj;&?% z)xld>2CVgj7B>6DlTc4Ff?Ir;vTGsl7}**KWa(n@khtCj?*P>Rz9&>joNi>#|=Mj}!zKAeF*7PLsF>VT=0eB*)QudurF7%DXCDZE3$R4LE6Og!qg zX+Vb*Qvb;FA1QK4rU>Pw4Sp*F(48LEyLgrvJ6@jE zpi60OU+*Hk1~5M-wPVV}`3SrwbZ`T+j=0C;&4ID($>n-SfN%)|e5(4sNxfAk{S(i1 zQt6%InG6sv5oH}Aop2KkIsU*W+>Jq=l2^B5slv`*o&y(3i`&xiX>(PV9GyL$-qP}f zljIiN;xB?1#7$hDIf+G{oGy50;p^2r_rIyLTB$<}fcJ)fNDhhO%_9fM@PAzaGbdYs zF<@gr^dKzJm|6!tt0a{HtK1NKrtFlrC7&=~NK47{)NeB=exZ)4vP;m(yscctdyg0Z z3O|T!3Dd<}ONKbSq6VeWWU*ast^EE}ut5SX~Ki2m^?z?r3r_TNmyL#bkU+2mS~dN7J6fw%rVCK~Ix2lMR5j@5G0O@F#k6 zuMC16lb)1!+(*k5#k&4&iVx);RD7O*uMTXs12)aXZhuQ(J;!+*&`1U;Qe|T<&S^-7 zu-uWR{^pxy1-3NfRsD`W^yFQ<`xry3q zP87h6dfIjjyknnh5@C+)?krh+hs_2Mgen5@cRvVQo>THY*qI?uxIEHR7AL&zeknls zuR5NS3eUfpIr|)dBzLXHc6&!%dcX$OI_Dil~(xL>S=|iehkR5n)$-=+i#LarDO6Z?&|K(&|RZt%4 z&csn5GrZK=C(7=URkz1;&dv{h&dFMoIO|rXuZHWs$JcsZ(dyHQGC_ohVppiMjI{Xc zbz1b(og?baF@q@a!!39;b11CjVcWvf*4{NjVe>Q7$$Y20Jj%w9^{-=$(Vs}twH-mQI6TeQBl>>3* z5K3xTh69CdZ$&elOX5bz>eaW4ew`Ad+UQp9%9G*J{Ep}-YDMr+Q=dDmi}TFYA^r)z zBec^McDA@rxRXN8L|w^4F+ZM1YRsV`yBH~D$9;7H7c6*lH7;-s<^yfA1SLg`#oOgeVo9Yq0gtAUI})m<0qA&&lc6o zPjYLdu0pA6Hf8S?*Wy?IwAos!+`TDe-gJ41B~sVAN-H@Z5w1j4Gk7(4r_W{H6nSai pEuT)aOD#;Te!DC2CEtOau;S!XY?RpRVestAjhy(@N1B zq`+y}m<0r2T-{vEjqPE)HfMBY9dS94`rb6J37MwG?!mEm(5yh8(^lJyAia}a!DRsD zCu4@ohnbvLN(gRBaw6=<*QS$Tq z8J*p?f7i_DLkEhLi69m^(*IsCV*&t?#7tK;>q7nmv?xZRE0@d#0_Xfy(}gTK9|h@< zi77XAg>{CQ5W7()`9;OAa@OsB+CY3u8f&xw`hG^je&8_ABMK$%TT%x^kwOF)?|THt zR_xC3YS6jaD{{p&nnRq%&?#w%&&ESCm_8@rSVn%uAmkq2rlosx1(b?!_<0K)AmoF- zCEbr+&eAbxR07IWD|0(cPDdN%^l7ZHec0igu;_oYs(tqOV&Q0V)ZNL**B>asp=e!M zZbE`Od=kW+ZRc0)8$YAc!n>Y%jB4)^fJ=7(qChHW)fX8`iUcA$KC}hCOfoT~4xpNR zl81?WUeZ>ILCM4$sNp!$X(yqg-hFo7#;T>9M!w0q-ub(Wgok_^6}xFQ6y&!xRi4>h z1s*+CR6+q5=4vqq_nw)gQxrGoP}OCAu`9G9XLf9pPkWs`c-Jsd7a|aY5qz^;l{Wz4 zsU->|*Q%&!S5B2ZK)E71vuHH$o>{-3sRK!G@h*%1F#l5L*da_aJwmRClUFCehnQ~< z_K$o4EPDSAkPjy&&_Kr`FUxc?HIxP*6WbVvSE`4Cw;4+pNd;T?LK!$7yl90CDi*GM zhvW>%S}Eh6KS9*CLjj^*m~GvVN$WZ0J#(aYO_gXw3v7n3 zQLI)jvZ?{*f-V}@34cv&3gHTfmmit&<6CZ;$l5oD;ap|CDLAsQ>ytBtf_Iu|sZ2h48gLRN^!HDv#(j-DY#J#;OAj9*LHE%)PlfHR z;h$sj5t>4)n+=>}1wlzQHfxjU4|!T8vYCsniEZh1p$R{vBUAKml&A$PQ*VMVKeob| zsNp2SKP1YKYWv_xS8xXe`xh{|2x{Q&Q$8=uZ3QgoS|$VthD@s-I;HpXhV%#o^PTFN z30u4RMmu6p)YP72*ZDz+0H;MzNBFBK_ zORMS@rg3NqwG9s~wjBUy@tr%gQEEm&Od13sEX1{}eR%@PwE&ZrZWT#Y8P zzI#r*$V&dK&U3ie-<9zR(K9Y0N9NQ^gtsXScT*8M!_t#K1!P~&LD{Q?pKN9DW>{tA zycORa{B;0NqhdwYLYj_5SZh28kA!jtcWWkebFjdkaTu&--<$``3`P4?+W_gyQwKAr zuu$9o`8N;DnY!@m>>TQ5ZL^^7^RqWEyvme_c$Kj=`Nqjshn&&kLrFV>=A6WYRh7bVqUFd`Tg`*Z4`RHV>iB3siFhG|+BIw1>I*FHn|G zKg4OsL2pEeVojna60aV!d|}J-hVu_Pu=S6$kT_e}e0{rFtqf_@yE>irZKK_4)gWJ&bxSfVjgI>ek&_{ZDg2 ztp3-Yo%`I$sh~lCc7Wd?RmL#&qzZ4euI=$XVzrEA(11Dh&4@NgPY z#PFMQ?G*PEK(z-V^x0g0Q3s+m$kX$jWmvHs&#aqYROi`HC|Bj>qucDp1HN5sN)4B& z;(-H|G5LnAHMAJzN=*20ZhsY2F--fgWQaw^a*KMf4 z#z_yis`805WMW4UbGj>v)z+T(Y6wrvp3>ofTk2vO4ARMlh+hqT+HH-}lyeG%@|KP# z8$e32s_VA3{EI8jxs)ZWXTP zu!zwJV0qa#L5ii##Mwu37|VR=F$Ns30l2>&8u~>tOq}TkV`H6=8#q7(6p?_`8Y7L*U>g4|A~VPwV=WpaCQtNcf;~i5ri`u08=J7 zqAUYnplCkxbw}LI4L-P?iAv|pUaz~uhv|?-H$2+tj?b280q$oOXF(*rOuW6r_E_-; z@ZN7#0jxfB6*_9G@=?w}u)Xh~82D&=t1#R@Ksuk?&MA-TCln&aAIK918B=o_3u(jq zO!uOv@_cF&{ipIuswaX|A;o%Hm*wP+3Ze)1A7);(AS zSwk;zffwQh4)@FUiRg@rJOw%usZtUFue(-kallTS6709$Br-F*EC$E{b{$GXA9KSh z)+TCqV9p_GRK*9%_Gv;fPs@tkrPUs?j%}{WpzHkm@ChYr%(I(h;(n>Pr3;1YNKr~0 za$Sce@Gs3lL`a2wNF!ctqwsIe^nya)Or8}*C7Z~QMKJDE_vExi6Eb5T&{maza#|w6 zEW#_+y+WkLtkWHxlwG3IrSEML9UGYLQw8KHF@&34eIg41%k^X0EI zUJB)5Z0>>6;0SQ^W!px|O>Ijlk|C0&1JgH+lR5%~SD6bKq644lX)ovy{8?daljE%{ z$99xDL5T9K?y&-j!i(?$E1v+;OTkz_G{hxf60$h2{gM1B zHsy%`YP*R)VWBBqk02Z=M^c?TD~ZHY+YAs&pfxBA2{fA>Pf&EJ;*};NzJzRQfPBH@ z*ULnG4%}@fBbEa9w(wt(k=L`_{0v?G{8}lK7w_5rM6l&wYI!?eMF4~^q{;2G5K*TQ zsVPq2_<1Nss>idX79XI8RO;xC?j{f^2D%#r?d{{XR;Tm_D-_W=mf87e$YzL%gFA9E zhWvCFCK;t zazDv?sqrvNSJnlxN={1*6Z)y9Q@w^GcOW3~7n7B1EN~;|Un<}*3+e(Q<6u>x0MA<2 zHj>VEo!A8@6BD$C?8bHostn&aM}u$+Cf`R+yX?0fKh+dP% zR-d77oZKEjY7GVb3oeM&>WOdO5H{ZWuMJ&d*_7M7o0 z%z$U#*%RP6Ize2c5kG#_p%YwTC&r%+iXKbQHW{*D!6b`zJ{lp&lMQpLY4`FPMg8gU zSl`?)Iy7L6L1Eg1`)4b0?@N$ zxgs0M{l&5SEEg>0V~?oO3uYP5i;U)*IE|HI@I~A}LW?odM#EP0q+g2Q$=+-&z7nFZ ztilA;jZB^v1BcOmNaBV>Pl^Q`iTScAa(VzqnPtnI)D^KbHR$CI-Z^AYmo6Km`3`uP zqB`%=-(s~;Gs;x4Y3Aw(&;K@cQy^gAX62lNbjl1vVIZ6I@{()lRuGgRqpc#xHAHj0 z@;ZefP}D$V>b~|IlpJoY_KXJ6OF){h-5>O$P8Y|kzaRrdNfC&_km$GoEo6`;4`Ijy zFx5!)=Kom<+JfyeD4})`qM&ZqYq40s9lOkR$U~>@fpeAC7rw-a+Iq;jYzW72Xpu07 zuh})DDY$UVemf1!ql^JlbeK0g@sBvP*p;M`rrC}8N{rb*ZT_OL_KX8%{bK=Rv(5ln z0*0?EDKO@yaUS<{nWn&1m%8C6F8AsM^bxbjzecAlk+-2^W76LZOyNK!FHT?WhD;<5 zg&tF7UNYpy;d~EdbFT^o4c7CC;bA)5@oe=0jmx>euw75Ui^}7$SF+QJ!Z5Qyk62nm zwWC>B1OUU2Dq40CbdIz$&R4;=B-M*Cr5Vw5FY>GJ0S~6mnh`*^m?2$#tnZcs^lUh$ zt;}wa?U16p=L(}&9J%?zkvwg)$jOt0Wva*pvfR!F*xluQW#fDfT=nuw-Qzs$=kL67 z&T+u0cNzISjWuTVOB6^o{(5sNuCSu4F%4n$s*vOVz7AM#C&=_B_~K662!C@~`lCI% z=PG8a4^F4BZiU5%%mEVafqGE^etjVZv^OEQ7)~<62<&+BA|)3u$~gTiPvfs(`jqo_ z8f~WjhiRxuPX|nxCad5koX$5TRi!8&hng~K4p@Fv+YJQ}u>PFTwGa)hQ2eCN{3W-+ ztRxM_&NL=PF5adwe>G_|RQm-E`zxdZ6h0n?Wg!1Zu0f%K24~*mjVc|`}=}!1qC;_M9fjKrE&#Ft_)u7jyqnO zOGZv5;SyQ)>)7VnP>adQN!XZxc<6dH_djiV+#Nb2mm#>V{*VGKvT@M~TahlvQyXf8 zdw-<9DE@vfj;l?b25*C}pL)Px3deARoVmF`qQ;hYufy#tYvoXVg2_r>Uop5h*wnnH zeOrm*+g4b$tQgyAF{Vi4RP!UiU3IdqLmDNp(9j+(xa^9fl#XW$wvC3jY6ueW_APqKyQKq=@A!D*r^+~8S zx&IYJu>Bbsh!qm?4j!mICl;0oPsBqM;^?M5t?LHzf-H~nR5{<}(A$QQLQvge^eQ*{ zV`k-%)g!9pglLJ<&e7-&MjGt{m0X2G3Xn|5RdABgws1aaCgJ$6!8t#MOB@os=KbkW zaBC=+6W2P*h+c-mi#1zZhvZ5?fv00==wngVEK1NL_!#Y@o(B)k!Zd0f3o?!y+vW#3 zl7NOlNylOPK2{#ZWc6+inwQoiD1}>2x@!Y%@3%Sv;9B2=rEnFY6COw<{K?^bAm5I2 zC<%eknw5<3!dauPDV%LJm}GMaY8sZ7dV<#x2|V_CJvvUd!zF>m7eNJ^lHav@Fj$+CsrnF?wj8t^)9 zs%-b|-)NB2%5KDM^-AQJZZrI;NJGz*iY)b1hJsIeegnJNMDr{r_Pnn-W1DtkzY@o) zBP+JU3qW$5pNpn(Jm9Elq~cXRwQoWf0fpaJX6Y5ZC4BjpqoMdUt^-EXX{pN1oCUYW z1=7qyE;7uo4qS#))QAe#&AI{1)?o3g+fuzjryOa(KE~$oV$sXnQt>A>M{HC+X4>F1a>~oBoN zi4jm6D(RKo!?+o49hA3ySFBwZ((<(M2lJd{{!T)*wU#VEyq2`m)rpdz%dE9;`8 zvK4qdoIAeLB+Kv%5SMEvG#)Z(k6`0jiGg<%5w!ayFw~Qze!t9z7eFU`t!B1fd^nOs zEOX)_sxdt`2p9W;XFJwdB^ue`EVBcd+2*cXESF5{0=zs>B9Q)P3a-|+`2=*%3-Kz` zDDi+F;;Is3XB-H8rHiVL39OZ%HyY;{6C^Eob{xbqL*z2rG*!Xb z*P(r`Dt+T0-$_3-Ha;&i}&OVXpmGZ za*QE!J0h^7G7yP-B(p$jl_bUEg|6Bh*HpbJrU zz^N2V#yRo`2XI&k7ilweZY3s*^rBv-R^p(^icCI(>!}q`1bxD5w0Z)3R3uLWrV^gT zsvV?_6{_r(Qi=tak-Dh3)ycK+z&~<9?IDichO1IqP5um%xa9kn16T6I=ZKfIyHc0= zK|AAgk;+U8xJaC?3{I_O2#c7J&zL6hbdZ2TMJpY22%wyQ*VV%`7@SRG?%|>Tzzc?; zlOu)hC4Rfg{wK5NSlL+-Z*qA;OyV7@@~mPqmzoq>$+km=PBOP!>oIj%EmME3PtDPC z&=Z5Ci%3sqDu?=2O*X?XP6rodwSf>p{ku;y*isf5$rMCeS%g+<$;BDx_NXZ=B#IBT}Q@nvom!(5tl4SZ}KjjUp0D)7ZehGAC*R z4YYADE=VbifeooL)7ip|NOj7N=4@4{h}&$&lm!vz41a;2Fi{P@#AiBlO2q8kS?h1e z^3zZiCn(l^6L~|s`hbm}W63ikxA621gFi?dKVaI^Oi1hi7G8=P0t5nrEnTg^W!e|4 zyc6zIdwEipJ&#C%1oSx4y&@Z3VPsSD3gk;>;><8%LklNZ*C-gR$(_QA%vBUNXR-*1+1|eQ4>=c7n2~xc zEP$aSa$S);tkCjiW7hu|WJu3fRxG|$%UhuagBPnN9_rQ`x})1jXG1xVMRkGPMIe0b zNF;Pzc|1h_jNs$j@dEP}yo(~KsQ$iA zmRE$I0!E>R?naEZ{L}(!A25=fUiDX~Uq#uvtJ`fT(oKFLKrP$Gn`>E*IJT*Cx`c$M z)F!EOCTD)KdIJh%7lx)NdDxafe4QYRdr<_LHxMVu+j-8l9O34g=Onv<-GPt_0C-%b zB#{J(a(rvg;Foc!~i!LXzY5xPhPX@YUEct zWVNX7G@8XJ8b%ignGAA$+$$}j74Xx-ujq`C9>c;udrWLOg}qu(mUH{wV~EOt2z zhZ8!1iM>>l3ru&HiI(gqY9spbfLW~OShKS28Av?Kl7&^~b}J{S8%82>Gbu}PsoUd+ zgKY!yl)4x&q_6Z}GSzjLto--^==ORoJNB>sPq z=n39bJZbwwUi~>?2FD(v5K=HuBk&%_diKGvSn&?tHIWCx$2Xd`x>=&Ub<5SmrgH;< z?96F#l~xUB>vzkA{Ged3c=O+cx0Lp&KlktOU;d$(djo`iw;Z_d{Z9lxce#5%uQNpt zXMeT~cO4D=?b~|*XGTM>Kf`5j70(tVZVFqU>30e;-sLG%G8dTdJ$dLgCfCe!%a|>)M+maLx?yKr^WqLryJEYO0a@yKA&?EKm$2mY=ul z!5u4~T#tfo$zZaFvtk#@dwH!#{WopRgyZjq4C_z1hxi=e`3Aq&dx-m@c=q|W5d988 zZL59Db8mQT%J}LtXLOyvo4#x3ubRap>uvqP`z(b{;n3wyO-DDw$3KGX@*^!(n3*E zy8O84d6ZW`PLaI3&e>O6vRW&hk;gopbP4 zVxO`6yvqp0M8{6q@*Lg4tT9{HpBsr==VL*N9Aw_%A6lD(*TDu2eH>YR|Ml!?E+6Cm z&Jz}UTi5LK&V(Wmppv82vuz1A)5NVrdDtD=+^bCgvSed2v6G&HePM#30~M7C*0Xbf z+7{B(^uu(5`{qmqhQ%Q$=+>e!rkKq-N-?s;IE4Awa*KMHt^dg;N8`GGNvB<6IfDUo z!BiH*w5{clRhUSeh=Bm2Sha`2eGLU(s9j~Y|^&*JPZ`613xNQl*J{tdF5gW zp^0B${xORq9)BA@%Dg@=v{u33Vh0&Og#KC7u;~KXuPbWA=U1sHiQz(pSMAK0y!{j# zgm$V12`dG!YQYYPw~H2>GF2o^KyC*mXLgcw(se+0RpN(D+90dK(SEW#48_FgZ!*`~ za#cDLy8Y}ih;gqit^4+t4+YljaF_xp1h<~faHnxk>@3|1cPQ5fhj%0NAYgH}^n708`;cH)PSt-yd zN44fvzLYJfP(pc>A<%-d=s6uq*O*>S2Dhf>11Gz9NF6=s&`npLcPI?(J+VImTT%ul zoC+g1ASNK*SQQ0cSO`C<*71E0{p#etP57~?44$WC4H;1Tl)sUsXq6`2#@nPSfq_d` zhS9h=wD0#lkO{a^M_vI02UK|eDqGU|fn1}yxARy4fYP(&D|L#D^;OD*UVJ8;3DZ_ zJy-ngU_jPqGxS5J7pRelF)|dV61Sh3Q`&Uu@a_QVsaLmzx%`+5wJ}=yNgBHkK2`vX$DprLgJMfUe z2)B(CdDNuq>h*PelMU%IJUKl$2?};L?Uxhl8OLL&OgjXvdG^vP{KnQb=-P*OlPLAT zcMJ(x8Vh#x(L{h$Sd^=O%eD2=Ik6#sa4L1Zt$0RNIaR9>>q5_&lcGBuNNi%<9hD2T z-5?FU?5!0>TcFrf;AE3bobbVevS>w#a9+oB1)oSBJZkm5x@jQ_;Jb~3lf?MeY4$_R z2Gd04P(K{dFJyS#uxCSK|3P@9R1*=q>)A)Gzvlb&(y#(##zwkgi~n0xyyJeb;rAzy zT^R#ixMNryl9o(JJ4!04j#D+k+0vYCVjRxEe5}l{Obi*PKGOI}$=-dwiJBJIV!8AF)Q8-jC1HJ%M z1%?3Rqq(GB4^9EfwW~G{UAZ<2pW(VV7pEc5v;X|j=8fsBJ)YojV-yfU&iwkOQM{EW zV);&c+qvh7{(((iXZ!u8IfKKodjOI>oNRWF(540@@BpYZ&^=aK^J8YWf@J^opd3qY z$X9R(`S_@Ol3VReNF9B6_=fr;F?x-#lQ5uMLeK#VYCI0y_P$SKvX2vFQ`{*`z}GR6 z>{vot#KF|--!+(XL=X-bbtg21%{x=kGk5zQ(akWpm7&aR+I`rT>2nj%O-7kW@4>ki zw;cRT90~h!t`!j&*ABd0v;mlh#PdmwZc->InQ3L9(}j8FL);uEGNtS8EvCZ1&nM}%ey4YAF`hcLP?Q_ zC{!DBGw)&H#vzqc8eevcW`4&|M{#g5TVaC}q)0kd7d+94YN;gfD096(N_)+8Fn3nvGie!C@wn!jJv*j;(mdF@Y1G|u(2wUj7Fw9^s~5;8uoY!*ECV*k+c zA|u#lFYiNiZIf`LCw6Y|=4b$SbyFu5>YKSz+ME^mnHu!1f%WWlS4`p-xT4?8GtBCy zh62s87AmOX^tkgcv%-wS;~Nik$$*0Gzg;GY;lpbDrQc@wTvJ9>3XA)9B}OUT`K4nB zkH+iO?({qRatb(22xynDyGq5oksu#)nXJqc8aJ# zJC2b|M#ocE!%BE*-*Nu@ttO<&T!O09%CcyerCuNzzhm49z0Hnovuk?bK}mDe^nu>kms4-; zmx(8?%p2W@dsFh2?;?PBIrgJ37koPHbByIY#zz;c4S`typDq8riMd&JEedGtIqIM+qd=x%@yYL zUoZZ6p)mn#7LN~?T&>y}_ll~EYVl)RCW2@f>1JdCnxa>3y({Fx2_4YGJsf zZ5L&R$WoR}hkJ4N(a9uP@4rpyw7`z=XEb;gtm*+EAoNv0T<^aHeUzJ&Et zyx+~@)*ke;8s?g|4!MZDXwtgA6*rh?^KCz7as&+4+O(+_`s)9z*A|#;-RU$K@kn#7 z6InGP)>s{G(BKLn>;%EbP)}h#Kj=4@@+@F_gV}m0s=1L+nX_Ah(dBvLyO%`x-puaU1)bN z`0zIQCM|Bvne=_F{q-g&7Jh`4*NZ-%Zdm=EwkZI%A~<6NA=XmKA%81|b75IGrSCqR z3vyGCdHjOoj+Y6Nf7EcL;UzLBIqk{NH93+d#|{wJd1(m3{p)&*7vX)&>x>0rHLWAE z)Lt_z0Y>+Y*;hHY*KN_BIT0S|{q@W4E&_VE$o)vp_%Nb(8eRl2?XBs=3d4UF>a`n2 zkCy=YF4(Al%H&RB`eu8#R><^7#9OcIZn~N#+5<+1dQ_^n>(8^uPTD6ptK{T+XxqHb za}E=+JmMCq+nea1OHNV)+uzx%TFz8*cY05)wr_F&=y5wC0L@{fbZEyBpTFEy^~^Y$ zV;5I_a1Cl64s-MqqvtgFvO)5s>}}C7Up!)HA5xGPj5%LE_Vzt-l1c;qG1~rzIn#x| zfKk&O5rC-xOq~CN5a`MN!Rv(YJJWn7#H!szELea7F~eNX2HnH(&A71Ug>+1iNBH<$ z2q~dT?drDRGuVRm7-KE1N|KT(8ULe(A0Su}GW}aAIv=s09C_jNL(ln@0uXrJ8zRg7 zZJj*<+@Lzu^Pli@b!W>VHS=63lK~%NuLa@hvE1MPe$Ivs_xiz|DL&-#uqT>-%#Isl zQzYjEHAtd}mkwFr>$bU%_HcdnIb+L8q*y8$Vj}3wZ=4!vAn^u*;aq+fPXPdtw0MvW zG-5R4lvkQ^noccf{!$w1BGud!{J5t<&5zWjZxKAH2ZEE|lmM*%yAFRs6lh+Fk62GXTt!gR^1A{!_!bkDdI-xiRs49o)q0K^x>6kr^Aj=e)Z9 zKJp9vFUV>CY(Cz^*q=i=N3UNGF{6-%i-eZ>huRNGWHHL-zmpKNzJxGWn_Q}~+XoSV zfUo&;wY8&Q2X1-3$cw)d5>MdV0ja6% zBHd{8I@;!8)O9FX+9zR)=5(pOi)P=#=Kn-xo(T*rOQAD+YVw0NT?hxw#62gKb)85) z7ro_G=X$}%82pV%c9_j4zIVN0F-W+bP>t{x z7rmf=b0MsZibl$|ksm^0hweJRqpc__1N>>Po)}ETp&YQKRpwVc;9+gdb>}%aP3FtI zuu{PbMJT($RRE)=4TZCmVTlq5a@)>u99my)v zLg~|Il_A^x^a=j)zg!~-%pb3_i&#MFg1Elv_>#&mm;|+-)_VGly#WRF<>#T2t9l5Poort)B5{|f=Jw6{4{^^CbuuR#InfHeV zJ5&`b`l5|AFJ<&w=oPekc!|?Nbpc#hgNE)?HGuuHc}oQZS%TbA0t<*WamE#$ft0BAQ64q^?Jq|zRoB&2G=OGaX6fHX+8 z11F=DgA9_0j|7{zuYTfAQ@;bA0L&yE=-|yEq1nRc)7xpo(;~hfZ>*kK%rO$t0)1Hk z7d#$uld1%Jg$RJbE9p`R&hH!5L@d*er!DjE3u{&vR*BxMt@;dv?a91<@g@M%@U@zcY= z)#?%J{eXJ;-mX|b4x(Bk7y=a3B_f?nwQ)^1=xiotkSl~xHH`ht33Kml+27UH_Hg35 zU>{AF80Wxi08muLa;m|xGq#E%?Yho^`yCV^LvHFO-V}{TB=7(UACr!`1<%s;25T~v z`up|}{$m-13`Nelw*)hz@{CV;knu}+R3!ET_9Ll~icXla0~k7$k>q>9?5nv5hgsKO zx8AJ=qX}{KEom%>`-IhMv;85`xOr*|f8q*ZPcq4201pX$C8VE)Br`ga_XWA(yve&Q zK6+;2EKkj%jT!L7hPcqRlxij=ccFkkNfs7YfUot)3b+Ts!EBjtmN3U?MlRXP8qd3V zJm~YKu%v81RT57`%$m}jYYf9M*qF>O4(Y#bd11odM#|3y08D?UT`lcB94 zme?b`shH{r*-Yw#M3Oep4K?LKfu`qfTd2E3+s7BI5ciFzaTR2JI3$Zxnf8C~LvbcD z0NKPluiOHRedQSG2#Px)C8yRwZ129cy#l2j3+bm^b?KY1@nE^q-K+1L@?lLYnrgPS-q%@0N7uLKWS-r;1!d9- z><`Qev%0=!)9AnMCo&WBAy{R1ai`3cdYY!? z^Q`ia;S4z#1kKAJKN+tlA}<5+rNE+EGlmC7nU2fSswcl0p39cU;FfZTF3g-eyfQ_P-SdqJJJxJpOX>(m)xFtksn9tBi@ZZN4~UC~9Euv*B211K=2; zq@o>_F4`0i<25x1P$xvTHZ(Q~dXz4fCYeWCjn^oIN|~ssTCQUxF$x{>tPjlVVXPR4 zUu4PRdRn?a;%6Cn^$KmrWzp0g?RZtFo5-bg*`9(coCc>ieDo*tm6gN@Sy}xn4r@WA zG5h)#WRpdg$2W}ww~*f2hjA2b0fb`D7aAQL_OLo0fX?__b^;vlzcTDx!w4Xn&4Y=& zF^qWp*ZEg7t7moX>9RE8oeC!G2d!e!UIapAqw`7`fxa~8MsYgu35V}ol^)&Evb_Nv zMeEi75Hmw9(AoU{iylFkm1nTVgL5DI64T6FzKWQ0VWyo`6t(q<*lnoMzoa4Y3KLC+1g*sB^Dy)9Zd zuyxYcJi;ZwZ{Y^e-@bEt0PwT|^L$*~MzmcVDk9Owr^_ig_!XjEG1 zKL`QxO(Fa@p32ZG%H9W~3rU~ZhW z@nzv!BluV4BABiZqssZc`x0%983xZU|LA4_|7(r7!0*@h!W$_0;-k2eG)@S|^y>d+aaV z$h_GSv%ZwgiM}@fy?z#x9y$zNGe;XJGhG#XE3qTr!`D>r0_22co@do{I$2L}!z&FX zh27l(>FBWW`rw>Zd9XAQB0 z(H=?#Ksd^zeUz%5BoLO5m^x>JrX8M7*q=z-elF$IzL8|D4ECChB0Cs?5&l>ieELJCU7|%6f5C z^2dr4yP{g#4yo;|z)&IjHS{hw@O!(zk3%S5bz_XrTNU7?3n6;RlL39o97g9A2}Nzv^F@*@}#jAJjeLthP%bgv>p0Ydc?Vffz1=NQqZo&(P}&zTlF zgCULOa=!l6TtJB@Kv0 z2Ua@6+{`EA^o&UNbBOMM^e%(QYI*|eG;rPjKxtpjiEylob%A#sI27SZK;JCev}f6y z@498F1B6@Eoe*gDBIyX%G~s!`p?q7;U{r|%8O<>a-!{m07LWJej-0(bZ(ItL2MY?8 zCOior`AI};_)va)Afdq^1Vj%Z-$2^n$}=|Qdayv(1p4kf3JnYUJi88#!gea|;0jBq za-MO?TI?8B0zcLl!e}M0Ig2Lwm7iIx92bH>HS#4Q9 ze*+UCxrWwWD)>PHBPhW_da@?can z7ZT9xD_VniGs;&> z16-AN^-sg&sl%VV3g#1l+Z4U&z;Agcjbv0`524l50&hoL;3=0nYTDrV9=2<9E;a6b z@Pz^3p!PC3iBIeDm4Gf2nG@UC3jCCHd+42x*!Rin@_M*@8E&2{gzKD0TmQr>YVD4y zxpo=jsi312i25b^UIi?Zd#2v43u7N%0Ofn{QH5pi=HoF^)Ek&P+3PLKLa%Lwc&@Ez zbuk9>J1vrwYsy`?B9saQ29ky@A@rGE>D|Fc0HnWz4x_zSRNhBSzw!Dc;2(&pgw#>PnuO z*T(4;g+Zvm?@KdckP8GY`JNZLkm1R2gpGLQ@^wNOOrvZxV4VPh~k%Ml74Ty**;Ml z$FmOIp=xqr??OuI3h%!;Ajd1x7ZESCWkt!VM?1dt`K!oJyW}^}uhydl@WY{y`YKAZ zbX8p`7wh%Bg`wr!%4BJ*8)IW=8n zNA?mZ`pW#a$`YI~5BHzDZ!|IpF&|4Ek^H9&!kK=`Q^{Fg4V zVN*fyK;n(~`bL|mGHJ}_=dI3N8D=g~cWnNspuvbS^QXJK-x4N7`&x*wLSQ~7KNW-=(-{Q-2fWrAHJ;A*;+__hCtK3*z}toDkCS4Vg|^H6*km3if|L+`qNI z5zU?g9d9J^VG`8vq^1?PzdJ`u#R|nh;`n{IlnWBiFaW0)}_#MN1vxw|7-Mmz!_241{V0Yz;*LA|L~?LNy$9meA$>|DoJ6i;qBt1;IBhAwL$E5QujnbqVYs8HC4B}$}F zcx1Bt-)XEcdM%XxL-*k&>{X)&Y(@QQ%xqRm$FY!YMIkSmpRuldTroQx22(P4%LOqY zKT8-P-6ZD%G|R$E_OI1dR;|{&$*ex>nWX<^cz?#Hjq!^pmAC(HbFm|bsol^Ks_Bj4 zJM0|x)mf3PWcKD5a7xuI@zuy)esc4qCgziIi>F8*a?6-ciX)Ug0d&v@Kp2*T5#hAC z5jx$9rJH4~i)T&XeTj#$Co+$oybu=fve8!AX%iH{6`nkOBG59C6fi9wK)q!7De}Ll zI;ZYRm~dOiNyoNrTOHfBZFGzkTOHfBZ9C~;#kOsoZ|`%pFX|7}7*(U@eCIQ(LJ4() z%I$sLLTkEpAx3{@#=xf5Xrr>y)PRxyFik(O8jDDcx_~xq4~4_JTSLcU{)XsJg}^zq zC>^Zp@&4Yx;WBTL-iiW-+)hv3nI_-vqGm5Z+%DOD0N-b5V>XPus_6Is8fm0 zNa-U-GuU<_(K%2i&grck?`?Rrsrsc8*>&HBiF?sfI+~sugL9N-6xpzz5gL-OoGAYj zKMSE1=e3kAWTCUl%e6Z0+Z6lwk%SSxpc zBF-jv(qtlX+YFX}e1B{Wp7;?HWDF~%x+J{pmZ%8p@ATD)1!fn^LeCXD}J zB1%N-!jBoKR$%^*g1u5bM7>^j2HHG%A=zFfIWq&*-EXn0^lgcP9V`<}fltqxxhV6@ zcX|n|N;PlVE7`bd)3_pb(4w~LHDW&Y4K+-Hk(R$r_gIiH6wtD-BlcTqYgR^h)5g-q zB0-C!LE-zG{gLDlW*oI7wGv@UO94{AjjuWVT=t;)uwMULvWA{M_+WL&T-~+VKU<&0 zxbZuaIvXk4#bTaof2UMIUSHWOIYIB|HWRnc@S@#x?o2U9RuV$zt;GtRRV5a(q$!tE zKKINbu3py34-hrRfGgjo6|Z*#M9)QZx@YKF?uF6SZwcn~RM7>pNfz}~;T+{N*{LaU zCRPFu>alc;B4W{6mUOvVqMFnlf@u0)8Y;c=bgt!JBopE-m}HyMZkjY1ek&NdUKnFK zZrTVVYaUSFepfgySVz`31P=|*es}=sh5I(g6fN$00m?geeL1bNDret>vs(y#R(Cc7 zC+*x#fha_I_b*!_3(>yWdC@1whrafuT-^Wx(t3wT$^iB1*&d(hUBozpoM|x3fv{tL zhF{FfG)ed$WE__%81(L4vW;cgNP~-ONmaqgn*{>eo+=#}84|_WKT_96!h_kPmqkSF zaFO^UfSs6xo1?YwLhzPd_tfG=o7Z4@A)`nmg2dFaz0%*4j}PAXfT+FDhEui}an^2! z2J@#OcP{$%^VmCxvqE~R~~Ps15SF%0D=8x*2{%L5&BV_MZGX{0|@iuOdr@5 z{qsUGn-i?R63tFW%OFY@+}{<%aR6FT35{M=0C0JnNxvR5!=Sl^xi(~#S~@Cj@pHeC z?HI&1_E3~oFL*0Rj{dg{HiT4UsO@Tp5a1&h#!i>{rC?=SwHSs*IVxcxBG-UVWz)JJ zf<#6pC@P)E1U>KKk>KApJeEHr8=etvU?5IU2wYo2M&%^tIel`AE%P$3a=#j0d)PQ7 zp!jc3LVZRJABCgU9OJXY{pr~310&Eh7N@sv# z0U>4LPct_kFh`m(sMXItYh9(Za2QJ)+(*9%qdto-3H)flIoR82RRwbS12gtErMdE% zpFUmF^K-BS9Agl=VZANuROoEz^ti?iF!PD8&%ZDiNblG!l@;~e(@QUcZu5{zY)^-D z3i40YG>c4Aas_0GxszA{JJGmni{lZ6-9IV!U+$&L$TV;@gkvjzr3V}BH3Yphpf#{X)8iEJ`Za>u#O(_-&D(9&ob|9rSC2KADJ6eu zTQeB$XAp$KH`d&*)5SQ5QPWcaF!-lIiNH&}n3DZ@ zdKR!_3|jL{{2PxeI7~;F^pUTc!?0?9GJgPGbgNBEkk8X>%mm9qeqUqhg%Xasbfu-S z@EmQXjFPHW)djWEu`5aGi_}QlLO`pbY0zq12H6TFZ`bQmQki>Y9riwP&^=;8+G-hgmlA1k6{o*2?D1Y>FmOyQFM}DkI1r#LhLb zw!f3RV%N)#L&5z3uvhyB7pZHPhKQ531x0WOZPC6*J6hfClp*8^#YiVC-jbqx+%z@9 zZR1~A5_cA}8^b)~@>zSI>=xg`^QXSe_Jd|m6u{ccfPWWPyQU8zC3TIk07z zst7Wucx6EHdfGMk#4zjZWAMQPL)dxpB-q{i?tL4K7>B$L;36!l4^v=5IqL00$LlzQ z>NZP|)5dHS8yG65ijTd;ns`B zRKXM!S#Zw8#P?elUHTID>brEr z?}>l=2C%})>$!gQeH;7X<@GgX^q}N9E`|*mhrWLDI!DbNNPCmw36urK zBjpi!V;hFMT8~jZ>%EL!yv$6%-M(v=7#fZN0IUW#M&#o56Q-8)G^X7FvXJbMhU1Wg ze|CH&w}KVm@Ruj+1Tq*}Of9PVq0k&6AlUWdVJ2Zkx?omHw*09iO@6e*2{Q>vrOYcx zfceK)g(Txbqj<_|_Txb6OnL5V%sUVcP7u>%&fI0`aT3YS*fGRGRPO}qNFSi@tr4UL zAe1D`)W2V(y}ENzyslnFFj|>clG_GbzsRF7Eq8d+D@W*_U&ax`wVfb3x_Nh`gYHE!B$vE}eCSq+ zti(R(F|z_b#(w8R;o8C(El7RkT9K=@e>ZY#b%^_pk!uJRd3E?Gd+7?^BHG^80J?Mq zNrL7}rr4?ABPDKde!IF}UGk$%f!r|W$@|>i8Zxy~a z2Rj8%_%xX6z|VCJo-VE;W8R$=U(r(rqLROhRx>LWal+DFPYf+^#TA*GlPT~Q@cP7Z znY|3nUFOM2Q0_yN%$dQrUkt*p5a-P7?FPT)9FQut*zii?h`3O4<7cBv?lz#B5}FW=Bda$dxLdul-l!a~(oDhti9CJBTZi<&Y_fmy z;t2ml8H`za7{=k5JWkT@U@>==fiP#YWsMZ`HT}?#h#x_3rA#myVbFWgB?ZgNI6MBe z{hcxuB=@AqaUw_se+pTK2-t-zbH5D4Lzb2r^ctqFT)d+oNg1c;{Ut#YDlKdlJV9}I zFm?A%k-(Oj9}8X#@j5D5;IPrS!#^Wu%%6`|vb+F#m>z?Rda9)Z5{0T>})`n_!nF_?lJ zu+_b7Y~Q!V5+_TVh4P8X79;k|&Y-N{VBwnLM{Z2L`wz8BUMrmKU!NIMu{aJgx23_! zZ|b2jDc?EjIBR0OfDCAD;SuY}OEN2Sh`DtmTuyH3>PUA0+py2t_C_gq4K}-b30uGp zFgc-%VkcD%K6*^m0k1laA-}22kIiuTT2a>&`1{IKOk=$vjpeS*uI)5nqBl$K=|ZQ+wuHCH|eztkN| z!~D3W+YVCnX|hAQRnj3~_94ChMDR%aF1hKv1_|)H)vks(0oc)iOp^MtztCPB`>^U? zW9(59Z3AbQ_;)1T$A29FWDhgO6zsB|Z*@Rot?Q?Bjt2bZq&R>7zW@lKAubjOtw>iT? zj@3gfTjtzh0t{R)dj1YS9CyB4eKn9aIW=0iuIq{OM7!eSi2R&m9};6q>((7zL%u6A zh8cVsW|OaWhVSWktp0cbXQM6B+SfZj@TkWh_CF~nKR$`5M*C$jpDpR$&wq^f4L23i zf>hljEy$odxjOl$^k39-?%pr%eZG!8ZoYq}en4{9z^udmzZsOn@}C*R#m)SGazy`; zMu+WJxplWl%`4uzLIubW_nBS?VCVmRrpu z3#FoprMydjQ?W7J=4B-2s7ya(mMs$9z9RY)?Tzrg4!F6m4H^=GeIf|T{dl%5zP=qi zwjnJl{swf>%<-&q`+25MXJfLhLFBBQ4Sm&xoB#6H_BFtCMOt~d8!d&Xd4_PLZ_bY6 z0`&EZfEMQejr? z^zQ#V%ZfkmueU3UHJ~=WpIpi#(>%HfC%;df5t^hYY8K0^?*(UUCk6GbQVR4Bz`C{= ze*tv%vo-O9je3wCI!7CQ0-RI{qT};Qd0~mpR&q=JBaQqr3D;Sho(P$KIStvmTR5f= zo7ggY7i`|Q0AcYJX(Y6(!v9Su8IFp9Ae1B05dxoLDb`+jjwaIA+NoV)1hj~7jm8t@ z7lB-eQ0>F-$ZkgbLvs;?g_?Q;jL{iF{a>qyb2nQb8n_i;yR22;x{Q4;KU~KtY^MM z2qvrM<*Uae{^PV^F}n>y_RnEgg9gwREG#h%2*K}=%>JBeP9z*6GM5BKuzqev1^`-V zU|(fIs50)jpJTu2Q5^EDElm(iLg1dKP08(oETcK|fN7B|t*nR{VO2(KKZ|q1(Uvl< z%!riTyHe=PHh*)0T3)UA?m3ahX#MnpBfN06ypL5HFj%0Rq2W3(LR82A*f+Z|UtGNm zd8H&{?l=$Jni7<<3r$FlBiK1>9Y84?%Ag0f9WuET^_ZKe^*hW7?h9;>gWPTY_kF(y zwz0fXZ=Us&XS^rzlEYk*$l9JW3>&;HKVrALWN@8jQTRrA2!)}`(zavFYaFcKVERvz zW2Gvufu-tY1Op{`?ceOBhPww>Z_Foz@)Df!>y&rS-KvaTGd_uXHPdnkPrwK|;~XLX zu`%MD%!qAV0Xh?@!w7ahbACvKMj=qyxJ_d$i=^H+DYAfQvXkeJOOy-$Z96^jBjv1D zF-p3>8O5HX0(~ajl^KdLn4FGcwLEtO_o7d$k5AO_G)l!p(q_){;a0w(J<~<=oztU9 zPU;@=BfVOBh4F611cBle0U-0NXzZi;at48q_oSSwFhZ0U(+DDidM+wxh2hU=sJD66 zU&RLHlU`}gcGr>k(ck?iq^qL3Yi0pZ-y^>%@Bcbm(S2=W5>h9F%nI7fLd+foLDL>i zM+xd^T|U0SD;$0}w#GH;qh==<;}h)Ab$X`OZuIalE`yMwU3FYZCIcj?2toK_UJa`G z<0@)_3_U}(GWItcO{+?*zB5Ty@IS{fdJmVWw?oXRa$1NcjVy`(@a0Ik9Zn1VpbiYT z3-$&63Zj0U06Av`lJ&o~n8z3q-L>D1(~|}|O;pG5;W@026G-y)gX9v5uvfjzX~&09 zMF(w~pKTn}AKs&UZ~*>F5?L7Td+$Ze>xC5v7myONF%9Lil%B@jb(aS)#bb*gjo>51 z53YyGCw?g2P`XtQ-V|?)9W%2c9=9Re{EuEUG4Y7L3YUv-YbUR2#J9M~1{j~8I<}>! zCus(c7FRn+zkGvA6lsiZFJo7j$hC$N@8*w8V<`p%{k#c8IoVPKQM=WRUj{1Tj?DT?nXn&13$X0qa{46>@|i$XP-Ycui`eq8P3Dy z=uAk(g@Pdl8vt;4O)CZ;$7#7K9vdHy=7d)v*LaLv2#M3O(jJ7YgXhpl#2y?RJeL6G zPw1wOnL`LgEbAQAGG)8kZiIDC&Py-+VGon3Sj)NP8Ska{;eZtDG%e4nR+QaBv9#s} z7`D3OmD!0og7b)a@NAGmivPkqiW08#GUoG7rl@U*IiP4Z3c=ggq>0<^8h#>#FbNE42V-I4 z5NQN^Uf2%m0b%%wV^M^kP*EaAT++JtW|zHp%5$@rhd$KPm$BDWx><|(2Q1f7_s}Gd zngZ>3N}F!}1zN)rXc_e3A8${p7h+pp zEEDuXJ^=yuThpbn7Ver!JSo;TsDgBiyBICZc>p>c*tc>9`bj>Df*C5GrF)~mX5WHT ziZ;s1Wd#L1%|3PTv;a-8S)2wcVXwldOR`&{Mp?6c?b|<0;3Gaz%mTSL-?_1PS{{y{ z%-Nx7)Pf3WHpywQts6s+LY|HeG@?LqiHp^VN#9tQL33I8vh;wWD(KN>a}x^6elomg z34jlIQ9|MJZl9&_Rp|Ykb*!r2j~0$|UoqQfF4&SMdP4U1yWf6$LU79#69e44snrk(TgpsY&{v8$F<7m#2NXIK!c7Vmcy-enG)@vXY!@21-8`9k5rj60 zmLL?hXs4lTCWB>AVwk=m)-=>-MO4HEOnsT(|rnbYoD96m_qF!S{M9HTDA@4e=)jm54Z;h z$tnFOt>SD_t?TBMz;6r+?)oSpC!nr;b+kN#DzoMyUoA=MYlK98*hAhY9@Jb2ana0R zQWuo?txgW-El#e#75_G0Nm8y#!`wLRy5mX6n<^_we<@ltDL4}f=}iw*qTrWKpm9@H z_>aG8@0dCXdxnXRClc90MrnSL)E(LuaeZ$ALlMmb8!YWBDq)pbq)U5?OBkY z)pQ#>Z=f(avM(KRw~nL;Gx{W;_Du6h>1u+90`jL&nsiRrt-=*Y2}d$* zqmp%slja&kgjg{-qZB0tEtZBY3zNEYhr&vv*?QMa?|TlTi>EXm|GjT@09a&g_Um1} zy*+3oW;X>piZlH8paG)-BYV&4v7u7DcDvWh`GZP3xa&a8O(FLJVOQ3N%IsU^nU!jK z8?Q3lEjP$$qD&4i{YLoOafZ%T+Uv@E#FcGmT~j(MXk%Sxs#r>I zQ+i{e`J&|lRnAYZHbwJ_8Sr=<#?i$C@qs(FE}U?WexY7s&3DR+hg4JOO)vbU?TyTJ zu4)w;mNM4%NRMGHjB^OoBwq3}TP#?fUc!~SYfRo=XYmA@@QyjqcLFvGv(~DFp99@s zmG^5?biG!LKZ|R&2b>@wT(9X_g+-$`t)!tt8SM8}Ig{F2Cel1CX^vD50^O0xwSYY-3ISf)^fNExeu4f#oWm-h@(#E37$->(=j1Dgptj{q%y15 zWc-WRv4NHmY178+JJCm`mDKSZyFAym>UoOK$#M!=Zi4DH7nxW-Y+c+?8T;mrV~pmU z?DUb*lCrWDd|S?+fdA+>Y+3J$zt#EI&wkWkTNOH))&J!mUzy$`L#4fFc;&dEUa*nI zpm&Z2%dj^ceTxLJaq0GFVgDsn36E7PIF6L{bc)q~Ikas{-m*qt7a#A6Z?>OQe zGCrk0v!(BP0Kjbw6yuB7ZO0Q%Y4FzI7(d0n>YBZtTn=vs0%8;Z4v z2U}Zzz%Bztnai$C<1h=@mxc6RODBh4_1qxn?3F7~0l=j^Ena)~%p{wc=$xhOV>7$| zY{4g2UNXC_a)NI5v$aK!kJ1#a)rFJ;^9Bg>_IluTa?toLeReh`uG#SbLhsv z30;$6l619iPcL!4a0*@*C_!q3o}mnN06iI9b*wD8Pw1r10#cw&{9Y?T1tSM98#kga zT%-))08uf6U3F-t+B&CO6MicJR&Gz{D__T|xlfxpP4dLeE8&L8pCirYgt~=$ z8{H-C7qCYoqKy}$vcN&_J-)~y7VN#8cN@pwT1bWYWDge-`hpE8y&<_V{?CGa5{mA~ zQMqcMLV9q>ZUKV2J6?{OCLn46K*KCV+#F4ofpuvm`{HqNx=!eD0~cw0 z#!~%XD#R5vNUJXm?b3XgMDODLJ-)V)Qqr>X6#uL!1t^Y2%PAk!$aBmJBR@7OZ zKC#uMmLDa(Wia&RMf{3Sj3TuqCvZSGZ3xozMum&l<1VHK9inY%UPXL)%Y&|3L5KT zV(`Xd#dE&=c+PKpxrzh#Ev3AMkr--dB|HT%KKGB!xc($i!x$)A%}-83OL!V?hen%| z0N$9?g9|3~-YO2^@k-Cb)QLi1*PfU)C-8AsPUG@nqOGOI4K z3Bud|%Gvd2QP~<2FkPq?z-PJV z4VT9zn0K#C6HV;Hn~oiCUcgoF#6!T(?*nidYn0p>T0!icv|pU#`W@A`)eX7D37x;G zkW7cApuvHxA^y(eLTiplgA5{r05xe!4U^Zc$s@$=A6WS*%di!ZrYry zwD2*Q)5#P~X9-Jq8ttVx@NmQtY~Uy_u~KlQOYtd7bU>Ra(-g>;1v3>jf6#=59$Pxw7 z(BIycDugBiOF76PL_cU`*5Ac~7>U%!FZk)zu}mB{W*Dv2JiR7V|E@=vlsam4HKOXr zxqwT6EISF6oLbGkKH3_eheeY8z*+)w7ayny1J6oDo*FnSo*?YrKt?3wE4XJ$O$2ym z2g@k*2SeSA?5FA<^tt<+PTO_c;QP+T6srG&x?cdto|SNi+S2Oj2E2qt*W>jPm~0l2 zXkaa8iN`GH^6i&2f#3T`qes)BnDX5$k-84qX#4GvXd0{=kp312!u!5MljE(0AsWIvR$>>5;N7zlE8xN+<`*i6i2R?FbYA*fteArB6PZA9s&dUIRMNE|Ei5| z5`+Wb@gT>UDa**}AUsfMjgZNML6l9y->O$QK7))jS-%br>_F5MMo&Gm`hjYJmKc4c zb+iJ@u%iqqgjx90femQ-o%nbYWlwlt}d%LM@YnZXWM++8yy&RiDeE!2gv?Th2Mt(+KTkvtF{$-g7d-`!if`$!A;0=bW�P21&-GLfEtg%QGnFiME~A$XR0Pq{`i&uk0r%-n6LFy~II<-xazJ>=3PjW&4OzSa1wpXJB%h5h(CPCfj^%9#_RS!nuwMR_jxY5{1eeXT%VBXyQC(V7(KV8b1 z2>#pcj~UDnxSN>AtC9m3k0*}8ZJ$_fQ)Z-3@7qHr-i%+YM)Su4FGVv;TbD&$z$NcM z6J52f9PX+{fJ&JD^S0XPI`wmu$5{F?TW?CEGGMCasoZ==3A*<)z?0D4mn?N1HyfTK z#NRWV|4&*fG~w#}b>>>t46T)1PaR9uuVC4p4ctM22=BRGJBVm@x{vdH+G7@p*0Mf60J8x_(<2$gsIoBTw$X4c&PIV&mNQ=MSX88O$N);lv1rWEWbcU4jo6urveP zNCQ=sFTOMhM^a=}H3ffdW|L{(E?F}=kU9JnEkPWgxhbQ?;9etIJuTuVQC5P<{Min# zx_OMPs%F&06Bx4Kz88`Rac_Lqq!rX2y8(j=l<@dktvr0!$Cs(D`>Q9*oq&}77kY`R zfE&1HIg2w@)a&o)UoC8D7b#Fx2^TdBe)sr$HUtZOPkr%wG9#$(b$<|be9nkxh;BXP zYtkRD%|4w4n4^Yv(d1p(cJWskM@%jKbXR*bS|A-MdSw6|Ei>5D`gyqVP#wJztL_>6aa zWclsQ5r{d&-5otDy<>ox?S?m0zSf#;U*yx;;Kb_nU2?JU)e6L>P@lDZzin%}-a{za zO-}h|cUZ5fd9Vg^fa{a4RcHD1AGTbp>52Kj^9C>$?zBA&a0&nm>;D-!jOfb7ll_M- z-_+d_S&8338@(bEYZJFY_~~8_=x;TZ;j{kyAWNgu$dtHjTsU09^B+{ID5K{Q(Y!tO zCGk#tYUTt9LgtCdm0k6{jn=(ANd6e67eDj?J3i~0t{?LZvA6)=&hH-=XO2JYW#ow~ zfbYlmgJQ9S^I}m#e8sw(zMPW8xA^Vi$G0qww;$VY-{cEfURmo&23N0tB-zIKi(b3# zUe0gp9nk_qeCGncTi=um)BFKnD`!M?h68QVg02owvn5YhWRWAj41Z@5-kdX#G;{4< z{l<{)O4O!*Y|=)Ud>{K)d>>B8$Sz=)0Md{JDNXv1OmA@ry*2%OyEBf%8)$6l#mdJj&H8Ev{oAv`I5v%^A@vyamMybZK3=> zH+9eZ8P!ss-z)cZ7ROC}<&@u7SL63sbtiQLaW@x4#O>!>MZ566l+v$9e3ONBoGcp_ z7L3fon{=PF(%=hri+~pRA~cC`096P#^1P$l@(45H^n4$~)nqXES_$~wE`1yElSAYZ4kZE|1Gvs0PjHEZrP8xgT57}%W zo;K9Agf%an2D_ejJyeGK4Bo|g=RCPLyO)`+ziN)( z)Q+5&M(Uypx(Kt5{$R$`u8FBQJ+o-A91|}Tis|_Ceke!W1##UHfLe7$wLuaz{bz|fdpcP;(|B3!@51DFLK53 z7Xe)I01Lrr6sivV9d_x3eyw18Qr=nNs2;_$i;jB7d8tH?aj{}oLd0r050ke{(Sp6q z%P%U~(5~cgQj1d7Qj^MSv{=v%7h)I~TCu(2JUb{X6O0F4z+#EA{!N`ID6cvB8wgZ4kr8@W%5Q3O10>z`WR6ba7OCx?5Q z<-UCdRB>P~hF@gGUL@+3d4K^%A6iQ^vf?$yA~!tL*{|tVRey|{QZGG4raPWS)L2`p zx|#)YIl&DKaJv+rLr+0*gL&mluMGq9S4;IImpr;$ZtesUg?P(T*q`pF_O0&L;@)6B zz|9i4-w;;=ueSBrN>BpoC>Bvs7P#asmZC;k6g)&X%90fqH0p+vQ&TU4AXIfX%_sY$ zK%!d@t%tEPbe=8W3by{Nuv%4Pe?$w#cf#9xZ!seUkW5YrWX|=T+7mQL2i2XTjbrtc zQ_@C`tm^m+S~TvpqK%*MxG&de7x#b68-~H;NZerp;ORF!6xba_48Xgv0h%2nR#*CJ|9rgy} zfCsrV;4(tIs%xT?AgFm0BqIoA6arN<0i+Ho|3zfGgLq0D>}A+SjPruzjT=wdjuAvZ zXbp?zt)a?cqq_p=)Uw8CW5sW_&%yjaTvuVK_MiRW^+Se0jx;Rr!piCd)G01sRl)fy z`XLD{x65qc6ouJCU=x7gmVzX}n8i*$w4Uh%q=)suflcQ$j8auiXjXa*mN@Wv^KJI; zGtApYOJ*9Y7(Gr-4H9b7-|+cKer#LBkQMn99`LA)$Ei;4gbvq4&~us-1U57hkkz?| z_VbS8Y@Nl_oId6U0@nZhG7xKmt*}cY(JjKIZThET_qL8O4vD*?EVLNWWpBTQM1&8f>XJlkx238Lf$}NT!fiXHDxu0#%ybEL|?v2$sHGvg}^jr zBcy|{l1~jBp{hDYU^?l^J9Dsvs&(P`o#@OXu1_Q>6YT z+|@m6$HGdHWq|@gSWS9}5?3Vhw7&4-XcJ0^)mSuv{>5#S;+TWE{&d`8Ycz)haCJ8x z12;D9vXCn&_65@4J4-t4==SFdIW904Rxx^vo)~no6u%ak!W&^Y*Sg=ZpPsi4`4y1{1SJ`P^5_8^=DIrur`|l_>( z_yBDjk}ZSgcKHNB#~fP%K&XZ>rEr;AI{2*yCzvs11FRROkAP^`BbvD>#Ul8DhfXt{ ziH2gbF(s!Pu%-*QYjTgc$yZSY@asn{h?F@i*)dKnTn9IbE}utcs)( z7W+C=(d85ynw>%uB8eYFGP9J=)3VQRPDJHKpYTrop31^R>`I%fRE0<+!+r~Nu z!J2{6V3x{jy9vnI|8BCjn#z&-YIkGg#C^zT5}qd0o}9}g$)}_RJ$WE;QE7N0_GlA9 z0K83_RrL~1St{%W2+l@Yh48w`U2VjE5fYr^1 zlA=Tuu&qY*;MJ}sBg^Oish120ttEin+AeGInxx*+yH+eq&nIpXWpCoL-ThisN2oqB z#?oEGudoY8HH79(VG~I)ba};hmh0cy+(p8^2F;n2G>%%5Ks8rwy^f<=bRTQTiQ0xF zfZu2v0|5X5d|d>cA>a6|%SpK*^iQ?r1=3%;A!A?fvVJm~Z(`W2R#g;UKDLqwnr4)q zIoR95ehxRGY3~1!t~=65>h1qI$)0telVRjHPc2vr-hrAD+Nm&n_(%G~xZF%s&6kF& zklz`BDk<0cSGd-P2*{*Y#E9gl#&ms_Ziot+tujKJ&i{}#Y(#)#v7 z&o7m*7Jj%+et%8YwL$q0Uaw zc`kL258-`;-=BM!e>U&~T=-om)b{ub$%ieTAMLN_*;R zQ8dL(70=5r{u@a9YQ_b6z|s66f6AA4pfes9fy=??>m%&Iz{+VUl#|4gg#T+ap}|U) z4CcBGQ*NeMJTn7_g0!Q~NCA;$>wFc)om#;-fl$x^0-}Za~c*LpUdscbsM}XCIYeIE%*Ctm_#$O7(kMPxi zUF^@RxjKMgfJ_P4W|ZGK@{Tr{mr&KuOcZ)Ffr+`(eQba$NT`ysSCWQ!f}_#tdmGkp z{;fscZnEs;ES4$cqTXi@)U8kQ)})BDx;>Y&6`+}$SN>9u!v?a96kDt1&MJKY#3xcZ zd!b`2?r_%*7sRL4La{FP6(1|-+=@E{TFkNxn3~c~c$9ZA$De1TlOj7S(E8K@rDc;! zyHyB3=(ey?HLY?9V{DRoW=+sdj`DId1GajKo2?B~Y;23)=v!=X)Rx28BE$)_uVs9W zVmPVJFr{@mTV-juWDPPJ`9#+NSbgcm!*@Bu2X)T->dSM3*|(Eoz|Rgmf6iZ_3@fXJ z=j^T*YC4bpp?3jqt@DPrit-RwGztx_#l!ZfSg@5&9KW*Do@I;;tp6zKx@64kk12Zs zH=@6@!QCu;Sm{`SoHOy(7hgHQFN7eW?}SWD6cPhi-|Uab5ou*h#0&2L)>@$rF6xnJ zOlJ-~{4@z6E_Qf^m={6dNcgYyySprCIYM6gXX_${Zdg0konbTY1=_c@2wWGr&&GpZ znc3oLLe;*jhB^0C?!DQdCv+YEcn$9#I)k*bX6r_8ZBvicRDUJ%cQ#Xb2Ld^KF{H0B zQtG$jwmJr5cw*fk0$17r)#DTRSH{6YLO2cQVlUF6ajuph4O}E50`xRcjA9nK-M80! zA8Wz$V%%^+Vcm=}{`D`n>d?i)ndUm?AEya6x_&iM>}$7+{v_05oj&fVQ!>_9CMClb z#XDGvcK-jKMjWi}`YkkUd0R((Js1cG!tM(bx1TE{1Zs>~NwC=g^}&ICK^_0JZ)t~| zE_-9B67dH^J8>Vo&WYxO;w*r z-^};{%2b3+H$VNJ>lK$_8GD|)#f@KesA;_U@n8y_NtsN!T6}Y`#smYYi81sNCkD#K zSjNd^RRPJW&2vG3gD~=p2G3Dse!||i%>FGHD8FjJC^DG^RB{I4M|l?pFDM`~ zeG{P%Rt%T{fyfBif^dEvNw=|I<+9EXB54L^fXta+WA7qd-VevJ1Wqm5_E6B-q%xpQ z32%{OC34|b2xVGT(;%}MFf&48=sDp`76*ROOHeA(m*pM39=*CnJ#SpJ+wqQ}6Umj# z9YTkuKGx0UgB{FDAN6bG`~6fB6&^~mS1u)9$1!ApaJ^}k9@sQZxEX%$4!xyOMn)`J z0|Q2#on3kCDIc0FU`2l!1`h%aaV9So) zo&Owg8LECmALHU5dT|vyu;Gt`hrCSA()i2&+J~oO!11pi`sT#H&|cIW+@Q>kWr5QT zDOVd}swUvV{jFFRH(ZEa-^e1n99N(6n0yucN33Kky5ET;&RGXFTXbD}`(eArxtji+ zph&2Rpg!c9j`%}}ldiGhS>zWSffqx*C;Jd!Y5&0mG!iq0Q5Vqq+a)wjAWFYAsd#WU ztA&nM@!o!Ad;4mg=B%Ze3Z9bNf?LqD!w_;inM%AILqdWvA+Bh1nAr<&Rl03oaQyA^ zpq_Q_)@qbAH984tL6&^u{J>irck5J=)%4;>%8{2DRf`JynRDQ#MXA3-|5C8qd1?^= zU9dKmV>*f4`Le+O+KyKCfw+#ACF!3i2hvIQ5 zJ*u5)roZzgX#ogE2*Fg=zQf03H~RLK^AcUs)TFGNlykV~O-p z2>{qZlr3`D+ku9vUOlYef@1J@Cv*xxE+*%j=(=5P>k+JxZZIaoyvZ%|h5N^?VbG_K zXHlRxO6|3ipyl?OFQYRcVVxlDv}eoW)}Kf-(_C*2%iAYz-Kh0&?RN{2R}PUoi}bS2 z5}v||Tui(RUnYC4{Q+A&0c4Qmuz`~R-q{2pL4$8?N{&3a8O? z;C3qg0D#CwPB}O#Q$6i-uP+>+67!>(WixlRp?HVrl=hQcw zFTdzA(cWB{c+zNq7V1o8(xzls+7SIYH=6xqy>A(Ja7ZG{J%2Hm9lsMzn~5BXxUG`(jy?1t@w=oqweUr+RBu6O1kg zg%R&{SIRVQ&)S%%;+vu*>5V$VlzZa{NE{MX1BvY*vq*IQY;vpec z8~@SNk?VjV)RiddMgo%QFx;*K3(B>H@<~|2ZokE4LpcAT5*;<%Ng>F)WI%i}JT(++ z=_bdHL4D_8rh}wQSfqdylsVaaG0!^*$c^y6@!& zex|$DNzUanp_GF!$MnFjN4P(%+3rjkX@g>fzisLsqn>v)ivvQJ<}8&Znjc5uN_t*O z-_#nc1(!*RX!FjNrB9}ir%8hYHg%L}NjPZ}3op!(8_X@I2kdCQV2+|aZos;ijwB(2 zK`QEPhRh0(DPNX+&s~NsCzv!*w0e*$`@kA`+wW3ucj6)&)=Q*$Q)t z-)9i)0-lEKSpe@x+PS%1u@|AXI!8`j#*D>RVIZI zW81cE+fEugjT_r(dV9wCbnpGJ|G^&PU3k`r%Vf;)Du9ni})#N5t$;K z;9Xk27=&x6@?jH@|5oRs*|rtajKSd*A5RgNElPIhbla<>WokZE7Q6ndx5Y|=EVX7y zlka&xYLR5qz=Iuhq)?tTwAo$mS;;$B{4awK6sjNn(}BO9TRfZeR$1gYz-hpN?Yni(5C>lE?E8l;agz4)Gt zO@lAl5m;rnETo8-;)!xbd803$y0GVy63p7-J}ppyCJQeBMkkw(3= zX7%iX!GN!}+ z{sr|;L*(F=8Wv7+5yaJ_h!l6wm_b}wn>+a1%#2I?U!^TVbdXt9|1kDmU(cn0b`Jq{ z<9~EXvhk3iwYtGFwS1WJWaTBF%(YQkhs6c~952&m!fgP;bc1C?m#NUNkxq*_D^@wj zX7qEg0(Vwk;~}id4LQ;4^Pz|^Vp*jNEP2*`SOl_p_3h3UH?5B9V7zm?3Kn?VDZb%y zlZR$t$o5up_`6OLU~2Ymmjt{OAgX^TmR9~uP?Fzhz}p@~%nKrFFmN>s(j?DQlIHW? z@tv48);$6beDsOcrTfvB%n+47+y>b`vwUqfwUV(t+C6{DuFhLrH)P)Mp0#60)wGx` zBj9-4A(V67>0>jKtMnrZZ|9)6>$+~_p9~v2jsJ9Iafd)322qJxTCk2UMY3HOwB~)( zepKF@#?7Ue?EGN2;QldBSkXH}FT`d7{iV)Nr#2SPUh|+cPzj4MuipRHy z5WunBuC7ZaJh~MP;y!E_j@M5;+7*sxM+w13b-VvLZrLb=nlEm&CYPm4JQr3Z|2m7_G@WG=W4^H*l!SPq18=hTw$EFXIZZYbCkQc?9vC@2Y}t+ z;zQXr+`yt*@r?si9}Zopq&eKAUf1f5nAgzy$k~houd5W+?jUZrl*tG^k<7?AJXXTL z^ABBD1uS9fX&+&^{)X{ezxl@&-YMHfZmN3ap@u{*WLk$(_6H@)Er$;lsUmynO&OjpZ`B=)k8RFNZ5%4JW-GHWST_WT*e|gJ5Zm1rM1T&81|c z@zr`Z=KJxGpSC+s@o(>8L7Sdc?yvg0TkTNv zHo?CQlAotORIrR3ud4sdV;4Q){kvWO9W$j!q+s7>-^Z@K*1x&g^Z8Dh+V+zb<0d zbESv%hE~#n{rz;1RDpw4AzvwYb}GV(EBaMcs}$D*ov9W7x-EQ2L(*h2e_#74L1Fa$!sshy+bPvXraE(5hjnSqP?bgGgVkrt_OeImkPEz(g+oqcG zKOwc^M?ub0EoGtV3j78`yERrt60>sSxe5rkM&{@r?jxR<*OiMphikqk6Cl(#UF3az zL)YxM@mpQg1V$YbU8g6~yfn}P86)zJP| zFq0Y^NNRNAN*60)t=QDS;`*W-RcX)V?}qp;OXb>kWdteQ%%%^xyVWecmJ{~W8tP6r z$!5>gwXID*C+&K=-5K5*ej=C>^{uD)ibH9 z9_Cf#)e3AoYGbT0AkgS(C1)rP$*Ze4Z?jxD2soDC8)pSz*bOS`+_bJH?Gy@Byw&%j zQ$`16IlbhUKslZO=S#>-or?isCo6&py9D$+87)+Lq;?~a^$OoMK9DRYPwUA7fvWEC zv-6B6n&0wAX)i`@sL3HKZ)P_p1WllLu@k8qWajGhDe8n4eH=H3*&d?fhT{6_DM7Hr zZd(n}fqoL*(xzuxuV;IH`DDzB2)S&iA3F{}e}z1+R7UCPUCS)JKtGH)~WF?IDZ?;%fxZ)D`lsGve;?mR7INlD*7Khk<~GO|bY{I&z)x#t5W+tv^a;bj|IL1}sE>ZQ~)xpIJ(Ou@&)QiBB zL08mDnyR|*%lvK)=PAqAWm;i6Bz!X5Kx{~s18%G(zCdGnw8D>g5%{>ymy%$^B6~QR zgP5JDpOep-EIOj%v&vSMfwtGK;EBI4Yt#;EirebtGq_#|-@eJo*Y0Hwxp5{QZ+6)} zUttvk8~KvepU$`5`ucSehORUh0vbQblxB0W<7ye*8u)H;?;F263snF5&|Ur!*l#V6 zCXemO;I~@Viq`l|hWU(8fsPCNY3$|f_NpqZz+)ESvk}+ofQ(4nhoYaUMb~#-l4f6>8SdBy& z`&#-JL3;TEQ!%Jy^Jz<@9!Vn`LXSRC9q#R*iQEA%`3HU)G9`kh#~A;7RG_Z+K~mua za6Hx73uz}n|9(dPOIfIp@XXdrBJl$gyf6nb1?Kbhf$_F3s0ah2w!-IrtG_)d=C4O< z`|0yl`SS^kRa?CD8ReLU!-{9~uf9JWe?GBZJ>krNfV~&EEDQw4>?K1|(H**f>l5j< zXhN;`%0De*ez|tfG&&EXxPF!&YouUyfT4b{6thj~V6|VvH%(aT6f1Ga?C4lEikmXq z2HU+qfBtj}<`~!{jDG-yx-C;1p8bD<;$g6-aWIPrH=6?&%`i2nv-#~OhkSB7vHA1c zXSwT@VK*EP!jrHAmMqaId9c)?HpI*5l`Ed5f~G`Dobm;`Lm z*Yjg(Uye>?)+rMkVw9{B7O58v{8%%rRgQh4z`wOp5*H;!2(7tie@&C#itN77!EI6l z%@Rn#;q)~l1{(t*-Syxng=Xh004xrK934d&njiB_j_`Dq@Yuxjk%ZF5iruR}or@aH zx^=VVyUP`?Qnh7DvIHX74$4#*XWXH(a0-fM;T5S_>hCj~^&hv_)56IO>_QHzSn zs0HM~w~k*xW<%oc7A_X9TW}{vSV}^aB2u(*uQ3+x+sf0*baY%RH5QVg0Ab~GA}MLI zIx&n`r$K2M6q3>sk?2ivgtB{;cMKGT#XnEA_5oUun5kwQui`1W0@GB(%<4a|!(wHv zNUX&`kcgJoGwtE>gf0@A_JjsBAMrMv+;ZdN&0*KMQrbJZBJ()>^72hh1GwiCsoHLQ zT2ocxC9Djyc=ckry6)eQ0o<4N=VM{9egTYS`OzNoTcaUq$atF4RJVZ;(0+cgJgVA7&K$m)W6#vcH5vk_vMHZ4JnLvDqF1!;d$>_=jVKH0E&R~GYGC)00hrq zS%c7u)nSwqPW8>Vzswlw+P3c8QTdgo8MEL&siT6KXRQOdUO-ex#dmE?m^pG%QC?Bf zO?=Ii-A!sYkM)nbQSLrm{g4blhC!w1aK}HaVD%cki2R};ZA5?gR7Ps_aH^2(h7~zaz&97k_~t&=k^I`-yD}d##n#cZ}cHKLBfao zNANIuVkI*FqT62x+#Q){6zLI-AF_VwH%$*JI@Cl$2EW0)>-Y#;cM7$RU*#$@609Eo`^#R4 z)5cs-PP>2GlGombAp>A;i3e|3Ypi6^wBHBwLrJ&z;!AKgoDQh`OTtEE?Th_&DNG=F zpPV)sRLTgB0PuCpMhF6D5`vh3f5o?!2d36^uS&c{$M`hRNKo}@3|4pI5ZJn*&9&V~ zq0^dQ%&q~Z;WHG!<9{)N|E zrUuZ_=}ToYy&rI+rddc*Ub2yI>mj8CT?0tS^g_|nfP_a*fg4LWD5|Xa5R3j=uhZzZ zw`AFCQsy&cL+mb;q%b#0NkmyR(M2bXO$^QslSO_hLMK`i1VbAb*9Vpd(dkNqJMnE)C%V7MFWBlSL$sZV%%{uGkKa5xk&9 zmjilLAZuNqV9_DWGV4rps2rc(72mhZ&D8SS!TxVa(sbu*HU>uW91#c87^Gnl_LMug zfVH*SeT#rK+9HGeIug&?>AKcFtX>HQ_v)H5QL=q{Yw#Z=TUbF;REFp-b;X&r05LlJ z^ll`XGsyQv{P>Hw#0+(?1>rEEX}?^Yb_x41pu>$fr?+cnzbT;%Nol0LafP`~Exl4! zZ1lNdn$nw7BoR)(ga74Dgcm0KDm1(NSeJK8;?QH2T7x9sC*=BDcx{%wJH|5?n6EBO zYXgx+1`Q&*xmR98-z8DMA9Yg#%RUG6WTDB5GpF!WqUdtt5ZObY=KeIL5ab5}^swbD z@GF(m91+(ct$Vb!u8UM1oYW(!XT+>2OgJlj1l}eJ)E`a{=gJ7FS|5YuoV{P zV}9hLa914UiDnAld~JE~0c(u=P6)Sk*JIkby3^HFE$9nDLl1P7f*QYtuVL*xw9>b6 zONh}Se6Uj zJi6{%+k0P+AGId<5CVi4D7=th_*j(ZqZn8hGBnbMQcQ>0{;lGB;2rMr4bZ$0z%v9T zse*oSqF0=SWn3LCXTJ~DlYCHkz~@VMn9RJDTWOe#lsF=JVdp>=F9qQYTh2AdaHEyS zcrTu7=g^P6X(ihHIe|L!c;}(jNWB38`!@k-?zH1P8%_ID@$zj)-|{=>=F(Y6R&y@f zov|Wab;;<^jSp9GaS9m1F$jEVK+rY*D%6RTG2k@+6db);*!&_D!L+W6k z3NF?e__*7wKqm0ATh{y`tf{VlNR;}N;_ae^CqI1j;odv@^(Hgb%@}Y`O*7xDhWyYf zug?^w;M7#kqo=d8_W42(wj{-qB$*PvWCo9@#mF>x*53HBw(K`%M~H6a=Od7NW9E4c@YrzeT75RxsUgWd=Kwq9-_F& zHg~t(Ak~pVn#oZaI*JLSu3SizM{knR^w6@%-cE3Fwho#SmRyTvbeu3)vbG=Z5$r1V zjt%eMv-;1Q9yNLe-m6!}Yiv}HfM?apvUs=VhF`sD?qi{`YTXNfOL|_bUBi`L)M#v_ zu+k-k94$$k#rgya4x&252)lnA^x9Vz9z`9Km#`wsx3i`B_B*u3&dbk)Ay>Db`wYRI!lVD- z8fa9Ob@_;6aOEX{;2?8VeYsA<5dAGFc^uIwqQf7S=NB$oHrL@3+}~kxsOwH2)>y@; z*y}gr4QBZN^sni=&h+mMNvsZvhioep;sOZ_8ZgS?g(xL>eyT6~%n~;gO+xk#dF;g% z9P^}u^d^4!v4WGvXJo^g%^g8-FfE8j(arBc!clgS6)15)S3aWV*8LX@s7{T89g}t@ z*|onRB?Uo1`}F`_0JU>z%{P!^I})$jh@Qh!$44WW9&TRUY#~mwGZ33OXI%MS8XNr@ z(uF~zD&O|qIS+QaA>5@_$=jA3!A0*cP5lW8JU`i#KdE3jCMB1_6FJ=vN2Yt}zwlbq z>E9P$xC6lfB8GjN@!Mj|+b|53v2GkIhEoJ4{Q6+cAMf7BGoBVOLqEJ&`0QN|1x|-{ zN}Rtr|3QgW$r~RHP1Ac?{DqQ$36ApTopj)(<02q-1H~5mty)7bmyjy72@5+`e`TqD z)jF%#e-!io!9GbAQ7O@^xYqKA<&HdebT4}+piAAAZu)O7Q!Mpc zKX^YdL?O3-#8>WTKfZJVun|0e_B6A<9AbCA?*HlK^YhyU{nq>5zqe5pE!ltETB!`l zv|8o!gkKF`yl7OKzW>-Obv?@t1Rmd#z6jplFzb~??1Q$%|4r25)XU>uwnob=Y#dIa zOqOllH%ju2aUa)^_UwVLTLMQ>R~Tn(qV%yHAr9oFW-=zfpm7Q=+plB!J&)Vv`KGMB z6m(QPR~EfRC`*}Wtr0w_*4!h`(b^!|ki!7?f#bcJtj~{IW30hei;!#|b}%BCi*lZX!w) zNK}{xb%e&x8LGfPg09=;0&t}OVq=38G^kA|G_ug(dzCzJnjKe?j;?1r`u=g2{A~v9 zXl&KoiC)ZzNWKEK(vfWCG~Owe%QD%WMH&gSVuOm{)`OglQjsf%}I?-80dc0sC(c;A~vqzo)gu59bKoTb_VWhf=ki}PY zB+fF62BXZ`VbJNfF4>dtk@AaCErTLtb5}ihFdJh0I|vFsEq+(VilESQwFxL!paakR z;UBrJi*1$u#dhHymuZ}|p+Y>)C2iJ-(6E+6D{Akh)jocB=C9OOAIR^8Y%^&mS#m9X+LcO zY85gC1G8+W-qEA?(OD&JqUP$m%=@U^M|t!Zyjgx!$WHi`irg^9DpkVxXjzLH?ylKE zRSyvv*?Dp zc_s8Ttlb5~#K1ybH6P8TnsMYqcqSC8Chejr94c9y@*=4P3L#FZc+y2=BC;pC`;Ah} z-FD;z&3FCmQj!#DLpZx+BPv}-OXk_w9b@By?~*4b2M5?mYR8sN6@V6&e-1*?A2Nph z>L)%J97@Ooz{GxuLU^H4f4+!>(DCL6Y*J0*Q`Y%+61kF=qE;kwQc3&8lsEal>Eyc`3R zt~wH5;ApB?!B>`$9XX;Bq8;tJ4D9;bw~f-QRX!--NIJzTTeX)`FtnxKDy8bY6t~xG z_B@rWb$1wMC7)fQ9BunFV@N^g=ezh779rpI)t-+1*xw{Z-TaI8n{!62oM!)&%sw0F z2_4!#Zbkb&`@O&Hem!N4dnNtz(cG<2D6?fCK*(|)32B5#{<-HTLD|iDKSuW+<)XUS zLchQ&?-3x#21457p8c#HV*1#_3iBPKCUT>IMV|srK@3vrImxQk?&sq8AZsw z$!Hoy78M)HJ152>UQMVpUMTBlJiee50LR@7xqtX~dgI~3=+h;Ifh?U9N>o|{2Xy0| zq%^yRth7|>M=28rg@c-xkzb4*9=}DWn@D#_thNqjcLLWMWn~cuTIh=Yxqyp9Ve5nL zvgF0*z~;9!4lgB;7VlKxyXJBJSVGkC{IXlpXAdOnWQrt*XcDaihxI{;jh%W1V42ih z9}2%38soO8Q>ju+$PN!tG8WUSu%~vft!*NLo7Cjt_q;nKB^jF&vMh1?xIsIDAGSFl zDJ5rB6oy2sEqMA2z9_Hy=W3-!W?T|g)*-!fFwG`8bVHXrw1@?pI}4ZEnJsKuqG9sN zvNG|Am?ow^q1&eWqHCglHvgsw;Q!L^?^!D8?~j$gPh75(-o{z6E`J&9f(=#gI#Rmm z3q4=r{di~+>}E=1mGN*hFCx1ed{neT7-K93pUgU|oFC@$6D}Y(f^ub|T~OTb1E9MR z_b6p@`7`ATsTbPA``p8t%Y7>>Ci{1_La>Y&viIf3gBYYKT^27%Mc@;GEy#i?B8p_X z=@=6*wRSOcgdc%dpvzhChtuf+NC8GpsvuMC%8w}Ew02MUve!FCRr_sVYu2nzayHao ziE*6HRP?4#NH16ReNS3(KkdD^45&u4|G)tv#<(rmlNrj?PXqHNV% z*z3$0UU^;Sgcj1VCT%%@3i;`nbzAi>eN2J~yW`1kj0fLLjF&;if0zC&8ZAIkq0lJ{ zW{&Zr<&EGEgSZ7rB<3%UYri6*+jE&_XKh#rmM1q^bZ7Ux+14bh7(1FT%};W{sKHrO zz<@fKxL;O2V-nJ6zMC3s$}@!VcO0@|B{P$8@1=;Nrk`isR8af|3^l;GSf^mK=L^xF zBlb<*96sBCJ0 zQ#m-qcx);Y*N-5DQQCU{D%<^)obcNQ)`xEbQwj@RA1ObQ38YP^C&q(W5thGHN~QUB zcZqy|pBXe|M{rkml`~cFRf96JUFn=4Gv;w64vo@~EMpB9328PRF1XD&o>gWXy%%w~p7dTklW1B6)*WdkSa;cW&GI-r+*X}6-E!e(iT zrKwE<@(A^Oo4jl4E~9l_XT`i5 zyb98d8)30Rs)pMuH2WUUpI62t7=w(TfO=Xp7iMjkQX^+3)zs%A_!~V!4gKHno;6mO z$X@h#aLs-_^(*-i11sr0px2dRQ4Brv_KFHv(Net|z}V=fAurC{EQrf%41eB1vF<~=2s5-P`c4f1 zyKurUZVt08rJ6N!mv$f>NKgtWr5~HG)OW2cKOS~m!1>@n>Y_JGzuD7RpQ4cSg%bP- zvKAuiyBOZk3nfVVP^Z3fP^ovf z?xvb;&ZudrX-L?HQuy4GxfL2R-E{p8WLU9AnBpj>^d0Tu3Ak~V;|_8ehtv|9$rM(Z zP&o?;$W`Lb9x8d3;C!>HKXhw686KF$W7s#I?IU*!*u3ypW!dbF=3397kBK{()!e{C z6nQSYudapC?PtK))!480ogSDw`(e>&iGKi7illt>`G-wh{hfn=oEk_iITD}+eA1W< zBt=rAF_TW1_yF8RELOCIjr>lTVbsn6&7mHNIe1Qkm)+4`$|%N@^#(5=1r$o!z$;4u zd^wC?xdLkYwojde#XR)`L8y$i;N-0-K=(qsd;REdlh;y8{SW>jJ;*L&7j&VjvH3pr z%Xefn*Qt58Q-4m4*CaZ;m1bVxgy$}%V6(gH0`@T>C2>RNR4*3(_|#74Hlym__7Q%| zc&T>^T0@M_az>A6ly6wyg237{`0`uYupW6;E1K|2fZM|#s2Rp(I}h5&MSA4+qrxd6 z!*H;-xnA<^cuRn`P$iSB>2^EiCove}K)XF_?u*U7s z)Mh15K7wxc$1!(MS4k9rD(9E8DtUFsv`W7(op5Tbmw1qLe(&-mY3ivfj1+a1b*9th z^9o$tM__L{elCbD{SMzVK2nOHq>uDy*{QflzFx28&mhowFQVDTC1L=km0;9J>Om^2 zvXxlv??qH4^085S{13r`AnYDzra{U;XOEBfK%az3249I;DrpF0PGYB3@Al^uhTu-y zszDu`e|I`#S~JYJm}zkv7R6>@=4Y0fI@$@zAgLU@VbzgCVq1__=X~YPMv>Rn=8}?O zfB+~|BB!qwSG?-6batj_1USH2*bMslPU|lM%%f#rzPWfF@iuJqT{MKqmnGrRvW2Y~dCt$7`t5=G=I{MbjGLCh0&|9$Oe^d3!~wl=#or%jlbsWX;gYJN7kJyi#)2yglgtonC+Y(1sDG`=l8tCm4U zs&n7#2pu6T1C71sZFC1sSMY*A9Gp7hF)KqfOXP?&gl-5!OjbNJ>>WGGN+ z>n5wp+f8?^b|#bPPWP?t&rvP)`k9}Q3(Z36#U(H-Lrv%Su`WRWx9PSTLgTv9ASbnblcUZWC425Kz%c~1!@ug4!I`7dH!*-dxPMiEko=QVHCpH-)sN3D9u0!l2q8UR{i3r z{RA`hNw*gPY(1?%eecrf_7mwZXoxOJ(SI{LG_jx*01GGk|3YXS<4(tsa$fftC+@8J-Z_2j+#Fw>>*JnqN*|QUR^(%H$>xjbgGg^mAs-#G4?Z< z|Gb^2n`EHIOr3Uqg@h6qJoVB|rN#VuNB-q-YxFi=9y?e+KW%W6e)DymRwNCyICcJQ z!cJ|`vJFaqaQ^#vn7cY>ojbH^^?BVAXHNL5n&0wDuY1*~eQzV>w^V@CSHs?ZHYmj~ zxqziz-mk{+AEgTKXJ6sR4ljg(-LxnT9<0v-u>WjX?8TWN`5>MUzRtgHC|?&+)mgPM z^kxiK5IF{+^lOMBKXM;B$e5~tZ7cAOA8cd1TVjGO>|Kqt!Crna5RGpZ5R=+I~v#nCwT8= zEZ8RN*(1$Ho}L0Fp_m5tvlui;$k(=POs16(x?rhHSUy`pYvQ7~In;EK{Qde4W~(I3 znWaD{^(&OX@WX8m-QeW_{M>$m^d$^kC4!kDYZ%m6>oHB)m>!FhUos8+>0%aGH**`! zQS$3excY%8l-+k!A=~T?a&NR9srz^O;{BH?*CTuczk9ui%cI(-J&}+FWrC2mXW46~ z0_t?pAwrL75V&nIT9@+*9;zy`8j!0CXuY==i!WF zrOHd640hp$_~9%st69xgviU6L%hg47nMsd8)7a%wZ%ob>=7g7S3F*(MRE zB+leA*Ywz!UW{U*YxrwDZx-Q*t}(V1rhw_|y`R!HD2S~S-K-)Z+AeG(MgwvY&}$Vj zKs?I~a#8M3)pa3-Ud74crC&Qd2_j96ET564a!jBPh!`_~rt@oX;!c3pIG!J_zdNne ziOHmGAWmc?vb@0S13M2AE2NdYX?er=>c;h!y|Ku+Z2 zQpjGF8Nyj0a4rPTZpagV?$NWNU0jEadD`Qz|JVJW{Xr1kyuVB9#;Vkqh=2N9wm>9c z=Aewc3Vn)=;aB93joA(D)xJ?bd-(#IMAnEjr<%5ezJr4m(((*1!Z&EQ1+Q!Y(Zu@w zLx)QGm*R*l5+;yho;4i)4 zp)3Nj%k0)4pd(W!=@S#}A_almhof;!lnJvdG<3b@HG%aXN%s!rfkq!1>%B(+#2ZaX zFR3*HaC^I}SXrhvL9LPf~F#=osa0$UMc z(`-ici}?n1pJ7TA&&&hPMa-vI06Hj1044P7FMrcrn&IU;f z(g&Ke_f~1I$jekSCkEq%;~RoEw2;bCAFnHXhnD{Q=@cI4ZZW5PaS@Ik2mDM&0(-x@ zVzz_|d4hsy&Zw0W07m3~g|*6>7{-pDIC`jck$hAyV(^KkMR*tOG0!YSv9$XCqGkky zU?iuYnDAy;R?yf>X>36E;e2UwB(1hz#Uj7Y@Gc+2se#;0=wWNlF0beHE%_pktHwF3 zkee%7ighIdg_MJw=x-1Pl$=Qhlgn1}(^ykea!2^QvTh^{u@Q8z7Uac}3}Dc3nAU1Dy`|3p11NAuJG6S5@$V1BC$G0ba$%R2>%NHuepl9cYxa2^+0*SWmX(AcyYh z0M0t|8B-FC2pcD+i0ncF-VfMr|%Lw)>gR^*2|MQrc- zM8?kI?>wHI?9^X$8N)dxxh_w-CmYnn2W>D{jLk3`<8GJjcv z?m4d4;?^zb=&~0Atu%H|aDxJUa<>-hj&ln^>O9NLtLo{RnEj(l*auRUl4FpM*;~&9 z44ELEzMUh@^f=V1$Vz?DcX5EdDz;@cq0`0W`Ucy@CN0Z)?y}81h5K8CJbE1Ak+xp= z`GgmKlA8gEXYR8(8#LG4po{FHx$85ZEr+5c-dxJx9Qh{blHEv#?m428}y~Lwh#RjGit!%%EKF$8-!hBIw&8Rl&u6&fQ#1*EOrAw>bUIug_ zpU3d3|$r>kAtTupW`w#q-V5Yk?JQ)NM89s`!j5r}+i zw74IZ2>4T?_7@fMgNtm(5JsMIqiI4SDkU6w^dq#lczS{T*Ql0)MqMufX>T5@mXSjc z#_U*Z-*^N!2MXimCRg^8tOqC$tM-#V>x=d|42<7aAm!!18+dUtPpjh6mn#U()j_$O zM8#@_k65Sm<{y4ihgi7ELwJp?^L@f!$yr#g=3vJwGE*X8nX_nA1s{ zW05_zQZY7)&M>>|j`qy=e?7w#CQug(nyeIj-7tc9mzCy2X|MiwbJ|*2fQ1{?7C2%kOLY29yMq z=fAbCUDm%^mzA0M|JAy9o&O9q6@->rNav97--R0{Nqk&>Z5bo6^7GFP&W&Y@`U|Fr zMbWsaDa?E3u%oW?tCc2EML8(d98Vi##{Qi_y+Zw$PiHKjk5(Te`?TTjQzs5Rb`Z2@ z`+7zRKZu5`pE8RE27Nv+2HOD?7t*+YV29%!gY6_WL=HN_Jj~)Ej_BBRZhArjpl@kY ztF~B>NAEg!br6q#cQbkQ*Jdk6_u=4}qyryf(9)fvw>Mr)x?_E-*T`Bxy))tCba;N6 zAm9nNt%n%Vt7C#>G=K*b;LJw6dX5aIb5i#8kDQvH{}WOh+hd}ovI7Jmm(aF>*csOg zj<87h<@FhZW{{HEPw3BuO+z`lCJI~{&jbaiHOMrnyqA`~K5(bI=W!H!YCoC!qQ~g4 zu#QSD(noR1dwxz-&^&BZ!~YsWO_GK|X`Q3Nwf!030sN^+C-m3bec2!JDOOwEP=&Rw zHy$z~s7J{Nqe6=BxCT^?8C?WC^+WtjAvlSGR4C?&oI>sFN}$AxaFvWnSA;sGqh#(+ z(V#Y$3;=r~Tq=+?~o!Vq${FxWS5MWbTokD*MXV%fV&f@bl*!ORX8 z8bnPNOKPSj8_{ONo*~mWa8B@N*e6Iyb~e(2cJMZ>*{4{Q(GlCKPjd)oQt3ukleZb{ z3$LdLVEs!jwE}+3W}cZT6lJSpBfFgc-WR=Vl>E= zh~03trPy5WTKY&dI+Iu2o5idZb->_E^Y*%a#<07CH4=NcXowGbRtHHa2@M55)FdVI zyNrZni+3Y*%=?aS4ziI=4?(yO9Wlu1ECwnGPw8X(76GG2fwY~s#t<8rzi^j#J*^3v zkojEUVK?}hn)tUC=edcRFLkkfg^4Py0$wg{ z@m->2Tdhp7yK|#9GF>Z3kV$(vc?cem?^;4_Hvl5s<|de~dyh$vgNtqRfqkY+se?_f zs^B=K(d2D1YlZ_AnC*%A>F-D1r6km2gFK*`7}ICjc-q)znWJGGq2vNG=|FNu%wHCw z#D|{Va4n+5CCi}bW*5+NW$;ol5L~k`4O*+7Ln%l4CITsw!{$MMy1dNJNh-hxOa5LB z3$kyG?xM)xuQ#g$bBTB@(t#4z21Ff8m z2jMnh2J)*lC;6lWn~WDIH>;TV0+uno*axG8E7+-V{F37)`0ky(E0 z5qvg>X}EZ?Uzli4SOL7qMhB3xT7xt}D8L0<&3@V}+pt;r5f?*c&-u^59F6n10-LEpMGp>i{BHrmaw`;W+yVF-MZE1FRuFK;vP{3o0 zVXm?JqT8tD(JE&smaO9U>ttaBlF6SGVY;LqMkswR55rEM7uWVp|E2EP-}$^FTO3NY zF5Y8kahjuATb7|JQ2~y0DTJgOeQD{?yk}m?s-^vo%JcG^NdNi<`P~jUrRzZG8`95T zPBwr(7<>%kMQ^yheMiU6Em2+i7@+cd>s{bbbPM@0qI!q-aijg-j)5^ zQ7`Tr{RaA|-ZDpfl6Lzk#8PF*kzhX$X~vPX92eEp7suq<}g>DpDfGZ2CUrrAc= zT=D34l!7jlSt?HGkyIP(sB7Co$uKzev!#z-jwK(eLD<}b6xW1MQp#AX*nM#2_(W3$ zqJ}*E@a|>#w$lExrPP`rWwHr{g!SP>R$Mn2(O`HP1HuzMYc!mhoi1%dqFqVcqTA({gcx4uG@>&394SFJ!k|-NNb6T~YpfG~t zGu|BGP9Ep*+-CZ;*48FwkPdoj3GShwFCjgeOQy24P0VwkneoXx4=QpLHyGJ$WrIe$ zoIOOyHJ`SLG-fVgi0Jg*xxD!+x_6iOU7$vKGl{-^{JTIQ!-XRHcfe<>+}o3rx+5nc zxdD-K;N#6hMMLBB=Xc2pq^|(2kEgUS734QeED&Nk(I~VHxZ2A0z z(Us{1|3b@>ck3>9lv6#s3caF^lEb_yN{a}%|sv-{`aaEVai1#0r{&LEaf;@Gy=K%jjbS=E{N z{TPiOYaYLr*06b{G4JQr}_G>Y5i?S zD&+?o2a5!YsIX=NmBP2s@wD--3XUqiwtPMp4ZNopGcVMr6ClMEG~bWTC}wKaaJfzR zXHxZ(IT=lBjO%e(pSP|en+TC=(!VtRm(+9ws+AZ1apLMk07aK9#Frj>6FN$#M9w3gvDvTVt84pwU@Ec?1Dbs!|<+lkM?4 zR%ZiR?xxApgCQ1VCbQS$( zfHLs{uG7Y-J!BvQJt<756B%(kvjA8w6DD4T5fM2jF;LJ)piiZD`i%ddjryNiTOm#` znlXac*e$D#e$IB#67>a+TbDz{Q?8s88k?d5Ih@V`pE%{N)%M_*aksG-lKJ+=v~xJ{!F%LEzRoW0qR*9av!#?wo$BV_vt(lvRkW6AwcEeWt@0an}C zY6yo=fcN;&kdlz{H9pf&KIFAgmBJ(CDu*{oU&$L`qqFJaq^eMve95~e5m6LpD2Lpc zQi>`ouWy~l30sATXcT9R&#|o_zjhgNRC;{;uxgb=d9(jC;?=gauY;I;XQ1z-qjZia zVnF@pk4GIfN)&fEA?(ZJM&0U?2>bs!y-lz!i@6_D!C59C}0 z_~B>vo)kevyNMTnV+tU2-dWR#5Fisy9p9Ir*i|e!>%RUESLf7SX}E6N*r+5G+ZEfk zZQHgpW810Nwpp=l+qSW@);>4;{DFBfFP`?jqxI2;whfHh%AEX^e`5V|fH7OXbCGOc z_Oh?x{ma508Gv(i_3L-8mY>jl&?G-xQ6uyF(lQVxaR><{PXh-6TUyZ4DGP$XMo%uM zhpbW&P|mz%&&-mo^1mwN>rq{bS3r{muO)sAOYkv)ZawF#E~ROazVjDB56Q+H6wU4* zQq+EOtV0Z3p61*6W9;{BftSLMH?FWOIabEbtHfwsr)RPp>z*40;ZIXy2ak7qgw6ig z`z6DfFb-hk#w5Bcs9gw3zusCTf=QFA3VsuwDAG_H0>x(*y-CuIn2=d;VQFQ{ZPMon z+6LdNMqV6alM7u=92;GL!L#f3gGQuRSEkv=*s~|?KSI!5&C33N9g}PE3yiHBylW^O#ff;_5VrY&cKVCALxLi1%44&>DwWA z|5&K!uN)Az2s$_p9)-dSh0BzAc%G!WNBg+ebtpgOe~ZN9m+AUyE&ODXO<-MMIZJ6N z_cii22w%J{(~BhV-z`(U-&ro>+gma)fbY(a!~VPeSW|$6)!iB$J|22ghzt!U|6!~5 z%fr3Jdem0)&edefzYLrEv#nk=pDk9an}**R?_9VbT@EuN+1J;OGLX@`{eTVDM`&Fw z_hicgJ>H)?``9lvW19eZCL`DN7~uHQfSrixhX?80`f)VzBZ!+Jk1lRzv5{a=-RvbY z7YpP_s{>3bb?x}2xEs-Vygj>-rS%~m$W?<+6e{*2Q;tpq1k6Pi=~6G%4#;UnYNNcV z#gI$#q*@m7`ShJW8y)>Te94WjTx@R-438lqq~?pKLP~q5<}H+N?3@Uhnqt1nJW+wxGMOsyw$Ig^r{TvOEY!@m{F9jor@_-y#_btJaF$&e(6Zw zYfYZ#PQTg+bIgv-F349W;V$7?^})@Dw=S@Ni{=59Gx~q9jAJrJ9>kEKmr-^pz-K}w zp#j$9Ez=#>C0LheY><~nbR^Frz3&xMu%_nQgF0z5ZbQHaisY9~w(-93;WC{?}=ZfhCu$tvRb4S~RF2-5}Nu#VTdXR4_sgB}B~V72bhoLg)J zF6)s|q|#OiMT#Xgj;dTw2QnHuC!+#wBLHJi5w6klDl1()Y_}sUeI^UL%m{iO`SpWE3W!weYa% zR8y^!An7JB)956;d?JLk9>i36p0}4w%5<)}({bg2camtbUUh#ff7K#IrK*5SXaX!Q z)OpWFeM)D_kSm#|8IcLQai#N>DM66u>s5;qY2s`tuYXUqIaZGbtAr{ain>pc@HS@n z2!}Q}>LaJ6MHg~e{nRHG7^E8pH#eW zwYF1aE#7&@t(3>qQHTiPTYVteqo#osmoKlK)JyFwqKHS_=)R-DzA_9bfbrlZh#i6Y zJQvys_m4UJ)H{7WV?w0`(Gf5_i z9~RNsjK7;T!q9Jbl%l@62jK6gc}!9Oq9a(S(z0jkBUEoOFQUu-zNv`ldD(^p{Y4L*?eVnPRDo0NnS8|2xaIwuUBZrT)g}#H=F%bm2mt`eq?8z%v6iyF1aLg|eLqx=Im5>ZZ#c*GBDfirtE+r?8}Qn>ccbBY=Vh42VwVdnVW*6u4_>J?13g> z_w5ebdx7-v$dldP0$}w|LH)qMX|*JYBD`RlsO&9`bFB z46V6dnfYgztdKO&wzwblUEeK+EP*W9k3v`=>>O$X$oipY z4B>KDr7{y3aLW!nfc;x_&5(^cLzTaMNxH59CPsyzqUsn)bsGjr(MY_@X0Ovl3cWax z8#5P$rc5=B$WbQkC1GU-SH} z71f_UmtI%}xN_fx!I6EJ%Q}l#2WC6O0AQ-M4Z3*WRLLKtqX5*rOp!a|LOz^=vS%wXc=< zC9N;UOcHG!vkeHD^9h&!>QO?pjq_6C_maLGSIn72T5Es*<9cWTY~`fRu@8+Px=rdG z<~ip$K#{zbQwdf+>l7{r(momGq$R`Z->3v$kH#Gh%#(d~eEtNMb^%Gvv+&v<({u$@ zHoPWk|Jf}9f6#E&-eoj?mu|@OngMMdKUB? zqk5{pnF9rZTRz6>&@mHGjo&iS3`Jn1%=lX!GxpPHh9B##ewreR~4JR)y6wD(Z zz)k$YGV^SsnDt6YF{lY)1K;y~Vb(9n>`)o(lzLv;zSrfl;!Zk3P%W}8&Wd9>U_&vn zsXNGq*VgK+-de#_@t>Httsm+N!qBDc1#t5j2lmBJ$rKQdJ6W z>zse2nfZGbE|6k0Jk5OTn95XYKH8$0m->ayw2v-$d)kuo&Vd$`__Ow%71d9C+lI=- zTGJ+KGm>x;m~f$)MRRD`OZtY}V{@ZOY}Wc<-olzv$GdOi8>Mt>=I0x#PFKu805>9> zj}$4xJ)eZ;y3>hr%26ZHSbb5RL(kCE*xvFE-l_B+-D{?shGiu}v`lQ3#r=M@^0=R- zA>RjSY9wVi*J^}XW`{+!fwl|NQH4Aiuy$>eBE)=bVS}~Q-+qCL+T;>zq{!rC4o8tj z%EODTCWAc-`Uj)Tr6M?1Z)}ruz+c-{1Up&a0sqw;^9KETL$mI!g>+tg>EtPS|I)eOu(~#RTjp`P(vPpg1D6(Ila!J@eg-=4apM$0zfmX80b?dzWU(1uLNE_?4Sm zs+Ncg@Rj)z9w{&F^{%|K$hrZ{(BRWWeJX7lxq20|!vzuA&^W=1yma9U}3cX~$`7mpB9Ba4+|v;YKK zph^_Z!9{%SbPRPF`T0OD82ZYk|34m}ve0YuoFhZ8cHn+K<=wz5H}qSt($5yT~z zb`tKo2bmZe4;SV@yH9<6&e17V3nhJH@HQ)+c;ex##byx6-tMa6jClDjp&z8M!)3H)YV`ezf&+OLChQ9Sot27_vbe6}Iai&yrlNXPjT zh_9i!N~dlxzWZ`Zr|!p-vnBa7E1d7DF2JIRZQ4uLP_gp_m^#ppRVafzb$PBfi)@+F zP!>ay+M6%4T-ZrMC3 z=GAeq28QU$=ohl*`3A z$g`*q0{1!rC~s^Dy*Quo9$V9{pL$ySS}zlj<|3%xTIsJ%?H-JY?az|9#6r^@nTmtt zY_^Qzgs$}|&y?idwa2c{l91K?Q}LN>rBKI&8$3*MA0C%mu2j%=Cb?jP`xqsm_Sv{v zU(||)^k*|Yux(#!DvlPjfO+o2hOvcM3%i6c(>-Dzu&=}Wt1h#uds-OzZKjpIHQHOf z{v_4rh)p1Mm;VJ}x|at-CnMs=@6X&TiOXmd+wsm#&C*>VH{hj5W8uN&Xe_~ zUP(dzJLg7Me)&ppujvp|s~DO<_+y@(w|*UO>Ka-eGB&c_Z#uf)#>ehw%_*z zK0XUk5i4k+XgvAGV2Vw0aA+ndB1NGB_unErfYyJ^h?j85d>0+B(v#tkCv5uUbCT(4 zun1&z)4qGC8%>bWWvVn4cG7fJARrUxtgZRZfcCF60q7+vV za0_3mGj!zETpE=*JMVoNbh?t}K98*P4>}ssALJi>)RRGr)C>lh9MfPXWrnkn>l2(VW3YN5!ht+a$(m~PY9US%_AeUMn zL7G6`01X|-u|-Dtw%UFEY^&+2py4SWhrCvYHA-Zfrh91%+)pmu)v8JqH&>f_faW+? zM{a&@LTf`LI4)=#{j_HPQ|xGyV45p0r1yDOvN)P!o-tu615cG(e(UT_Gu>r(WkA>M z6m7Oaifi(q{<*sQqXLm%GQ8w-UtcTYlw9jIx&IZ3W zH-jHyeuLdfxHaT zF>hmt59X+u!#PZa#5s&qCnkSg_(53|g0?(@4$ue;Bf0?o3TE_P5p;+99( zYu#oE>0pW^hbp%F#Ylgy^>vKzv0rw3q4yB2uj1h$#%|uxUT*Xg(b61eOVWg!%6`Ec;UJ>HS?Fdx1Ktp^ ziSH->H|2S3{He6EGBTyU698iatU&@rei1P2WPof&;S|nFa;ykk;M_eckyJ#8uzEF% zX|76wc(Ke|^F~%Ks|{-SYCPoc@O23{48$$b(6r?2-g>>fgVA+|^8KW3j5jqu)2@$G zbbwD^#h3GvLpi%2Fw!u!u~@(F?rA@Nx%9!1NO>!z1>d)m(j;1OmQMqa{7Y3<4YbqshSH(nU)QI>>;3w2T89jbcr{fZ^aMypgzrC~Hf2G|>8(nAR7zte=%kVsdc4OM|fWqV%_`N&V zJ&O9yqH*t%B3-f784w70%pCOVel7ZuHD~d#1K|7iGQ7?OfAI29BZ<~kY`nw&t&QL1 zpJ-%Y$ww3Es%9Mx<{b!dh-RCC572B?@vQ`0 zrk4v={G%HmZk}qip>)gfZlncZ!6%s4%d(qfEIXE|2b|x{FTO2n(O7#%5zbiRHpa9s z4W`SB^(%+6AfCTvAvU;LNk=Z(C$ZojYJ-a8hu|vQ6puKa>)XS^vLT4%ck@^TJ95Gv zQV(8|k+wF;m5BqaDQ~Vu4-%;VflAC(YxK0-hLi*2?eckd^sc*wch`-%)BX*HP-zP~ zZ{-wR`rW2CZ0E=`t`z+y(ZMaGgr1BJh9#+%F4ts(cx@ynLAx8R)EHJ)I*}!t|9`Q7cN4uGa9%oC0aHJ!<`kS zc%=EJv&BinE*{UvJ9;&scqcMF2mF|heGNo-y{|m$ArMSZ2Wu(>rlZ_jc8MvKg>s-t z#$aI%Co;Rm!@pD`t2G}gYCKzB>_Q<3q=+#xheBEWp1e$u=}|kSZjiKCw4wY`o?w5>FP;f@vxU4`atjTZZ?0n`N+8{HJ^gRO@3 z!njV8NtM1Mj;E#LCY|2Q^l75TejjeXUYaI~wvz=K#LVM4tDkQ8iaq&3z+0?Wp+5L# zx%BJ?bBOJ2n*CYDK+q>{;S$&rTJ(!73UEqC7heGfefj%mMv@yb*-YCZi(qjg+9|8H zS}I{ZD#{RdJsG9a=WWR|D{XX?g$};RCd}wpolrnnxUV@_MBhr-$hCLr@g%sZuZyH+{G8R6_Xgt?5N+VeX}-!cNO3Ut<;330`V>xt)Y#fmxSBs% zMgkxXh7`Ii>S*3`7_)^#-=BaKRY0s0PxFs&%pqhU&t>43sv)Z%7Vyvn%*nA(2cJ_) zO_a*Ym5l2<~7Q%lX~L7j(P6z^>8kWCcZ3 zfznuQCBEXkj3ZznisN`~Rpt3)A5svg61|$2>#)dKkL_7L9@;h>MKp*o5X7AXTh#A7 zE{pzv;KR)y=D>;jCI~z~#3Z;5dg@i)Omzn*++u~3zIzr-b3v2;I#NHy0Ft!oXUM!$Sn8~$0SFX&qV`d2y>g%BJc)*Kj~c!=<c#{jd1QtK{ud>f(UL3NiH~q2`ZqH@Ap>Xp50n@uFv_N3r;q=V1v;g zUcy+_Fl;RJ2PpAjhk!k;LNm$~F0>1XN2D>E^mvq>*~N{i*V*k z-5@Aqh_`b|?d%-gf+_(grmT4^nPB#QlNgtO+2POu2Mj@f_H;$~gK3@Bv+s}#ONl+y zW0@ZXwd1W4v=k;I3T&1~D;WYWTdze5L;uJWp1w59Yp!M0Z}*{N*Biuz;9Ezkt73`f zbwdcysft(1IAf^4W3C=8aro}^1?p%}_hVD8PZGRZEu>4PF+T#jlA9z1lU!`yC^KW% zp0;-0!08%PdtSxgngam(m@IRvw`#=ZN@=bx(_Dip-Lot zSf3q{D#!5nWi@@x`Mu7(T4R47PbD}Q=FTS)N6+~SyrEBLhZ7 z$)K%7msWGT0bLu(aFL#DN5mGpg?lK{Xp%Cfc}9D6;RHS>4KLQ^X~M-qJsHV)R6C6R z6nq|K>r{4ai*itudd^vu;b!yTq&dff7>>s&V=+c%z$pz#F%~;c_!K%PH&r4}vesY- z=l8N@G7H*vT!N&_{5YAY=BOCv{i#ba|7BV@?ZnIe``)-V#Imwrp{`9eLnBI3g1cF? zX>5KrkdQ1`omD=&Sug6g&q1q}g}tPedRVkzyp|&4+M4dE)j|{PO{Kxdu8|N#f^fy@ zLHSO6*sTI!PT$E0>24C|Ra zBKCNS?^D@9J6Kubm`f>L;Xup_T&A*0bfvVh4G;n(5wHY$G>NTs*uJZ*j9dT4TUpnc z4!5-IP)X^l3qd9Nr9i=|S!jZu=CmpSm|H#z`K!6Nka#r*!%>3BbeXF={xsg9<3GAm zMEHhRb_tZCNOggXf=SyRZKuh2;A-Njzh~kkch4@VjykA+r2VEI38xu4!^*&c1^5(_i7@OIw&@%Hp6_R- z_-m-sWv@SfwbzRtbueMc__hXbvc*Qh?k6V;A@eMDGuXL8zFh~3YsIMf%Lv2ug$gO8?bc8{LbJnIBwa02+4(1+*YqKWfrclVwr=pMRZ;0laG7&nAp8g? z3FEE{|PPCgwdD-MLxg&ufHrO z0_>?SWejne-YZ<#rYjb%>}^L-u-lqF949pAv)=~FyHVc_5+M5X8d&e9(5P!sH*Ic! zwilOl5R+|FF{@<~Hy(JL&_!GLwys!XZsSS(wbM1pe|axy8TtYB*HZ`5$V?0^oI}A9 z+U8;u4@znEhwHFRvDV?B_%K@0Uq&=MFs18h2+nn=oZp!g*d-_K(vW#hI6MBi>iGhh z0#DRg1wb}OXe#%(4}!#sKi!oSS$kCg6O~4NM22VTs!Ou9Vm77HSqARTgDkSB-qqu+ zMLo~Al6#XpDeG#IS^@O&mOHW~y-XTVy(j9Q`+_R9%OE*vhpWQSd8=d~@Rcn|&z(!g zJf~szWgJpn#l&dl(I{5Z_{_)ks`Q9OIjipNt?I56gfcQ6 zIR5o1vJ+R>n3RobFB>sNZBe{93)qY*vS>%q^m=;0 zmJxooS$f@UR-oN_U3B=+?~T%!n{X0Ps@=8xHGiogcMUHm)s~boC(s57m&!wtfEne4 zlgYq5>b{0a;inukrPalV8+`?Uvk5KjLZs8??4-5HDU#fgLm&q*v|7lR*Ym%G%lJUdk&|pN^4f_ z@L_1OwFjU*PJfl8`O_hL9k+{BzzO^StswhkEq0~bE=CY-Glz`CzIwho=_ydfTkR== zj@>wh;=bk^0rf_!p@Wz!7s5X&_XFmQ zGbT*Q-fdB{SD=a{yK6i^Wv_wlk6=XZxR~(=KPUE~%PXSEAM0f|P2P-b=2Wsa4jeUgxR4AH}DV zcm^b>7bI83^OfEpnoFpxdAMOVx2I=2ZiqFijclcv-CD9O;xE^=_9N$ZhN$t#?3L}T z_U}1=x(*u?VyM4VJIuyw|OYwfyokxPWD=PX5a~meU-ad zZy}9L0JouDZZkIY;prdLRFm#!prA}8^Hb@0+6wB$iFMnn4(d}&g~$$9WRFW;L8#ez z_2*?atx@+qh?r}Pt)yLjTfKGbCC)J#Wti>nPLsR)?OaS`GE>N0iNuqbdBHP9Q|JX^ z8?EX;{dTi}Z85Va{)?;)$RcuPnKDRXQ`(lX=kJcF(#UUWc?LPbOKT`m(Y5g&6-Xqm z``Rq6tSejlhXUsFF4OBIsQ!L*3PnzEEi|HQd9ET|Kbp1xey>pDsz`_0$1xcawlcPD zI7XOrI~{dEK+qFk)6WryQkl4sn<09D@f>uS!4OEh z9b0tmFdkXf4c6+PW>Y`7@Y}Y{$oEA9t(WgRWs1*DIe}}A-Rd*X!91ZNzo?a{e6l!a zNaRUS%l)G7Mg{EE=sN%;_vVWdMTNZ^`F6+%R-6@>ymRE(^|j@^QwOfF=TX@H0AGLB zC+ug(SWhB_fIQu;arLu!jdwEizAG4=0zhihF?ZN&A?x_eWcdeujPpn$pV5E3@%M$s zccv0e%~piFfE5C{{$JqJ4Ieqn6s*n7a16WKB;(73&{NKXBJNj+R#EoN2)aGbEPxK- z_JM^$2k2%zfWuCUww_gN;&sTv>`lxi*<%Z!bJA)$0`Cr_h(ujmHAh$ls>yTg|GZ(zU(^bT z0+gD!Al70N0pTrC=#~Uz+oz)@uU;OXc^|m(y)f2hntVA8F_^?Jrn-fOhVinuL0I#Vi zKHIYmM#Zd@DDmGCc%vEmS)AZN>B`54z=egl$ZnnhfJN(^4*ce>Rjm;Kn<>;lkoKNa zs=q&nM*{pC6`DimB|F$i0SE8MM&_%`)uHScgF*&LCD{&##3!)}FXeOG?R=?7yP2n> zZ5&IS;t=JAola#%ftexjnUj@{ys^ZpG8$_0B6;D1GdDX9kY*CJ>4ixW6D?m!ok%aj zKBhJxeT?^);%G)@sHsZ?adZ4%p2%2CCVNbV)Ip!>M1-@_x&El6Tk3{SzbM{6bd?qX zxKNdwuz!Us)XeHa!$#JAh2*y16hq^)%;UHev#}TdK0B%PA=4z5i{zFh-{Ds^(;?S9 z6-ZW1Sdom7(~MTuK0s%ILG&51#dukGn;-cZ4x73Xpm3<8OW`E2ge*00X)Ybrv_j%{ z)^`MKMjW9n(kxN7U$)Q$o_{Nfdk@*!-KGi+hU0_JubYDXhkGg5@ZIoz!{p`V-)6gc zw(wW7B2L)MN1BaWFGV>CqkY~|dO-W`Z}Z7fawWq2Tyxrz`F!8?t0VGjQSJ?3f`0~> z3hW0oK=XFO&zYr`!nxDhN30`$e?8nzmxzJ?EOO?Ip_3}k?JNU&#jb;%1@u%xTk7c`*0mJ+AMJ*6+oVGzBoa6#T(Mc`&Ypeg&2ck2lt011vL3P%uM5m zH$EybUS=hIfT@?-`BL9kbln+!o=*c7etUj8t@FAbk=fXMK~Wy0Q1osY^=ipqo%}YV z6JQU;u9U$i`~BMtxl|fz>)SU?8jLv6Li81>L@JC7Pp0y#IO1 zD6xw5G~Git-s3}p4cdv<$WNe_pNLo9fmPv6gW{j#R)_c9N9QK^_6d#k06I|}un4WF zA^w)Xk$@~p@jK&N{{9cE*l^haq%2Qj0;mlezG&aVM7h7F_Fc!>h%}-q0!z7ENRj!BQs3#r8?KVZ%2}y!u7L}QeVl$L}k4=gtu-G zK|@hIFEiHMqk;u93qq(sM^U3_I^Srnz1(7BguguEQ?8P3up(nphT$r)v{iE0w|?QM zaw#P|F>XOCFVY|$iVZ1X++~mEsB;2lyop=RBCpZ#rBTq9GKafEk3|%4QtL-SiDs zB^XVs`B(n9BAtFW$*L$bsDs>ym1+Q^Gm3*z2V#x92MIjQ zZTzA@;WnV!nE4%`%^5wZmc&A}TYAt_Q8@IE7EYVJ^+Lsk&D2JlFlsTo!gw)&G5JH7 zwQN#M%2aG*IqIX`&_5nGoOr+Oi)k%i0W10My(VNiOX=Mb)*i??nTJ#RMcnuBrXpKd zay_LSF4`S+`j!=0MM>hkrqP^9FonCu*_*R9IC|5Vamxe1DM`vf!I87c7@+y|QNH7z z;`OH=m3U~qiAO3dQ4ROQbWtum;BKO*a-)nqytMKQ0=0-2{z#xcSnt5e)S5x4$h=5_ zF~Yb#aLl|8BY7GALN5-Hl3u6Gn0uIc>Tm2kF+-|?AAC3_pxaIHij~~+gvXn%>WumI z_po<_Bq9~yxB47KP#*Fk+KFbkf5BUPhb;&N`vScRPmNTO?Mmlevma`hC?L{%>Rf)b z0=4JJSYo~%Ree>8Mu{SiwZLlqC0t4Po+7ZgOuewwFT7LhOw(fFY4OrxBRV$urr68m zr5*CZWg@=}9$gKaq{VB|Qp%w`klt>s^7KSLeH{V-G^sEvVrwWhKAiUN7FTJrd4W=M zd(*{Cl!{^RrE{{ddo03&fcV^9)7kRv{<~4@3qTXT6 zeydRcjh|4gYKdW~Xh!#$=+T}}?-yVWqa3SyyF!bC4u!;JU`&a$7*MH^azp4Z%S70Groxawj1S}{N3To>ky`>(F4 zHWfR?7~^0FbSEoZ4UN1W@Hf?Id4#vn%&2?-rz5SfpUg~G%m#r`c!U2ivKeSYyUBkq z_jP5=g;EtURU%*2?3+cf@zfrK)qRPeoEtlzVlW)zSmd8q)Jz>w~5PnE#$u zi(l<5|M?A#w^0*NnH8=ehB*C9e=uT4PDhf~#2cT~gllVaI~r0ky~lEMRMiBrmHIe1 zYoHX?d)Ul-ABlw>qw>}>Dc4SSp#XrF8ts1qR^UNr1ivS$ng)s7I6Q#o8PdShs=*i0!H{8-~0Y zINZ7w7mz08uqFpx1 z{AA5vAES!nMM+Jiqq=tykY%mAK^8)8(dFd9RAo!n60~CLwb21}*?AbtgD7zRx0dvG zHbw25yAYL@UZWMcUT|!16n1Q=E1t~otKu|h-Ag-IS-Oko%2ms_R;$JuuOXj)%kN`u zS-4v)ME#S}57pY&CN1CUwJYR*sn&*tw?F&@XkdIIeDw!Qy>q06O-kx1CN*>&FUm>3 z$6IuR8sxfgOWXmE!CbWc*xIx_t3`N6dK2=9jZSq!ZP={z;G=^X{w`3iIaRED(*;q= zaczZ~CAdek>zlTR?uYA-`N|9)SEFRtvQ1&@YzOc9!pT1>?QvpmFhWwSbbr0EXZfJ;g@`Sp=M*Q^&J-;= zFXu0vS$tEL^#&88~S2AJf_Q9{N`0 zhyHPwTi{fQ8CeR^9L{}G-rR-$Idwy_SoXkZ`kf4i(WnQvqCC>eY1rw*mOoBEePZOe zvPZDn%j()Cf3dipokRr=T6QoEj8Vu-%or7fk18KMK1^8}MU8{)NXHg94Egb4Z{MrLste;|tT*Fr!8n zyAE)#B{!lUiyc@UrwCs3cc7PTUhl8`zRz}2_xHcVqJYWkGMC|t`vr;3J%i{=lDfa1 z)_h&jR+if|AS-<-*YREmv9~Ik)fd_PSiR68_O%xHg`NFUnF{+hTQz_v9&)I9QzSJR z=m0bWyT!_AIO3K(!n!^mY+{;^b?r4XIuAG=>mK!iSw*__;N0PAPdQJgH!leHt!YS!LXv$?tt#e>q^6SLR!x#|` zm03p3ww6D%WKoE5!_%%J%b1sL^xe<@LD%)e+;-v1O}){lmU*WkOIUq;k}!=|F18+1 zTBgC9R(fkMPD)~9uB|TQ%^bJ&(w+OZ{|#A~rt_wpsYyZllrc>#eFn|21OYUR*|aNo zRBIh?a@_U4xVrJQIY-J8RO-DB(89|OH=pr@##emp3%6GkfY?(OvFYEcW#m)EiJ!5p| z_<1~Mi+-wt1JFRvlG8fmN`H)wU z_ZZ)e>G3frZ~t-0pk;?=)g6NP`@!--CcMEl&zTt`P% z+^UHznb|N4@%WChpk)zD%6#2BPORi2CJcBCJBrhnF*fQ1$uP_c&NB$gn@W4;RnNen z*l)b#8A5%v;m3k@_s6#NzOx=-sbAfIYudhyAIvf}{bMOq5a8#}M6=^B7*B2?XZ|{3 zeli>x(xRXT#{`(U_*eo?bMshGUQ>q`j9-&n`DwFv`qh9tPI#U(hwEt^<#cZ77Az{;3N z7rlxM3H#zE$Oz zqbacv&S*eK4mFg-#9V&=S!Ib2c!xi4ZW-nVgdfvm6?En7Gk?D$Z_gA~t^mDA{5W_0 zeN56k(JnldGSSsKYnJ-H#OG7Jj|MrE#62*Gjlep#7|y8uXUv`EA?<>X{8$aipDw9A zqZP9ImZv*?mEP8qu_RMWF03cmrdharpeil61YJN3O_Y{aVI-nF=ymoSgj{E%B^R65 zM9N>$jEbj1%V=3{=oi1a;fG!YDb7sbdRn!b*hC`Gj=A*l`eSIw2zjy&8CssW3Twgw zcx5S0?y9I`QM9-e&K$h}Yd-w;q|wRfU2TBv9OT0cA_~%pg?R;MOy_`FkTDAHOm!Ke zXBl9+jaUGs=_OO5*<5+DoojNHU5kLa3Y3eiIQq2hpZTISb^4*j zW4X%;M5{3aN0!<=>YMC=!dS!&m9(;Jakq}DwU^YHu0WtEkXZh=3^}o*R2O}y_z}rw zk5)L;ITqN(VbR=K7FSB(UQxRUo^)_XeqR98p@_Xbilqw}O@JLFr@qNJqVQu~B&SoF zIuX#a2)IaUH4O&kNAim}sG4Xj!!5(!CoggWG@u3cXpD*e_A4bFrMUMLTOsh5)2ZDh{tOfziXA1#O;$8-}&5yfn@DRW_ zpo#KLR?h9=NLhalQm`E*{qPbuN4qp7CsogP5zPKk=}7^oJ2OooSP zMYtXu>874~by*jDqOf@Zg$vAybw%l)8gz*q6{(BFF;f0Ti3qBQxy6@qsKaH$IMpk* zfMi;j1pj(atFh18c;&A9HuMe#q%{n~YwO^#UW&csIf{&9gy6F_^046PYYE`tXv)as zF~eL1*>y9`Zb*S>v9^-_rfYtE&w9~{O3;ax%P{IM+>987T`$T^wkU9X zDO5XFQbosA!&fRlf8i=sP?s4g&WrIkwdDYWue#r+je8`7k1>zf1pQnm?vZ!zUq(?< z;0S3;o4e!P3&DkRogwgbAAl*PXWe^$aN^O2a;LFf(p7iygzA|8KuPs&nu|yM5G7MX zlyc<{`~ES0+)yPkuNub_tq4a(>((AZfm@;^&>r#$al;rn5LxuL>@$7@q=+W`kKEq! zZ#cttajv;%Ke3b06RrkBK@MwzC2N8F3W#S!p^k*vQ1A4-bD>xUKvI=u-d>(YRekN? zCR1_g`0VoGm2zo6=ix?@AHXeY_BmpzW_Bv=Idq!g?L@{$GDXy7()JV_jS;L8 zTnzzRkoVx?TxH&^5iU4mRTE_Y2G^rbUkYN?E z0oB)xxyb97@G0B@pLq9|Bqz4%u8wA@Wj^z=9WjQCbq`5Oicp_~fb>kg!r#@Aq6K?Z z?E(hMMusB#p6!o>m}pZz`8&C|Sc6+|-LP8Cjxk#YC5o;*p!bw8Xjl=Y*D+~N7EA4Z z42rd&(Y(D}%Vd@&hiIYxk1P2dmSU5$JPRqzNmYvy)rF1VJo%?FipCV@nO`GAK91dU zguflQyQMmVFw3GW&$6tW5=SyW--(c#pFtgJ5-NbwJSS|(Cm5eI-g54!IRiPkDd`&rW>a7yL6HzS8q<0=G^wl;n=KAg-gVb9aLR!YVK^j zN$z*6iOQ4g;TXtz;gVhMl)~y=-edy21Q=nvV&c}dxlkJm6tekqbz4EKPml$EUyZ;6 zVJ*>LYtlse=}{g5+N9forrGwGPJQw|sH_Z96_*uy!1cUlwVzD$o$+bCTt!|%Dn0UJ zgff$aV-gr*b-upa1f%PqZqpZlMZ-|BP6o=10F@GR_HjiNn>PCYm6m)nfWJ~M`>d>dPEv>zJYL`s<8PZJquD#4LRG(rq#pFNl zB%mZ>PcPXD$-GdgmVW|mQ)t#8Un4|-lM;_f_)+B(BGMdAy^e_yxlG$G=3?{~B^#ae z(LIa)excUxyYu0m+8-=B-hjzP==9$03ybGu zBe@u9s87Q-=sN`KuJmI9@j4wfmD?;Ob`GgML>pt1X3A+bdo66KHocgi{35z{>EdBj zKwZMuIMI_!7U9D6&RDxi@}0pK`9z!(8D_4I_u4bQHsfqQyOAAmgrB$Y~)mP z@SvFtI!aPS+1)DF%*$R~8@s5&)pbNVQi$|^Ec`dYEh(=7HKzgL*+dbwkuVE#PC=7- zM$swLw+Wk{^WlG%d_bP=(vhLT_CVPKkg1my7U5|BBi(x2y(r+DcLRJ+l$Ae}>B4d% z!68Orf8$Qg27l=L=Xcby0<~#6)P-%qrL}?y$mYo?%2<_xG4rc1rokOtx1WF-HS`UJ zP4X<-{&PzMOfLDH_&gAx30XuI-99z_lG2W>@sLFuife1yco7|i%ArEeFd}IKV42$O z?#WkK<10M&_s>v;x?}BniC2}cA|=_S;*s|5aM!D7c^2BMHBGJtlFgrI$Hbo6bzDKP z`j$COHPdrY5Y7Lg>YSp(jJ7QtJ85IvNyEmr8{27Y8-HxGv2EM7Z8f%id&YUW=W)O9 zvB#cst~I~)V;3c$UZ=i{+)T+_zN)(>@2sjlHYnUMrBJC=abjH@A?ScvpeTu3nsItT zC46t*4+<28okxKxBZ-{F6o@ViOp9ymOWl7H-1l0>l&5WeA)q;I7R9)$GTFXQ1{9Wy17s5vh7p8y_*t;O%m(yB7- zuvzt-aR+UxShL4#@j+CT{IkGtVJJ`65#!d?e>AS5TDiML0KCySg&;$sC*QujI?rLz z=z^*Cv_9O$HMG~%8J9g4t*KYX9{yT$G43g zWa@P!&hv-c6?i6$1*dMp`L7yDJ@}_aIGCCKM~!G}#TYgt`dn3W06pQCm_3Z)V847j ztZQur2~2pl?un_>?sI|eL=maPXrBGen{#qeW=leR9jfrWk(k8hZT3dVXE|#@+g?iK zd6!Q_0>HOhZ)fn^HFS30zfuFuqa8id&Bqu*j3D5>?&IR)5!ie(ktT7!XnrDOBllNM zN0R5Xxytt8k}4%ERF+fpATR8cFrSc=qR9DoGcg~n& z`f-2vM%dil-3?t!;ZC$1d7ud}$46O_$6*r`IKh72rvLQLJ|Q-7(N)XB%fnzs^ZDvZ zS}XJ*W^KFZ2DIp_zNCq7u)90^*+bUIeG;>_Hko(oFAg5}ALpm9ZyHbow+ffU$l5i? zI}ln!`21YkOSc7Px!=DPKdMZvgc;qh%d}3etwIoPL7nXWq(-&ESiEQlzHWD`kDwtp zo)7txXr&4Y1hathm{}Oa#b&+c$u*+i5^NRlrMvP}fWejJpOi-30xX#ynVM%JkzMEb z`s+(;e~0y&muUExx<6%9au@lVFNCv==9d1(Zyk(q0guUwz{M2YJ;le`Y*;xIuCj?Y6j}dqnkV&u~3=H zyDUkN11j?yWB7ND3M0_y;50Ix)Th_&SNK=!ptkrwAe`ZK_zLp&bW~el*CX%A3@R+F z?}LKvqoWh}t|iwivUQd>9=*8KE3B1Dl`LsD*ANd6XGRkq>_p79w*Q)HiAA?E9REZ; zXXwxYDN2m1u{I9nCs`-UB0DwGh#TDhL00x23xxJeEaq5bPMCXI=`dm>#!K^xTAK5c ztj`PMCQ^Y*HJ)eEVT@ss7DPv#1x?S)Ev-S*<}=#45F}zQxm6h3MW-5m8y^hp7bc%? zt3=#UXGL=N7w1lVHq7>}#h7xY*8n-yGD{utVpQ?1dxWRUqpWyos}B zK^A>*uyXKH(r~fmm(<16S&6G^Ny9^_d-o&jIETsL2NNx3Y{~}WAs0CX$-OVKQpvEx z=I1?{nF(T;VT8;wuW$~9@fUSN34n}YU_!3JK@=qHZ(G}87$FSD*=jra-m)%Oo zW+cDpG)}G#SUbVP4~~6`^^yX>wAV_>j>HUWwJmYc@;iOO!rH9kR+FyTH;a;_L!Ez7 zE+`xNKU8@JlHyaACu~&aTb>Zkk(#PlL&MbgXYs84o09Ox=R&X(El^Z8KQt}PST6%> zjBrjn#747MVQHN67Qg5Le#gv3cvm54Em%0jPxCKv2y$Nn?V@GO)FK9C)_s$oI-hj= zG$evDf|SMX@3-RiWWg~QRTYt)7XAvO4oKiGx$3?W1d%eTv|XjIu$ z)Kyvy-W&<_-7JE2kkrk7JC&v2u!L*@JA%p$I3>9H4Xeg?mB(vhdX$vu;|;G; zG2M&6uq6yCWG$C`nWFh{FU$HfIap#4w)!QUhnPYN~{gIdmKeRWO>m z8l)HYDw;d#>J73X{f{dKmFLm;$+YlMEpkA_&oZ)tR}GAWUTaYy;WIhr)Xvz6e|=Z2 z3#f~TDaic}VW+?$7Cn47LF@_X^?-3NA0@oxFWdDoBr_+NJS^l2Pwm2{GHwX=BGUe7 z!%ZR4XC@cHjzV-_>!H5RaR~^o<~m7rSfXPx%ogozJhM_6!vMDNp-Vq_*K>HDo!Wht z8c@uTIN%pAhQp&#&cOrX0_VtInSS(>lCyG>6=t@M&>z5DM#YH%rZXPXEooJQQ?&!slWsH>5ta%1b3yNTF!Cf+b=(OQsPt*qQFJ~Kxn);M6z3%=q?+_a-ECCc@KIRZIMl320r zAG;sg@1$!Qwu1!STK0oladjVZk1Ll}+RT)pf6f6j*QB^=N5c1`u%{q+Qv~}I3CxGN zCRwXHX&F%6s#R+0BjQ>ts%d`@KOS`(!p*3A4V}Z8V|4T$g@ie`itsazy-YhG3U|u#1Fje%Ksx^w2=>NH2k9+9DBVxJlvAu69G@C2=!jAlSYU&z`%x zozyd0eHJE9c9j`(!4h&K*X^@0cDU&eG}o4e6BnFH$!{fRy|y%RUT-nbgWrv8e!<%Z zKr3!{7)99Kj+j_POXB7fk{Sp>cjCe&+ z1++MVQ;!RYB|_~L(F?{UYs%KmL4HHuH8?VO(g#)hQN2u6Ry**8o+Gi?ac^vhRSbjX zvk9~e$vP3oypK4SV^6i@Ub~1F+v|vg%gy0sMb0iKLG}cln2ohIJg(qxE2Dn02P*^K zRDNLM^4&2)oTi@Y6aBM>0hSlX?x8OVyWel~`v~eSGwVrkzRuu^ho~H%Mj@5<$~67> zpO`>ZrqpOG>W7KQm_mOi>hW53D>FV+ao0g9!8>j+|9OofWT7|!Sv?bq*j{3~3;y{@ zhVD6RW&v`mAAmHwpx<+4S0=8s>*NPSAqj1IT$2AlTY=M{s!yU=Qf z{cDn)R)`YU+fc46m@8XT$UdT=N-=MnsLewsvxB6q?ezXk>p4BIyu73ZR=*z@=^)P7EYRRB}Fj=9$_!yCbvQf=|+C%pa_Si6%qCF`A$ca8mo(*$MhEZ&?+S5Qg6a0mYLz#AC3wG61Ij$vjygv{fjL@oH!$J=hvMhK#balWwdJbCfh2?TL=T)-f9L;|KAIS+2>=K?q4!xTyemIV=sDXT&ug7wMQbM~850FP>E?1w* zc&|olKk6klQEjv&RWBD-rECN7lV@<=vL|c%Qq=o#gFk4FGxsrWMwishvx(y@a9lTp zN_w@M`k;yT0h=bx0ILNiClG?%QvJf6;s?vH8&|Gg*9@lnKx-Bb?lfM)Ks7nyKm7!wzvbiM zJquFQxVqVa(e2e?`tE>dG3OcBaOR`MbNSt6yz00{JA+g81Ou8>y+l9p&hk_{`G5yby`KBhQdzWX?yQtyw$pgXv$xJV}zX=*n^{%Zm0J+@#y`Oni{EInYZ z=@oCRb8h$zH+$59!RYPmFpcbiIHJwi;iv$T)MrcoZNOI!yN?Vo5S;#H*pRKSkFAo3 zU_A99Mw(35KriQNS;w!Nwl<5EgK$vQ*!PR?m%OJF`qtOlx$XmZ*JL& z;oYE1QP99nf?^C{STxP2CMDZ!&L1t-f&2sQInI!vM?2L`Ys4glqI}$*)!9(~r*t6` z!a{#pvk`Bl%a-4R!Seg1P-xvgzN|-weUoyqNNVE?l?~2cxLl3UqhClzw!daWW4FgljiP0`|8i z;ZI8U_ukaf^BB|kf{vz~B?-=K!s^sy_!K_eSeHvbnUtGwmT`alczuRtlw@y|EMwvO z`EEw>OM|QohBY?hpW0OSa+H@jGG#>WHtg4n)Wv}V&Z_9%BhjKRG-K0Oi5p9WoS&zn zwL3A;UumKMwg<@E4K%aZiRO9Rq9%tyr&c9o*@zbxlWp1|{CPYd*x@f3jS@3hDqGF) zc5WoKbz6IOe$CY_>)s9w=G5gMD>K54^73^$`=ALXS-9pJbKK+o&+>{8nD97-R5#sc zp9keHaUt^ALrC9DpH@I8NH~J)8KcRZMtf+fu>&lC)coVyOr*xxow^v(mnTs}MmK$r zyU^kM^lhy1_qh>P>AP<@i+EhMZ-i)u+ymcy$c+sVRh2^9-&bc{cPUcwT%qJ%x{HSR zZkH!WMuLvEM1AvnS-nn#jkFSK~L#3t=WLact>-2vXsw{saU+OhB_l|STNx&olCoC`>lQ*K01rI z=VFX%%&R;Ydky}*9`1Kke>ZqJF?hLAQ_l;PZ9m*KR{;L; zj?H2Q4BR6xB_}{zUZ?DOTgq>h;#Ab%@1yJ0yXJ^ae72F3bkOKe4DjbP;o(zcS2cI;n zq}nc-VvSm=uN8zZ=V7Tcsb#;A8gp<%Pa3A#-NH!@^KyWR=xLIIsh2yiE#NM@B^;2d z3W6bGAnWN*i5H1ZQm}j(r-wR5My|~uO)3cnn^@_!llLcEG2oT80UZC11Tkuq?b&ZKMIOt`_B_TEADvgURn5 z*$^G(4GQoC1jd`p3ye;!JV39$H)}*tI1Y14^>m~uaQSC9Yu%sC)_GdgSRg&A2qTID zhnykI3tp_Cb*LLhm?r7o16iMv@h&eog?+?GAPHvbXt@JHbqwO*>^RtQ<__k zk4`py`^UM{AF4Yyt(8o_4MgU^bPR3s2D3M177r5qkd|4Yx0ky@3{E@y1C z?uwGT?CSYb+!#(Q$uONt)6Mw@v217uocaM<*Hfu-O%_T_xmK;bX|oFp!2JGGP(^N& zVsP3*%d(vQrzk*}eq3P7RAzsy|}^Xv`hNcVzP>C&Ej**K=0hn!OK~s zN^-nXmaghPkPK}zaH@QnkpMS`_EU6@O!c_5m29f-XjWVEMfP{lQ?LdmA0v95d4nsi zf0m=wj-qTC2dH?{_rVhbX8637!?4e=8;GCUh&#;Rlg+<30DZV z=vlhps6JeLYfJF{z6+VCeXQV6Q&f&dOamIO1#y>{uHx#&`DdJ*2+oS*h}DA5{c;}I zW`CsVUchgk6>~~0+Xx)7NgRf{>rCEF@?{LY(1Wq4nnM>0Q6&)hTbW%)_uMtA+$m{~ z$nM_r>i}SSQ_#!1EwK9QZio%V9`ry}2vva%;nVfO!@cDJbM= zO3LLz(Z~Gr2(+UGw)xA0j>DV)e(>;>IRZXhA z&f4tDjE>LbxYszV(xC->gBPZUVPIl` z(EMT3@vORurVG=jTA16es+lK--qupw`)}h6&QJiH5|T{Y<0QTlUiXD}YjxN^c${Iz zje^bkv(&Q(wLS)>uHJ~>w9tu^N~FRh?FnCJ{j%c98{sE(+_9q_oUx6OqmzTNL5kEn zC=wZ3bT+g5Ft~1z$(s-PG1FE|TBjMKYtRw=O`_F~}y@p^ruoR#4Z^T%>IKkv*% zv(*+SR3tju1_H+Qrrw{TvrnF~p&4^BX3c~0{En4HAb{)iH8u`B;Fm}vcEZEWg&uI+ z&;JEHN2RiAisJvmyq-<_i>j#bN%_K4Azspf z@nl<5DaggcAd0kck6gOadsWuH(o=YE68tB^wRZ7v7ff2&N#YV_VCJ8 zeEp1;bW*wj3DG-TrwVQ%NN^D646wNr!YuVe0mh{2$y1ml{4$=Gyj(L!$CKv;D6)!;El6_(` z!66EX`M}%#z$oMg%6c0r!ThX5r>eduF{9b})NTK+bhbdzBUjKlvZ|Ex4O7+hGe@UN z6L-%qiNS-2yx&5x(TK3K(X~3@FYi=eTUvO%z@#)b1gDDGY$YE}KCnpqQAjA+Hniv@ zQeA%q1l=ncMPpM(4iE-19woYdVh19k*|o=WX)=Jr@p^ORkl`XyM27w-po0BdjInVt zx`^CJ9Aqz0iLBlSRo6T?E{oE+Ud{&TZ!sD66d%wun(qzD$UGF4%u-xiH3aUZ>qR+WW@N;cP`fAu zS&Ac*Bx^!laut$)f#$WWjE|{^l+UaOv!JL4aSD6ptg4v}uHl-eJ5K$^F@8qtk%ZN6fFDtXI~1LOsl=XiwZuePDc@v=%St9DMQWl=G`Nsr3t=OawmSvY>^G6#13RD<- zSDi{)C~J^;G-OL*TsOwX;`hnqxFA`*KmQgm8CRJUhh?=R(J)%rIF(n8A@qi6e)|_n z9M}$)@%bApQ7!a&Rg9UxOJr-2ct^MwlE9!C95M0JRnUW~nlINwbxU&yt<~9zxLpViqR^MB>;Es^u{H-=9 zQmB=b($w~o+}e(OXQU6hqg;Z07ToZLl0ObF1Kd-*8%~WGC9V_#e7w0KJbl`oFgb#` z#`1-asXfe?aoaaVlROl0;K{y?CPU$d29Jd}1!)-%LJiAth~2%$e<1w=T6Az4gzOu% zS>UXPoyIn!MmRPKa@JXMrCYDq9^7#9Gp%zcL>tQev0Yaq@)ju^Y)7Bol}s>2CL+=- z&@@?d>Wa%v{&Z+1W(LDgQ>4se@&2>?XHMf@e-MLcE9*|5=$S#bf)I~P)Yo%9e<6v% z#E2gdoB5|${AO;yuk$en{eP;M_u9R!rO`% zJF_|4PHS)mz@MJ{E}eT!?I5&3e;ibso6`K<(8PNvX?4BOFj`fGU)U%VhVMyznTc9* zt}?z^27lDleCN8h74f_N=hZSOTUTUZu#To9ANoU4h`e8>v5cw>fckl6i1FT!epC}T zthjcngesE3%rs%%d3lrR$n6WNUpWfKH71~)bvvY$a?OLs;I}oT)U#TdUP9zlTVt8( z?SAHttLsz)`rl61B{|-8!|trj4%`+~r#d{eDlZ2CaN| zI(#iRZ}WPsLL8lQzhr&?NeofXlDNH?s_ZV4iXCp0nthpz1byhdM#8|&e6_Ctm*md- z{OC#kFC5!QMBbtdnOplh;zP-lh}E{|z^kJBTwC70#M>4F;s8eo?Y~cY+hb1jR1tr} zRR2o!p7|8@4IkaPkFQmD&t;f>p_Mn;TOlNF*b^&dmv~~fN}E&s)~px$J^MmOvfKT}ujNOA$8v6K(V>D-6#u~cUJt6}dfu2% zL)xPG@#lAM0F$5d+qAo%`}Sr;&4q;3(UH%JWP)^3Q`EG!DmL}T)w0sn@laAWfpq5f zL&oXTOZc}g9>o%D+eNn3*@e;D7&c^kq`Rqq{mAo*XGob=S|eLbq;*fncEgvOq)#q8 zyTxcQo`g|tcVyX2E7Ko=;WLK@j?!gjGsjcL;67)VK%>zq@ulLq4WCVWdJK2I{E*cP zU|WWaX8A|&-&YBh6#J=!$aSZE_ZdxAr{>>YVzscW8gaJzR3Wl`Gr`LFIize_>jk}~ zH;9ln5?;Y` z}>5SGR@~&Xu2>E0*YMG!jVmUBZEzi1&wGfb7ez#C z&HYsunHKsxV*BE_{F&kTIiUu)jw5;6`NI7PfZ`@i8Mh14@r#er(7dOI%ypFHDF!W7uWl=leZ-ImW+C5uc@TSbbp(VIv^}-bx(*mODv%0+bSLv`l9ZC_lgqT&R;McPn|A9%xc%kIWGcUhi(RZNE4^F4m`s zR*%#}A4zAw>vfb4q@Ww>EHbK;&fvfy=Kf=$Z$o>mT{We#YO$@7gp6gw${zK3VFGTA z0-m;3oc)DWnra(o^IC=bh0MfVT^W@O>~sr-N;;n-a7WggwjY$kLt!Ud+0t|JZl=w) zAXRZL-f!H-oC!J%AT(3;XohZXDBy9xn=G{h(vu%PCA?ZLEQxD9*tD z4JB+@9L0~lPPL&g-GohNzaAIcJsY*kTLfLHqw!3J-k7_)%eGRA1lrL3ykx;ru7`?DF}w$(7KaVbJ!y8=rKMl88bm6 z93v>-Q~TRuLUA~0*NTbU&baEpwe0?KZRQT0zq67N_WdHA=`lGZh{cZe^&gO?PZiNk z@2Y4fuuYlrVeHh{l57y=1;~3Mmr8VlpTP=Ux)54V^*$;$T5tPRJY$=RZb5aX%*UB? zd~u7{nJI?*YjTQz@KnsWCJ(m>yx44%n&?(j&1?H7^xL zZ1ppNXQZV@FhZJImSmp9!*Nx3r5^y}?mw~Ghsr{hr}|;PjoV7p`+nZTdZY^slm>MF z7?$SYBBIWqq;P+-K1iRHUiSrAeE1U0=)iL{+18LVj8r!6FR$xSSfJg&w+tRt2=1j3PwR6h36zCgy+{0 zGEl45Z!Vcq_61w9xye2BchLo%I1@;rs?%s+b_>j3w> z2cCAcKZdz3)Mbu7R8dO$op_y}FP?JrI8un(4)fsAhmLw+6_^=D8Tr#0b|n;_uMoIV zw^t~T{)mM#v-1}w6CQ603zn%m(K2uIdSMy3Dc|FZ);!|HgI_U>op(NSK{bD_<VkT_0YvVr>xXS9=MV|9s>TR|+ zHXR)ECNAr|Eq9&8E^EwgMw&I=sSC&_lh35i5J*qK1IC{*{w?VJ5jTCZxN61~%=+LN z+z?^9rrTJXNr*=V9fSQaVbwz7sl6DsHk7WiGqEEwFGV7TZR1?5oIQwGTS zZ*FF}s_QoOge^RCoayf-88rlsvLv^lY&oFyuc)jB^m{Y(Lly9$)8C|F>N<7rULXuK zg;N3z0GPjO}HT^r?3|}eiitcl;3jLI#K)umXZ#<%5&^Al*koL zGLoD*|Xlz zz+@IopZj$+shrBlt;a&B-@TIcCXuV@+i!e-@b$9tlQTQ!vi?*tm&>AUl?UTNkM1V^ z&Q)%5!mEKC?Sx4MWyUg>_Hm{b${$NztbF!9+onq>Mr|{z&zB@8tV8n+eLX%;R08u# zv(oPE3Gxk`)$KfB3Za3}$HKJO+8&8Hz{3C}y9CobS_zTI(1TPLN~yl+o;~wq9>+cP z3r+R~cmK#C#xt6W^;`!O->6<`+7HFe8cT`DdHSbc;H%=33peG{Xy1(Ud>-@%(+Kht ze^xVz2zE$cKt<(SDAG(4?y&pyr`ufzklB%sr%i@P_iGo-?lLbRt{8gSu%F6@0~T0` z;um$lDd#-!v#Hu7w=Jl0;$?NNoa-A>9F`3^Kj2w?sG}lTqeP8+(mTlK6VOY$ogpoP z`&PB8;bv@pE+YFj7=w-_tSbg)Aw?lh6_?XwS9B+OQCJ6w=DdOvJkni_pN>C77-t!z za(TnuaRqUI&!Taqjgt4(v{XVr0QzvVRr=m)90npKOx96Rv9@O&hMIO!tG>)k9&hW8o<_!@CflSwkoX9Iamg)f}4DC5kG(|b5+$^J<~ zV!!6#xnYCUkCR}};UTwcc)^td`WGqG0!j(_KaEvX7+h4*W)L6~;o>&>3D6LUc%p?lT?hVjFSq-Ed`(2Y4s4C;oMish zs*vQpbWYb@@VENGl@+iB4k&6RFmtpua$i7Rjmj{;l%zQO|JVOwgk-aK-e8xACNZ9o(gRDcOvYoQ)F^Dq*RPG^68tLsP z;YyH6ifS4cuw>V&VSclI)PlsZj~R-n{#t>u^J(k3KSg7V>u5?po$5%1^%ifmUB2_3 z;tjcBr}JfB8RQvYP66#KnDf-(Nh*&Y20;8hN@{cQxTE_>7T@aob`1LZmfp&|+?f5- z`>}FQSG6qU&Zb-@To3!TGDzp!j&jdd4DDz3bcpTdY0m_A1w21YWS=*FNBEk{&6EATG^;SnqzIx;cE=kQ^83c1T+coJD>Nr*R*AHO`|#O5!VtYRYvx)FRONC!ScaV z<+_fvnBHy88mXfxfeSlXo3xM!)C>uJoF+;+Jx9+o1%AOtQ#u7l*qAKc}bx}hs#!kS<{a;A*N-BgdKX}xy)NgE0W!(hHFm;k@1 z51pA>y@k+Pt+U+@lSNyFD2kTTec8Hr1(l%mGGKw;WG9}iU6Z{Yc9M0my7s&iOtaDE zdMymJ!ZM^RmG}AS#R#G&^S4L(i*xw+Ch^HUuvgc^|IcC=FWs7rJC!g0U0jTHlTb!B zh>vNM5T1GgX6t35SQ6uk9WU^}rs^cV!E_> z4<`O-i>b0HFQzJtjw!fRZ2tKb+8Hjzc^9LMX#_w`R#N zAuZxED|XQqWohrgpm=+p~o4{X>uC*PLKiy4#N)7sp0Aq z(mY@G$bk9ofzIc9O6)IgvejvGBnZSC-;o5Kz%x(6}udRUKOIC*?733yh53kN2LH6f`n!MDB2qi)N?uXH|BU!-|# zpueKNNWGtld)DO+z_7Z>U3dCV_Z7rWtf-haq^rMPQog`R1=4a-YFWY1Qoi4VQGzkE zq!4+5q65bp@xmz&J|IzI--+1%RlIg$^G!T9utWbtK>i29E+LgD#kzTR-X{y@=~ATZ zA&<6QaC$#4nT_^dEH|6+dqiujcs*w38vWhtpHNOmd_U_50XpxByL}qX-e3Aprmtl6 zyv*g?$9n_k+ppw2BBZiq0;f*yFCW$;6xnnufspN26>3K%nvR}ne4qQIbm@e8DR=Ph ztL6GI4e)B^aMv}zsmf`BX}~H7 z^sf&mqrSF!g}cpJE1N3nF0r`4FQbYF$G{*FtMmtTr`eJUg}btxUr5U=sjz06&sNc8 zfQ{COr$B!HxuxE;A~G_iZU5%aE`};UH=1FOft!*H6M2>-;2*<_H_~t-EX^A`gS|>C~odho;*RzV8J5La^ zt2N@qch)%wgWF*FH}xVJ&_A_oE?Kx zp9MJtt(-E>oPzaOYFKceWe~h1cu_UUQjw*uv}PrJ-99MZgV&%_b2!xyZUmSizT_W@ zYU>uBgBvLr=PeI;naB@S?(ok;k z&{DEWe@kg*-uPRKhINhMygzxr=s>C>Fo-wheC-vdRVV#>nOg(A6g={DT#NBH+*wgF zR({55m&twm4n@i!WV{obvU@y6ld0q$suED92}6|G2Za)|y4_x`6vgq~9dHKA0sSDz zHaDK*`MoDt_1qweyEbrTJHEJCAPY%PwMs~%DJ*(hzLUJq#s zgmF-ymq2a)f(~j>4sHE2scuYVU;n6y3E&*52)`uYH*(HTwwZ3GDQ!>z8K1cMi zq^G*SW=J;|sfSVLkI%3n1!$R((83;(A{&ZQ8)qGs{OR_dDVppaOwdoDzCE4XGJO|F z@}mjD7&olxJDo8_ATKV=J>XlC-vgDSqE?0pOled=KG)}1V4JtHm{BmLdk~Y6JB{M% z@SUB+C)g6v^Y)N-UY6WatTlUPuWth7(2{AHi_GuxhZwoz0-tFY1q8^6xw|S{KAVQD z*4kP?h(4We*vI%eV=0*%KYnyp9=)omft~9yR(G)XCCv#7@zg8AbXeG9 z4uF80xaB=CGiUFp85Jfhxid4s9BKtmCgBxo>7#k5Xof8AHNO=~b4Zrb%hT zL{7s5X`%7Rb5GTD3#WaJH?wUN`@FkC-wbix`D9IA?a%v)&Sz^wCC!YT#uS4WhY8$< z%MGN`KukXj0ACQ+*~UO90QDNP$rj>=8(g`(^v2Gw?2sl~XYCU6a#L0vt>^_7*;|QU zYU`bkR&`mpX>mHs7h)DM)2vsL#bFh%vTeL%`jna!i|VQd?+J?l-QSQUAl#~0b=zi0;RBQDxz0lGo+G$dO@TRY7Y0Y1xRGiw@_ z^>rTFT!RZdocI8EX(pv;7apjlH7R;n>u`RotI|91O>lO6J#s6k1nF{@z#M3pbMO>0 zhnj&<_P-Q{C@xWcD)PCr6sMM2*aa^*6$_~vJdmmVIuKZ=5(R>}&d|tly>76Z+|Yu` zA5zgoz=#{HSd8jz?cQu*mds6FbKI#il}!v9^u{ zxs|&Rtwz0r`RR3Gh>kIajmr$~#XzrBwAP%*?Zt4um286Ox0=imX@K-6V+_PX_3<^9 zoo|xkrZh)=Xzk<0ftcg!1sX|i+(FBaJ(I);0J}W_0*mC|oY-ZD4B}rLSRzd`1Z_2U z=)C*duS5aCBNjubL9Boquk%zf{o|dbB+o+6c4-EU&HWA9{obD4B6=LVArBL0ND7*R z#UynOZgjTnbAyINw*pB~)0fgiJBz?)?7t0vncFy3b;MGp>Oe5%_PK0}FLD0h2V7bs z049y(^B2`z-dROg^2puQ7uDw?PWMr<3Y1Q7$}8-py=P#qQ-9_5rw)G=Qf_cw^&L4M zaS$qP6yUkAB@u&fQzAEA)k3FA4^m=-pVQe6;j}txt8?>M z-=F&3iS&XSjJ6aCn*ZLC!HN>(@rmPzw75>sR{W%eG~`v!V(Uzb9=~A@x{15|3qmH9FdGs`SB1Wi*s#yxvDzq&L@Ge|&dXBNYCMY?sUu!l(D* z%gD{@UK1}B0Ru{)Q1B*c>SXZ@*Dw9lYZ3eNtc(UPWk_r?=b2{t=`d3H`;CcF@(Ekm zyjYe!;z|lB1q0np`Ryr@Q}BOx0JC)XVWY0qzHJbWIrS|EGgj=Fh2!inFuS*O>PQe4 zHTIE7Y}!mv+`2K2V(>lMV74V%;C-Unpn1&&ZAK|+#oWIUHl+KbYE-A$4(ZbDM2uY} zcVA7!+lb|aMDoje-M>rqY&ptf*e?ZlvHjF(D_-KU1T8NlT`Q8$zgyuc283(-RI9kI z%hk>UEhm#mHs9>}QPZ|eil1F4`~--$++cpm$0(Zl5mj8Qu3WCp%(iANTAsOBC61Eq z>;K7x{;V*-nAZx%HsR@);H>m1tk980)kX}2QkkWMWer7O4E}b`_>j`eswyl z35+)HWR)%=Qui7*070_miW)pI5=|jV2$SHU`<m>MLL<4=c$=qYVq}^90OZm9+?G)S~Cp&61UC!P>uu>_<{7OjCG9}1xh=Z!~0r&fluQnD~+M~L~;MS2P>49+I)uo<>6s~wSB=2@U#Rfj`eRJFJEPCTZEwx z!cx<74_#%-Zd~$bw}mm|EVc{mELAm+x}b%$IPpPl-h4+@-%kNn9gKjFIX7_Ss7w2V zPo4qXd7@tiymEC{-@3o{!)Z57MA_c=!?^_-Xf~le6>!~94HN|#rXgtsr(R1>3GDUI znqmWA{<(z3z#MAkQz2z#+RawMvDCL0I&#|k??E6mSg7s#%-ka!ZPCJhKmKeI^C#M| z!1E-~sxlie(FfEgFbeB!=q7wO=@YP43C<8b!B~-L`F0NMMnlk!*LnO97+A}ziG+i$ zRezHsrP8jqz4Xl25nGs3Zmqjz46HBw|lGq zaRdJ;5cLW39tQ~bzcQUpV$D}}(B4J+yPmOKxnE|E-IKg;bXs?-?49dxw$PAEi{BO4 z(iJY10K**OUQE^&NK6$fcPHz7THVu=2*B(4Q?41aW>AjsGt6vOYij6iKIZw)ITQgc z%q`*ZsWKUUIou3>B%adI^{R>B8l`9hde?GBugoy7qoz z^%PxJdbVg8rE-CUY5bS6G2te7$B&Z*$=6^W7RjI=$nTzgqTu z8a(X79bsJcWkQE?h(Vk&U&eM#bBETC%_hFY(wgY4?&n`4wj?hJ<7J|z9Ho9jb_AG% zp}I2|!15YhlhkV6Kk=LQ;}(g+NEAvCurYLfzY4pD1kMVh?0_n>@Ys)Axb64`YC&1? z`Krj~ivcGRSXrixucQyWwk$SiSyV8Cn=h)QTS_*L8|_pXuqWQl zA9nwLRGm|FX3?UoV|Q#j9j9a4wr#6p{jr_=u{*YH+qRu_?4)n@-RF*R&+~el4|B|_ zRrS>i&*yCf<>wUv>kV|c-j78sx)`FBodM14%sBIf*a3-S-x<=&am|KYM(^iJ09uzF z;i-&GLOP0kdL%KOgr>AWXRFBJoX_i}Y4!w~;N|(?y`zbMH%jU{By@v7`R6cjE9>Kg zLu8W~iDTgc(M?IPs~WfG%D!|8C%!Yn7M3!5KoJfnWv6bJ1K$tE3yU)zB_RJnQN4V( z>iwXjuoZ5^(iF96$yotZ5&xGeAo+msn7op(Cw%Vw&`E4y?aTvt$845@y%@e9A39wC z6txTnCE$0*=IpUk(<((|LtqkF{=8Zmo8LBW;zLZG6aRQm_jJKb8z!~JF7Jv&{-*s8ldgX zL-X}jd46hTMSw5)60Z}AjVL-MQxwIUBvvU)Wu4vG#Ktt{V;&P}eB5#XtZTP#BcvoP zI8krB+CQHUkx9L6zXQDoUIP?o6v&n*KdD~rM@Z)5!r1> zDnaL|9h=sV)xUXZ5vuj|dGot-?Qwo8z7`E%EXo&WUVWU=cUBfL+>vIW;ze?Z=X@v* z!iCP6B9{uznZ7c#3qH&NYZxvhrt=FA`H40hw#A*~O4S_7q9#!R3<^r-FzRA&?LG(- z2%g-G{E6UECSrD-8Rf)}Y@U-4R$|5P?H?o%EQ<;cn`0_Pbt1kCiQcEoI$1W(`wwji zIgr}L>pD2CwKQAwv=iklanLr-FhKE!3GBYVu!BIPo!>=v)eDskmn*0$r)i z_<@z9k;8V~D1W;|frZp;7Oxu?xSXIEgICTUvO6X3OnJX{y^eVYuzES_7;}-~<=5yl z2nfeVASd#HJ0!$nw?ZjB7d!wyB>W9hiCa?>H#pV>?J8JQ?~ zj&?NQ0PU-IlglsqJDkLUnrONQY!CXy=X57?3hK*sd!T3gbzsL&6B+Q&@nxc8z zA+qhvw0Cp;DdeDZ78Ie#NwCfL&qsB9IO*}GDi_FXQPfZ7!*DZ4MTwR8AERd{bs^|K zMh}(8pK+B?DwDeCiKE|dN8h6oJ>4B)qwLVg>#@Qej`ig*Nc5re<;G>Cqvt|U2HRP$ zf3tZX;DCzwvrKVXxWK!tpHXg_6#D&!SrBzZ@m|dwps2Q~)_W#FvzSJjC7 zs0n4(HyhTM!ze$!AQoD^7ZCLA9oe0kDw|YVL$GB&FC8L7R!S+!U!gbc#qoA&-7i6~ zQEmz>6Njh8)dMw0kkw9ERHo3>ii}*EItRADZ(p&==ogxz#=v`sN>%N~7s`W{EL&3} zEp<~1k8<4%pXReNc}fGBJl6=P>WxJbVA&_}wV|A|O;gqO!uJF#0MOeA%vV3a7I`m4 zv@YTOW0#>$tb;-niE9`$_x5CK5bbO4=Cv?XY(E?%u_+99%4oj-mlht;k41-xvCn}7 zs=2heBNVy1EhL0TaaR9^rDZi5Z}Hmxdq-dlQM9!Cy+y=KzfB`7^<@V;D@#Yc172rP;2B~0jnWA^WEk?W=GJWp#*(ED5 z2UzT=X;>gkOK-kRtWG3_?rd37nvDBMH{zcBpJIqaf;jKM8vV z@C#Vaj)Vd7^&e6i0&Z?Jye6&g*@p)t{HAqk%d|t1g&dIqgrrgx#4d;B+0Oj;lrHQOkJL1@2PgXpVdBM|EyUHb9Aw z2MY$crY}Fv&cE$b)Mfqr@0NP6?6?KvA@)WAR5e9j>7=Zil2z}se@crI4W|aN`#JuY zrlclM641l}7In;7tt^!viP~i+wnr14QJr6QoCVqKw+BVmjlTqL`5jgdX6idI11k~c zcsKN*2|@Q;&D{rzW1M&CjqY;@uyx`EjyKP5S?Drh_WR&sVxV{nwtSw}IkX?}E)mBo z&8M^b?XdJ^58e3Ta3|)|u%vV@OhH;naX_jE)YbU`zvryZQh%kX6?53p)6wO#Q%Wtd z;oAB)uox0?d_M__Rt#J76c>olpdgv+9&n$gNz8U4X0MD}g-^Drl|y8cX-sh^7TS^q z^`J;$)R2wV-x$?9oL$5rdutAt+;2t|OiuTYytl4j;?u@FVsxtt4EE#cK3ibZD-w zBBBW1l(PSlluKcnkx13j;uBB|{>H+p~1_MI6;On**)k zUapQ)3E(Zop_oXfD*a2YPKSHJSt+WaSK$XvgIQZ;S%Vt~-#4h;I5m`SX^)M+vNEF# z(C0-@+rW8eTk^s&cp$Kluea{&Y_$hxsJjM>wZ7Nh{{!i}ps*mw9LP^<9y=bKA1#7V zZLBW#w2f32{B!Yu0rh@8ptrzG_ts(=`)lgL{YUO;3#e%O+(zClmf!qtf@Et1k+_S;D~ZIYa)tb0iyCx%wJD6xQ4)}Jgn=OhAY++ z_7bc>gl6OrO9>blp@MMNx*9|aX`jOllsI;|hg8u0Z%B7vQ^fdpY+`qqgjBHKa_@g=413OmPhAr2PX)I_~5ye3l}+d|{84o0jq^GrN@UYsa>gN~^0RWhvBj66hH zu5)5|Oy1`_D|LB8;$P^14o0N|USJT)`kFhuMI&#vLrKk)$h44%T~1svAfxAM4O&jp za}|rqAH9~x{+tSf4Z6w(u%a!ji&!lJV z!x=tq;zcmCacK4))`Q=>ho2Eqrsw1Us)ifQsIc~SMZW92+b+$v@u&->1ZbGheo?Cr z1qGCMI`?{aHg?L$tlX3XRM5NAnPh;qTW0%goz&<(M0q!*oc9&fHQ!#@9wgM~YJZr+ z^*Wxcl<>20nCTcE$C$FXCPUT@$crde)Mr}0Pnj*am1btb%gjfMR_PGfM#0ofFlR0) zJ4A*LZEn&xP9Wz-?HVg%V#dq(@t!KstBNu9q1kRGin0}VJ@6RoV2l-4pl`;cXBCPar}$ZI`RpcynuvWE}#juY{Uc zC?R}|&;1#&Sto_44Dp!=@qQX5a2pZ{u!tC04j0j5>98~pR0oNhw#X4Zomz98-T4tU zgHg}hHyVuAd2xvW0+WxsdYLkbY-2U{i6!lzz<7AYJ4N;0TWs04epfLaVn=86L`npV z+rgM@Q3lB>l8)1R3=I>v;Ix(iOi9F(ww5g%uG}PLp)k`U4`sZ~yjao0fE#>v7!5ct zpvCdtQMiN4^-9?v=)k;GR-=1l)XM-?2I`jlx*M6|Vrn3u=d#~rj2J5lE{-e#X9`ks zYob)bLEltKUq^Vfq%V3*-#|DDHkm$H-5dgZ-rt0r$B3uQEpenQWY~7s`@}O#hdk}F z?m(D>z^{I#Knnd@dfdEow9Z(LbyxL$%Bw3p@~J%$vD^~JgwU$MJpH-W98W{Qs`K(! z@@g0wR68~x$9WmUoBLXt3~U~a5Z7HTsKML+b2X)RKDw;@krAYL< z{`yk_mnmrjs;h7Nf%7q#-gGAdt_UZ9b|MJD{Y(g;SSzxLi!oeP;a?wY7)g?pV#_s6 zAPP4OVZ%T_@{jj$gnh7*j+w4=oXb`7+6zjs`o15`iuRq)3Z~x=ipX8BFXp$1!pad=P@6gtO#-I9Rmu9lbJdw>VZYFh`HPA4;O@u15a#>`8N%q!w z`($>;ExD;mW5c7IQ0XxZHoRuK@3W@O#1LIfJCD~^M`&5=#+dwjuZt)X{I{GM+TmLm z?_R0755Wh9xlV`1g6)>Kn=a@@QDhvv-Pd)wAkM)?;Skeni5ccpI?0&n|!?le3c>w z%DPQo{TtBi$(GAkt6SZE$kJbtQvZf773TK0s);xo#7YEs@RY+J*k5zquUWSFJuc*T zp6~kf)D)`)1ED9(-vc|KHPB~WcJ&wl6Dvc`)pcfim@606gygHi=4M>r>}@|Voe<~^ zcweU|RDKiI>60Pg+{SQ|4{}&5SUUWGL5;X^)#GBS-ewJNr(8R>eWcRkMD{Y1zTh%D z;ka^q`r^o27dS8I(@Q1SXDw~l(r0;DCirm!cRi7K-Sdm%-i_}IAI*D22dWkqAP0g+ z?ts(UScdG|W47zuW|l8|vPn%U){P<->BA^l&wA|o`uk$%nm(vn&}kx17sh2hQU_|* z4xUnfllc_AOqfy{gE{8tY*T8VVPM5~Vs~2n3D|<5nr)l^mREU}(kJ`zDelG{B^NIx z(Vt7bG>;eBv1|cwZ&h>L^782eR0U!z)Hvh`ysHNw)hIP z{S|`}ON6OWt}Iw;2l=Ulowc*GhE5@(LltVm=Az}8Fp3bxbBut%L05WQ@z~J>{h8Xf%A@jRx}rxbaJP3#Zk9 z__GZ?M*kFgW%wzS548BwO(iX&Ph(*C=NCNXC(k>+$1+XXfYwGqdewCzU?xD-W&SVY zzWmZ5TjPjH(A|VVR|c_rvMIy##BF{E`_N}F)-$u4icXakxY)+|h`!1yKayyW68pYr zG3k~mSPS2$6n8K~BUA?s&})OxC>Kv}wL5^np#ZiVVYukSuHY4aLV%)zcXOp>CWyGZJ{~e58lS}PAW=k;7L}Y2Z4u0n zW2aW+O2dmeQ9UB|;6DiU3HXK|Ba3V+^XvMBhW_2VPyfFWjU7*0cn&sg50I*612e11N{2TUM;8cg=W-ok(aRgo>P4${t>5cO{*n$5lwI3nNTbX{8_^7!|Be*Bm+UbX50U->_>;{)6zt0RT6+9!d+?pc zpVTbgZYkjTx#S7_I6odZUcCJCcLpq)puZK}mJnV`k!W%d-fp@*(vHxB$@@z4liKyF z!&*k%_MQES!4I&?4bq#4TcdmaZWb_Z#LkHu3|&>w?uO<$41GSI=b4!}KMz+c}TC7!q9t05sE+7Z8~#(YdXh9P{P(%87o8` z`8mqDOT)TvNoS0)CZ4ybRhEwVNs0%{u3ywUotI3N7BZYN(k_6nMtD0gvxp)7`RV9k zdT4$C$Kbs9UyIj1pdo1mwX4dk*U{l(BkTu2j6MMZ0nG$v2Qd+LH`H`p$FA%j=Yxg&?y%&1?L4wUDt; zNvwc1HnL=l*$T?n^78Nh7`%B%ygV(E=*oXYNt#f2dA)4QfK71Mns~BOqh!-(J)o}dId%MR!KoM>7W?IatVN0Bcqqe z;TK!vt9W}#$LLUNX`Nqqwu=gwR8M!{_Bm#w-;pfX=Mf9N3FoV zG&J!vhGSV!Gbrd5xVMVOfqW`nEJunXw z6K2#tNuAU%%F)_7N)w%A_F1+XMhZ0hPgf(&m_4fM4~Kz#0sz@+iD7UIdJx@wa>87q zkJ3)Py>WcuLvO&j=wK9HtXJ~D@RUrM5DJE1#mhOg1`qN!A2p6DRyA`H=1JnvLx!8M z@$f?D0KX?iS>>4F{hUqM~95}wQ*IN``sMexZ9tBPXSs)VkTdg8989+;(9ql+$@ z94(m<0yK_~5dcUi8*!P!XnJr9={K91W%<8%k00u^-}3JyMfw* zhb#qEnBLSaH1z5koh#mNJznd_eN~Z6M)BN+V;~W;2XuOmzuD6r7C}Wlcd78lly|v{ zt29k_=_{4Td?MKIBQv)>zXx#9Fm0Wa1h!UA60{lEPy}!_lCm-tw)7V~Sz_>T z+QqJ(0>-j6`S-mVP{Ml1nNx0Rn*Q`FRwnGdr5MFix+<|_F)DXE*5uIJHMr9LQ1a4? z!CiCV=hbW@ZhPk)!WZo4C7P2=EmU80%pm>+PX0nZnMpmhk=~kq$inN!1hGvQD6%RH zQsNUNWCwZYZslkOH&qZUN6_4w^XywRu~rFQ51@_xBKN&EwD^63@zk!Ya=&WpE{7s-xT8 zDC{;Nt9;LikfBhi!FOw+9-H@9{@jnl|{!&**_n`YtXS51hLCHFV@W)=hHSlD&Jj684!LyWmg|x!b8A(4gmFlT{H3YG!QxzEhR0Sa zKAt7ektG**3{kht%IIO|+!F@YT#dt81E31go%p=9v?4ZpP&mX)+zmSbYQ;(x}#FZFXAO&Ik;kMJ8R9FB*A?}*H6}H zIFRw1Y|nc%?1@Vx&z|No{dSaF+{J{XTr3o!tfII( zIiO%*$0~FqqjX{NF-fs6uHd7#m?v$0cE{l%v*l)W^HsI$<#o@O@j^)}x49P$AJvsK zmNa&o5r|kRLiYu zhrwnM8ywZHwscK04zzD?%`Vf>*Do9q8}4*2;47_6r;4YTSH}%2x~EVKDR+qB?K6|~ z1=1B9ku>zztA=MuSd#DOJNEf_WQyqRV&KfgW=f|}8w}1e=hSCIKf9MB!ESPce?58I24 z2+B0b3dc#I>e1LO>cL7ZyPE01f8 z4&%-Y86aw`y%@bn?SdHPlRK@1zPzR;1u2d|O&hoXVZg9DccY!{(buZ>z7^Y)s14zA z)E`rPOjnH{$8s6`3xNOj)}CNixPpO}L)Mi=Vht-MeS?Zzz&dg@{*;ohS8EN#Lxt!h zK*88LfEnoMbI_lv^XAUdcdZr8$ex!;dvYWt3s*>X+iw?K+sT2_-QDr3b{NRB$mHwt zarReb4j5Vo@d6;scJxmY2WyCb!WVqxt&JErZTZ;`daUVxUY1_~NV(=6#J zmZn7U){wTvD*ogeYOPQT2#27gSWMder#3{w`IDP1NoNvFHU4hP;rbC`mYO`t~Q+WaAT!17SBdSt8fJjYE6tg=|s941(g zndp3K0=7EXr6XOg1+CJL;k_)$l<~6wH*VXhJaTRcA7B~PTTp-xTjAuQ;A-qf=HB+% z$eTWq=U9-FH5P4EVWtlA{%HHMr3t6%S@oPK%n7GNDhEL|-srk1vlWuC8@&~H?8WtW zTHdK&BS}hT1-bN}YJ_Xqxa>rthX5XxiIf4zyji{*Knt`)1}a!(iQTyA{QS_NbI7Q! z9!cj13m_U8Ibee?I~L~vg#B&l;FPxR=Nq})cN3YRn=tW7Kh}~an9oaS;m=Zc&Wak5 z8(!E|Wn7vezb_nWm@%6aa%)~>{-lFi{3oVVnVusyS(X4O2=C+p~;pue7L+Cx81;FF$L-JJINcvi=Pjc_dcwu2A?e>bS ze)*5;YZ=~%=MMi{8<`v;0-#TH#kGy`MH&1@`p;~H;uV_3-IkltRBc%=_$jN*avKt2 za?*xRWgW;P@Y4}%O(4=j_jdWelw4%QQX^u|R8PB(?9u1)%75H=c4G{$;=l2pYR z9sn{n6p!LEITs<>r$ZaI!BP%uX~Upm{MZlCos`%UW6!AC9U0YXgn5tT6{tjI|+Jp@P1xt9&S( zaAb;clohOjz%db@VH&MDy}rlaO>6g?|NUw&tdHDZ`6{XG1asQvBrd#M-NjE9N`d4%V^>iZ0I6MMb<8+MuuSHj?C__ zNE<_##cRDmc;xp>4YDIIEqYZlqUv7RpLHLw?o~eouuXG{Hcro7=1$-6=Ir%=(;{|$ zexBKv+gwu<-pjA7i@ZEaj}yq3fXVIroCF!s6_^a5{ssnAf=<`r7rMi?+F8qWHIcC! zgyt;GJye59mXSLxSCQCY(qvQDlu!V_md!Oo0q#Tf?*ThDRj9~Go$+TLRwK+L-K7ok67 zyNm--vnUQrU7X->xUhfxnLQKHYE*KUKw7{cDDl_G_MZ~qxk@U-T2CSa?SutKosXM} zAU0LbpPi)2HMI~G5Ix;KGul)HaS4rr25)l%F}g&zyv~SbBlr$?p;k4mYD@?n_H=R= z#ju+9AplIG&y*2!+DfMvbAJKI|;F9SiFt|cNOwtZ&h>WKzz^^s4@LoG=7)5;g z6$b<@T8ftJB%|8|t|$p-w*|YtlE)(qmsY+|Id?2HCRg{Cys5iCMg!)zLAb}rkRZz4 z1rV%3jde9g6}E##ASIe8(HBV9l+EX7iTEc*29M1coiz^TT=weI`ffou%;d%^?&`h+>TBCCG9SM=PXY3@y z%&OO6<>9R~?oF2eBI0%XI5*R>D94K}Zuj+kZiNc?7yL<$q(hyCjG%t}gPWNcjb*i=61~fkVo+Fp~}KVfSZOn@sS~1MlJOGBhn(prfR= z3H+PE3sK@6ElYJr_c8L~?A2WHf-nzLVYUfg_k*-l0ePm+fE5_c!n&T^35itju1Ft8 za_qxuDGCcgpgH^0$kynyHT&}F=`>ts$eJeWT8656yFEl<4s6gt#Owx$?h@i(ZW#!i z9Vz_Nw12(~IJAvYqijEBS31f?^Po-OHQ z)eb7?{_Fll3`f~F1(yO?T1!2!GDMxMsSiDb@g_!iyXH{-O;-2f-J}VJ2>myi-H9u2 z?S+&EMJSPknd7?g_I2Vm_;O^eGBCw||32950O_Y0nq*oj4LVlLj6s<~WJu_P7Mr3b zytZ*lbOE;414m^doakWTW;0>8I2m_xfLicW6J8D>Il%>ZNnik=G0&7hJO|fL`p~B~ z(02{3R8HK}Wml_^PqL5?kMz9#hu%_V14{pZIZ#seY67hFFU`TEs6LCbKZVZ6_LEIXiHY9(uwv0noo}N^( zUZRw6RaTs24I>h>LOC!48`G1~Os6iPf#=k~$w`UCLSv8wxDgLnQYzK`A@!5yuXb}S z_U!22*^ZAk_2Xu>eegA623auW)W>?DR(c>X*c#o|XLE|Os{5*f?J@V140gOh<+_Zj z@b5mJJ{ESG!5$t8Mu7x`#*YQ5{1v*wsUki}v*zUsa2>94 zV?z~+VwOvF0RDxwp>cua18!R((!dv7ncb0pPM66r77-8scxct#=_bligD~^uU#Rgp ziUkwKvu2lALQ&-s;b{s<&ACZ=(@Gq)aM9H7K8tQi3&GkIISw{Fq|Qi(3bYnAZch?z zZ_YRR*P}nYmp?r2nSS(iMMb`n0S)>uRVEUL3b)xP0R2o)hM@Rl4p(MA9#sASccYQK zwSS$Sz}uN^d-lg1f2v5hB>OMe3zoTnDYW|11X56mB4~4S;wtMeCCwjSmR+VS?g+M| z_PcI&3|P*>yebKJSLhi;9 zv(uv@V;I!!m4nyVw>nzEiP6J0_KzY&JsUa~2=b~;l&HtTz+I$|H9@%_I%KJkRxaz?BO&Lw%D;YL`}%#0;3G+wVEt&`z3DZ{sV+ z0nRYE-G{Xs&7F(&TNee8MSZ)=r=e*FT`Guy$N0Q9(QEg<0&1X-c4i}{2KH(@x1;Gl zHvDeuI3BfI)@3Bht}w36d{{_v46JCUCf#UzP=Rw@eu<7uPxpaE7SaQRlQ8u3ItWcf zD{5bfSTBw8cl=1ydTut!BJLC2mcX%MK!P4$Ic!cnl9<+0DWOxBt#~u!@z0ObV$=-j z!Fb@5y%}aqTwNp6#j$~H$e|kC1ur16*m8uI9fhL&*?2yuh1O+>X}L@_#(tEH?)}0y zu`VlvdBr+&yJE>ta7fMKLF{O^F5=6xtXkW*iSWu_2pQ;o(IrzWPlh9NFu^m zxSAd5>D+HXd%U~9o2a?r9N2um@m%k-s!|ye(G7ecrLr3olyLEUN-5C!8tME@qk~q< z`rPJq(ER5_?w?iCh+eB?u&oUy2g9`<+%}65Ws!vm#z8rBv?Y+s|KZZkK^$8GNYIR? z849iSDOj;p9dz50@N1&+oXo%lj6+r|BoY|2)lJ#>FDU;uo~~O({0yHhm6m64FMWm` zRP;HAMVrdv=0=Q}Tr@Y*Ptv@E1GYw>gS3-=pp%$6>Y<+qW0*H4gUcs3 z!T$!sc924*PWMGCn~f4{2Cy#Akxvp6IwmF?UIGGaLsV0$Y6EK~i;1-XQsxwUJ4@8J zojVD(s3b2tQ<>nsQ4YqA&S|%iV7LbQ`O%4P zF@wYGBrWf8K|Uz0F^qqefrI`?u4lj$T=Pe1_7F_GSmg;$l`sWM0`em=k(7`~7;oI+ zgPD?sfuxh|x%#B$)1F8GC@MqDlbsmClpMiugI5HFxK@J;hXU1wA%w)ul5X4J6+x0f zAj4&Y=K1VbaFJV~ib*oqE(w@n6b=7?KvEvM9LhRU1%L(-Lqde(^7K;)tlYo{>Ww`- z$2Fkb!Pus%tafQHug~pj9cnC9SSIxZ{@@4={r%hQ);tP%DsyfXVE>lovE#H8m$f-` zp>=VR2@%_KpUNTpc5sH>K?lit)wH&7J46;-dW)$~sNLWiru7`a$Y;#@RU{EzugoJ_ zfLNTRzS1()gox&zF(|&CJileTRadWH*>pBh%T+D80o4(^(RDA>qj_EqO~!JDi~Z<- zLmK>VHmxiSYICO;@aXEWd{Rc;K(@SIyv2hMpHsEqc8WZ|F?9bM$MLK(*N@^g2{Fs< z^YDOTdAD)xDzHgEJ9?CP`$vqqm0#hk$9#WH$8bIl;#tv}cvzZUJ7LHQQcKZa2DhPc zl)?oo;F?+#LA$}dS;ra3uavc(jCv6s;*6U0CPF2;3-Bg5K-+i_!t|#zC>!9^8T8-X zr4=5E?_+an9&X0Nd{vaaFGelGbuzB$h5Y80H^%dy?&Hi~n{AuhZSmD>D>uR%aT$Lv zb6)&rvYMw(7V|jfZtCu4oBsAP*0h@D7(ru=RE&*zVPvrIN?)J`%TTcywNgt|uT2xT zeBQNmDA?1Z(=iuSo<)^b-kiI(qj$Q|ti|9D=E6NdBQ&YekvtP0?RurEHK#< zIUBZ$$_su*Iw;BxPGwPZ(6*A}tXeZcflv{h2*EdTXx=r}FUG&!0 z`ebRbP#-2ZPTZ8x@V_WD!DYYo;x_=d^7y6dzl2O3!GyB0Row_WJxOZSW*H#L_p&$I zEu+pm+j$S2(%0uG%|GZ^!OlrJ(T!GH5DWeEAwm#x-3?Lfjg>H;4QP*^#)(ZsyeqJ|?LirXdPd-)c6=jUUVI?(?n1_I@L7~{ zo`t^0p$9(t^FlJLobQs>q2LCogZ)A`N=oZ_p92OeigP$v0y@?*yCn=7CKf}gLoZ_L$PrWo!BL`gTI{F!DoAC9;5 zLNOV}lEJsSK<%YT6Gvv9_ihMeakD8w;!d5uMMdq3lB|k$oIPy4#uo)=EwZwMgub17T!8=(4zslU7OD8e6)BM z-pQ7?%kRuqh3d2#k57JEI=3o9t=%~GmSIhyle<3GghIpOoDlGv>Q;p8d>)DPITPmh zwW?;?uM^-z(B9aqD6%fe9=DKThZk}Kerv9jnN7;I9a<94qM|JWkF`$PU#=Q(xB#!z z!7k{jpt#Rbx#YnMEW%PsN}>Ts?{Q-a5rQl8X1%`wn%qYSIHQ6PJ}I(?3`t3LZOd;taz$bb`+L&lDgZ{*$o- z{06x;09O@D_t%<_i>3+w#24l=MG)8KyiR45zhV++j3*!(iPm6RV%W`N>C=%=Tn4Ry z8t>CFn~Hy^O#i51PG-ySg=hcpEs{NhFfM^nQra{LD-oc3T11`bCw3=4{3qV{i6+C@ z8pa)u_~ZzRhXp0=x1D|(Unm9(pX8kcm+QDn&DoMfq9N28HeIdt0*E-NfJ{O#9dOI_+rPsB zQ**O6WmmJeK~!z#oiOB7FU;+L!psKdY}I3a>JXq@`$7x%xAr)#!%Cp$^D{doVxGdx z;fG#QQPA#smqT7h3_KiI@4HKj-kCitNv9!VV7>fxx64@|u)la!odsV0yzG~N9BHDv zYcP6|D=p?(vv3WkoXsCuT>dh?xbYzK>Bxig9+G~vG5L%d;jq3)ProKFjH8WdGd5p> zO(%eJP6rvrApXdKjPQT}i(Be@qc+A{IkeHU?M?R%wNgsZSzKdlL5$0pi2unzXP-wmZC*s*ax)UvoiGc2;ml-lQ5xFc2vb?M z(bjk^xy8BbkEOEe*`V-BC5Wxql;?sv{Ggu8BHJD{5DpIQJFLIxUAMu)%w7QEJ%Qpg;@wk*U#IDgHcC2CQ?S6v0Pf%Lm{^ z?y_J>9*1DZ5Q`#si~-l(!ELGSz!_!dM9Qx|Y-)MZhy8e#4E-we6n{rl;<1j9%aTq5 zg2$>qbbaaVNfsa6ja*v^F~IIA4jEhO%G;&XH%*QMJ%ES?%131qU( zxu#V}os96|msR_4D;&Ro=UXmBh+2G`XzA@mPDxOUoZC0S~Sg`WI-FEd1|XxrCat>aYt!V{~tPFwq*l7SUI&@x4lL5`I^qBgBmPd<4KLfc8w|JuCq!7GqJ#n5_<|Mc+Wto@I z&eMCM8uyx;@MUi=#)aQ2g=af@-$V1Ub!%@^2nMB1^N|%;m#5~cVH@j^l{SPy+&QHm zn@;#e4$6(J+gle9QqwDi$7zu7`(Y=7alwpBnmNxxs0sDkNho_nOaLIw-MYhQ**T_r z+Sev)38?6z8U-m5T->NUHYMb_hA3FTX)DXLisi^jm#rj-iRn1zU3QD=H=YoWn3Tqq z(aH2i%P{M)%WO({=VK`|o59;V6n zjtV{E6jJ*u1JAvi`l1>nBMtgUG4k=4fgU4N^1p#G%B@4<$aem|boZ@@2@hUy_g9o+LVKQ0S~i_RtW#FbRgj=Yp3w8{sI2DfP5h5#>5H}^WDW|B06EgnLs zU@5tpf5PJBzzwvNv%R^O&P*e2T}#5{XkO{y1l4WS_oH*-pm@xvn%b5Ca#IC5_mnq4 z8$NgNfTm~Z*+_DYT+XK9NU*il)V7)J#Ta<%8~*oSt($TxvG#+Uf1w;MNB*|(9LYi5 z6S`OKkyW8@3;-S(A|xAeKTo_?)Mr>8JbE-3<0GG0moM~i(5ilejZltAsoj{)$LBg- z7mW#l@S*Ac3^VZKQ#)6^Tf8S8%l-OIGw||H&hDz+_kH%7mw56?iNyCbxSRc839DrR zse}j+7>DKY?EUgy?{5`NPw+8TZh8Tl)9nv>`f|l5P6Sv7*x3OlDc;xFo?=!yb>4`= zz;?qrd}tEF!NX3nsRczP{b;+poxe*`nBOHSG1%9>9ndcDzrKD9G&Di|mnR)Vj%acGlzIf;s4W z#4eYzYT!cVoGrvTUi?PUwDm}=5KExUf>THO0uT&ED+KYI{+i6uI{MIPaEzJ+A2ZZi z#vi|?$Dl9}c=R>S=rc^0d>JQMd4;+B>M53(QGS>poFre6)`1Tk8Wk7Ga`CNX4dXG9 z7q=S=E678)r1dCWh=YOvwW_UL_eYgnixbjx`-N&GNJN$u%sMJ1X%al+zs*F**$65w z4ZsBx^yO=ued3BEigznG|65}Lj*V188oVI)(=(VuSh~@sIs!PrWJ>s9w=`p8Gt}{C zE0H3=*wlAev%URsM&au%KSvCm8_*ra{c7=kColNV0}tsA%uDma3*Ec!KC0Vxus`)+ zBflkDk+p-1@W}4#1%bFjMvET53}r`r3BW#rn5`UQob9!YnU(i*!;O7KlV*4>q=#pY zE0<;4V?W<4LI#KQ0V7gnc|!?IZqY7woE^r|oyqtGWWfIDAv#u?2PsC z!w~)R$&?eXE$78vp`X7Ay>;vz;Zw(k#3~&zjYVPi%bq)YQH3#EfjnhU<;Zplw^d;@}iJ5lFz)yIR@$$ApeBL&im`D4+wjHBwo(jnP zXCh?cbb}yb_P=gh%1*_ZH~HeSNv4w{PQ{O9WJF(wWv#@{5#+mY&fc9p+9)TGAD3R8 zCOQZy)N9Ah8eU3NNMu6EcnAT{Mrp(soudj_Y z2S3iTIR!G%22o{3U z@W12;UtUFi;F)IimP4i2sF{FmSAIhFsz)was-EQ&dl7{F$tG{~l2Z@~DmYdT^q#dN zHyIX)&;?|C9vp*LdI)_Gg!ctF(G!L7a0IIJ!ySwS`NY~I#R z9=ng|3$E+qKz{+}tNIwCmQz1Q9%S8_od`-uz=%{M5J=avNCBuT0R9e;Pqz5zNyc`mg-R6_9#`B zXg@|33OsAnO7vs;S^|6pQoOFd3VQ8-G@jbxE%+{t3DlH`E1G1%abM#J)jTsd`4KMP zk*&86!S=hh2sO~X;6W~o<-7y%NVYg9S(tD%wlq$wNYUqor?JM^tFd-JX3Y2^VBBYw3Ec{NzNA}MitAeL zR%88_*zK!z*oeSg+GDgnMNhl;BTo>NT_aT{F78A`;=+&u-{h@kibNC^whp&1(pU7a z>zOeAzvzL8CDs2oVi7g|=L|A2vHg!Ti2ctQY`_1HGl-FS`-=c3kRT$VoAS=K{#OO7 z6P#P3E8P2ElyRztv71z})GoG$_)*?zGxST{VWa7X%R)3rfEA<{Bws{&>NE3JPuQ%u z$3K720hP@1ZI|oU5BFtiZ|^SV*MsBxV%YJv{&XThhqpc6IW-SlFNZ1}}HaCv8tIi)*y>i}UAUzwDSE ze+R@giHE>ethk0BJ0l?s)mKjyNe$S+)jw!bUhRYLP?+W#=T9~#CUs#;#$_*^sauAg zLW~{Y{WdY|lAn@f=de}V$ElLwUWs5^uW>KwcKo>P_*8%Ip-wRjPRW`$QC9_l5@q@v zY|Bof7o&adAdMfp=E%%4f3C>_~Qb@o=+?kN?xQLuoKN`lHf9 zk&p-x@~vYJyLVc+I$35~&lZl62>Yw(>4E|9dyPhNSy20$}uT*f?1!Tfd`)%bSP7sUXVS@Tdmw^6PMK9ra7{lmV%{k*N1_Awj)+TVo5ZeO6<*aeOz#e~PI$6chV4OooZB2%f9 zlDXS2)jx8Fz8uu-67XG&T|a#Vitm9cfco28vhS8n0NK66f`Vf&H}O}UNwt##Ib+A zJUhxZb4Ld5M}Wd6NHbbbC}w5%pVCCwVubW~LU69=K#O?Tw8jQiu#n^7zKJP-hPoII zQ*mZYHf`f54ZKuAc+z&bML!G=t7pp=^&i#XjfYCBm=B@LQ%tO@bu^pC_5rCF7(Rt5 z?`uruZ}n|`$CVw)L7mB~4c!w^8If-i+?qoi z?4pn)h!>SV)_nnVN4w-6NyGN9-3pg6m(QkEe&gu3(tyN6n|J0&4^N3xKcp$_LeCC< zyHlhr43H13TWCIh2KjnhhZK&pJBowb&_Rxazt3XhfdVgc$3ZcP^2<#x)#O$^7D4$% z7N&uW(^oQz>-%}iQ(Gs%uA$2c3Xc-T+B>g0V#ToSO&57N{?rN`>)x|Dx9`1*XB|(T z*h2Ko?Zn4W9Tu0m9Zuw`mLoLtDCA^Bu8tX<%{umB;ALqvF=d9&dY84uM%3Q+ovxY3 z+1N%jhHkoT!9ZJ{3M_&ghk<_fx2-AE)6jyDI$Gh@YC&AtQ4l~Afjg*F?T_Sn%jWirytVzO7IVlhq=B8H>M3mB!( zlLTstn4N~k8m7^B16f()4L0Dv9kdE(ya>e&uLL|vbE`*y_>!Ab79~jgfuq+)w}>Ju z$z`3PvRr2Gt!ygLBo)7sHZ%1bEF#Xov^)tII6D2{Awzi>o{MJ}^~DATc<(!q(RXyH$22VTfGf%JmR+cMlbc^r*WLYg~jJ0iBL*fAywdPV1;>X-EO5St)= z&!C;7Wm&qQjcBa02U19SXWt8%9gpknqsFY(zJW-FIs$GXpRox!1oTFIGmEz@OL758-&stvt-b3pz;)g21J*QmBiC_m97Q*`$ z))fu~+-$w?sUedF+U#vPsYA)12$}+{r<{e&w1Y5$h3&7he=%#=1%g94Ef&d;*5 z74+%ccI25IuEsZbei=>bXoZ_32`?2b95dDx9rweLO`&( z=`S1x(D@gAZ=XMaShZfwZBhXc+ULGR#Y_$d!6kI^dG4fM5Uo5LjJ`PDsq{)Xbd5PF z%|qXnIs23Zi+|Sc*|Hn6+?XZ9@V>0Hvqo4s*M?&scLVD#c+y-ToA3&X!3-)>X1wSO zx^l?z-dmPsGS9jpk9wjM zy7Qw_v|ECBjqp>Nyw=^!F;CqJ6+{iNL&g>r$Mj}tQ7H7#*Xe}UIU}a@LY4EBL0Z$P zKLs(Q!xXG+U$Sf~$kH%-RMpiKqdH61QL(FD*>+5yGV3gsTND;SAM5bd`=P21O8L(J z6<8(FII#u4g7!EIjrJ5Bbf_%4ffzzuh`}jb@cwS@8GbUwpHg*YnG90ae;hW)(iLqA z)v$TKx}4j-O#HxIjdKJ+3fge03&I`35atxMzDn(K@FtnA*r;CAv1Pk3T|S}Ev10nO z9-qL2O$Fpx+^M3xcJ$75O|mI=LvCFIs5P&A6_NT%xv8l4QDJ47lIml1aKUs^J0@FWLkL%@*PJd%W}dXT}fEO3$2gXB|r6?Y22LB zgG?&_A7*tJw)1NwQHfr3@vmzNz&94YNw6E_gUyrhL}X%L5`G|!uqEVqITD%@a~^h` zj@@AX>lvSR0{TXfxP|dLRsWq2kh`cco8Sjh@lE{a@0uLLd4ih9h<`d5*2B`Hc)5Nz zK~E&A)P3ys!8?=7mCFoqibh!o6Vz>Z?Vk*GwVi^@s$PjgW8%VRUfwGQ7$_a$!@|H6 z_sh8Y-$F5Uv;8)~c4+Gnz9r5MVHwl>r-S9gVlmut9Q^AOLxB{E!hR25^7!>_RAos3 zsOc{W?feZPMzbVZx04zuvgiYdD7CGFANSI>58lGrZZ5nq_krQf44p$hl~(@NEBodB zu|MEFd6`!S`FjuMyF}*$(1`|z2F1xnW6NJ!W5ikEg8N|istJO76xcZas!xefqd{)w zi{crSGZ*k}x<6JXtYw28E$+-=T?iz6Vh-vGX;qe7Z#~}3ZQSxyvn##X z^gEHzrFVJbNp4!0tJ1DEx(y$o*dJLC;GQ()saUYM`y?chvp3s##$YUS#-Fes&F?ASjdO3P| zNS0vhdu;#i7y0b3F7+P`gqG|bcz!>Bo?TYbE(K8haBsdnfik!>H-dV_Y4$JO{FV4W z0$6O# zcJ2R38D=K-|B*5}(s9K9ssB=6!avjM#upg;4MGBa#P*-|FR!iKaYOboJGl2xLQ%y+ zlYdx+?Z!^Yxx;#CHANKB`+LRX>oQq;sP8AVX*A&-<((P+f#cV^4-kDHczTarTeiF2 zN4)RX8GFFH-~8KS+BH0WYZ}TS2nK-4^l;qEV|7+4qfu!eQV-qZ9GTttzZ~=1|KhEB z?(_DrL)0;Aqf7Yc4lkct;{ZF~Z}+?!Eu27$ZYCQcNvdN!(HIUY!#bjS%b@Uhy_}Dy z8Te?HX&jO#{T;lRVM;%i)ZNmr16B{4z3eun04w$94!;kDUQdF==Nf>*hr_Ht`>pb3 zUTg0+>`v~VrGg}uk(+f}t{YCY>2u^a2i9k9jt_;OizUA#<`?gllTDfS>5Wbp`+iZ> zJzi^U+{%lWZT?=L@4xSoK`cFAQN@L?*|2AiaW0ktym^7^ers%}2f0RW_xUOIxLBxb zKt)7N!MtS_%7mBMyUGA4(^vn!7B_mm@4DvByF|UenVkc=fZdi^-^_K6FndMutBl6w zD$T%1(m%%{*3vo4PL;Rp%K7NVzj>53+Avt3YkU|ABIYf77OVYCc~|UyJuiE$YBr)7 z$63L$(u4)tXM&&hSF!S_bIX!#=E=j~)N44x0t`szIZCbQ`uzdcUu+jiblubA`IIq= zFR>-BkoHjWYY0&Ni$Q)S`0*EGHbM6xI%|&%ByAAa*7Uj_Q#NhmlS}h~yq0gJE@Djg zGT&pA<4K-o17p43BxNcT|E#Z9wX5O7;jIxWnWb~78~G7*#X0VKpGXS4yf~k+FzU|+ zja~GsDgKMF6M8_|E438Xw&B9 zpZ4Nlaz*Vsg}b0@ukNV=$RHkSF66+4(k|F!)Xrs{aC=_Wpu=AUdS$wrw=$2EbMdml z4*vEwu&1(bl`C32IG>Fx8##K|0xXR1r@Lwn2u$F=HVy!tjU!!DT=9j+rEKpPLs|Z7 z<3H>YuiYSt`mV-C9j1~Txxc^c-?5>ER{f+&lfHf&*ms>K^o^~q?R?~7WobpaHs({Q z)9PZfpmsw90``Mq!}%QW_GYp4e@L@x&7vLcV4!wcsQAtF9Jwy0EzNRRq4+JZ z;0+g#+-F}qeuS%S%N=@CbH=*>6MCInQ)}ypc>7lrr>|S*YM0qa#&xV;$^lH|C7oyC_7lai1aV_{h?U`_BfS0d}l+iazPg3C1Wi&Sh$4%o6!>e71^fGVC@QjB{T5RWnN)~Yyegr z%SK+56s({Qxzb&15TcHHfnWr%o^9_$;)oi)y2C2FE66IGLK=?LFPXgP6>&EBXcle0?feW@Wf zVZ#MX@BNyXJ{BuSZMAvmeKR8UJpeSghj7UsGCHkJ#WNvinCto0Y!J3lz|wRDnBOiZ z@)lFEYh`%ub^M)c2<|yf?0yB9B_N>iMl~(6ji>p7c}~U*?&%-2sUbFX(j6e1i67$} z`;j?ie`_kSi@ozn;bE^TbQ=c953(uz?vfCo3-!fOCHxw#R4tNJvmE@!ngN-rLsbgm zMvfoctQ)!)Dp1`g2ds-j-!{kV-BF0b_`tn2g=1ZCB}LTFb~M_9&^yzo7jpZJ%tu1^ z&WuyDCXn-YiwNR&e->1Qq@@A1yj^?Gn*QAk2UftpO#2!`hf$s*8~CiW(wSy(=yJ=D z{Qkf;7d#!d0kPF}ERw6ex&ofVw^B;lXsV4cY(axXdG8qNsTCo==y-FnbjhA%tG02b z;p(-?Jv3pX%5yLwQUrxE6;p^i%$+Dny$&--_CF$KYWDJ<$`2FUAYA%~>ckUb9r^;DX;syC z18+m>@Nq=Osm?hCnE=~Q7`NUTShaz5fi+{=TK)R9Ec&-}ah3Qi+FS`|l|8c>LJ6P8 zgWcwYGf~37`{B~w%zo&jW^9tHs5L8GF>EJrd5Yj0UrLQGbwxY(QcN|DvL}!ppgQg+ zvGC&Cw0&GEDwd&gOh#IVv6>7f z@ddSb#(@a-XXE!c z*U(i5^F0)b^nfYUg+|T`*8>H~*~Qw&r#lXkBPj#XDMMhrLVS?75Fn(sRm+bG`%k3Y zOgx5+EI&Gbnr`eyd`Qp{GmGWAe4J(EP7EF|F@#^XAz15s{q*Y@V6S z2V-&b3sq1t`R8mTpM>I38uj7KWNa=4DJ$_dWrs=FvgceCFX62-L+1=TRW9#`IrTv& zZ-kV@G2KfU^a3wvBqiR|RPy8mw6<{(Sg`epQkS@t8b*|uCmod5Nq1bK`&K6Bl6u-O zfbUXf8DNii0u$$+`&u=WPOJ)*{RKz#hUn1T5B z5iwr$8Jtpt#S+twFp{lv>ROyxt){hLV=$32MaTrC6|MgaAXU8d=Mxb6ZWRLoAAov&9RL?e!;zFLg>vy^l99_JdqmoK zQqq%qu~)#V2M>Vr@|!sEmv`~NNvSLB;6Iwxc=^BDoaX!@<(yuU0~_DaCCy!d?7??l z#8XF}DGxR8oaG|&2A89kDZ%ezSkmtjpJvZ92hAD~q^vx~bE*l33sryHvY?@Z%9oO_ zen3>h)pVd6V#>y3K{d{WGKKQyY-_CUXjf-^6~Sy?EJO$0)Zyrv85G;gSqwbaC5j%( zE>c$Dmbz;&Hn1tt@KiA8k94uTj(_$DH!wnTJ-h zSLH1|S}|x;Z2y7mH|idY37`5*=?_Wv1OT@MvM;LNR4LwzNlRlGJ+ei9&s~)A$?m`z zqbkqCy0a9LIm~Oh5tC^zmYbNq-=&M9}TcyewWsjRvK+e!86dJ;r)Tb}9#N@b8Ug8-TdM z&vttXP=!k(E}d>H(nwZ!@JbF8vn?!QvQ)=?b2EFpd zgW_TBA{3rMyT?{OLYi}fGl~Ya7~+!I)L}GVeTJZ zHvyi5deD!f;YCYx!4sx3?ZblH44M1CQID*y@eD(qUbGWUNT$vvtA#cRBQTFo9g zCf!^u3eY|Y-Dr$*3}*d``*#7whBIrm$O89ZJ62r#dtggpWwD z!+T3vF^56O-;P}D%qH*5>VRM5JK@smB*ERngC;;y)Ad0*8=Z zb<=X`DDVk_P>zTQ8m}n{qYTl~@p$i+I2zUUepJRT>^Q-Zi;yra(~}O0CK>Mdt`G74 z=_sFZWNktCvUAAy?I^+A4dN4dGtwrod9gOn*;Eit!L6kAbT!@qaRAp>kNZY^njJ8o){5lAm_7PqWOS<%cgfePEJ=>94$VIba@pgQ;(s^T-4r=5pht0Ip}(ktj4D+RPjTmER}J(NOj0rD0+)p5Hqn+@AO(!j;{&Y9pW@(I(|Msrl@_#& zJgAVBsYj=U|CUrAyZ(K>$W}cp5x*5l9+046dJ@j#Ep0xNXwER+cqEc}GNY2#U}e_L zQ#_vwxp(u+9ocPfV#)o&jdT_-FAu_#6I$QA8mGhQ(0b+&&Dx5%K$BL|E@LpGCg$j>*Y_uD956p23F37+%}&a)o4>ORo+KJKT98)0vqHHFu2 zg5}`Fu1lg?^co#BQ7Aq&!WnnRJbtoio15w1JVA#ztjEDOfYFH{|42G%JwoQn`ena# zn~hMm^7*7nNFhhpysGpAn@dnqT;v>z451S7#}d9oc= zc`P_O|2Uvwp6X%Z@lcIq{9J=zmSPrLna}Ikh2*dNAhWW1n35J7%E6t((k5UNPDz(0 z^@)NWYixdlg|0)i$;z!1_Vu`&EnX6>H|W)`x63?w0~IeA`@K<~6yKAdlEd`XqV~wY zcPEShw8wZE*qsIx{;l2T6dqssa~l98LrhBtWtt?+is)$7zP^kU+AG>|98v-4P~JQTxh9F)o}&8do}=b?T{g0?_UeD5Y)tK%(Fa4(o!PI((j=ygzOS&SrNR; z(z77-TuX9S=1;6kbe(%~(Imkp!+5L5^ccr<3KV~TOZ)iHd==54b)ix`W^VHJTa5omN?+u z)JNb6k5NJL1D*%<(ntT+G-2L#2^i@;lO)Q(qW;I1?y@p*MHs3_eeStSMVW%vI#gbl z^H}A&1s0Beb80`5I5`a*-^ruLymw298r>{7)oJrsV^gPz8`@zeORBjie0_UkNp#a- zkX+Tf?}==HMJHU#z1^3Z-aQ;RvZ!-OWTU}&@|Pzk{X4_K)}x=@pn(cW1L)JA^Bx}p znxsIr?HO}aoSea<7GW=``wB9B4DFU92Q@sb*kfk?I zI4K0jJX{{aY&h%z-nP=GEM(UJ*8%N0cK0ac1uVy$HtQec3Pd)fTDk_IL>63OTi*+B1(adrn3x_Cy84Vs7 zVGU+XND9e_z6;Z47#~&g7&Ft;5l8=k>7`yvwvYEO1`-jc#1`o|74m54Q#v@8X zcfY~u>FDxs`FVQVyIV$ghu_WqcH!Cb_IlVpKfPQvzg+FMzukJr<$62OpOu5p%gxir zfq&KK;d!{3H9zIY;p=Sk@$q^mD4cz}-j@&a4x7a|cN6zT$@$i6shp?F>G)}D4Vt|sa3J7dm zL6U00tTXk`Q64i1Aqkj-QzA=0!mN7)!e*dO)37P^7O5gZp}}wp%MVC zlz^;1|0Yg#1e8ik2MA#j5i2mDJ75<-#n$Nq13-aoCaEDxz#^kzHk=dEtP0BI1_i`m zSRl?c=D3abT4bUNCtyC{xAaKBZ?vF@6gCrO=-JsM^s!#xvx*8~%^{uQL zTCXZZQ4;}E2oV0F@`X5K@~>qTIfGw|0b#Ss58Pj60f4&U8dyV1@j?}L1uh%r`_H?W z-zVy$)6dZg7Y2qBF0%ONpcrsp@gMO_pi_(%NITL|D=49S!7)ww^H$TIp#@Cfuw)7> zN)!d>x|g=}$l0EK_h(l15Z0_%Q?LcJyB52JVpJ?;0WBy7v`scVhEFmW1$Yz`Vgtin z=+5v805UcKL#D<f;Y~kh;3* zlwdy6I^$TI4stu@{=A(1Y$W5L)XVlpy>%a``=>G<74y!~LYH1VImRHp2@C|S<^_U>`rTQKjlD>@aUtU9v`2?19jlH~vQq@&Wn z>-?470Vjt%-ca_X#qMK|yhv@gP1+UUw5nt{S}Cis<~!&O5}eU9yK64cnRFeRiK9fQBwMw0BH!IM1IRm_+HYRkh~ zrE#)yUZ@}*qXU2g%_p#2H;03F855>%f2cpOtQlHy@^_Ze{dn!jD)k;a27$2h)0!rW z7u0r5GRZW?Uji#;7E$9NSY!i0SDEv_J*N6Hjy4x2H6G$w4DZnL$q7_`VjvWDAFgOH z7WljaJhps;yUrW>KLfkz&WxE~c#~k;jDAg*Oy*xjRiFaa+JE7+c!98nPJr-pa1~aJ zUB%&b!xvbV=#&jE&vXZb#af|pVewia=uDyRn7GmjmN2x?WLSxf7&cS@pD25-E{uke zMeV06#0M}VM=Vjtmk^VSi^X(DndD*&G)iUBelBeSe+*{Oma%mI%22Lq2H9%|g=Ow% zpvR~+w0DkYP6-=s9V>@Ap%PvjNLy$rW+GA)kYIPqiNyp$lJzS`GNI+m1=xbxNW3rC zz*=*9AE=EfAN;*P5qm`fc!gOV1OFW95e7;6;@FW?Fq>K3!zq!60~DLsOd-Vh35GH$k=YAW4L+h4Tr3+$dQAz@ z$>JC{TbY4og1xQ)%!(%kb&TM1L>F2?23DA5ScGS(nePw*5d7L6?MAqj_#`xOk2A&5?{$xEexslK!1BJFTg-Y5Pt*2Rel zoV_f*dNJD;CKbJH@>Ey2Ti8ij^KdSzMh!b&vsp}ArkGckNqk7XN0~PbVe@qrGbxG8 zumOl$QP^o|0u~iyaY{K7p&f_0;kwk{eG2sBj8x1<)&Be)kytMBRIf5Vm(^PSTWQ_M zy$kVL6dS?-s|!fZkcgO+bq(fh7VP$rK-q5Z1hi=A5!`}3`FcXvL1HQ7$rTwA$9nBk zlR7foV_BvE-0_dRmVX=^&}rv#z7v4qg{E6Z`7&eeF6`JE%APR@hMOyyokH_fHMX?^ zYn2QzpPB#h#1n9;E;zl8F^R$?%3hH;lVPl-@^U1Am(1J0cO0hyeusm3*5Sxc&K-j- z`!dmg^DwakYd4-&#vJ8xLc^wCdu)M?n3BTQ1X{ndu$=K?NTj+cfzBr zvJ4tv_bVUk+ZGvrw5>o-)xQ(q!;U8=cugYbvt6G5O74$~&UnI-Z(KTH}O3_{^Mjc^RrlPCOGuZ8eXtv<(7TY~@e zXfdnozsmRwsWSrA0E2eQNfvM3Jwy1B5A0ls$uqGv!EukP)7mPN#CR|9IGIT`wj=~F zkGs}Xb}iy@roNX*pSu{xV0q$r;wst^WlQv9O7it6gcO014>VBJo$5sP2~U(7jh$9z zZBt&XZk0pz9LQ%cERKG6=T{J3sn$btKjs5}J5y1h>OWD0HgBImu+0SVk!eKoQXFb* zt*HrdWs0~m&Q%Sz(}_y*6@`dYwpRxD$H{?f8#fwo*&JQC)pkD{73ssCIi!^>AGw1+ zI;8OxydizT`B! z?GIOwA8j7du<*i@JVK`>+S_JJmAlnpkBu1VWHQGKk1YiYo|q<`hK*ZN8BAQ8$)p4a zex+*X4-tCN!$Lz_ms^@Ma~Ylzq|^o)v4sj-70ioLa;vHit?u}bMc_Z9!+g|vDFLl^ zp1fi5>L;bvS*Lf`XCauFv2Fm1V@57Ni4`v+LHYG<{spqMS?T0_C7xMQ=Bl}(zbBq_ zYJlzxH{R~sGNk8#O9ytdQ?+x<#*6v0n0<)RF1g%_2^iztAe^TcYNy-qRli249edez zAL9Z7X~7uslBl=-rn$GS+%RCXC|5Vu1Qo8+)trcTfS-yL-CBbvnL41-)aHF)v4MM0 zv_#7>61V<$#T_BdGIg^@%#R)WF2C^d*Af2 zvbEO=d;MxZjq@zpJWd6z>7L$wZH=Z~5c&20r)rII@bK9UN zWdk;1C?DP);&z{2UVrR*?=E32i&PiJQL49VyVy5}Gj#*NPntvJ%l6d}jm6MZ_$QY2 zFy1A{_=IHDEFF+liddy)Of6HYh?v~%U-KEq5x#up*GAl5TAS2>7CmLg%$_!^Z!7** zX0d@Oh&v1_JO@6RYN+nTNvLeMck4#Zcmnk6JR57rm=Cf@EJM?7RhkC5AMeMl`EV=x zh|}gJ=vJ_gU*|+lx3g;6G*4!RHymnum5h}Y{lnf4<#%p4Hv%9# z6=ZJxBEr>p1%vD=y}9K^2W{Gh7{BUd>R&Vi$AQ9x9Pix}8^PKjG1Kd~%ugG`fVQ#F z!c4l>GmSX?5l;1dmV2D$PYtz;2|UAJ0vA(9*){*G?LQ$$9Q*_XrTTiHGbZOaTT4Ol ztA`ghZ&|j}`;aDcq(F}~XZ*7A;sl_7vVj*E--nxlg>gYwqW*`8!JVcnG`udxXR)Xq z3y?Aw3B5Y@hb3(>rN*E2q{w!CrBhpdt&WYc(cu~>U&*YWx+BxI+VZg$`_yE- zpK?%-Dm)yROOH|}xk^a8OS-$Tv*;2xw+jlZN!HP~siD-AK!+?;X=ods21@U`wurvX z7_TIDWUI}%_(<>|THJof0rRUdhRoGou#uMzOUr!FY8Do+V zPpG`qLL|GFuru0kg0kp0G(52|-W-y4#B5U30 zbjNF|vUP8`wcrrCu5cCkhHT?sqQN1Iv@%1SDM?Y+g%5hgQM*_S;{JFX>zdn?L^h35 z87|i;YUm>2FlR;B@nc>aYI;Y)AdrJL7o^)E<$V)%lLsDS28{)o1lq5bv2~;x~01*3$t&?F__n>PPI<3k2`OZWHqSLQgaI z#Ioa72zUm>c^&r<#4sD#E(0C9Oy_T3jSthJK&8A6>}$&B1RnsB#kh|_0Ygs2y=FU- zRlLON8H9>8ja<0OWG3_$=yWXowmC}wn0Fv`;x?f~K^A%Mhm=ZyC~GLr!*>Xf0bfv6wiM-9SxLqXMb)ve<;R*a{s4$YYIH0hMr} z;$xr=%Q4FO%nP8Fd$Z^iNhWHd*psx0M~13wWN2tHDsU=Ur@XQ$DePZlE{(fgf}5kG z2evP%izQ+y_Sw#%%iKrqaW1em&Ql__Z&JWa zwtzH@dRM^4R>Lq1cz3TWdW--WGXwwuSt#&e5-_ zSP~di=aoV{hHL*J=o#~ zY0CN}`UN;ySR1O?`Lin;BTJu|Wtg5cT*xYHUE9NEugIqWSd2_9uWqQ?PTkebTXA%W z1=YHCL|{hpCQ?3~7#>Q)D_Ni-mDs>jlhILB_i4CJH20VAyvA%6J{OL4tr9E@c|tU>QXRstxR;bEP5LBnqJeVPuZ<4ag(+A*|l z_||EL`4_{d%1x-1dkK1^tUbCl=AHVZzR{Uww28xmN-I{*5MJ5wx2M-alrBf~#zelke+go`0qx#Z#QcWruoicGG}pv8Mz2DPiS*x`Ji-n-Q1~(8 zBtIc+U|F8IahWQfC4v|36ZP4av8xN=7Ox{Csmh#*9eCR2+dMwp^TJq+?Q0pgl2EFpJbDF{bd*rYkGd%D_+(j^&LqASyvnY*CD;K?7U z3y8rLE@O%OItr@&LR7dS1iEXW`!5SSS=b`vJ`JVRzcA^Cgbt8gEPc#+(a1QWK*7@Z zMiF@C!(=Vl-@;iJ`|))4q((skWnjR?5GIsQNTtSB#Cu3W)O|fY*u`|AOcS+I#rOSR(qN2uoJf=y}6m8;Frt00i$s>X5q~x~MdniVD);4r~M$SY==ID;ssiVWoq9I*^`HuEI5;ou{5xC^i zkl9%kepts*WS-YUoE4Qw$ARgSiymn_q2pA{Zpg!*gFtKC%pJ7PA>P(?#Z@e~ToQpN z0#Dyt32L<1$X-u@UtA=F%|TCXNRN}4s{h>|^+^%&VaTsmhGaEZtKTk7IWpixjL8#o z5;kz(hoDG0+aY#f1}uPwj`G)+4ZN7GV{S+i9L#UBIn2V8KxHkyW{`dg?LTku);# zhiJSRqU);l@WXppwdKY`3BpeXX3mzYDo}Ikz*M9PGw{WSf`G>o!3w~hjp4x?Kg50n z1id>k>#%p^4I9(k?2xPMrDJFqSvU?noW;{85xCpNT9Q<%5y0+olzCwC%6v@? zudhYK1*3eLCLmEc&klk*W^jtD?>8xB(5po6ffUPubB;6$k%JZhO*dzu>C zhA}4gNN<^KTq-!)3K+gK5$Fo;{w;?? zI62p~5`<;s?o9W?A^o>cDFLNK>GjFW43Rv5cT<%rMMTjjsqz{>YX{}4tkt3g<01FK z?Hv-UC#;wLuY2oyU+;|f+bVbO-}6t=!&&{7UEhtK&!Fvx+#tx!ve~SiqJ^x8IZoR= zy`Qh*|YZy&_=94r$b965Il1zQx#D;QQrkHgnq8h*#{8(_koSpwWjGdqYKCu zwtSKeOhpZppdO3CCM2e?XA429vW8mV36K5WjP&a=kVB!9~nzyzOCMRbA-NKe9 z=Jj{@$$70) zE(V9JG7y1QZ%@H@ecJNX2C%Ee4H(MRR%YNcERx=|&1*F+{KjkX zmEd;CWQ-vMU`&eh1A#y!&*k zm52jx02HfA@#jv))6>NNdvmMxu{ANF5*igU`k_jP}5NXz2^(@t*JEKt8k_b*nuJqG3B2@%TRvu?s>iKQ#@2R=^cK$MASK!5y7u~6w<*8x zd{Y!2;1lv!#Csql_vKAQ;DmDYj8y8$sXbo+$t~BgPzE3yQ|F zXqY5NNgTs>Q|8=Lu$2yTY0b>NB1Snd`LYa4HrM10yaeVP6goM#QMz!eRLfM6 z2BnVKEUd|H43uBgBv@wF#WDf<36vssb3_YCaECy%UON?$aayND+|iM-igHYN3dY)YMf+$4*!zp>VN45c2fK`LnHO zf`R#qBbZCWpC&L^$bJw$N>r$no3abHm5IR=x1GL=4*D0Wk6;JkS}m*NTU`UBO^Y{6 z>842@wjme6%Bc)zqBh(qM3(0vmw389|jdJ}Nb0QUO zg)L=zzDbrq|Yozv3M#)*h*`Dy^WWnv1r;{N;a}_Cv(}qb@IA?20BU_YsNPK@RIIe zk)7;GyH0%`z~)%b4{#xyAPlQ)`~`wnbO$6;4a0*)K!}{&Y!9SViGru z+h7)I&fhta;VZ|B#baaJUS`(=$VVgSZ0e8ht|BFIkm6yMohV9I_gXO`_U-bXvQ62n z@r`P?uXPa|C!Z~jHo17teYh&&lQ?RW2$5cVZ}If*@b>n2RUPOn+aCIgr|D~N$d3Ad zY@JhkCQ!F!W81cE+qRulY@?FO8{4*RTNT^3ZM!Gk0BQ^IUns^~3y z95{(i^&p30hNV z3WFsSkRw@_iDxII4oC>^|G6!|Y|8si$ETp_N>CFLFmS76nC1t8F_GCuI!M&hD*DM- z5R8hBxjOB1@H8_|@=9KGlq`FE!Awoms5L1?>@2u%wKC zp_5mDGs69it&vJv;WErz_kr&e%}2A#{FxwicNi{|A1APw02@M6p2TbD{A;3z{jHtn z&5vawE_qf+1tDA&Z}$Asu@O6#A**wb2~Ki32JiR%UJj%vQ+RaeX$UTB6PNX@8naa@ ziR>B=^eea*=4Iye?b`!QF&;b;3YO#NVphR@evx&QkbgP}NNou*eRRJbj^&u-f;70+ zp%Enh&UZ;c0HWpc5k%ZnF0#q+c#o}TgM9fZYND3NdDXwifFCq|3Zxz=(iE%wWaGN} z2G`~VkCwrg+#Oy)HGC`(YZxHUgY}q|R0QU8LK2?|soTR=`{g|=hnBDzD_5b@HI+Nb z?O+VGG8+h}QvqVAPr-aC&)6NP?plJVh-lb^XA?4#`YPH~D_+7=zZqD^TkW zXGNEb3);hpw4$IC+nEjvmkp=%Hgt9i+^Me7IVx+kHnSY~n}l=d3_1U&xx6#v_BIx< zbw8H`Kmj~nP-7UXp@kAbf8lO3U)?$}M_pdEwtlLo9qabA20#4rHVs4aOPT15d%WqS z!Kzwgwt!{yvP-(5WPNV8qjA*jv!twWT+y{xTtxoyV_^z@+@vp8H?1)QVQe=MJ&Dy? zwCA4C9?98!Yk7|u7B?Qu#5eq!S%WSWiw-I(fIQP(R1R&czD2%u!H>Px869{L3lS{? z0WE`|SZQyg$&UtH)eO?nF8Zp&d4&hPS_|DoDxZ3+-Km;95I9_YKG*2nRQ7!!Cdi#` zS1T%MsU}Jg4tg2F)g8Z|**+JNuglR2cg2R+n%)+-%k%KZnvD54@*#lnr#IDu-yT{QYP2nS zGXQni+6QBk1eGN~FWL;TWuPEV-+yEZXzoeR2(vP!aF$i-)4%jrwO95AL7jPxUk@Hl zj=A`#sDJ;hJ;qndHy7@JkyX&W)r>CewUu2@wYawq)NHk~CbQt*I@M~|NHQu(Y}xLz z6GN6*>%Z?2cgQ3XJDgEV#87!DrdJ&-X1_+3&O=o;vUo*L;V#nVZObsH8xMK*ct09MLgGcnntRB z&VWGroY6|3DIAW2{w@g&uw^QedT0 znE1gg%bczl(QbAuiCb>E)laeDTi-r7M3VI{f>6e7$rATri;kk$%1|t2N3e&^i22~P3b0Q*?6HvF^^YM`inCb22vl03Mm2osB z9bgWBT$Q?Cps!-CpIu3It#oR3pe0^X1%mcZnpILD(df5_u6lq z#4Os%I1!eWm<*Q#Pzn}kvp_T{Ysq!$#Z6K&Jg@4q%D{d76ivW9c6))_!DvZRmsRYY zve{;XGNFh5DfiGpKGTtu!ruy(*b7~QwHEUq|HJ%I&uDzg9#+wNEgectTeohQSjT69 zPe|b&O=^rgG{R=a>>3e7wViWkem@#>bQe%3Z&6BCTCVZRbvc-ky>SA|oUX1{{EPrU z#e2x-8i4=j`u&A;cE{QEnkNkSgA4x&Y@%snbtK^Gi2pgGFHZAiiN30}yGuaFd6n%z z5nx6ckHo2p4f4FZStJ{~jMH|MPdf6q?Dx^-Zh`6&z%&v$EGRs(;FI@O$j}L>8eZ8% zmLW^r4%yeMkG_`=$qT=AMo~sBIHuxgZEk36G~cIsY4rSc)~UxpkaQ-@#qlgLdz8#64&Akp?g&w`}N1be7v2a>@UmCrfH zH%*9TfE%+j&|ih|=+hUY$!L(M*H74AuLEiVR|ItFRyBD%&t?kN$xQKTGU)8=3kx>_d-`t`$UrY50fVbi3|--l)Bofpdqf)d-UloqvIuQl1`2xNCclO2G<_+3ZF|t z6v8aY>5-tpOD^%H#pv^k0TLHfbbKK&p7>@K#kyaelqZccvdvrFCbMh zSw#wurWO(P={V&-Tmj0dx~l2{5*QQHM$(Z{*3@i+UC<@+Y~kkfCIunC+%BO%-Ip5o zyIXvciP$<>N%+Ik^fe$Kvr1Hr9&vav1NmoiIgl-E?IHTBYnmu7R3ZbM$2SGLw|J58 z0Zl28S7FO<+zZK3MYV7I6_h+@4#NR}h(~Y13}S^T!V#zO8t|YTvY<3MM!EFowNy|P zvV@U9?;+br7xKrXJIvhC-S}G3;i~C?69Np~^n3eFEfcz#R?y_#}K4K4cN| zI1X*`-e(;^wUp}?f)RSZ1C1+T%YOR~+M{Rtbl^(ebOo>J5ciS^y6iAH19n3}tmpMZ_$zAfhW!zbP zJN_h)2dArmO-VTU@bN>w0gK4nULPWizFT0{8^2JLn6Q~EPB1zXA;0#QVX_UcmLLHL!X~8c+^f2A z19GvKiO8$}{vkC)h-0kTM(^wY%1>S-i5&Xv^F(xVzcHlu6Xi0@8JI{}%cg#N^$(V>6-cHeZu(f-RbW3#gGB;_51A}R4+m1FCb)MC6`e`+>U|c`0vKi#YnG% zj%Zil%+0Z=O{We1_Web?;qi3r1SzDBcjgY^OJy|kzx|yD#tt9Zw@h%H0~N2We-BT& zsl9+N0yh zKO}o|!L$)lWz5DXiUNs?3Pu+XbwzQ`-G2Bdlh^W>$AF4#AU>K}ZOzcDoMjH9jx?`z zTzliry_>UTYE(oqkW#E1knA}Vv0!)eu_XN{Koe}CrE^JriB*=HsnK3^OUOgmh?Zwl zEQOXJokrW3OE_1cMpu7(~*J^ieQa zBIEqwP^VZXZa&|5tKGUFKXt$p#u46Zi-oQMmhm^1K1bSaGVqMRLArwpJ?y3QK*mLItBP+bz5hCxlYt<-X3!}d6+(u+Y1g5 z8Ec=yW?mUU5q5<_?2r^O{uw8DIk~b6zP4f0P@M7ocLZHwmsilL z&h+ttT|ole*fi=7qQqC9;Tm$Y)L3Km1qW#v9mrd?h}q1-!SO^qOiYiVm#?HYBD6w^ z+db~UeD^yTRBX!n$gIgPhgSJ`u6Ccwjv-E!19F|VU6MF}B1Fk%?*0X5!$cPrpaqk> zjZ;?PdC(mlUcPRZ!A+yWoHG}RY0_C$CO;c?0BK2X(V_8XD6dpwjEGA+_TuZ70Sf&G zhNRQ+T^z|{T@d`vJH#|frH1|?PL>T&>HLzzT#_HkT6iA^kRQCbFYh3fOFRNfVX5{e zQ1VQ2s7I$yfe*T3WP^sm)vCq-*yGb?VVEPTOfLP6cz!HuwVi2uv4HrZ1cLXQ8Cc*1 znvx`&<{Pkb{nfrSvT0L;m{wAl% z^LUyQ{$%KQWfG+YosME26)Cnp=MZ@?2#Q=|d({iLDJl{&fDzYlYg4hrkszpeiyn6h$vrxnHpMJy@U2s+U!q6P--jT( z_~LiHp>(FhWOz3}=@d#VqY964Q{45k#7P%_Eeb$ONvWtvAbi^I!GNiPDc1|C8!a#L zc7c8Wi_LxpU-s*6v2E9mk;X3UleBxZ=t!~8zquh*ACt=Q|5)Gu0G{Ub8tWy{fha16 zthm`7QA|=yh<7WGGPKzI==Ssecn91yQ|O7x=YETYitQcwE$RrGZ~_7tc`^kDuW)^w zie`O~IlFH;VA~(B@-yb8wRv7Qw6WvN(kA7#t~a!RT&m(pTuOA!c~V$uXC8ITP@+YF z(!hw?iLA4=@2LPk&8)bObr<`Hexfb)>ovg+Bn-$ zxhWe~4rrPcpu<(wOuTt@)9sJ5WA|@i^=l5S@D9 z48aCBa&xa%`4n}O-j;Qe5l$!U^7C;}vuWwBxMy(f@mNOfW$+*?NkLtL}2dp@pAGNg&SMN4IrC)t0iMbV^z#ZtfAM?bgqjpFm{2P zY>2|B7uHUa7n`o^;ajAU_dip-twg7~Jjo(2()K7UAs#~v_0$&Z>+rLSb#KvQ(jW2a z#B{$jQsl#o%K(rBntEd3$YcT0URN94UZ!Ps3!lc#0M9c=sv9h-exUZ^pr(QunH>Ym z5#uo!iLwTo>8_P5bn1G+aLPYem;Kn3p@q=q*Jjpp;DiX~Fm^#0KuF`Yow{gvaAC=n ztvEqep>|3p4EH1kHp>CHFDg?UMF^-((X2NdUXI9?QRUuFe86pwpmZyro zGhJn=>T*4=<$#rwE+^ecxu_vzd*?H%gJeR)eK3&d-aab2l19@K|C_4wzG7U@wQIlI zipaag`kD#Et$!P-7bn7`0bYewlXpcP9&J^&0Ht)9XttL3J3boP1jGl^0ui!)KTF}o zZd>`RFdw0-kHxLX><{twPZQ;Yg!|dRkUv>-x+y!b;#D}r(nl~x*eJ8^1e@qOPEh{W zu#ZS$p>+(BHiCq_k3n=k2x4w%rR89mZb3DxNMEY1JUHLDiq_gN6rSCr6t;<>0-|2~ zfV()VL82L~p{FRF^d2R|x%m;+eT`zHso+8)l>62$zzx40&R+Luhi(i17EsvnEQ%rV z;5zWEm!J^daw&JPv*$RoTcJ%*u7ay44=$YiFYrc0LY*4wKR7;2G$>c{^UpQQ3iZ`3 zT)7qk!!X^91DzOgdZlW*c@B)HhSx)j0Dx+));M=!{AHVew)F+<%E}1Jzuzb{nmrEc za!mfJTm%^N(E2Yz{p7Y4qG>Vo7Mc}EpV;F|X!P5r80Z3X=&#b6Y~(5uBK*IhS`XsK zkl@c-xv=M?FU!jHJm+BY2;_8MPlF4Xb*Z1~#$k2y#vOdyF2BvblP1JRmmCy|0i)K8 znz^q98c-IgO&Etlm1(ro>3SPIc0U&3yUIrCv-ZEUC^}vIi#m|}+&A4IIP+qJ(Nf!x zV6`0BPmN?sDm?RcYwHFxzH3zzPO=F&F%s5Tz1;=IdWi_{&_hR7aFmi+u*-9^HH6_` z@GF4nAXk-WDUq!4g(U30i(!EY0Cg68`|4A}dU9#e5}f5ENi(n^KQuXMFoh@}F z79<5)M$K9Js2SB<2bGki_P##NGpjoY?;ZiqV0}pD23z;lWGNm(k03WY;e)#KXOZ$sv z4kf>3gb+a={0NObFyo#=u_Q?X16w3yV+1eNO^WKomY6Njg=g2cMp<6FolWCm+0wXM z{Rg{WaK&84phvBBqpe#rjCcWuGPIcYkSsm}x|^;%E@ybGs^{8Lb}$BCn*~`#P>QjR z8Cl$3ZLK+0bpz}w0F5qh_Y8@RI7-W_f}0L*^Cb$Uij7WG{7-xOIzoGSw_&3m9fs>7 zcDc8d-n=z6n4{N<>|XJUB!}TICZ8DX?>N&CViYglF+KyhPhY^!uWAjR;&ef(?fY;1 z;$5~fOGM{g@1>*eq=I7o;cFFv1BdU+j&Vi68zHNQFBps&@_+VA|EpdKj1NWuVB_Lo z|NrZySNevrc&+e&o6=QAN7SzsC@#DR5IeuF;#=J~NNN^gzn^ru*W>$f z=7w@`M|pwuu6~A-zci)< zI&iBMBsC)QDaL4}3QofcG8fu_M9evUfee zN4>9$=6MxidkLY}v!2b}dF1)yz*%YT-U0IV7lE&U+&SAY!&r8fwnka!x&M5i#=`f7 zZi;W!KkE|%i`=h-Dz7^QhxhN_V!QkM^VI1>_<*fH34Q12R4TKb#6e;FoXfh4-K96* z4|`vRGY;%f2LpgerN@K>VDK+lFTE>}ioT+Sk@w4#GW9|0Wm7f2YG3H_su)Al{x?_W zX~qj2p$pb0Qk8*(Wy=)_H=DhQhrrs&kMuSg%66QrW%+^MFE}AT#12a{&F9_#NTlbO zrv_Tiqqx(>s|Bi7ZIpDC!j2?^6#T>n4RDo!ZSJF?wj`R-b*`dG#HGZjMGBAsg4NIC zP6zoYK~&*j=uUvL!Y7xan=tpd^OcnbV0Yxzq=MXx9$Uo}{edO3qX>R(l(C$}`F>F0 z)78})4M!u@E4`>#Tgcuij@uaWmS3tdxuIo=fE7c8ipM8-zADVw0Ubt-i?z`kopa(D%>Qn=OyHtg=?BX)25DLcc!+1qCzsWP5F3wUX4mehLjO_#^ zYK`lna{cqwjP-Qzrs;6>rhIHIYRAwJfR?mX1FDP;W^0CBWX1;)f!ujqqOGW=f6&OL z0M2o+9~{fKF|NCj+o=!~ov^%8;Ss()mz$ymHJ?8jn(#KZ&F*H<1ezF;!8nRFsq5V@sU z3MFUyY4PNfmHM_%W?RhWa^MetSBX^|boDc*|h$Ju^cPYJV>6hEg5x327+fb zM0wXSL(wUWskUrgpU*{M=T0WOyL_G(2qNEegu$fE?)+2+FU<}B@vzDRoLFr6>5m|4 zw!IF{Su6+Qn;iXbF6AceGgM8R^NZB^4eSUnaZPBtMRzRAKxbGvgS2Lw946E<{mPG; z);yDC9v83MHZ^=B?kn@;yyHS>G`eWkhhV`E*SxwQ` z?ogO)N$5~Dik4w8V3;v+YYC=cKMc39dL_x9!y8u5HY*&J9=rm+i<9cKQO4>D9Ln`o zeoH^roZs4f0^dzPk9>M%gL$V4G(jA+ zH)apOui(90u8xG64;#RUj&FA60_?DPO>bk_0B$v zgI2!%LC~tKb@WVZd_bc6wvL1(MkzE$)^4wcgVf70w}Z$fR~mCdw=}M)fc+P6ri~Me zjg*fNfshpP(5aKh-mpZlt7K&VMzBkz?*Z;)Ed;RIk)#&(u=f)#)S)#qhV-jhPG4u~ zWTnNUnhKkX$MXTF`jS9sp7V1v(^_Z4Di=#DN3c=mb#zDQgr)V!7-#X6HW^hTINz*E zH{u7c*!v--daJtGo4V?V1c)RMUm;L3XYuSxLr=E&t6J+p9?|n6E*H8tz!Kj9m5+_G zeF9K)oT32Be*Li2+AHvWHbyI^#9&;OWsAUjsJpa*3-0F=b$vxvE*)a3D3E`M3NMT} zSHw$sxF1D0Ek`WV>66Ho!*MAh52UHGup5tVk zbG4GPe!+DRYFS2=WPwDf(nhd5Az2}5!oZ5=f3z^Kx^1AaY`w;ZLbB#vLa&kQL0`gF zce!iy;>x}>Lo!3u998Y^7PNl}KyxLVkfV6?^hc8sijW$1oAzSZ{yq($tFyw0$=xs6 zc@_BO7Bi#$0A46K%<+(*?;8`bMFJ>Y_gb#QNr`{Bpz&8N?RrAtrU%pF6s8v_0?Ec8 zQA6G3BT)IP0L#2;#(qk=w~kxP5u1!G4+N;zGYh4i*#Kt$hhDFWlH;dPtw1bux+r#~ z0iBH@LMcRIQffS#O+RZzEK-}lkT=!#EP)I{mVrhz7nZHzW!6@I+Y!GI_yoAD9;KF8 z-R&5!tN?1CIq<0PhGwPY?_$<$9r)9{JI;T1p1**!dJ;A_H1uUUt!yd+FZ zBM=TRO5E!;lm)(9)KS?t*_w5ZhBr2s1CleI=V3K4DUn%sjPX0&0v6!W(o944H0Zbj zLFxDeXSHRZTkDb~>aB}Lt1SL%*g^h|=&f#SexAtwVz)H;SI6#do!Xne$$Lb&+eb_8x{V(lD{|SCgIcw$935iaA=e;B0ojPj(_t{gv|GEY zKn?D%qoczQi1ayR(<$js53`YWRWgyfjk=1*{q$5zhUZQ|Ca7xJq25*Bg^v_hDL4}34=^An>aw1tCq=mz8I(^G8b%K zRr(A&T2-@Des=5ljI8@}P%h09EEmJ(Bf``5SJ$zBu}D4v-!qC`63UYt(*gj-ArQd; z*LisHV2H;}A4deK6d>puVmol&+yx5eCi>aC+Z*+rkooc-^l4&&|_3fQdE6*r8D9R@~pkazwu zW}6xB(qD+M`R&se)2TW0wy781dIAq;_7bwSx=xqysSu9s2k8O!gelMq;tz_~MAwZ$S;r zsyhpuJxc(*c;CZ~bxVV~5dekW8|&UUA;sSKU!7-Jay{Y}L)HG|KW_Kf-(}jxwGO+v zSFz3lAJ~<~AFs1!63Xx2B{tVgWs1ZSsar_8(J3-F*-k8{?^l7HH?P=8J0f-%oC6MT z9yC_dqCD{`sX$CFgjf?3hNy>1Wz_}4ax?_A5z2sUBgo;?tUoD8(4;sw&gB|3E1?}%CaBL-c ze{cXbcK1htUGS|J-L=cUOC%k8*=8=rRQt^&?OWs_=k0KoJTuJh_-q==I=%V{nVNk9Ewj%}vuZkeeAUZ|8!A)V&NwEHQR} ztumd1u-$g9L7zObmcNS@8pdKVZHfuY1j$VK)iM5XUr@!Ydg?T4P?JX&-IYOyX6Ahq zy_4jW<^+OF&E{OBJknd9;Y}zeA2k8?+Kz2tfA>&`p=Y;pq~RhC2!~hMx%y06;4Lw* zsuK``w>A#QyfvIeY^Ga<0n$H2s^up-A|gFbT7aYCl^*as5$d(E9W-h;r^zG_*rjSI zpg~bx4vI=1meAuB8eWb4WOH-n9u_ZSmQW?F3#ggJgylPW`YT{-CeTME?~?#x=iD8G zKhtVBQ7xtaq-d4~3yMm{D*I!aAjqKhtlbe9f zyW14QKO&%V6wOZD#UE@t7IuKm`?w`)M$rfJ>nESW(%9poA#VZ%n$3RSI(=Q^qm;&E zi{f1l9nDS9){R{z1pPa6{8)I*aozJ!*wsN6{2psKgi?3-FD!5cr}Nrv2L*F!9h&w@ z9%&;iQJhdOzCeR5HQoKsLiBJ5@GKOAUvb86fL|_PM9#8gyM9q$!DHWVWHalLpPbf#)`fxcMMZ}YNt$Zyw z!csy4hi+GQZtJ#Q4{SspK3p&J^If`RD{&|t-a!)o7C2$*?(=l`p3i{tcqiT}w?=Xq zBdfed4|c5Rrn1*0V*#Mu8Et|a7A~?wJk<@AokcLJ^i4Nkj3A-1l=2!yan1!3LNLkp zSTo&Xo6adQR?A${-lay!Sd{I(%;f`#tk)8gf>`Q!%Q5Vj=}Ta6OYwU+DJv_eP=3Ja z2}Pv#w$jrk$7;^jI-v$A167Ch_q0VEdyv3}v}+pSeTwByf*8OEaN(;rk9>Ppp8dM% zxbznx!IcHC7Qfd#Jo|4KQU)eI@(%^YrdDP8q$O74BIy9`{ei{5trjAfoo$94ADfgW3PA1G{qtABa$UJNWEoy+ z>y7UZ3k{VZI?&X+&_ z-}meD$I*?$jemnQK^zDF{IXyMJt-pp`{u>47I@zhFapC5)c*!wfH%RqkOk#U@jhr< zX}L~Ssg8V!3h+CieLg0RSCG(+(+WW_@MqHLEC?*7iymB2nHl@Iu}Xq&%6 z?j6YOK`r@=x%g@hR z$uv|Q02YxALfj45#TQdH6!o|=?HtyF4x;K}!v();7oc<*F>Grk#t(gN$so4Es?y0( zh8{LDUfMscPdlQj0(30BlahGQTWFD_X=I)@%keN(6WR-?3`yI**Qx88bS71dEUVERe9Lsm zzGTOaBi8me0!#~JjH8he!vO8NJVnizwU_%w1AckTSPWx41~x;=J_-pFId^9*ARKmC ztp<-I(!6?g=K9T`4KLR)2hGF{;{%BX08=3b;fhJkahq(_KZ4P`Bw?@oYajC`@f8(Y zgAxfoE#;Yv0?B!i>kU9UhelDd(@>}pt&!Q84?N`0_(GlWHNa6Hj_d1ma?kc(vc9-H z2Y77q#)QcQP82o-zD|GXqHM#i}rd#PX(^M1KzEx|3a>cQ^ z$B>089tb9zE)g4B+{;1w%fwU#>0eChl z`RW!8x72WoWWHX=nk}O$S`YUoRw`JVY!oXZubdRWFJC{)8%HIcp^Vj&_s0b+vatv= zlFC}Fte=hMGRAW(Tu$e$i=lV5!&rYaY7Lr+wMr@Db!de$*}o8R!nq*?aiN-IVPMoq zrAndbwn`*lQLJ8Q=UCtZQy6JI0-)fEPh-w^d-Y0+to9gx#7ujtb% z)YmE!8b8^(c(IjS5J99ke+^gZc%Uw8L!@et4A(HQo9RC9r8ZPmDr>H~C7Kv7w?9=b z2(OwJImxxPtkHlKXP*X@XG3t)g@7e3-9%!C`-M+QA7EJ_jgHa5?)gm_17`Mu^F-`L zaOv5fn{}525T@E#LX10L57Kv6(om?80c^N|xMD+G2$FYI)KXaKjwGGAZPD7uRY{(CVZZ-{~!T#B)XlsZuvnY0t*B z5U(-)-L^~_S{*}%YH}4)At#fo+H=)+7g}MV7XA=E_SXFlIp)DXz(&U$>(_5l2L-6a z9+)D|CExbtgQbv-`>*?f*xM(BbzjI_MAWpkcA)tk3y(6h7p9mzVIKP)$usz`!oPpA zf4SR$6yFy^cH@4mh=Sm1WMY^tofq_Sf~P^v*$oYtb-xfJ8sJ(qNcZ|Y+#ufk`|YbD z6pQ#zxTB1n{A%+a6~K?v9dam4<9i4KDzjwa$$UmH*5o3tZ zrQT5W=M`ZByBJ2iHp>$)9wh~=6InhJ+YqO0lWjVQOYsJs=eY5yB@tSEPwKnQsu`^D zPcP*qx`leU+7nTqi34CW8^pTae;=bDeeWDz*D58#rYH0`jw5~&5%&MP$(8&2w% z@9Jk${UC<)wy4Hmpx?k8oSQMZ#%p~NG_|uz6F?G7b)Bx`OAWiueUvWjqW0vQ9w9@) zn=_Ki!)O@Gla*gVE%T4 z)>EP5qip{trSkb$SRN2(V}fP~Vx&ZyV5UpDHlM(488Ya0rG&T%tXzm}CVMU=eqYrF6YY!nQ?sen|N~{L=A)NNvuw`~ZbCj%G;447-R{DCxcpkfCQ5f!FBv+sp zs6i-^0Jf8q80HYPHDx`{fkMQA{rhs?CXeEcW2Cke!+!Q>gpl@JiXyj0nfFVYtuaA{ zFBZ}g0^7gGw|L>7CJ^D3#z=-CeMy&ke)hU|p!m#C=u~TH!TnJekv-V`Z6Kc_NXG!vZu&y16WV zj`qG51tv}z&4aPa=2Fu%zQ(jEqL}Et2sF-d#qUtM*D2`A&m20}{xS@t;yOG7rkkw{ z0M0=c5QMXk+31`gd-KE$?g%d`NK!Q%D((pDWG=;uBKt#QTip?%*eF6)P7dN>U**w~ zMK7{+E04K4kY=$CWd8(v3@jqhj*e5i0&AcUPvebuA;Eows@jFVcc|Q7dO+?QD zpx0I7CYrLeKYW|iDUJG~WTtQW98JWrx05W(P-X%QKfqM8+}tu1V9k(KH@R@r0Bgu> zVoq8ssYJp@s8_XTPxmbmqF0pp6&3i{O`P7)K4gr)<9giZ3tMMUiO)3-b@j(9XuvK= z?}D~_8Y_mI0b`S*P~OH?(geoA`rp)r7X&&qc+3N09BFCGMhD4k=>hQ5>`PmvHf~>ipy!rX#r5JJlg;b~FZLe9Zk&pet z9tWbE8n!;69N_w2)!m%n21iQMgw7PWg_<|cF{Ug`@IN7(r0h27;F zJ*pkj#fOiCQA5W;07UCIoc)(^fUZYMW3Pr|M4>54&V5pz69%cjT<>@~COc-;h>eZ(5N?n`bnNeKj;?$J_zT+->6U ztWZirpkp3oZSCRLP`*|8+~wdroXhjt-U7S;uP!1Sq4$Az)9da)ONh^HzKgY#^G4w1 zj%iUT^OhqQT;?cHM72C~khrO0b?qk?e-hMitrhZMp%)nxehVrkz_?*vF(WKc9bUZH zy=HItS2zaU((Eh9f#h+YXpF;geNfKoQ`O#dCU=`-V*6BPxLh20_`E0>3*r5G$D@U} zm82Z2O^ZW6ni&uk0?wW2$so6z&Cma0nsf7;W2Jzn8kpK%@ zT>+%Rrj8HyLL0#lK>jPepP^~3;I8pN%}C(%7-!OnzF(d7*c~tt@JNdp9w{I6fSAer z_wEcL0F|0!?J{^MM&48nIuqXzkLdSUzV46JyM8QMVQ3tx@IrJrxDySAS_cxSbU)j+)QVTYAV1BH-e1XpLwvTLeaLu4Gmuha ze&Ty3PYM(Yh+jXm$*=&kG;RE<14p@U!;mmRu%|c^>0Tf9Dhmx3<^ScqH;%WNkP424 zps>cU)IZL}BCvFqGi1jmU4%pwWI-%%bzaXu;Yr=q1mAC6#!|dL@Sv%EIX^}HYdLy8 zOY&<$EiYR1(U1n{&$R|`GgNDF?QD1-M~8r5DMYRk;NL?>f)#Co^)_A*>7Aozv1TI6 z$AV5RUlfPqtLJ?S-gBlooGrJ7L-Tl8Vo_J@>7sL1XIm zVAXvIVBxRo#7@ol3-ZQZ)8kkXP$TF0w>!aSJvH^a9#6?{&#|r-zrZsF{55^8TWMf* z_V$K8Zr6b($8#qT2z&WA~;0^5#a4| zZ^e}xenm|b{K<2>xXA}+ot(InM0jthTJDcXO+6oM9m>^uyR5^jTtn##0vO7Nib{kS z+REh%_L8*<_*U$?tYb70Xzbj_mXP15ChfYy8AB4bu!(I`fr(btT_z)rpp1N-SH6O5 zfWbNlSJ$|RkyZ(=_Ewf>V~@KzqRWgM2d>!dVWq27N(T0)m@#*&r8HS>CI5uuwD?T! zUp2*1&g8tvN!4mfKIdy(tx2?D<%uHwH!wJ~R#W8Kd9#vB266Jr-( z97(>x4NMTOS5QFVIF#S2bo}ekkFC8^MnvSJjx0=*TXtm5qC#vJEH!8A8FvYp#Q)$l zD*&sdySS(p1gNBc0)NO@$ZgK(GZQTN?Vxhq!j>JPvlKx28ORamwE^xm9)IRy0Q&XZ z4zCDP#_^$J;$@)J9Tg?p{G<$GKLm_#+fL9^v9KGL4cM!KcJgJG-fwCJdvamFdle+k zwQ>b;C;RY~MseB%Qrw88OPd#y`dNBbtS3(ky{1-u=3x2k3;n>vLTWAMAeaxK>r`t^ z6IS`xl^h!y-zL$)5UD>%b;4h2fW}#IbG~*><@9A)PuJy5_r&S3Y}GF2!R^l z(2tz94`s4ryN5eD4xF-(BO6o5paLYMj9ymPRsvZ;7d2&+Y96ed_9sw;WN!k2Ndwij zP;9aB>xwj1{U3@4GZ)K$Q#`P=Zy=d0 z!GKLbd+h2s2mT+$!{omy9w@~sQ;(O=RUY_NBU{DGfkjdgj7!fOB^=QI7BL<%d{CI} zP7L?%AYVVA1V4cQx72d;w}1=0&mHa7uY*wWgB!xu-tL{Ak8N9Nvf8ib?|~ArIO_#q zo{_7s1;rPY3`Z6Kg1}N@(mP!-z^@nfvmBNEguCv3GNY^c-y0eoso&tsr!D{N+uOe>g9j}>pAi71SI2by@*lC3``8!7n&MMc$B8 z7i&AQhD=tHjDBAHKo8!-THrJMZ_&dZg{&WGeJ}ESkcTTu8)Dj$HzCXOI#(jL`^S&8 zUhwnjGtZBR@SiGlp_mL#+q@g+jUbh! z|Je>ZuyF`wy!0U^+P5)wRcdDJ7eSTpQ_xaO`QL^?yQ4^%*64jF>6USx>y^^HhJQebroce7XgG zx1q0MkwW{@!ce{^h~)~&E>Fyfw`E#OB$b0jvid)4ol}rzL6>&Rwr$(CZQHi}mTlYU zvTfV8x~jTtqyL_XiHZMTF=dsp4pDY7^tY#k#A z`dk?zYXhxgD7lanoKiFgxy@(30viwbMJa%DO&-MRdeUCA&PlQu>neRYHf^bUFS>jP zD#I_1Ufboj3KI7)_ltZt5XjZCtjI+3lK7b;sjo2DV>E2E;zj|^K=~6%q7oc>L6j&O zxUq&^WrUM9)RKWqrBYW_@Ian_`7}Q~#%OClPjHR8nzRYV z(G;`vt;KiQqLvVFw=tbT5#cL@y3TZRCD_Pw-fq*jCC6|zi1wD#SN>(j#SyCeJ47aq zF>X`4F)bT-gj;-ME2^ZOAWITU>Yf4;GlSy(a6P_j{rV_pFDB?_ZE;Fh;&T@mcwowr zH1;gYz+m`;&3K=Iv@={f$ur*6~TU{EMB) zKmG4RnN!`*v@U7y@b@J#aArMl&v$9q6K91GX>c>NKo2|%FPwCiQN7ziG4Ma%w&Wb+ zn^hR}mOCRkPf_o4U7RiaSsXWgZEFY5?~RtVo9`BIE!1GApDI0Z`xAmI&?^Q0&eRQp5hUHbBB0-dt$yb@pG06AJ6tr%2KbR3v~|$SkiTkMQD0%ub)CS zqa9m`H8Is!`PP+yu8-bOrs8^GplCZ97|@QzoQQn>FK3*i2-35l*y>!-LM(Xj*NNJC zBvUSXWV-2m<*P;(MFRfuDFYv)a`F6<lJNXZE$J<;qX-KqhGn*yj#va*4DR_1}g`6H;8LJOL=jvkgO{Hnuo0 zPB%*gnP^&L#-zE3q>k!^)>~(9{fs;Bgt*vA@})!`aXiM51pV}-*iqz$!ZpfUCIU!4 z9RiF8Om7(g(c1wydg)@uj#uVj=TP0-b2~24b|GCjCT+m{1I|elYjrwkbKNg|;VgIl z0}%OUS&){=ia(yR5G@F~+|DctlDKs%33hfiMY}l6>ZbYSDTK!_Z8ks{3=GvM_SskY zl4VQxL{AZcrm&cTH5B6dE}GOlqwdb< zV0#7tR9)>H6AWZ?HItRiJ>d_Uc3j-K`6i;cFy8%+;^Aev7eWqFT<$=EtsiLqLxX?L zv#flz3))A^$l+4`!KNolX*bKC)Lwd8IXa?eiy zxm25RTt~!>EU9{PjZEr;W1uYU{HkZD=#X&drlqE{Z>BoU8QM*`&tdqx$IR)`a?Kzh zYLL9o4z)9liHul<=Cpfsn17NdHp3L*wOA7OD8zhCa~sp|c z&CpA8$7*YlflzJoUMisMY0VpCk=2}qi#HCD9qx3Ig7V+CrlnO=ly{%};nav~k~g zutEKnp+Z4S=RdF3izo5k3f!Pd=k`5V8l*CWo?@g@UZZ6YWfzjdK+V1dS+Qnb3qR*# zY?>9+w;vFr=(ib6$qxf!Zj>sDa;Da)=&TBG?`}t6&`cshOj;v1=oueua$#=pa>bup zE{WA;g9U8jNQ>%rq@y^P!(7seK6dxF{#7}mz&rYO5{*E1*vQ5juT66OVr%MVd4JLN zx~UrS8m>*cQ^~4q`1EeYc5Rz!qx3!BE*zV-jo`eGe>d(^c0T7Fs$YMub~pW}ir&38 zQkI`iAvv#dK|T|2;is17Rop5H?p4#Wz~a&yY6Q@m%`qOP*79-+`5ulP)k&S#6#UVx z7OLF98QLY>86o-Y$&|(lG+&#-a0YTAsIuHVkm2O5ZAJF}^P>nEcQ|3pLneeMeA(s- zd1m{a1U>0Sv&yx!t+Oi=5NHbiHGnT=^5w;nAb8`VftsuC|pn#|}QtAdvHZ-Wf|# zo}~Pdv)aNC(<~!4j}q#$XWRB8vWNp&R|?S4j)vU4C|i0SIv?z?Za31!LAMtfIhwPg zO>IB1@BpMqmg2C#Z^6~54l%#qLTh~=c71iT+%rL6hA0W&CO4qZ80b)8N^9Yxr=_A- zs#`WwDPX&qvx!vpm1y@L-y7Ucw?|8hw^#ZOPP261gtA(6oYB7z@}_TE6Wf1)bONS< zV2H|#5NcEs*EPHD|J>eE{}I#FTyqVtYf1u@M{zhaH~t3d7wmC~oJ1g%@8&Qb$ij-o zF~IO~EN6$gy{W59U?*y(?_?ds|0bHz=7oAXbC>5moYe=u(0Vb}#bu4f=p zZQgHqJ{L0>QgX5GxPe!#94O4S)CBl>`HH!>JU%-kwd!s?u0r(Bl{mY_yk@qn zVy1C;s;ROcdu#yK3emd~ncsX_(_h`(GM^{!ClCjnhtXpGB{OOm(Wy7%PfqNoA1fe2 zq$&?3Vt1bNXc&!~ifYEj#gprK5+U&I0sKmWqn*f}?h|g2-E=XX1*|?xKp^D4W12OG z^>llv<1qW$myk{~X@JfpNbW5vCo{Q7-q8r3VFlv7)yf_?oZc7qLgaY0`CAY$BlvZ# zBN6tez-2rL@{}(769Idt?u=9$`olsgRzC{I)_!928nzRkp%#UJMe9D)B*EVLNv zchVy*_yF($#Jkr@089$o*ESF2Uyn8WzaPfHU*F3w39s9p`#JPczX7fLdw`u(C>47i zq2rETz}LBf3@XCdV1-Yr3SrsPzxTla7eYp0+N5R6ckgZ;%fthWH=ti6_tb+a9A$Cx zZ=P@O-wE4-`-UNADSSxw62-NGSWzRQ2?V}rV(LIT_zMSj8KnOEi>iXWGXBaEU%Gz3)cv?M{=UZ4b|D5iCM$A%Zc7xYq?~C&x5qboyYno zqFtAn`$sl#iu9w?W9f-r26bh}G{lWV5^#Ll7_|GT3wHvwmVBS!qD#uqWzK)$+_8cP zW1`nIf`OKyQdr&cI6xHNu<`_}7#46RLfPOG0Xp+SZJ_?F{(Hz-n!mEL6)1oxO)PRz zrZWFsjiE-cH;jyWbDx-$GI1au5l9nML`%lRL1OL5tWHQXC(wA*WD`M9Nr(4|KnZD` zj7-Cj#o_$Pvy+sGWAng7)YvF+=z0YQ6&pN{uGZT-3D#1H$m+mj-YUV#3r)p`n*!1R znP$PtQo%Zm$#OK~WFplIQFTG7<5*-SW@d;}dl_Dpp(=OZULB%X0%u%PO14mHGI*f< zVC#ho2KPwgf_QemcFdWy$eK4Nhd@D@Jn3yKKnop9wHkG7p-4AkTxA0@0#knw*NK>W+nR6Q7uOV48)S?ZR zGbp)qKk&Z|8XNj=N;>@6TiIY;$h>fLrW5|OHrrNGpwJss{W_bMdTUg61?TO6Fc#G& z@O}T=w_I(eHtGQ!wKG`kG*?oZ_P*i$7%Ok%IMN)YaZoPxd}TbzC;7{JXvr{tOIu~@ z^t4c6yB>6e4DKQN=1?_14n&6jT^{8ZP%PvWhTP*F6?3sDsOCl)-2j{NDYiLc5;3`E zK`4T_)GPvyix`V|hz#7<@X-Ol3q~s{R3I%Z1vZ`4#0vqh3$YgPbcp+apGIIr z9OA*}6L8;QVU$~af9~zXC0B*-)_lkj@ze-#HAzqc@pX#Ju~n3a(Fg><5v*O&+yn%g zuXG+ldeMv+x?5=at+_>2_&zdv$(#qZHg(a0y8Y(M2cxras3Kt-s7Irs?Fzv^-KoKh*3Eg;E*v9%3AhAoY(Z z(Nf1?E5FWHUS$S@l{h$<$=87lSsHzD>}zp9*cAp`*3wewQoXanOdJuqR0_p4rIO6q zE3jp4MXNE7@IL&jnq=E15@+1~j~7U+&8eAZI;CM1t5ny=_xPXy(wE8^JCtn*v2g{i zoK@hA-=hp81|a}0-HJMFKMKVtAXlyO(R!q8(Kr+?w)D*?|IsqLxCsA3E?)w5gd>@n z)LLKumU4lc_NbP@UxF1c$@Sf0s8_G9RVXNLMt>k%cNcz*sfsX}a#m>hN?QdbtCaRX>mr#9$5Z0Hs>4q${#_s68|EFg z>!8ZQumhu(btvLGnTGxsRHDTge)g9R4lmBuMlCw06;&k{qNZ!n5>~nHQ?mA5SkP&Y z!#vY47Jr()W(LZDgwIGS^Pr4d#FA3Jxnh<<y+7L9L%4S9jpXy zo1eYZh4@=HYnDh4T=gzZUGm(R3%QQz9#jEOHp!2P;gNk>4$(D?N1TGx|JY4bImklwFKB0osqM;SI7M#8oI)QRE&faU zbWv-bQ`}(z%nP8(>&9t&n##ZVD?M=7tB2tVoJjbkk+cnHP%QFnV$>QJ(YZ+5+I38@ z*UI`II(kOYGAzdv$^rL)fLji_-gB)W6~|sdkz^0x`wOJVE?JMhme@AH#HB@XCcB%4 zKEJRx2^e+At=T8Fp>Tz#F*m-7J~%H0N9GGBRMnUPjAum8Be85}Gv(;Pxbjsd716Go zAEjUm4C{J2V(R=e(FPEfuNdwEyUANrWn^FNV)9aeW8%A}w@^pa(|EUaaUvyVN8`Ub zm}n1MviB75@EwSMtw$aQ9)=-g4bvA3zY8N!ZfgmM9&k&`Qx>|kEvn%=rueT}%E(xq zf+LIpcuIn$6e`}4RBu>h4I1VB1?GJ}2V;=BuT;EBGNj?a6=WVl611hZPU=`vSK^a_ z!`OKLX__(oIS3?98p@5l_+sn-$hwYKRNBmmm7k9EthCMgtlQPD$#`K}L|ho(_rbpM zXB6-5&8>UJ_lz=aGUT`Vzc0j}&pl_o7?H z@UQzFL~9(4kw&Rw$Xn&CUiD7wn~Q6)KW^QpVcx<2@=UiKxm4b$zyF#lIX8YoAu)S6 zwh|K{@3V6V=RNC!iM`)4wB)o66F{9qk5Gn#$!(9XgcE={c;R1#7np=K3RgF|wLrqa z>X?JXqElt_>FRBnH+Px`4Y`pb0Q6%3IIrMW9?2rN#j=X?gExP1!gdWk*BR`78485% zWmSPj9Uju~23*kH@n9Yd8fGpkqt&0;^$;X)jk@`JXS+3W>|kFSZ8&62I&>?g*A{E! z%g|{v4GkhDS!}|v%+C?GPMuC=f8I=buuAq{wfD=hrw=DZe#IEjd8YEy^c$D}GGs(g zDcirf+xE*aeHUDD+UzXPC$rxI_z+Dj$FQN{+tv^6MyF+ zEch#>z4ov7=`(Z86bAZ(JMo4ym|wH_jz@L|?fC9$?~m#{Bx5=D70+dyhOj+l+jOwS z7wVarK*OguY-acke|1A@1+3aOHyHWBFh!3n0}U%<{ZfG4EmQEqgCm?-+9g~Wy35b+k+dBYV0XA zXia+1E7-6X`0UjR!)Y1@i2E6}yv3GoujK4_$dnD+u7&j9g=w#D0tzQ+86Ty1bF)Wa zqu`n;g?0qZ4;Tyv)8b>Oc6{{WVe672#}t5xG%>4pd~8hjT8x2bQZr)zqZG7S*c)Eu zJ|HAEj9cy4+_usVZkL5Z_hqQEH^wbn$nm0svi2rQZn_WR+W&89ZL2>wrM8=&mYRES zxA*M??%sREkd*dMnYeZ%_m9lYmDy`x!RhK)<8o=}=xA(Vgu+rklT>`f+FUyOYKB3G z)`H5v4t7E`AMjohxsUG^k_5)L&@x7v4u+S6QQcT4aruAr^IC~z#+iA6Q7%f32;b$;g zO%3loP~_&e;@e<)=`HeR>n)~&C68Lui#~DCywZSOm8RvjWArjJ+EPcyVp-}ekaM|< zB^{QU@!sZ4E*APPg$Oe5w!E^TJis}%!$OYpeo0|=JYoXCf7CoT;9#8K)TK3g^$u+H z!H;zWOyW7E$+B%6k2aU?k99>2U*0ygt8o54wQgR$6GnRQVJYi#i{PWOg4Y<;seZm{ z<@b4q&o-}l4s@IF=H7WR2(@F(1E#LEO+Xf5{r2->GIi5U?#j>DzvN*`&&Jukc-k&! zfGZc3BJ~vDqu0S)HsGo9iUE;&HeZxhGUXh9uZnQ|DAS$Zvhs6lnBg_`cKw^GEq6Xy zt!Ug#Q2Ncrv`R~p?xnW?0cwRbSM&rVzu~?DL7_?1_4`X&(Rpik@;l!ynvpx)JIyC+ zI_|sKlhgZ-AXgP&^lQ+kN5^712-}&_v-jVkgXRF#@tr1bc0Cu?=WKS-z}5^65Nae) z^~A4R46@fb1Y;TU?u#-&$F*wm3kD^YK75m$%tBx-F~akd8semytLq?M`4AT$DSCLL znli9ol+;rfjgiP;`=fq+>%7-i+gSGYL1!Ijyw^JVEe>Auo-l2b&KjD{cAg~GJA0>~ zPhkMkrV|ieukSeu^ADO(Ja@FitAT_Zd(e<9Q_(bmL2&AcCFID*)dTk;p~$lStr45o z$XFvf;pJ_07o3NlOW|F>C>|zrLRm+wjKCdQ27(setco5u6H{U!msJ?2Z2G^C4E-`* zu*!z^jbMU6iDgYR5>t>gfW9H?xrSR$y9Qj9Vb`teZ;FY{YjRYQs^dOi$~h$O@Yr)1 zB<<)B`}?~3DZALp$lyd_l8e(97fY*-hcsS(MDmi#%ZeW^>(!Yv%DJaKdfqr^?JVk8 zX^h?2PK`(z461+j9puN!x%h&oAXQRA-#IJKV3EG*I51c()isr+lmGV3^A=VmZUp3^ z%##@yETG++Vn$~i+9*(nADvV8_l5>G0=L0J78&sh4^aNhH4ZGF6 z4>uY&T?~8#_^o<$@LHG+1OW>atOL`&Q@%~d+U1s4BBGwhG;NSYU`pQA1aOs2Zc>!uc6h!+}2kf*QH|&MiFU4n5LaT zp-bY>{vPkr0H2%!V8>S6PIq>o=N-*fqY$@;=DT!;^CTZ!FHp5CCgcVo*2*L8g$YClKp6{J;@dOe{9xdx%ZsO z8TEZt=~bHTslLcgoU~;*paIotg1!5Q#jbcR9UO7TChIP#>&xGp=t+jGM&#A0+B>z4 z2CMa{&HXzOyPvli43dmLjFMtWe0v59Z7m^f@1Z_fHI!Y|BF2N~zq;L|g-p+Vns0di z>ciF&I{~|LE?O$d?N)!D>oqJz7JB*(&@!`DS-Z(t=;RsjCni%tk zXGZ%SSuj{8%w*Je;o)#>)XiO>$394cyS_Y_G4=mu$u?!6#Y~;Jd_EVaF?aqriu%`@ z#Ao#Xb$`5j{ku~6JX-dnQMd8Yk683W=r@23xE$vBmrlbV_#4)4a?;Y9FlYDKt`~69vugm%0 zHmT2QUwK0)^KI?u@bxY=KfL7YMW$p>_FH{w60Wg#g>ucexMz|4&wKo8h$c_Xv}P+) zKs^Sd!LP8%L+{qRb?YYI=TeNf1L#(z&U~5bB?PUWK~={2iy}Zbm0?roSAsTnv4;d$ake<5A6JwZ@%w!RT~CE!^Uto z?gXxSpm@n0%b;v=NJq3fZ;92L0P;uL?p>jZ4Psa-YoKNFf*>`;K2Ny zC(L`f1LM}8+=Nd#%<+bgeK!K3$(|%k|&X~x_d*XxJ9kaAHYR_FV z1%&w%-ZldBD{tGqcG~I5nSU|949sFdOWbcyN^>kAid#?;@kW6QY|3UJ01Oeo-BIME za@tX!;hwlRYIjfnp2vlGt_(WqIWWW*G!~OEj)a?604pny{XHlJlEO}+t=sB+bZSBI zdcPvdFNETd9t*484GNce#P$+e^0Z+y4tMTIOrMV9Pp)5QO5TJD@RV>ieZbO(D8vYw zanmg!aQmc)kP($7a$(cn0Oy$5+eAY14=z2}fM-wb8{LseUbs0i*o9>cEH6rIJSaa#rKfp&2NZ1X z&LK>NA6j*XuumGrM}9y@q{MK3#V1|*t&@(f+>W2(F`AKQ^v7MV zhkFxYk|3Of^?g;m9*}HD4rV{TgAp@rMjy1gedz=ma$WzdfHLBclb()SHbpqo)s`q(7-7DwrPz!KMJgKVii8?I86S#nNkPw+AB>55^F{*5 z+_a=noyc=IfNSKY2E`dd%Iq%3&KmUFr0$Jrnic%-#dE7|m+`nVe-N`FYPpCGFW5fK*@xml`NE9#O37!@=WzTe=)_ zs1I?J=Ut}MYV;dyHg3y5F})nDxa-&%D!$ik7F*3xD>H`1;);_pIp|Wfi!h0zonQ4^A?W#S*pHMfB|%|QKG?hgV|}G5MZ@YQ{Bm;@oe74Kh>v? z&=F-L`~-bNKFY1p*!$&E5+9W#gLi2kH86k8I``+p6e&st@Y;_AOVr5 zfk-DmN6GrU(LD!86Fis~wm2(r+U`#!opRSc`sEOqLE}ayv zG}$l{AU^&y(IiV$`>>|~9vUSJ5b1hhVE#9J=&5`veN{!Srh8eeN|WuS=8DlVJEsi? zLg0)6hJ0%&j*$CQI+21%v$&g z^rdi(`R!|6ST504s@D%~;n2GnriU^xlf`6Sr@X=;AORm7Jc`P7S1}ehpL(=UTD5m9WGf{)x29|CeGzz&hn+ z-L8_JnL>#q=1}bpPLR8_ zc2(N1CYI3!P37cqz`46x%CaT=@!`O|5fZ4<HewPseNY=L`vu}3z#2}I{Xk9QnA+m{kd%zsgqQag;e6jR>sy9=X5sz$3=#X`M+chut~dbD#Fz zs?NY(iJgHqX#JP1q0{xSn+UW2%&;3%VX+j`|68S)N*QOCAXm_73FF1bla2JBt>J-m z^#0+I1QQtW`)Kt@Ur$u8t)TFb_s!2{zd9b~hkb3Re{JK$abF$>@ZR!u^8GN|?h^&D zb(^BU6=ZOE7Gang9ya#=`!TEybo9LGjU_E5eRHbgtIxQU+i0mxq5Io5)Z{^4Hc($h)_^aY(Jyr{2n9MV zDu=aIssBk%|0$yibRx#a#-!_B82l(J9Rmjzl#N!2lbQ@(D=SToW2>T&)8pr(s#4g-ucR%d;@rI+AZsEcAzL`lK&}RnrvzC;kLyH%l~%GAD4%J$ULMj& z1Gm`_-*WBuZH*GA&|c`uDYOFM=%$GlD;%Si3VEq(B)CmXoA9v(=q7YUh_*#dkmSq^ zghPz#B-9@xQi=gOfSLY zR%L(&1xTOJ3_N(sMyCV{<^?gdpjBo^Ok!_RNFUKzXcXx!z*zRxg=qkg-CE}p955BI zD$sQ!Qfw>)a${<%4Fc1fD}|X+#M0XQjb+%t*>_!AcFd8ccTrj?uur2nxCthw$82^q zsX@Q0>FgF(!6Otb?7mKxQ57v9iBsYS+L&jA!R}f@oj2VwO+o=F{I81qA&3x`K7Yi@pln}`tfCB?Wl(&1F72@2utrNEpC6$h zDl0T@ypS3e^lv&_sx;I*5E_ud7)q;3CwormwfUGoT1}8y2hCcoj+Y#4;rusu{7~Q! z?o+|+8;7+e-A)>UkS!RoGZ?d}zJ){7=hOx{3!c|(t3?d}K?VES3 zy#p1Zn-X!z+VwolBv5O*oN_VINhtKei+GPJc0#*N#+Qr6an(HFh}lPvbRVCyrWce$TY!;B1rEBDRvuTcxr^3 z0%=vC3n~+wK-J0EQ2!u@DZy*DuPN+lZf&*=H#04v9V1DKJZ2upis)P zH7qhzuzp(V5O z!mx#m#ZlA~$ygEoyv2O$6o{ScUV?8o2qOAbx0|m~WxHxkLi00s4u%izj+z7?Zbp6Z?#LBBGFR%bsLGAPNateiJ?TI(GZ0V%Ai6vPdoJ#>0I@%e-sKf~N#K!kqEOt0 z#~iBXUOulwQX43UJ8j5V27VuF^azuV9kC@0mjoS`Joe> zkrTs)lw&}XhcdpI?FdrcWh+QpoiR1h@kIe`cy40Dul6l;eoT4~_eJ>nJM<`d11ByK zC*96k{1Sr0qYv8FITTV{>xF-dCw1WLoCdGEAE{ ztNtOH@C#7_6vjp7VLz(|;-^<{ODZ;HUVV%A)Mqd8_0cQZ`LV7)!jGxl&ud{_uEPL> zN>7S4?>^zAUfqG2_nd(mtav%g_L~h)+ih`{%inh6k(Mqbud!z1c-E~gb<^HFuZ55^ zhyp%=(*mvC{3f3-Kh44@6OpNmZPABzJ3pz`N%7 zOvH^Cj7dZ7Y%xl$%v4x!8#X)1VRV4|M1?_{xYNjEtOkTNRTyzm$X9vir#(gMOD-)o zZ*>*$+z|};jSugL{N4+6vh(6aKv(=J-s6U8Asa8Nn!&52%XLSCy^Lao(R1W1ONjs; zhDoHER%nnWeI#>2;IFNS2RClkf;k9&#Dpnjt(`M;Ya9K!tsS{xj)sHhR8zpw_^QU# zXH%lzpLWO<6U4mb^h~4chU^1J@jAMkwaq?1^`pe@2>rLGxaMwbvQ{M3u?28DDa!)g zYdP&Z#O$W8LZ3WEdrNsx0+S;469WbN^SVzLjGL>t1D?NvH;~0DK|WBkCE0r=N7^Oe zHH24SjI4~)z$%Ns7~1{<u@$ln+wW!Y3_pUbz^KC^Y@qFkWn6zoV~0B)F5Yc zy`e!*-83p#AUx|am(>9^d^qvmzMlg@5VuennE}aBeAGpc){k`0TNQW_kGD~JwFa$^ z-8B}pGit3{5Z|1zzqN*llUjfUqdk zaVSIwK27Ul06!0Brk{1DWnIv?>zbp_<0%0&JV)Wg)4k2G&AtInhY8RPup*{fn~z4N z@}M^zt8D^0OJmMxMjjY}IMw5vKaH{G4BgBQHGd~}{YOdRYF?VurC&JOXP1m&E)U`O@xOVwmFuYD zOI@H`V8&aq%}G}u8s;i#m%&M`9+LN~CAOL$LTkA=`P@rG?7*xEW3XG?YxIAP)WrC` zi2cE+OZCfGhp7p1A+EXd`d*NC@KCvWd2HX1FK*%jEw2Gbb5mNNA!5U`jy>9c_6c!# zu*R-_rFQTU(@eVt7Ry@B^t5W#+7SI2=HRbnyGuc*&5Vgh#B#lUTO?yG-K^T>n(qSM zG7DrX6Fl|^{e)P_jZ13&Cvi&wcSy`Lr9!FCt?RBJ^OblTWckrlsPGBl`)e8Nd&;Ic z$fa={m@^JA)GD$Rr^#Fgt}q|8y_Pr1peyNZV1*MMO*{!BN-o)eutlKzJHjGst}$qX zLg2Jg5-_FS2ts#L68f9j0v0lSJs_MUBiPy-rxFtNbD&&M_j+ysi|q9*g8sLok(!@Y zZzcQmwth)*g+y5ei<*FGdJVqZx{VRB_gd-nHa-u)Pfp_**^igF*MmBaMFi3>^uk1+ zhKjuC)$&U6Bos!e+LBTEE(B2#(s#S?z_ej_O4t`W)?MXC`TYSpkO_BOj^ z#R(pOQqc;{8W#wD0%1#FN{IF}xxt4z?jAQ>o{mXF$H-34ZXzfbiCDCF1D_#my*TCW znBBrD5VPte5M0@I-a229VMo{$G*Qn5(f#EsJ^O}f=hMm1zn8{KS*qQ~Qs%k3vV(JP zA=&8H|8+;|*DJDrd+=y2f*SY)gCy0d4l4<`tviClDLcOLP2WosGXP_gBO#&&%Sj;CJl31Cs1>oUPC+#2dq4X(!MbNa2K9TnUy14b z5%mX5=-SL}`pgBoWbjljRYXo|jHUze6L%w0S$i9&&766xj+m=~wDsB5#@!aqAYu(* z6KfcfFxC#NrD{x!S~@lYxA5`VOKi4Q{Ct3LpgW0VnGza@!GbjTNT+F6wW5+0 zq}*!;7qaGayQ7(Vt+~4SWYRHLLv!_a-#p5k#&VrO9`)$;FTCtm&fI%UO(FyuVFiZ9lNAiOOJ5u1=)*CmwNG{PJ=<1kW=w+E> zi!N+jer1{j&N-_ssRPdQh@B~szP;?Vjt9px$>ntY`C=_6kTt$UMyAEefX(o88{BNstT>oNwR?{fl*uLp8+Yf|r zsVl2Vw;8n_ytG5#XAc1K>~N{-X19RRAMkO&pFOOaDS*K%#TdKqXae_FIiLr5alfES z1~k(nsi9w}o}$ag%jzT-1Pc$K-D__kY)bG~kverza>|UrUwrJ3{#^2T%3_HCc7BGq z!|PV7?D~L5>Wx9y>7P8JNSu~yl_Y(b!OX{dSvYSH$Vs4FxIRA9u4pyURw{NAPK!h~ zeAbM1HfDa)W13pL>XQf*+R2)6Ijw`@=chcj5O?$49IbEJ`U6ERV=;Pw9yMdu4!4~g zV*F_dbI!8+JKupu7lNTBf?N?UxQ2XD-5113xAvtohxtKc?|jcS%pfy3^`&jQR9C4z zTkoDrNjmDdO*5kW@Y|)GeKwuuINVito-&c^19-SwiNI+?21(Z0(~>=PIt@!Js<1&S&<@CA?G zd13o=gOK^XUHz;mIA6oxc?i$}*0g0l!F8B#{to@}=h=q1u79T^3jW3Udcji3KLAx^ z9X!2)lcivWC8S}u0MQ=;7b@IMI?lcMhC^0vMCnC2SC9!E>6bq_>Yf{XU}}|Y1AdxM zPCxq5n)urdZ?|*0c`QF^SbO`eM=c?zt}Cw33=b}?DM9@eYU89lTvbL`OeGJ#5ngqP zeD;5AJNyf%c#~p(IJii+7NryUk<@SfCnju<aUfDaJJ9vn>iw}lHsSzIjpQg0bwcWz&brr%$ZsgEo)I&{)_m9n-Zpv$7gX8C*vRI z^Be#8ebavf?{NRi@7sbySOyV8Y5n8(qv!)u6`}vM47;y>p}&C)i{263w=(hk+G-yR z{$Gecx#oW$ex*d$1Pc^&Ch;NMrX5cRrgaQ?6F{;~r|-3bRofunlu3#@dzux=x2W|j z2SJ=yW6_z#h7;2l>u_7$YWg`B&?kC$a=;u*xIEf zFIDp1MH3@z*P|_B__iE2&?v_I_sWKAk$^pt0`OQIE;M|q$ka|2L6$oVub)JxIU3+s zo>zEa=n&L%1uiqMuuGg_XdjceP!)<{d=dliYh_}+gwAZ14pi7-0u2|_BcY+Hba)|1 zTiadeKWtmU=$r!vA<-Vsa$Gn=O?8D>>1mu@Mk)78x>M=N7~x@c=PN zdj)kS*qP=)jZ(Ap&N`5j>#`Vjx(iepb$nwS#>=Y=fo^_Qb3h>hsGXS3>e{89$v5vl zY()vGMa2rv{gmAQW9uB7GYz9`9ox2T+crD4ZS#$7+qOEkZQDu5PCA^PGv~w1)cl2~ z>Z!f=y4Si^XAAb6T7Jy*!`KRUHNZ~1wnEVtgUujUCN)q31I8CmUW?EntX#xTm3mbo zuS>wE7 zT4v9_c{A1u?b`OTV!B<4ii$PVGqkV1yKX@D!hP$3d`I%xjsgAC{8b>o^GW|Ja?lmJ z-}lKanUmAsOL`9F1ISi5073`jgX-=|fXL**zTUTq%9X}%_1TS7kR%j^M6w_Av%C>eYPaqnPM? zEYV9vlg?;Dys*w6Dmh8$7-L%&mz9?>iR&yOvEQe%?k|+F$~A7Irn; zyXJu+3haml(P>(kJq^H+)3zeb<%oFfGVtQHFO|8jPOxWGzF>(zk?qG}sm`zB*ej7f z;MgL3Cuf+jO!>v-ychaO!Sa{rA%no=Wy8I(1zRll9WR3C#o~&VZl7ZL#j|L%6r#QC zA6lvq2K;^0h0)+0J`rfrMug}w`_Ox{u1Z!DmM6S|K01OI-Wj05waLZIl0xNq5LWh4 zc2QFOdN2^oy@chH^`uZtFAVabLnj-VWtq-$b0iJ2ra;vPoDN*vtN4iHawtYSj@x;kAH@{9zjEd}z)~oyCw&&4rLES+@_(W4=rP`eBue=$>4=O`d&peGj1R%SFdtG$uoePk!0A^1UT zE-hzpG-&=Yk>&tue9!L>v7Fn@XqmfK7@>@ZgHEct8>0o!TXT0cakNIBWwv%Hzbv58^@0h5%TD;hDbsm{ zC`a960z1jncWUpsEQ=c~=xp_*AeyWvnNxH87Jrag2KXeY=!kk5b_hK=XW`LK_>ba8 z?>Q!gE#7rIQcAfV*g^>(Pj71Ew*)PkK`}rXNa$0W}?c;=B-@YcdnB^ zo*+c?n*We!f3?Jssi7~eB&EGYUYzrRw9j1WWu@=wK$T=OkbhUE5LgBiW;X4+ki4ZL)F`NQMF z#LG9D2?K*p*b>_%A~U`XSK#U0`iUe4?0yL``Y&GI3;L19vE~-;7Lluz2Pd2rWqf=+ zrvK1LcjPN6o3QwczZVB}gx&#F&XaqvXW9yXYZG@5zP}RsHIZB9dKb7}SJ8+_xU!o7 zZNjG_qa|_;oERyc&O(fOwa+0I+7;CR?e#- zm-&6wAnJCqJJw3XuW}O=n5p&7RmP|XyEJl%e3}98JE$+UG-f|ae$xOCU^!BS+tT9~NPM(~H4&*iE3BBP5i9@jbqp^LZ>#{>uq&Q|&uO zk8)&=E3`ZLmA4nzkSLyR#J zUPXVJB6AqzD5_t^EqzBr5vraIsidl(YZAv~~2A3N^wJM}&%@qzgifOWEVE(Ms1sr!^pRms1KQy$61 z6I~UD6AyywYEb2^jG6pMzGC3Y?1$F_=puZtw6-He0F^25^sLea<~^-gc~w^o1y5<75cRW+e)|s7%Q2D zDs6-dk2U6@k9X728%V6nbV)Pysq&`<>jet&?$GtL_$mIV)a5oB=zjjLwKuiA_<8hB zRrB&Q02afi@ea2NEe6GX26lDT^)Y9fa%+B#DvdMw*1GtPE`Gg%^197agAQxVkd2R> zVYo_s7|Eo53q!dxnB#I&k<%&YBlGn|rZ0&DKuN(<^`F95r9Nr6t@^*yhj ze|-Fj8-NSe!}cVleRZIrKP2CIfGGEoMe-RJu##T~g?AVWlb~sC;2jM(H|qS?EjD!I zt<~_vpQmi-$%NnG-nZFv?s$Q-`8H5J{Ev`kAB*#Ca64iy5OAkoq{%W&es( z`SNmj{Y@6Oy04;=b-*ef-shdG08SeV_T7`&{A9d0Q(i#% z!Jp^bBd(&tOE$uUo7&RoyxcrQrbXCSKEfD8R|`kHXWGELKi?_0#6b@h0e|z$1;Y*U zcY|f2;@(2Q^X=p4fKf|fCCz7+aDU`tU& zltG6D0}v`GYPDkXzu$Ne`aU^*8KxdvpDG5i*lwqgTAAS>iz&e&9!3LN-=j!#NG_YG zcj1Q1BTIj1wzN##6u?m~K&r(TV$1RuE^8QiXSMtadygz$bMiJo?&xCXVTP$IIut0rs{aZ!Ea}#n4a&P(;%$3nop#t*`}s zH{$)zmo=iLnwDf?e(xy6+JEmTRv#iKy|+j0!(w38f?uMR16CptM z0L0{{UZO=t5CJ}Lw1MRXY)6eFiFSO+botdA=tGOXj8ntn(Op3WjhSWu8GGbdR-R-e zY8-A0j~EA3(nRnL!RfFc?`$p-O&{COE3JS+r^A*MXq_eRK3iTM))X_1T&~cr>7vLj z!<{%Zja-oi6hhQ9b<@jfk-)Q>VJcQ&fT1+m#w4!xHa#`mS&u}4!Y}&{`0Ur|%MZ6L z8Jl}C@mz3!`VlGKQR~)GFnZpw!8X3%g)R3I=J}cILB^qzrlIP;(tU~VCtn=QBNAP< zmywrdIfgwKO;R8v-S}I>^5ul$S0FG3WO(CbAzX25E~Y5F^}|yd!eHhzDR_WF0d3Hn z!DsqNej!=p5e;OL3k)>>FwzpDhf#C3)rVo1rhM#KvWfye7+RK*;0k-#LFv>rB`mwB zE0&+M?YcemyMBF0{((_s(JDQ;?$D9bIAE@qPLae}jW=J&esyB&go=rSLD)8x)29#g z#DzQ;0mTC9kg+_P<2)jkCFga;fQ9Dck^~e=^o}zuRMLsKC94dq9cig(O2&zRKOijj zV8`TD>CPSl(aaQe*{s?i9{Nmek=#@EsTsxRh^xx-%=99`V`A~Bcj_Zg60F68S{%)Ghr;^riwifmYGRET*(5`hXjWgm(`?IO8I{D4~e~B(j0T_yk(*#@k zsyC<}>w={3d^}cVBna^3_UysKv}p zb+#S~`%OG>Az)6iOgE3R4v@iMF+hn$}DJYend> zgnxV1U48MBs0Qh+gmo3#L^`(ULBs3qZP}Xb-fX`qYoYrqkipA_6|g~%e`Ck9R);2( ziHyT-_hxgc&?-9U1*M_stYl)*Fov^UP?_g!tK|s>VWxKl60zvT8r!v?Xgu|;!?ow zjKp(t=9InCfh_8afnQ?{=jiFYm<}s9%eS#N9a6r12!zoO`rJA^yqiBpwb+zyc2YJnyd? zT2U7dZ$3+t6GV0YS6KDDAtPNs=`Db)1C`@8tzeXc^c0?eO%%v!4w5qh6T$xc(pbVG zex?Mq5kFtTX0K5fGnlXq4XSOL-RHRDQ6$pFZq2U_l2m77C~$tH`0W5M&aW>OgCkfu zM1E7{wCRPc4xlbV0-}IY8sDuahTR(SST=Me5sWMuRh4+Ln&UvdR4r1qkYy_?K~<7- z=6ETM;t^O`KJ#MYCk#V!@7%itH}+9ne)Ckd(7a^15p3y_adK|4Ss?^DS89mF;q2_Ex@ zX2A4`kW}FUk@5EqGQ_+;_5YaX?9Bh>IT$k+D{H#W6EF^-7bpxtnC1VE?rQQmE2QbqA7V9ejP#o{ zTj|yQgq;hD=n?PV-Nrue0+rH(@mWE6L5d`0R^J@bg}*}fHU~@&iu1p}O^#0E>zU9A z#YYy zU2ZU<@2mAch1vcs{YxPnOk-1~GLGp9Y|W*EgAI)kxB2JwZ~dRwv-tT%GDPpJ&^&)d z8f;xO0retl;F}Q4d7c5t{Au5w)w3OU|5vF+U3nU)+8jHG-2r@D--YhAsX4WJUiC)h zl6{P+98eWbB2o~q00-}SV)3FWNnRy6dHjvsSllL2IoFKoYn>uaq9B=jXw7Z~lZJD9-Zxg2Wn`LxHZVnv1k#>Vcq7*AxLCRvPI zy@P4oTw19(8QjKX!%>*4T%&(Px=+R8C_<+l8Bo>+(qJVX>F3E;f{=F;ftkKeE0v1e zHt7~Bpm*pgG#OQDlsb;M%Oz5BIQ(CDb*HL46=t#*xh51?R5U#x#f-wT4Sw{`i8duz)sFq`9u(~ zG?3HF45bwvTyfJ-&Z!eR({qlOwwJNm9dV+GJZ0C(BZmr}APNx~b;y zyR7G^@Isfn#qo8)SIlF4p-? zCfEZi_8Ka#7Ivz&vt+E#!U})e9aodQ$#iJ*EEGpl^2DrsaquXKJ&Ue@2Qz4r$0}HV zRnol9%uLdvj9<`)mq}6>m7+P7)qtKsI4j!ASgBGRK0NANogj?UIfC7GkUoTO5$Q;A zx26S7!IpXeHYj)BmdEYBF%)Xww43p|OrdmTuo^K){H6x~-ZPIbNt}=Wo$lVY>=usV zkJ;X`eftpFW_Y;axmcG3OShZVtzC7A9PqVhKQ0+fkja^`tNrJSDt>pyr7k>)Qiqn8 zEiXnXbx!sc{ylFHa~Nb`dxsQI8>EvHCD)0(FFY9vh-=t11IqF4oJz5RXEmqEr=m{8 zuY7C;UFM>y+Cn2h|W!llNV*!M5$ zFu4-p7NKit)E41V>S*h%MoR#0<59*U0qTJjgNRJ-777$+65~tIXs#PCLQhs`Eqk~V zTM?@QU`r+^QOZhWapCn+7FC9ECX*0+wCyJbhr1u#=zq_-&1(*H;;(K~jKP5PgYl#m z@3zjXKD!VWWuDWuk#3RU%_wb8+jNQr|KJAwf%ME~4IABFpGI@|_k~SCg%oOHVVyGK zrffOsIH1q!FEwCvr%bfea12wmRW`NfVbGTWEBMkpO9^Zcdb9X*mFy2(I-c8MLC}*Y z#X9I`Eli8s8&z~A=Q)=)&be!UnpK)vpAU_ro&Rh{lBVb~r03X!Sv-ubv-H#ZS->*G z`~{L#H~ut7eL3~1sM9fV{+#a}6d#AC*xt~HQQ6%~i8b%8qLzJ7zP~U-d#bu%pb(G& z2%mHuIF(vFm8->Xo^MFxxhjZ#8D(m{lp4;6`AE^){m)6w(6NoJx7wZa8%v`q7Q=6o80~-9wTZQrV0nG?mMY%wpHW^ruoqF9I@@j zUbYY{&sP8adW%jAKRUUa(0_#T-h4d)q&BCK2bHPNu>S8!n%g?J2#uMxl;`rAWt=rj zp`w>gg1r>Zc{Ug@%XHhX94ozFxbS7hbS`!)y=(~#)Q=j^zkKqVI0^nHy$|Z*aB)Zg zu&~!0@Zvmven7;cUj1w#d`HxIb5Jz|l)UI(TiiJDV(Sb_e76A_mh_==R+agD*j;HK zwT7k0!u3}Hf1!U&K!1$K=E{)*eGGk1XVsz^`#uquNBaJ|SPT*kHq5U$bRFimSMwuUt?c{RsXMoFs}Y4$XsxHnJBmpRLd!nd*Mev8sb;|0l>^ zLh;HLCJ-r?BX~JgN`%0EPwB0oq?jQ>@jJ?;?0dZI-)|&Gk#N}LN=0ev5s$vGg)RB{zVP^T48C9>!sI>UyG*W3c5&ZC{V%gJ{b{kmgVElQNN`Z*g=HzZ1bC0&PF$I2 z4~HSSQ7)hp@@YCO^;kLg>jwfjK~k*Y!hhmbI9MH|5djtnNx$QDmhr!@xya;TDE1lrh)=l~yv}f0oLQG3vJy^sfP9sz@kx}( zq>%JtQuxr3*Lrg=p2cpzsuZ1lC3G2}t(Z#$bth5}ptvFIY{X#3w5DL2C_6k4lxZvq z4~y(#4Uev~-xh8WQzHzFMi%?4_W3a z@=lDeCVa^ndL4hhH@8ljM=e-5Ni!EQH>>y}4-Do-+%Mo6R&90)wNEcJss6^fsM;F? z4q?zeQiKUJ144asm?1ZTQ$ti!2Uf{SAR8=ROfd-L0o%xm$9x9#0pe6U4Vhw5Tj7dM zlwB^i)rEsl-0YROHINbF6Hnhh2|sE1{uGU9II-G%p#sWVjskxC6s-WyLPh=xW8dL5!CCLv->85{#gsN{R-&Mri*x`FvGv%jm{m*A*T=zHd)U z2M9`dh(FfGze!hTkO`iS-tyr%c*TP>4r4gv$`MhfHtGN3g|_kLmO+u&Jl4Bmo# z=qRbiv`NqlKM`)U{8128Da#5haGFcMyg-o^&7cSZ(|KIu#EvIzY@6ou-+D)SVTUz) zDer6{+O~AA)lWw|ygup`=d0ZPbQ)pPMd(Tvc{Lyuz=g#nXh#4*sB5TtTHT#U{d76e z=D0cO(cTJkW$sOuqm1ZXU#bHh41Un$bIJY>=}tHK)E=re{tdtE3ySrC&E3q&1#ND5 zNRE>cVN`qz^>h0Gyq`W~!Jb&LC|v>s(t=ntxutLlK9&<|G+9TWpOB}Q2nCbYpGPoM zXP$>F3{x)zV3tIHXuxzL5O3eqeC%T~N~;Jc{Ok!6tb$PT9S@ZyBsqndGA(ftQE@A@ zF98TzKZy`9%2I{vt&%-{gf8h4)(p=Ja3&a4gH`|tsxyQ$g=a#4fOGHEjnMugY!Wy;{j(B`h6#GJLiT2GamBS%AJAZr$YnhFBViZZKhSJ@rkaX-12 zch?JVHg8nFy-#NUjaRT6Wsy*E>>N8o3>dz-pPSs^DgZoullD7O^4bqn0Uq3<9U7zu z`v8o`BYMl8aB!xzSUPxTX_m92ubG(-N?%g6{K)rQj((tceqQlt+p?Dn)%&sZI%aGA z&)q~~RIilB zgQNqr&m{nQW3WuMNv|Tew@&_FV{?ZH56m`0g1>683}t!G@0;P}1-!oY1>D@ule*j? zC`PyQ=FOZSV{2x&97SGmtS4d(|9|c9W@Bawm+nn~UxQkecMGGW?Tl2uP~te8bZBeW zHd}`|TMmz77|!mS0;CE0N>};x^G+z+bYIj~{+kz72ips~+f$cP;Ntv+JdbWenKu+c zAdC#T;%!J>nXH^wt0t{uy?&m_+fG@WdslR$#=qvQ5Y`rqjnW@|F&^)`<_syGu%zU< z1_);WR!SWN9C>#elMFQmrVBS|?r+ZtTcV`CpdT+mw^NSU1V_GQsR^(1e&SpmA#-b<^54?J^sSXOl%|>9Pa3Yb=AE2)UOFZPu>wjec`t91=|4b$y|# zJb7h3TY{gEw8{qS|7#UV&sGMd1#qzb4}HE?TQ(lA1HSi6W0}xWi&2KWU?H5SOW1j9 zxQ!^>Ns%`l3-*>AAh;ZiR;r@-8UsJX%C3eo!YSBf{n48#eQk&U#}l8`)0Kv{g~ft;J=q%s@OGaziRb;!hcl1?DUtIfk?iyXv0f6t1m#MRS;s9A-amQjP0o@!1%7em8vs|rcPfC)Z01zR6=h913!8z>x zk22rQnHNA`*;uk04f%Sn-7Y0K^4lz+4Pf7QAYtPEX+?|*Eme<{=K!(&aUrU_T|j1WVepdSK@ne2UCD-r|q$F99#XV z!9O@jT9=NR(i7`Tp2#otuo)+wdA|Nxl9e7e=L5KJmkY~bWMh7`Dz0c*;5({wb~=d4 ztUGQ>7};aV?yb!8MMRm1(YIPUPu;kEa%U0t8XN6ad*<1Skw!^9D%hqe-!fN~znn1) z@hv|;aWD8wAiqG-Z3Is@-dGGSI$Blj-OhUmdEek?bLg(E$S4Z?qvUGhfXcfL5a=?dF#7PeaAEJQWWem(k`@WoZ< z3?Xwii*z}sDe5|GjD27$ZB(qCraj4F&}sECgyWT-Y*iI$kR_|vWqx*L-huT|t#n?i zMf(hvmG5D*RF|}x0daOCUGJQwR1e3=mfAFRv;G?Xq?=z0-oT1FA zOJkQ48^5SiBmTfKm7UFI-HYaL#yJ4jMlx#`U6%_5d`_AodIo&^xw~f+Duohf`3*Tl z9K9+rsiR;JOlCmi?{IE8-jo0IP!ki1%h{~y5CaRQV}O=9-sA5H1=N`ll)+Yw3GcB& zb<@0>K9ZC&)ITlS58Z0*z+ZS@6O>J>Q0^hWv5d4{E^KLC@^ljSJ0rmetRev`de#)> zxYL3b1cS|SkIDmGqUIMwH95#KN~I=>%AmU;c>jzYnc(j;g1gO051|^fkhCPwHnB8L z4N|hNi%XJmpt^|&XV^4GHJ-@&SuzNinDjqMnGtB0*|MFNHOp!=!lvPHz!m%|f^SVG zW(avU8sIsZqy!T1@kz=KAr5!?Qlmu z+Dow2^<{4hY@du9DcZ&2xgH?I1edh8y0*uw);uvoEAo)KA;nkOVGHQSix8opm!43~ zE>e$WHH=!Rtp1YCtGWm5QdhIl8Q63~*xDSi-JB~muin!2fS;fIfbr|CB1kLZ!vK{d76Qd(znCfI+;r8t>5E%P1(w=f$uVviy})h* z3wjys&Fn*lEINxYuCahN^Bwv|hEEdD;9}|PcObew7FregP~5UclN8Onvl*En?t>A;P`RE(gkr(+Q5P?`lCFYhlz zIuXpM=hlznKAr;#DPfM1S$L;44YQuLuE~mf7CSE4r0^^(LTkI*ctTGj_kNeFvn0AN z8g2@*WDqQd5;(_v19>-Xs=)nP^fja0@hY6_4{t-0nLP7yO^m+e+le@3dJ~WfpBxnT zRdhO*S79hC&s*nITNIr!+HHu`FD z3%YNJ&luge*zx-pgZixEl8#HoLks9_p=sDE1ct}66;Q1AFOQ_nIyMKe+Nby5y7Y;5 zUZQH9Jo*T&Dj{QURw@5@O5Vr#Aps~|UYSCYZ^p3z7JpYMiO62cgcTvX$Rd(fO(QU7 zfu6Rjv@z!2={g^AH-;2#1y!k05@Y?{lU_enA6&%2CACt2`VlRu3w}TsO!3 z*}WqMOuAfJC0PCfNpP9kCt|cU`}yr2ru*LGlhfpw>bEpQP7kvs#BenAapj4ubUnPW z+*L_BUel7U8N@`&;Q;xjr$w>7YG9K4>BWD;JDU)1?z&2Y|03=3$uDIDocrsgPfI`{ z+GH1hQXD4xto zw)l7%q33c=AYj#h?!!_yK#=`(zXZ%WTB`~KWru!)q!oYBb&GD17=a%K^HOTecqY~` zXNaTu&dUZ+r1b#$px8hDqZX(wZfA%S6!L9d{SNFtH7UtC=TG3Ir+On#u^$ZQG@8o+ zC~JH!JV1^O^qTMU{PXxrVsFj=7#f)GS>Y!yKcql6Ul)&=9G(22v!;?+0Mj2$IFn1B zylhPyk#%*55n*+YJ9m#y*zL1ZJo~85;(b(%Fii@C z_AWl{!5FAVZ2wO`axCdb!GU}*cq$znz+zi%~P94f&W83t0!n5YVY?SPb-764W#Xs`$liP^L}i$5Waqm@fbQfckr%HJ}-i z|F~X!OG!9%iqU z|KvVNrxN-_ouY~EstD-)&>4Ix`}aN1&6ulx^W07u+8$ROBpjZk|D7+1=vXSxn+DVPAaqSMiEV--zLvx5aaCB z$1YH@H@v`fW0mW9$ez50H%xV6YO5^}JkbkvsPB*9ncIfyWE_9z3^>FR8JY5Vv*WsG zO?9rkR0)v9*R;J%#mcIubJGHH=)-L|cLopTyM1dXd!jZCyD5~hiS-um-p=s&bLDLX5f0zBZs>~9 zVg&WrELqZ4#*8FOBBA3`_S@`X6u`z~utlEgc;q5wl4JuG;f#mA-ShFe{nhLNKshzeVGJYXe5dl}VH zd~AtbqN`fhvqga^Z++3iCX75|c_-!mSY~g^O0?>Dlag7$JY|6b9Uo14Lf>a#8euGM ztQ8OJhhq}EPE9C0Vi-Te)NVZqX0R*pQ5_JeO~lheuqo`Qp0s1Um`uY4x>})JcXc>@ zwUW&o)T8#CQRWRtL$b8|38=;YHg4G>FsVVDzw9Rt24M*<_29Sv`K0(f!b{*@kFI^&)|1OuutFDt8K*3S)#-f{!va z-eoZf@qDkS#ypFWzV;Ong-wJ84ooMTZXYSUgd{z)b9BCwri=4?2Jj)XbuPCFQ72_^ zZcAZ7@_eVEqOu>DeBM3_^b-UE5@5dQb%kCi5U5 zWbF(T1SLXcLT5}IRw4i6*@vBas!4q%mP@{}$=5EVcY`atwY2YvM*`(Rg2k^>9x|lg zrn$@&clo_l(LYl%4d7*{!OqEA3rr2ik`zc(<~#ukvA%?g9cW`?ll2(fBoiSR4Z=r* zSpq(yAgju*;Sk}(u;U=|35o!`)KtQ~u}_kJ$V+~aPMQT6-n=nNEu;q0c2b837cZMG z=oe>TjmNZ9-{?c8$il$NJq6=phndQ2O7-5Mgwc&BWHr2+1LnLzSIe?#5Q2;V7)kgcvJO1-vaBT5Vk-Iil9A=yNybPF)i+e^Wp=dSmFV=XTgH zIwTo3`W+Xnn|H%L@H8vQ5W)Jy&u>^{eTjxp^HWR-q(DjVk$c-B##D!gEeUp>0@EO2 zi8MJ~bEw`30j2?lpsa{WN7I3SquBKc^4Lygo-BI|6f`NH6Jj!|D}r}fYdPx_P9x7j zS%=$02_0OwgAR46@NfMp@>=e=FJbOQOJw#2a_Bf(m25jg5b78^rhwYyUmp2FX>w^? zT?tfAB0)p8|7CMI&Mfi`x8ay}z4^&Osh@!rts{Un00k`9XOqc0dFUthwupnA zwW{nIGb*8I{oXSMbu;Oc*|CjOMTd#%ydh$BEgu0a!G8I%8z3?CHYsoR5Uk$3e(750 zC9TEWS~&G4hYQ}nrM^^DbQddCeQQs;F;;OnF;UX0OMgC*n)4_ByJ49!poX(B;>EmIZ*15t&!HeKP~r!LGJR6oNGD^*+6C z*yUx!b^4>3*lulky(0sqb8(B;fwfUoh928AMLze^ifJm!&POH-RnUf^@#_ovvGuRl z0S5L`gJIa!%$ZZ7rqrvZ%~`wj^gMs%Ms}8g3BonCl_sPWv@w;)!}brG-cgWXq&7<* z084FTL{Uv&4YigyLANT)?eqja-oU6(hvY>qC7#oMa}dAvJx4pnQ#IFM+TLa`=2|XbD6S;8irSe#C z38da}=|M%$>7R`B%8VndJ3YYWVKO&<_VEm+1(v0Z6|Gt?dETV5hRcHise#Fl@k_8h zXf+yCv!#=DCl!FP8K>55pycK}iRP0WZ)SHzW5X(Po@)Ila*R++S&whPTd7az1m^uxLjiBoh=f5JhL(0(GJ?#ATqEn~@i)Py|_qwnW6Z zCMSj(cdH3s%c=ZF;O3ls0`Z1yfmzy@t_EYKmEycCnIhh*Jk4r^3uY_0>bjH4ZYR3z z;QJP?!h?uNqs>VK=cV+LAK8ckOwTFzJT>GzO2Y5rxnM#in}ph_=fpS@nAl>$R6#76 zC|9`k@VInp@(F%mD!7jUElaLca}d-dM;B!mqe88_Hi$xTu*n$l0#j_zlQ+*nu&b*9 zn$2)>v?Or9-ncV?nPzWlmSqHat=f$ii{Dve^3@`jipg$6avW31O1_1gU5`&^AS>|$KUd`5N z_cxn{n+4CzYGmAd_e-&AbOVVCg2puvP`s-AX78;J=UPP$W=GMOCS9U;M6!#xyD#pD z*zLA&N9t07DU98=2X?)H+M3?Dfy5adP&Fun_pJ?>b(wVf>-T8s!Q&+y{5ij>OSG56JB9_d z15J73t4V$fV7J+67=PcsYzSVfNZNkwm|FWRY_zI4LJnWk14k5aO2Ezo^!mPQ`qN@5 zh;6MHycrH&)_uviq^iFnQuYPR&n}YTl3IV~8JAdanw|!{H=jvWa06E6#ww!Y7IKB= zb~No)5qD9zIV>P64bmMyVyP<9GkChnpL8UBtn%d;x7+Nizi@&57UnhX&=aJ^W@)sb z*;}v|B$qrt764>H>b} zU(rjOnZ)|49}4~HilXA4;~CQbVBcs=r$YMg0AhG|Zo<^A8vZ!X+G5$!qU%Co*FzYK z(bm|StIG(<3is4@J-Z_FNMuN&scv(C*|rS(R)QU2q71?=f@&F_>?Xm=F^MHpk5*t) z@Oep-%1Z@=&pd&o^L|=G$>+{ev@jW%Nlg|~@zop}_L8{Qy;ckzvfTIb!UgCbHxim0 zS7T`&(V_U`3r44Q$@InAv9B+vi9DI(z>oa3igh;E&ZYf{U)Qe7nXM&hf@(IacT$Tw1{9fwKa@5h}>kK0&xVSvb7HO**!gv$=?? z%fpih$KO&$#5pEE;zME$C^_j?`Y`f5xl;1Jatls>RGY<<^Kbl-(tPJ2a+VW8MlO63 z`x~G3VFgdfu|@xv#9^SFew6Qo)tY+E&u{ncpk?ou-9&!B*ppV_foNsVPu#_}9r?1h z>k}hjPYNADD#5&I?|1hK@a5z{D}Fuo_E znw|6?(c8lJ&wu(u*r3wKr}#3_7jAb~96|+EgGlI9IsL3Uo=9audS&1R>U!|A6szkC z*2m_;{vS!UB=%pL$K8)lSuJq;!0F+7VcO>a9h&%~zt$7^c?P!q^S@bNEsTzX0s~W9 z@Tht5N7PzvS-t)LmU)URJY#}%Fjtf*Jwa}(ek6?N)bEBwh!gT(n>S>idp6u3ZsYU% zbl1;s(cAF$AeY>D`bx}U5RUFH+{?S2e`cjaqMkl=&rX>$M}|x@-0U~rfb6{o9!c~A zyrYB>9eQ!xq>vh;YuCe9R|Q-fE7kOmpHJ`oBuwOPwhtz&ZhuAq`w22+xH(X|CXIl+ zk;BpYUG;0ev>8JUkP|=tr?W4g$gDgy?zy)WPMqtxk|0(0XmiQ%cSf2jUS&ZWwK5uF zXXXnxpVM3Lzd!F=KfnW4b^m3yM09mFGqU^71KHu^d3zx5EDO)Wtt;27}US(cV1`V;I?*9)N0Nu zA%A~%tdI1^>ow^((_E^jeId5rk$!&f{?zw!z2O>%^f3>gtPv_Qd_R9Yyq=wO5q_$y zCh2Me@&GzlS+RP$VIx})pD%NiCP^LIkEV2=SZ%ndxy%O=`N83PB0tx-HGYM2EvMd2 zUZ5}x^LqX;f_Z+3GI( z#CQpZ?*p7qyhL=qykEr+{V zNaS1?iX-t7M4fHLHLXm7og{JFT301*Q*G+`HGzg#^VR8IAAC*91qDr{<)$J7q;I+5$IxBa8UbdmHxpU0GT#$ z%Q`O2mB_IXpFO^5$Qipo<8wb1raT$Lc?)cE%8nt}1q6%BVHRWx&b*0H=CuS5t>%5w?GobR+qFA_9vMa zcRwhLsg2KRY|08*Z&#hK_QBsO)huIKA z$YErbX26FL1oEQ#vkG;l<%_K_QjtoE8!QJ-!Y(4rRwW@wHxuH$Ub7WZBk!to-ei(&@rmC zn`<`WGVtN3nA@Z(zhW71C z-0&d$YlLbhaIiDBq12t+)w@~1OKjseXrV)4v`%cW`>PB}n(?V-MLwYYMg~8-;6gs!11_tZ;`6W1@)7o}Nul@&H?-Zn2v~BCAZQHhO+cqj~ zSMtwF+qP}nwr$%+)yci~eL456_ZcH%#*7%V_tty+noO55F7ZmLJL3wU%Vm-k(f$Nm z+!@W-=h<3QKNWzsq<5uN;|kw``eY3@z?Prb()0mnEW)l<3(YNDl=qI z57SaVX?BXa>pcOqS$su%XOJ__NEK`|fX&XvdGiVgj0=#R@tFA2%<@yVI*ghWocFi4 z8;?926M`xQ0gxlQwa>&pLhMQMTv@d0;WTU&6P=anytgU0(R)?p3?UP68#USE#ClXw znbqxO&oXCjsueA;#Wx6ow+VH^gG!1j+{+GZ>UUwmp^ajy=a_xVuDh)8NpCU>aY9jm zYslMfUH}xzsbv)h5QQnyC8mVb)t$vD;ZowqHTlbpb5=2P2OiTcbFZZ{N-oJa>Q6=@ zagfkH_4E6|AZ489H(iQK{POcK3LBl9J>p@V93Cv)!UEvdEsX4%qPbbKDA@YhC|^H# zXHWU!K<#Yo&{6XdWBC?E*#frk8@IDHwOL83K7hwgE+R@bHS~(~j{f`P62)tX6lbNM zr9S!AXNXUxWTM}e`2!zjBopQhX=*O=|TQm%t{_7E)~RqjtIEanD-{c zZvb{gV8+&JVEwJ%#~|JAKkxVBZXJPI-HAzZkw}cjqe|C6!Z?j3grTSf$+?M~csZF>{1rWIuxG*#QuZwLvX%!8v9;@21Ze zJ7kpamJhO=%MGyTBtH4Zu`$Aosx zWS;#N4|-iJcg1lmy7o;&As(wd{PZAlKJbK!?t&%fr8vi*_lv$wSk-{+1wKjsqpWI@6pcIUXH*E9leDieK9ycaoldDi0XH3$&?|Zk(>=emQ&Gr~oH;5X z$$jI>Q`!;Qmo3yk%uRbE3Ned$~;(m5;L+TiiJ3BK=Tt)aH2m=`Ra@GHxKm?Fe+XVh$TV?23>L$FnI3Uz0>Kh{#(2}a&^MXEOM^Am8E)g<883DT8 z)-mGQi;%hZo{d)S99v4edjqa|I%O@B{$5!uE3>Nv+yBPi8AxCqtjA=iQ~gi1K-$tS z^`iL$`*zs7{3Nya8-Zpc`fo>G|oG0^yRi_RtGL`>|T1@?j>F>ZyDW4ejA)-DRNW`^?&flIP80*p-*!U z*14PldjFZeZ+^Za^&LZzv?+rT&MF+r)L9y9W3Hg^N%uJd0gGU_FTCOlUTX>EA zudYJ74}`R6LFo@P6YI$9Z|#Csb8k+ykayq{LOPf3~)**{u85(Yy(b zY}4nq0w1pi)irY%xsWzYW+s0(;Dj;;w#p#arDl9^jO)fxb)cE7ij@k??;cwD*I$F1 zmA(a>Xl?3(;Tlk&&EiEV-gYLjxHdY7bXjFmag=6eE+zTr^mQ1!G+-WS0SU-iY26z$ zwXMRLU7k!DiM~dBV z#U+blpDm^A^7S`Re(!6)=bdyY@o?3&KA#-S8Dme~L>W$H<+m-)d{I@gs}Dh)OR zk3$2nBauJiZ*jLxNDDb}c0&|2#Gum^4*NM-J}St2cWQD&r!D1CAV37{T7@4I3}C>) zFEbi9MbR-WIlELZt?_hS{=$?Q_LELxK*$C;#^sL$wwXn3NJD)lGj>rywtf1zyj+F0 z7T+4dd7Xd|E3iZ&P!ZUx_GQVxw^c&{GCk=k>)=x~9ma&cL@V@x>aviFjOsRqk){u5 zO68Bs#JuW3N8ZTBVuZqOO4~eM$cQ)TR1x%33D^mx*U3Q?Y=g_j-`2+HoZ&WXsh=~y zJBu~v*p*}Gy(#^Oi2Ype5C%e*tIVs<_I z({2A;7Iu`Z|7k$jX>?Y6%8^?yGdlHR?lI-2$f$|qrgQUEMudVh5I-M2fD(C#l~9ql zDi36W{;tg}bxFj4fv`}#c)v`lfZ@LS2RRxboUstEUywoVWE@VFXAb<2tFovuP>?5{ z?v5_iW~@byUxL5|3Jz=cQYj9#l_7}h1i>&T^brAh`xog;1~F%326d+of4%|K}$wU*Q@ z6V2fomegLD)aD>CJP3ghyNDoF^7YP~c8;k{Ta&Q@LJjDDlxK+`Q)EZA$zQN%pj5fv zDKZPEWx){h5o|Ur$@MK0Qg*^Fb~~k+(!9#Goz_}Q6M}ocQJ@l) zHD15IoU_$^_9g`)1LRlhRGPH`(^hk3u(9kJg3|^sG~A=Kg)Y2skFAY-6jq3cU*d2q zxDBkpU1?+dd2Pe!sRp;68u+#`f6AsweB&C(5UR|4>6|Y*#KVki*5uA@2RFl7=&AYB zlk(+cOcx$?2qEIX%V^ObEWFz6ob%Pp%GlZ|(vriN%ko@y50#SHxX&^G=%H~XMMI1( zP*FNV?}~0oNruklP9i1oPy#6)V5}4Vg~2qKwTal>Z%MQ@q(bsaIwO@keaAJdsiC=J``=z zW)f~*Ie*{Il<9^L%lJ;EKXxhR#^*$@BNPOPz;xI$2R)~@`} z&KT}keMSZ6@r$=hl#)m?i!9s4A2Lka-+fsy7v$gWYv{?gUjKjvAm|(#OXLiez}$9Y z7~=%!4kk#Q_PEaqp;e=DP(@S`=38R3{RM$>eOvIV!aAG~Rx|E+HRWnE;}M~wk3tgr zf-?Q`uM!P%S2*>a*-boOQ7`n6KuWS^(RTHU!>5gP_@zuT=b6IQzq-VVdJelHDT<{6*$l(y%62i{Go!BYt;P3gp3S#OzE{YJ@ykhf}n|IFB5ta1{s@J`TmJ;MJ z*H_P=7rGXyM*`Zc;|SGMj`TcNjvc`#$L_7B$%=Kc%g)0ouZ%ajk0kx?YqbVm{-s=v zC7USr#XQ4@q+2eG7#$12f5(DQidm@tt$V-}OC6u7#MnErEz9ein*LKF8T5md4GbooJQ5;lW(gXfCL zZ9XIZzGM9#)eSb0?du1pX=t(iGMkHu`0vB_^KcXB zPTwknPJ2h*esRwcZsI zK4|^NwpdT)Q+N*Mrrd5}C7jre{)c$5wAJ==&x_k^le?9v;#TLRnMYfwBxHGJt{yry z__mBs8A}#f4L<`TC)>5+LGeP*PNHI!J|**{J+b+m$L81M3HeYU2U!K*SnYzR9;?u{ zy(A6#qah*zK+3Z4A|{LQ?{iKHc)PXgkI$KX7h!L`YRNLcf!*+8g>ANz{sVuI+cMt$ z@I2MpDOBKgtrq!wl;R?4aZCQ=Gwmu<$t|(yar0$i7S=igWei-H8AITs* z3mzB>FSN4eP{S0Hq$a>dps0(quj6q+PW#U|CpfteAO{KI!FgDsPynLK${`6f&Tgb? zGw_o{{_kENrvAd(I|>o6S8+feLZ>nOTEwN!ZjL$Sg)z@H>ism#YxM0%zkBxoVTM% zO4Rp5KRIsEuU1I&noLsz4(eM*Trm#w4{9UE6dB$9x16+kV0SrJjQ};L zWNF#)yQNzKGRK+6dActfxE$&y`)Vq^+P)~o02ea|@xuU)hfvr~3oREscBghFp#cey z(yA{k+akoYGrj#+QFE_D)9@KW0A(FFT4w#~Fk68-UJxgwLOq)#V)L5*PS~8OkWd-} zK$0t%?gzkH{=qW~#C6Y5fh>f`C7 zkpbKH?`-tnW!1Yu*jRSCn6*Z({>zd3JQ4ot1t~5yR7{9Q=qt~~nQMdn7jd8tgtdrZ z+X3tmCe%Q~7=zw^RZfT}%+N3hKnC01`h@1u1)xk|aw06IlB4yFg-cPk+;(~InF~dh zZEkZim44(xw+fE1hU_PatR*ZLt;(j7L#UxIYVPBLEI5IKPM7)#iDSNQfru*XC%Y9mL6)E<=PS0y*qmainw}PK0 z4Hg1?Pl@bt8O*>$NIL#R9r_j$ja8_O2KOzPh>Y&<0~JZ-aVOvZN#Y$jms1%n>gu_Y ze{N5?33ECLpF+j^Bf)~fjPgD-mAfD>8KX{>w-{s)MSfMiJTW{45o3o}^2P z(H+sq5j{!|QhlnJFm_^KqU4yu5goQRV^w5&LGCy@TPsTM0dE!tq?c6iGn8q?H>JbU zP}+1ebsiL15o)c?fylnWd;GaHCm?a^1WBzntH=$i7tn0r^5Ar^m8 zck62#(!z%r1rPXo0m|TJqwn|%jIRGGfKJkClpO3#w`lY0IZolrNz&D4d^k=xZ``no z+7)Zy|3X^O(fd6Ms6b%9CPZmoV(%-~9ri?8+Fo`$U!8x==a3}hDK}HGL39?^m^*cf zec{P3a?=l1WPAP>YTA|;9R7-c`#ovl9OcCK4S!i=eobgM3zltDjva4V%i{v*9Q7&l zw5USobb_@%NWfs>=fI5K^&&FX?w^IB_omqt{jNxXRayoG95|g`z_^;D=1CQ6cQ0zw%OOnBW`6+l?n9Lc)MuzB zbX5YU6CYCQ)(G?q$)2L`p|H|8AFbCk@T_4_8V9R)p!vIs+HxT8B(5FYu`ktf6inEH zzMMEXpcZ)u@V@Qj#aJLoYvLQ%%6p?{>aQAb^wYLYx$=af4?`dy9~x&Nh?jUw_c=4D3S$4KvL31 z+An(TYaVFIo!2CqMd1!1NN8R6M#3pX73a{bP^QKL7~ND%Mw=K@F5Ng*$d^t1x>itQ zcZ2vl~iC~uUT&PG)~;6cQFV?U;~&ckC|%}Y1!zDz~BF`7^uU8qD1CyA!6 zX8QRsgIpMY!biXZzk4Zf1$8l|@^|3%*!#VQO|`nsw4DezL1F)KH3byKT7!HupK&{$ zoVu>*7BM=*n$!+UR3T zpY-oEG4_h+oQv8p)#Iit;_v3}UsxtTOr`%;v;JpL%*w>^e`$W*y3@}8tNERPhuEWh zRjW)BtJhaLyEL?_AuL#SVSes)A3ckrfI&5^7sfZ)bT(z2n}b?@`2m$L=2H4@JpB7W zo1%UzGU3txH-_mqE@=4a06u;MsCIe(sanqsd+8YbKD|CZQx_El2dT>eN6~`5@7G1@ zYfs}rU0gXmU4KulHox5G8UC%*Zl36UGQW59WXaRO<~$-EF%q9RoJ6!i(WvP^N>` z`kcfbJ)>VML1Ef$$kADP^}f6jkzyUAGX3d-nr=2Jj&2ZuF<u z*Q}sznx5oIo~#B7)ZNo7oxDt&oWH{OevrIX#9#GO7uo8Ii5GPP2;>$AwMF&)$SX)= z54zJ8oyJY@-Ob}#Xf&g3H?HKc;%B%6RhC_d{q!6KZ1#l$+gG1gP-! z-}C6=Fvs8OS^VZcf{P}}qAP12bsQFGTOw`P52nAkI?bHKyEazeq`_KI{)8JNwPuQ4 zj?_wP3N2V$Zy*2MSPCV54lUy#n`Ig>-GZzAvDURL-sbFcUU1XF-jvESnp3MA1PZ;Xe=4ZmS zX&$mEJBRul^njA zhtDyb`qEV}zl4PY?KvJFnv+{&YpOIhHME^}0P=6s%LI*vB4lZ>$tHrVnbW-ZyS$RV z3&r3z^eI~s=PFJTgI!yNJ2=EvN@}a|=OQrDMt4|1C41J~3Kh__Gf(dTW8C!I?r;I( z)Wg}(L)+(rr8ku#gjs(Pf`k2&S13eVCFvRu1+VB53<7e0^{Bl#HfI$T1KKJaymdO; zYB01#3;Oiz`c$ji{h>_GS0Qy+o-ry7m`S^ror4Gke(w6k^2`i7<4zm& zx0f-*PgG&pcOK3(amf#A62HO2|J*={Yy@)?hsCdp13MHEMh310se5H2XcDYI*Q_ax z7x4@qb;!((yK$0v10VldN*=p5bQ&?fmqiiaC)y3}rE2c7>Ma9P-VjSb{JdVFG)TB5 zOQMj!aF*Yy9fXT4zc6{YOyacc`Hf+}AYpUFC2((aEwP`I=%4!mgZE~+-ciEzh+=uI zn-+XaWPtHr-OCC3PpCXpd9tyKr|_!vvRNo}t~{j^o+nqGxoF&sc4((-HA>PlSM>(a zsMZHkx;w|i?%`&8yO^j}_l%jCVj6=|`%x|$qM(0fUfzq)Fw(kxsc@Drt?>w%AyD&u zdiG5--2SwOTKhkY+BzZiyr#oRCEwe*4StE6vX-5RLZ z3KUAJyO51jTpiN0owDZ*4={C>j4%MJ%KY@I(jr>OkYc3Lki$xN7dao|?T+w_Mr~FR zUz?lOlay0VF-z`c@DI4QY&&E�kwA@5HQ~x6wLKe_DZG`>h6yzvZdH0VH6}c8nRB zh+Ls*6<-cC#-2u37!jrtwQhRzmXL{o2J)U2m{n;{COP5Cqh;2uGi=4VCLa)Xj-A?I zB;yeWF(7r0u*tizEP+em$W;O|HoW83wM_Cp?X?PK)xT651~CzVkvwMa1^vNM+Hw@a zZO17ly^PqySy{#FNo&457-(Qp$tt8WQ$n4SvuI*Y;kHm&`)^6GFbDvJ*F@V2<2$Z$o4{WK``gy0n5Xw4w)zZhKZbBB8ryR^)WFBJ`}mQ^KgX1;Ywh59M>KTmbcYMoi@ZMa&S;SBx_&+*3JTk; zcO{&X)MBknM-6Le&jR&w;5w3od(-Rc)cSam>G!D9TKop-WzgTgK2-UuiKV> zz*W39ZeIl*A!s^~yQ(wR2DV123F!Sef|3i%Ka06TZr7FImbO33Q1F?LCsNNi_P<|r zM@!XRiSyIHt#*vMQvXtO%rlKGQ(Ppsj_&Z>6i?=3hhheM`V1H zLW+*JdNY2*A95GE2*WTf0TnHiG_IDM3h439eDA4`kRDn>jRTtRJwM$OUwpjR=HgV= z)S}EI0-Sy$>!>bnUOLqYLpQ%_M?z24Amc~^yDYXlEDUP};>xz$^?=5BsDAci!^_-) zKJljH%mr<6SM=>qXb;(N1Guf%z$OMY_h6m}{Zp4UijM^2-6UYts2k>*sbLg&+$CZ7 zh-3yWBSIb=+yFmgjAAMa?{n15GO|cZzab6Dg7yQVLE%%=09EPH^2c?=ugD|;Sf##1Hd zSWj#kK{HJ~>g}O|%qLT|G3OUFN8Q#Z7*|mx-8Ia#P5`xcUB)CcaSZyjs%+BNpGSD~ zA~h@eamVbJglx}?xOfB*H;OLki>RQz-?!KH*y*dL$}>}iA&}v0SorEv#t+ajD^LeU zu)WjG%}FMYkr*+Mfd$`blzV|L%QbumBFWwtaV2;o`a+f6&0P zSrF?BeF39!?vHshFQ1@RiBpisi0eJ+I=8zuShUc0XEc`E=>{q=>`gQU1_;;okyPpOFfr zK_kyx!6CAk`~Jt!*jwm_nUlZPlDiI&qYJH_qYI<1gOu3_F(s!flbE@+F%Il7{Q8qW zRy?7YJ~W>yvJtJUbOozLi4`zKT{a{Pcgx zOoka~@b{7355T^4SY0~m%G{koAoZ**VgUngwl$8B9);5O-)uMj_Mg57AxevW_MBIV zdoak-(Ho#^Te1+{yeR5yqNXSca?9E!4EtTrTLcn&wvD}4GwbT|VvLj;(n{4HNBb{+ z!4Mqg>mNf|BVwn4cBC;S_ha1gI4!jj2de_I)WL+eF$LYwWhoZ4g#n(k1uB`XihxR| z$z3%uPF&2N)qxDlQ6V^eJpPuq$!M1MBGpfMSPaQg??YrkPVHWUF?jk>QeqKeV2*hp zZsB>N=|~MVAr~Hb%2pk5_U|)8n&YI??y36rbabRs2}iY54qsdQ^EVXH&~L}@&u{!> zW^A`2wn<2E!>9LR-_fY;`=o|P@CU5OLas-A_1-7 zV^P!|+Bo8u{-L+&Iu8tE9HBBrR{Wh`PxH%kF^@jvw>R0Uq-Slca~$>s?$V+w2GkGs zP8gWcWGH!+&kYo$EWCQpNbad==WhLnkT>rgEV#} zFi=DmN}4Vt5tQRY7!W1rT+}Ze#WsQm{ovEm4-pOJ^net1VR10HaO(aQ@tZT@`&yY( zL`_;I)KN?E^`c1}SrOqx*R2Ep=wfLs0S?S~!6@^hYs@WjwjrWi{Gmpr@=>Fzx(d3m z1mA6iYFdJN1%`lOqTU>lvK^pargL)nZo*2t1lR+g;zi1^AClMgu`d}@HTg%EV ztO51MKc>;afeDsN#~U@oylUb5VkY`8S>q9~peiMz1>5uuVtW0=i4P>>@-yQ#VldHu zAE$VpBEel^wT$^)pnhgPJwSA44gcIXGds(v6cQ6f!k%fO?90FIvSD5-*rR%~tlks}#@6ACnKzR?Fz+sI9kynjODG>6%C39S(=5nl zZmO?mC+vh~pa$S;Oo~ppSv241zLF!6!YXzV9AQN&XvgNtab9iwwbsP_!4%wL7)Hdk z$X9J00AwFmbMk9!vr5Aqa#)8fSYeOQPZ10coj*`e7m$IB!tyU1VusxiH9WHK+^fY+ z$|=tKN)8Gc`2~9$ozeTBTo*k%z6MYJD6pieTwl8q*#n@fspZirBhscr9m>~j;K(2S zi{X^$FfJ3EBey(2mnCuH2?IO(iDzdSwc|nHPY{8pViGrA_$lQ1iCc58RNp>#(2~zG zUw~b55h5qPaYmcytn}~lxJ5^{L2_|ZVF9apOai+N*iT6QDioDLB`@CC9NJVaJ65bf zvE>wMp$+1f`(90BrK6np;T+d&l zi%vBm4@{^XG^!-;KXKz91<23&Q#Pe-C?%n}2ElX<%cc5y)SGoE&rC#(zG}aDwS=hm zCYP0z*C|D=1~T*P2Fg1Jg_GoT#5oIo6_H8e;GqK6g8e%_gSzMcX8hL5z4I04SJ_IV z3v{X6F9ER!62|=Tz^%y$*)h~ePJzBKk*ntiiDHU+M7<8-W?@m87VPP@DEIjQ^5FWM)Hf3zx!UtXF23QvbR zApQ+tXB<_rY>$Nc3Q^zz*NLE#-%QPcBXxv7u$N`iknFZaIgYPa_}F>C^1K-a=8rSaT#L@sZcel3$wJH7NNo%54-YXNWz8YU}QRi zeuCi5!MkZoI5%h{&~2P^0vwyZ-bA~tja31*r%=w9GpE;5b^j2fnDvp8U?y($ky6QX zi_!(s)FX`PC8lzGe@Dm&&zA8O9prVzX?Ka)3>J_hAXaUpFJuhxH~R^7x&psZ;V*pr z@WAe29pnKou8zC>Sfa-=_Jv5!PrZ8y)e^Ff=G2oQKPuz90eX8>V)B<489#uv?9PA- zVeqFLs}x$+nIhzTXFaAeS&C0a4*io{4DdBmK2>mDzTah-`u?wEIi780>r8CBP330E z4(>rq3fZKNbQX0O{oWu%(QcP*HuXPBIW&h?HQjBk7o@!ITK7rk;gW~Np@ZsBw-5=< z7I?_ko3=IBLbd;Lt-_QKKK%jfQG+{eAaCgEH}j2{%173KGChOC(ozT3s>IzUZcUaE zcTCBPnU6AfzFtz^+sjXN-&>Du&c>7GCC@6_jdR2XM~sna6rVr>ys?Xc9AHaESju^| ztX%0BvIuHE18U8Qu?=l?zieUKiGvc%_|A8I&%HXg4qQcC5HcDJn^J)5x4g}j#fSED4Xng?LIySE-BaDT0`7k%o zPsDZhVFz{f13UN($L+|a9}wlZEg_>bexKW$MyrQC%Z}}Cpq$`u%CNm)SNO|oX%iJ z@4UM)UwpPIdpUbQ5le|)yHg>vL3vy0%Ry6N!TtqoMy8H6fs%khJ%VSaK9qrSgRydQ zv$xVVgSH{?j4J-elyf2mdG$%%JDCyOE%Q&&79R6C+YtI__urjhrZv*rf2ob(^(MeErfs2>t2Y<6#|)jcRBFj#8*Jtzk7|E_ShPTzwHzyjZ-!hM61U?Wq(0{-7~ z*qHtooY89g4Qhl461$%|0|!QoU7StQ_1_-mPW|yGkhjvqgXJP}3M83~{Mfsy#Cidt zp~*)@{({oVw=a%c?i+%C_XSUv(0BjK5zH+AsTy0U@WDi&z_Tv)7uQoA$ieWiVPXvb z%X_jhv;Ti!54QhHn!d)HOdxIj0ehSgDrGuwRH{{|RiJ3Tw~X)T2}H4HOdDrMB#Qb8 z0tx#Y37U^|ZiEsV+{zS4Au)mw`L8@Z{-Oj0TqSKMsivp0=mRpQY_CE7bwC-;T1hYN z_HJW;&GKr&7|Px8d>-uWB9VUKiUNwzFNL-4Q(G4~0gtNbvd&gspKR;gxV-IfiK*w; zhp!f_Wn6Y49Tgc_iQ2^hO?>{YJ zyL#{67`d9TXUnX#Cq!XPr{tRq>8Md{NH{0xwN!6DU{6@)sH=N}v1ABnj1dJ98z_lN zc{e(vN?ZGS#e|i2P9w*^QkBvtcANj~@dAPAneF(m*5l5|6`{*haG`8MO$&?BY@74E zgDpYNa1P|rqpi>hwWq_f9H0o3nzI=G6}H0?d`Lr5Ipz)eJL&^N`4x28lskekIo&SrH^a8KA)= z0G#d9@ynLSOSs?GJgwv@athwMny^p2wccUpA{VUshVm9`wZCp8{x!NN> zRoc~zYnDj#By(JYmH_c7kC?Ex$QX`rKutRDt6D$~oqr>N^S2$nmcQ5V6}vd*C(lo_ z$BUr|I5TRGH!ZZ7dAuVCw_yB0s&fIscY}`opf*Msc;+XzcvW zq*m_r;F)15@$=I~Wlg=k8W3znTnj|vmpqAIMm$FJ3>Q&dbqP*X^(&q_Ok4?@8Qi`M z_@NN}k15i@bfi~rIEA7Y#+XWosu^BIBZ;8{^^jwxL`6ELX5X-%Ed(4KiU-lTsclAA zZ;xvDiqCxPVgMbIHx9g#f5KzeF5a&tzAhwb*To`Uqyks%___L)CATHF0RL1;txx(d z-&WAYd{Q`7um#j#S+^jSBYePg>KCnU~`fBYH+r@5e zBE7JDBPEQ6{I%Ir|NZr+>E2!b@AnU*KZLh`Wqs;hr~wEysDksOAgfTAs3Cq&&40!j zwBa674&-V`hZz(!Ci>h0fLKiWpi|MXePau)7-U~$3E_!nWf_Po@judxl!YgzTK2v9 z$1I=uk*;GP{XnR?Fa$LgEYbandcRo}gVq0Km6Xn`1f)Xz9_KimQt8@0q`|u)*O^}9 zDDa$2JFnQ~gEKz1(lnOX+J_30XJ&P0_+Jy&?<;#~Z2zg7*#Adav>eA24E?X%2aB zox;rLHfgIO5xF&T$8p=>gzrqa-;Ioc_#kk*j0HF(>AQSC-QK)E4ZePR#A&JdU{%z| z_d)B=FmrF&8EjXvJtUsy!H#sH>Te&PHV?NFuIRVzC)+1Pgo8U0F5(&VhF!(N25aD7 zdtb_bMV=NRSfV>(9w`-d!1BP`@HZhx1SZN4g|@4`ya!#WY5O>?wgq7Xfn=2wNR*Jj zdIw0TCey1^s@dihEqc|R{#>*a5}n1^U3xqOjHOmhVjXH9HCC=I7me&WIJ@}KO6k^1mZ|9*xQ|e!D(NK9bCt0L1V4KPi z>~=usR1m8wx|22P3{TV|13M4pGFqC^Rm-h1ZV^{(iQU;DDVy7TE~1Fp7sqyd3t&Q3 zOAJQ;(Nb`&b$4hIW=5z_}t^+IFIOeV5hit4ON>r=q zoqVmc2*JlcJN@YfSg`j?AC$$(!vpc1r)lN~i43w|YZ|$`hp}1H`KOvHMoBuMw6AUPq{^hf03S7hWH#{;ldB&7W#7tx zc^YxiifB}u%Lg)xmjL4we{-;ZJ|mdsL6}&ItTa&#&Du|PEpj-qOtm}^j7*snBygt1 z2|By0s1ibijMtXU=d}XFZZFE=r|j0NbGTQDDGF*BaIt=Bdd7@@9@NRr0r7-G3>FLx zbeR*YHr@F;x+71X4Vi(IW)V5D-*m+D928$s?3RSEYkZXva{^n9Q2Yam#|aS?SzUAf zchH}R0!?9X9@*UKRh46WuCFXCTZ1Q1f6vcys3l&YP-O)m`w$c>{#dpaupe7**5pF{Z7I+!I ziu1(--6l@nDl4RSIv9_xA-snxs~f#s9-M!ubhPCtEuu|Vr*y~`D$5@eR4!3LV!s)( zAy?%<@sQM8wsNS?dGt9K=V#V3*?;blqB^b6J`%?3;6o68pHA%9T#Ob zuQCh9b7mGr-A8Rx?fKlRXZI}z$WdctS-jbDny_d%FqSw(^$^8En1I%V!N4U#jJ`yk3!c`ItHnbNqD(FJhYVK*&l9B4cr)}${Ut_#S zA~YZw;wJScJZJZx-MSRux*1Iug+@nS7l%(tFEsM+bF%1uH=5aA5_f5hclFdpGq>D0 zYCHV|wFR<<@3C}LUeL30R7}#gSBhpfpYjrk3|=t+Q?S481K3N_rs^1-W;kxPFl`Wj zJd#n$ar1_ImKEv~RzO`{4uCNcT}q|NLIIHu0re6zZ1B~-X=mDX5m z6(qp&F_p)KK=EeYjAh5#%1syH^VSh$q<|5lmyj)b~E8K!7iD5;0w!PQL z$f+YOk+{e9F0WH>V6wWTM6Bc5bf|&OMNDUa3bTtVAh%v1qlPpb}!k0XoyhG z@b>hK)U-1KF7}&-rD{Xz7bgEmncnD0l8*h~G=^*mMvvkq-w=%Kkl0Il9{9_hfot9fD9XoStsuztTIuPb~gg+mFNPDa7; zSonUjn()NNhCRY9CWV6p$#8~UX8Dic@MBDi<3wJuut#DQHk~P>J$gv<49}t;J#wKe zfWkPjb)h+isRi1pq++q>k!j_`N^-y{8MOOFZ}%^#kz4GUI(}~A*FrFI_R86`o^lwE z;_4&q?B$nNT|uhM;LUlA(P0(7qK7Ohd^1VS=o=OS0Zw6c(UHp~G`k^e5KXm%I!hE1 zo{>}~hVYV=kGg>AGittg?>Xbfa^)T|z%f$vME7NTotl5#s@BgA{FyG9VAqqN4O2B` zLU5

LJJ=dHcoI3_`H(Y09n9EJ@aC;ad~l@>9YH}VbAO={^2HC2uTiS95G_B867?6-v_x5 z+9wGrN|@bn5tzd}TSfoC^mLhbK;H}8M}RZxH~C_`0iOgTl)IafX)xob%jjyWlPFjVFhEnRscO3GT3YJyGBu>hI2&bA>1|yn zsdkK_wrrxc00eZv0Ht|>dBmZIgh<0iYaXPkK-dpTSFR&ngB27ZZJKtoTvfu`o^13? zT41g(<(nIwX`WPL1lv1AHHTgAY#_Py;wCwlrES%!B&GynJIxYw8 zbBw`6hDkY>0p?=oJ=c%zW)Q;-eVhy!bAk%yZ~)#XU0F`1@g34=0&ty@ zO;h~gi1|vwU=xu4;JX*0zAuHPUdR?gKBmo(({tJXInb{gt25f(9z}N^%Dm8zCL2Wt zpkj-M$Dr>c#p#!hh|mG$(3A_}cD(n?kP>$Eb~yKI&X^deDVm4g5sleB&U-yLv1*sG zmj?)R5G-t5Uisg`J?8EQpU_nw1MoCjtIQy6i6h|T-)b7tU~MNk5g?>Heh09UW|{qx z&qE`J0}-`G*DbK(G?tMDG4QlxyJ)K>)ZF1r$c*!>E3w z9`tjzzWRv?#KqYIAwivDX*=ek`0ZnY8HEPu_+7> z5mLkhntQ3eNHDo3Wgo3lxdQ1VV9zndoi&{D;E?JAR!Fi@+IPrJ12-r|Za!1#j&7Ys z;Lzzu(+J2GSmsY*yRELJsB-(l+xWyn28@_4 zom)jV+78kY8JCr_DVTqkNR=XkN0r(Fw}QJ`mF}og%`yq$$tp-R?(a?)fORD3(KmmlVF{uo)P)jQGIlpFzexK1l~9V(+yjZlw!OWBf-nKRM(#Fy7^2zEOa@@L^m{lcEt zT^%w=iv(C9H*;C`4JF_Q!0#%lofV0hN=$#`CU-?!VC2uCiKSMt;E`ThLIVj3Ir9<0 zZU?b`DqKa9=F*IequO-u>4VTcp38@Hz9u}dgbM=b@ce|13F7BK()r>LxgsjANUJTa z25|c3=;~~&UKON*f1@NL7<|!dKR*hFop$9=>VN1LUlQ11@YvXp0KQSdox5c0k(QY5 zJRmBedG$Zq}kpe?_V;glj$inj-;o3#xG8 zjv;kUQFK_Kxkb1XQUGY|x|&Vu*xlV0s~wlmAs?rvnv-Y3JGbMsGfl6JfH&varLrn z+VAp~OkGbbALCNY8$`FygZJOVn0kvNefRq(thAcJtin+?U+<}82H*}|DJ(PsEV5OA zh*6lhdRYB33;AKLOy8IJGi;{!=_kl@N%=HEVXd|A_921l0U_%Iz-SKt)PPvZz||{b zwl!*8m}s9G{5nCB;{$Bu)f`=)4B!u>a3!@5RK4x!NEW8@K?`MF)Y?*emP`>@OxkKW z*Mm=M)RFo;Tmyj_y=_b9R>+=z<+9>KU?DLt97AVFIhJhtd9VRn}c?h%W{~3rGos)5*U``MrdA^>0a_ zdtY2yaGCvj_?vgp_8a^_8T24}?4v&tzt{saATC7?6BO*7+vPNHSUTDuR%K0bQ%ylg z7btwU6;7^OGyr;^lmt5RI*nD5%cX@XYM`yE&2pUO3{dZC=cA*uu^xUE)8Nwno2SVZ z;hfeHJ8?I!<+h#g+%XT2WhcBd$RgO56Lf7s|4kKZNcp_LD_rgcAH6`lQZNKV)H80L z1l~5)V7yX|@(qTu&F5 zknbgm32-1L;c)u^^)-vj8m)gXSCk$qAHi?W&xJ#-DLA zwdI6!by%XQ+I@NL`6T;Q9S_->)|#(4XG1T6T6ir6Z!K9}#XYIXuY4Q$BSm*sr%6st z+kxJ>9^pLSMQUBWx0#D6TUfD$=I!PdH*Jis4KSy`o^dtx+vU07H?hvs^H63{`sKPk zkAzqsu~l)q*LnS+c+Zj_+0f@x=!`uXM+my@daiFua`8)b?SVo_nl9r}V@SYrZvUn> zi;#X?d%W-I&*N1iGpW-6{4A{ym_f?)n)J$4*hc(G$K5gt(|UI5Vu}969w*5b+c&b` zS}LY27(9y$VB8=|Q{}hT*O-;+t^I=wl{&aIBM;Zip?)XUgZc-R0du3U!lhGe%;EOe z17so(0Z=NYFc?>JtSnd%9N*)$o@8)lJH62LiNaS}ql|Q_{I%kTLqOYy?shAw@@K5T zCn3MpanXN>iCF%}+^*S06YK+;o20T!4n{oS#*-ZK3hr=Po7DiayMycR_mG+Ev@_sq z_!~Gf_FCjWHIo<*M*;eSAv7--gQ-B1w**c%1}w{wx7UGon+9|n6;hCPe*O>;-F8cz zZtwNJAosGIM^aObe?lr2CT8a5NLw&WIPUix$^Uk+&j)qextXW2H+mqAwz$PQP}%Gw8h5qKfbw)%*}lfVCN8^g76!ub@5;%SQyiP|I^gjelq$g z0IW=$|C@t)s4i`X#{t*%tagG}ncpn##{>n_2;u0os}&c>*M5$dnQwvk`A#mTG&gb8 zW@|-i3u{(@On!TNo3~q9Ge!*xP&FxWr1qq?d<6-zdf|Ie{J}@QfAA4P5fuTQ9OAFH z+^+6F*S-(sCkfh0%@QU67k1TFxC}~{_HTESD4i&1$TtsxcBn=?GrMYgTbaB2l6Azh z(Dl@~YiSVp56!?haSKL{`>Dey&kbx;*bjS?_+wNb_lukObe{7-xje&{KZ`aldW$X* zXKum9Z)T0VTmqXnV=?Y`3S67K(tC-J(Qmo@z5xb^W;MHRQ!vMX4tgQ-nbJlOS2k;SF1J^U(foCvmx4eY%*K%}QmH6G8J&H&A_6e^(ZbP||v_%;E)`qo?;9}G@2nvPbIO&TGDY6T$4l78R zT@}j)X$|O8HW96cb^1hf?}o75I~T@(nKJ8}Lv8w8XiYK>-#V&X~ltFiz7Xjenr@{>5<^h(oa^B^oP{x@~fBYurb~6?~G-s zR&CiXWe_^RPgt~qg;$G^2YKR2;X?U%i;6Tz7>^k3#~rt|VMqs(0!{%28jHrj9fmX$59G5>|%RCXc25v1N>ahnui*PVa{2&9p{32kDS{K z;ypH!{~C~#%vr0KAuLTL7=gqsH118Xs6HtI_k#|`mw76AUL#Jai>t{zw+P|X@6_X8 zoe*pQAu5TSxIX!ZZCooR3~e4vRH~>xZNIvxkU;KyY&ySiueoYFM=y`DClzEjB(U-V zL>;&=u#N+3Ozlg(Fw_`m6}S{m61v)?pxy*UO*SV;4q_D*}_7VONiv>AP_JdGUc5DO?#4_Q$FP2A(CSgWH&^ZH^vXCFR@`Z#u-2KaIJ4%j>cu|G3M)tNF>yi3eEme>8d#~hLDiuuD1TZ}@V>*N(139h>ti4pk&1fm&ovBO2@N7W5#r3w+ zHj8N2Z~rmu$PLr$p0x>$Fx{Tc5YpoY021S1Np)ZaLj_@BOr?(oAp_-NWKT8V0>}T6 z?EK%K?MrQ)SiELOpXr(t33;H$II;Q&IQk&%^*W;ilGZr<9f|-#)j9Pw^|j4*p8Bkh zx6JuXmj17`I_oMrT;COZ*Z9gBWwMGkFA7czZB%Vb?}HNdeCV83=s}L&b_%Zk5<6DE zBM~F&7a7^Bx2oHzh5`WVR_F?tLo_Bh&22QI#%$aWB_k9HZp|i^O$QZK>n&Fxz&@HY zwt5HmHZPYrI?T`$Dmti->F0lBN~|zC!c>G@(1sl71kn$L5i=%HRNu14DK$gsU}^x0 zs-zDp!T zx9JNzrbn-kImCg+7Rph{NX`l$A15*YB4-^9ie4zpWUSDaVPbT;>F!tgwOGn&St1A) zNJ=yZ6)K?*Y9%R@N^TCRz^WCpm!=D|fVOiyOGwT}!IWzd?ZWP#D#l6N#|8um=O3Jh z2ya04?NKQO(OyS`$1{>;!bZT{sKTc%JK!?p3$`viPwG5Xtox8 z=C{2Cj5b7>A7SP5mcC?w#g-Wuih_7WEhh5uVE?yNtL{l}&@T~KenF&Mv1dxZ;YBd| zn;guZ-Vwoyp2;%%aITXOBB60gQNKWXA;uUaPtlItK?i96GVL0ny0t%?2*iG>yTZYT zFqL&rJL&-Vp--UR!kJmp{FS~I$#FJ^=G?=G+?#lQ#Y0YA8rnxF>qewjepBq@94oa8;=D?xxt=jJX=WRyKqlg_`?c&O zdtCMfLgqO6b5?g@+anH{X7T%Q3A7H{z1-}3z)yfUX|!D4XRfG*g(l`_8n7&bvVveN z=7G@Rd?;<~l>xjsj+ZqfHb0h>`f2S|2*Ni!1H~hPNX8Luu8pT$kEabF>0Qgdmc3ps z(im^#0YP!Fm%gT?a0%l4flEG~4 zG{4J|{YCN*H*XEar$QEO#eE+tv0IIArn>+Iv-{bsoP=GJ#{rT*c@w~gw7lVXjN@V8 zjV-+fo4MeZEQW--yZ_(_^Mk_{`>=W08tox&gdj2*OoJ}W$k3IPP4miL= z_m=KY3(bJG&-0QTo9?yGT8Zz$@lDFl;o;9=Og#bL55%;$#qX_)e}JyGwk^AE?|19z zif(OPpZ3p}x2M6Yr!r9a=f@|#W<6^GkF)nk0i(6l_34`Kwk_Yc+0At`H-){sxAMp8 zM_*9zY`+^7Ve9C1H}h5~(IPQpM?kx60k7Z9OmTsACSO(amAf52qTpf*B?ZC}2IMZ$ z=cWOt=Xcmh&F)qFa1&oEo^*rBkHw%J_SPsg(GG_{zj5QlCYet~BBF@qUReLC5LHk>^>OG5ReSE0}@3G~ThY>d|PdQb5#d?zF5 zQArrUMOe3J(1+NTG-Ey+BF zWE+1IDqR_%5TgEadE86*u=HyM<#nKe2fPUR5lZ}^f(a=|AR)mgG`Ns5b zQ#N8RYqMB;=xvqQ`zwKZ>)_z<`4g*33H1JG?`iMwa&WQ& zU|qF#$sCI_ozcm}Y6!l_Nf=e4%J%tXD;AZov$r-;@;BQ}wcV_{3f7y2sFC{1?S->| z@f@?u7RdQ^45Gnz8_VwY_+|%@c2{>H74lS(IGI6!H_dsWc`*_1ccOTtomg9Q)Y=H- z2|}yt1R^b82w>$mt@ml+miP5R z67y|Y1#DhjZ|QDrUZ$vod!y-Z;@a~<<^Bz{JS0|G-M-rDYHRVlJt44B@V+M(3c}ka zisoeaof4{WbafXh!C+Ujz10YncX?&$<>d9bcX|K0S6RK*WrnG?t*hI~i+uGo z^R@Rhc=)Yj-)a5r`wkWV?8&tBvgXHBhRVN`2)JtUzhOyedwv6Z^&z{troKNquKBhVsO5Nn-CFbr2z1|6iM+px?Gp-IodR0Dom#uS-eyadrwN5l*FTR6 z7@pcWyg81}FT3?RR-@kx&sYgUtY0*DPv>l2NbXN@KF=zwdfD{U0C$h-&fB;M0heSi zfEiRaRqwqJ+~ZU3UCj!s!KOdUA?F>`9EgsC@`p#VjceHf=VJtEMoKz;-mr^o6U{9Y zss}`>l!n3HG)`bgS|%|)-W3W+X?Eyv`dAHGaXexzi63^U3|HAh{7>y}b?x%fpJq=L zZ|gneEoEQ!Zq=Va97izx{?xm6z=()5FLL~#bC-71#44eB50nBkT z6Bk{!ZeV|voY(L2><(LUKQ6GJ;cUdi{h_8l$7=7l)j-reSLXpanf@CnTaS%ZODF?qy zJLKALOHAUAH!)0PW>G+h`8RgTlwZHKo1x0C%b6^3Ok=*Lg($G4kObNehP5cLBk^{D z7`KJuktR?#=M%g(?q?XtwK%ro&JLD1+6(893*A8}Sd3p8N-c9t<-l!Vke8^xogslD zXGPSn8(GKa6QtPRC)$uLOwmyT8bQJx#c|(IOjFpvLsX>FI}-q70cz10NUZLb`9akS zM0#I~lJALU!XRbwk1oaT=tE-l{+`lGtDw!>$IY+D!%c!$>vu`h7-7bfU%W`L+>Us{ zc>;kFnLs;nFHx_QI+|ZtPLEHM4V!cnhS*EXN_G&54i+QH;>KemPRuhG%0W!nX$awS zqNdD%?)`-0AC(f+FcN6h|4b&~(bh-{Z;b!g<)*@IUu*Jykn?pLVY9jW9KeWCp8=bpM~V^n^jAv{4OBG>ZM&?KE#UCb7T<#PI>lE zt4Ev`CPZkqilvvnZ`nnB^wa8${V6;5fhDBxnXxP_Y+x4?o7zY-Go)8@+Zdkp@EJ8) zEp|nPzF=2PwGuAKLNZ~bPl+6%q{)i*d&j@`DZ}SeHKwFMhW>@r6Gc6PurooQU}Ecs ziUH!>d%HQ+{p(7d!q#3#3|9%F2HO#NoQB|?Xr zlc^|N{lUC4)9+2tC>Z|?G#cHt6p>YRhy1J#hM?T9i)j!6|F5N$zEAn2|6^Gn!l8dL z43Au_mC_=9p05*Td0pQEXV7mv;Uan5 zb^rt2w%eQRN+GYzInBsb`z&8Z?O75i+B`lMemVLc%&*2L>(*K*4{)aC2*696-@j!KtyztEyF{|9f4=X_&1uw+R^+8i%mWatev)Mx9f4s`FSHA z9>h87BVqecbDnV+9Lnf7=mJHKr841o*u|o`I(wgMMe&X%id=qrrnaJBXc;KT5W~(V zl3>2WnWYKFKPVa}KgoLL-#_^FXa9C7mQT}hBn@QtN;xT@B`suNh)#XV2Y#ml>T{*R z0(p_h; z92j4?S5f#>_8<067m@+{pDU#H^@KcNx{2>3L|G5&_S|^1MgqOv%tM4%$xI(N)lsvd zPA147XBIY?QTrv$e@K|IE&8hfjmX%#xVB}{Blcr)k+Lgx)+3{)hBmj^s~4+xK?x~~ zmmzC$Qf0^~!n~GpAQHXGkBTe;ZVMkt7S~;`4 zPc225A<6aIv@_ugv2AqV&Le9&jK)LNay}bjQr&9ryp~}a%Wl&T80y9WK(!4uazTH1 zGABLclSZ)tbXl-`>1Vi_8cV69bc5hrGaNcqqIwueH;M}jrEN>8!8672rDUXMe z%FRTOo@IKu{juD!Ig&nXv@>}%L*U$p#ct8(TQR;(0|v>32FVX)9?$Qb&j*4u8?1;8 zpZ1Ld26W|Fv+;WVX-Ic~`WmC-`T8B$mxT=we5=Ke|0+@#do-Ku#kW~EA6@8DxaO=0 z8p6~5($DypO&Y24@O${n@gGKYaYrB4|5LLsH0dUS^qsC34BfuxHD!66yicRU|J0sH zIn`A>^K|2$tN*b8#s7JRXAA`26|GrN;sfow`c_MB8m6+Q4$!upa3=7M(;Jcb7!chu z_UrgBTqCoS>yPFRD`mfl*s-yDT))HWv^F{q zHj8{w4BoLZGay|o%C*2n?#SG0_}3NuNvgQsn~T8TB_9M1y?OyuyZKLBvYK!eC~~eu>%1 z(OUK?kl;}DY)l{(^5GlXJT19#;_+IzzFN2tuQ;yk@OsA`E_!iQ-u_vxY(pFEHJ9R5 zN3&)`mEZwUJf3k)O6|lYLwjv$JjV(L3W4b1)yzy|sejJsu5t#g%5CWmRf#O~N@iHb zXC8^dUI1QuakG0qj9{VD4%)W$1!ax%fr)aQbZ3zFaRiLJe>P=U}q`69iEbwKy$GJpd^(~4{_v*GCM@_bpZpgs0e z*^SvE{;tyHzPYu};omZq6W!v4hugqEyp%S+R9wIg>MYUuJ$U$|r<;&pPRsMF>+~k@ zH0z-)>N;%^dscDiZ?1`Pvy%3eHf6V*qvv~YiFJ+JF}uEOZ7u3ooL5|XV`ccXN%R!uIM>w%`+u`~09re-S00_#<4Pj=DW9VEWY+hewwUzw6&))Rp05sL& znM7tVa)d8hJ18H8tU5IDK!7bnvuCYbvMA%T<|`jX@UqvqmiTGEVksL?^AH`x)_)U9 zstyqU|M(Oq2Wu)rGze+xRwWoQ;QwLjB&SLL+i!yxd&dqI?}-DFjGLD{C)Y^t)jBo# zTD_^N|D|b|DZW?)kDL=27`q|Pb2l48s2QVsY2KIP33oW|i?*A=6w{}ceXr=r!yPN> z@U=#KB=P6V^P?*CsQ1yK6gp=;ubMX%aXfn3H*KiXG0hCX|6Fk7s4~ugzsSk)VBc&| zfx};Vhe_C1#eP$Cw~C1Qpdo;#wK%j3>cvK=6SoWnoc>&Z=&Z^Y0+ln&L=vMLBZRkg z_$ytogC=wa0)qTr9aG>pRk|GxB;Gj)*5Ir#m>9DGbS;!`JA_;nfex@F&;ojVegO>y zt9KozGeRC<7=|LO5{tfl5IBbyZAq92NQ>mH+QD81NXPZk#Z)2AphpM9cujsKIl|X51=c- z%8w`qNR+v-_OpOfwM?;qg@mSqjx^JTS~B`0mMcQ)SHYlCW0l2M=!2r*1$J8gWC|(K zr2(}z{Wc#`1rHLZZi<#^KbeG1e~)V?X#E-v9H-%>h%C7ijRpyHIu7b zplnXL32$iu`GsK%H;3Qm9CsB$kFUQ0<7e6n;1=BVQYXY~K|a9+4}ty*_SoZp1>_^e zG2Qczbc1a~Nbx>O6w`04xMlU18z_}z9}A9b-yYItB!U4&FCoD{oDW3=J_OeM-y2iS z7?+2XhxQX?@UTQYD$N+1FuAlaRmd4)1=hxE`7s&dq>++TPWNFyLT?1|O08)mNt3}ew&wZqUbnDy3)jbArk*nUP+o#@=ykak{}_$nxbH3`%wj*-~`>2@6X^bikP3;(6=}0RK*(7;*b<4&h3>$tN z84)q~z2+UOoBYL-wNg7a9C$H|%5s8Nzys$35S6ApVq8ECxwhCYtP5h-9pQ_Z5Q<~0 zFif0h(k~)+Ks-utZPB5m8(>xkWPisQsSL72$_>C2tJ0MQ3H7Z4ceHN@_}st#&h;-& z6HW=+-Tr>Pp9Xv$D%18SWE+xnZe7tC97xgfQBr@?*{U%`^C=c|59)g&_s4(%=orALJu|}yWl4lUi z+ynOp`Iwt0hTt&8IHbNA{xwGf#BY94WG^JACM0H#t%JoSwa90Y|2}{qVo$O67PFs2 z!dQFjnE>{t`P}!1Hco!{>&kY@6WRC~Os1pkn;5qQ2q$jq8|)eOVNaAi)Y4v_tCGqd zG>J+Y&w3<;V9$clJ5v%E)TF-tkkSxgk#@c7K2`E*Ds-i0h_sS(IB0kStf_0CXIpx7 zOcKd>M))-0gB!;-Yj7uK`pY)HfWww{3IzwNF=b~}X0H#)YcEkcz*Sf5_fyI>P|&}s zs2{uW9ZIySQ(r1_onMiqbm*_aU~>s2qC2*wWamc5QUPzj7uZDd10ag6+Sv4xo?V6> zRNh%)9jaD!^vOtvoM%}9?2b?KC#utTw(sviUjNo&g~Kbe{*=_G z;c9T=F0F|al3gTtMPpkdWVknFi(PtTwi8h%zVWQHXIoJru)9Y9Wc?f}blXCo(3M*Z zV~zD2GAZcoLL#x&rzeXBTK*0DgkgqKRUwUSlgW2$}q{hx$T=JM6VcMdNwY&#w2vO&~EvpR(%QI zb-3ANZ*h#3#JmIM?+igF>f_C%|NKxNYf{49I;{_iaw^_Ugty(FJ$N+FE`z>~{yj3S zrhLyoS)F%eb?#o$w)!7X;d25Jq7}#uHX$g)KTuV!L zpuR8boC6w*a2=+-2AsVykXWD!< z3GRjK>Ep&gPQN&{KvB96Z(^7CwK?fq+oTWK=l|L;Y$e>>RV-$?+j(stxNQHSe?P=W zzFYR$4R!})V1H53%PhIJn|JEosaF*7*@m&{ZSASvo^zCa(H6gm<_s=`#z@qyr9G!V z2@U+!CInC|z>I&svh!wP95)3`n7pxFDZOl48`jCM*q8w7!rAs8XYIzHL+JiI@A*iq zPy3HwSGeu@B)41)KAvMgoZ%0a=;a*2T-I>F*YTQsUwu%;>W!R<%gq&6w|0dEdzMY_ zX(#eNIYLNVPAl~>d^)sY(58NQYx#W1+JE|#xk#T)H)~{8@oiN*dYI`AQoGi5B5cP` zOWo^f1Chr6r+9D{buUFUdVs-5dj0X_YGyV1D+ypp||G^ z(Z!cvSUqpCmJKTfUt}P*ANSk|@&~!1&Nuh+``sE1;I41YK)VX){Dfnzf^VUxv7)XW z@EQ4E$*DV6uu{ySaxB8P^bwjw1?z#*XNEzvKrVUR`Wo0<#c1#yb{!0aOki5VWOTI~ zmtgJ#AlCw3fG7awANRd}JTPPsOjI}h3X-5PVk4W(bn$=UTzJJxiJB!&AjzW5h}?Ml+<@i+IO2;=P&j~X zR_F!YgqNY%p4M7rmpgAa28N*gkS#B;^^J>UTCxBu1jLluez6c3>FN;(C^9p3Js?sF zg>m||(B)w6gMnH!LNAp5EX+>1i!DZGvI~yb+-O5X`1N80UV@B@UDXNDeivRCa|l;$?5^Jv)E~W6?!E{G z>Zq@l4Q5YF`XIQhdIsz#pl_Q0jDO)?k+a(tkJYC)aL8cnw+`%U{Id%{CL4#iO3O>3 z%jDAqF{ynVQA-1jNzUoV>t+7V<*N1K&B6ni!df3gBZy0|uO6&Kg5LCL2cR;3;2nio zPmI>jHn12^4z)y$)LH4j8`PAcQiSj_IT~t1BZMpzLB zmK|t_s;|oWKcFboaPt~F=T zNk;HTCt1avBIcg&Epxq%qVrofd@gn+OZLuB^HcU}f6UJN&juHq`jlDNKAv&hR#au7 zDK{A>afMp`D97~w#_7w#9MgYPS%aF4rWQl;m0WMqp7jk@#$h8>uO5xg$SVQZ@AjM4 zs9q0Grl_)E<1JZCwrH%f#ZZOzt3MX5n&X!9Wb-Q9dL^M%k@h6tjsUmoyFy9khjB~9dhWE01!M_ag2a1<#DT*k=k6nEd z2h;s5Ts5?1!P`(TedUZvI{>IM>xA2653aEVJ6!Lm)r59>*cS|UYUGwxAY{l#B|AYy z&-Vw|Ev>H+wLUH2CZWRbQ{FW!4%-H8{Dtmx;Sk1H&bQ_O@9=+ssE_Z$^AFHI{ITg7wcMp%Yr4WUiaJC=Cxf8 zKcH_Q1JUC<+wSu-V;ZpigGp^)xfMmJM%cb8Qyt!5<5dSL5#&gwqGO0h@b9&IZ0sKk z`&N55e{*F&-#SchY5!7NXH$6ljD{4u(Tf(A^#ulo;nYv*NgH+~<6 zt^=CeLV)t+{n93Q?}6%(_gILVA!p%??Z&6QGJ;vbm`Jza!gH9jPYY#Y&u6HU{7=^{ znEAjhPH6_1EC4S@q4GIya$l$YC0U8rzR#brL~D2pkfSy@3i85(!R0K=8aB44LN?=< zvROYVS7(PY{{ZlLlPHq0?;jcq(eD@gZJM&X-2~;O!78#N4hR$fiN}Ohy9k zo%jW007z`M&YxYMFqFNhfxn$(t6EgnoWvAbQ8eD{8bS;EUM3!>TXtjan7u+{V)#p~ zRQpNKrf?I|`nO=}6aznUf9tI_!=itDS+bClx%NI9QW-3Snc6o8s50u$?LWz%rAfxn zbB-HUvjL79(Y>SEBxR`fx+g9Z+OmlB&rr{8UG!Zyy=mCJzA`s5KwQ(ob2F{zeoPR1Wca^+gv|$HZ%`KfzJypz&)N4qjDWKT)Kvc7YKAlu+b;3x~LFrixZQ6mVYSNPXnzu?Zw5ZfDtqALB_6 zvMI&Kvk#(X&b8<0u0Bo5#1w39G3O^1B25q`O2G?c>IkC* z*on=u4_cxNEHEjgC$J!%j*HC*brVY(8);CsyiRL*l=Gnh9H~kk;(vuB3~J$LL4g;h z*w(fAL-FhIg(eHPY5}hRSG~x0S)}c?vc~DO-QkJa1njRM?AEK^x;!9;T`$v@JPnqM zXHf#Zst24B_Q*hAcq4nc09eYM{2Ozw&@wV3L9H?hr~RXa%|I~%0=iS6cy>BerO(CG&JE<0hYH?5j|D%bYOEI;mXqOP-pj6 z`uHztC|igzI3bI4DsygN6M1IvQ>d%qe2NDU$EO6Frsr{>e?~pb1v5urwLBeL#a|r{ zuZ&NgUfwiUtIMkZi9_IZl%}fjH7&UJncH&RAc%78P5PoI6ODwXQ0)DUGzaF$*{2|f zu4?G(f#PA!lS`#*MsW_?_oblZ*esVr8Vr!U-$*#cBi2&wsKrTOc^Bb6Xee!j4oBPu z1~O?A+KmFlv;{gxo_`f?D=>ge#O#htMr^r?~6Q)4<2yA%nX78<-P2%OoM`B!gYE% zEM7cVoZ)8)k-#04{I>u<{2(H`Pi*h>vG-dlI0p(q2=@Kg{R}AwU5Y-0N>|j79!;NY zT|jqbxQ2?|D59peoo~h^*kt%(j=6K;e1!)%xvBYvF)L0&PdvGu=kI9%wQ|9j+6mpo zLO?QuVgF#(MPo6IHD>$%2gQ1?Ye5?VQv;t>aHC{_Jfq9hM>6@-MTcIy$G@EZ_g~Yn zQ;bUhOC8{}QK7q>0j8wl6%Dz5zQtWLU0x|0w}i{P4!skvTWWZAR{pjY^cb(~kHVqG z8UY`pUP|ayUp>O}oijX~$hwV^vQhKJHh>X5h);pf^#G!f{8UQ$7yz4w+_F8y*toa1 z1$IU90wjW@wYeK>h&mtJA%POMB=;6`2Fo9Cv^5%iwbv1{LeNsgow^ojw?LKN0bhh& zQ2C+L;y1U%BWa?uS`i!N8dL5x{A4C#GhAx&&u|ZX;fgLfw-;h(0DO`J7pavgf6Tua zHtcVfJC%HDES)9;j5lHIpyD%Qmq0`?Wh5KWZ8T}eZ+m`p2kFq$%q&A_Pz5bQK;aDF zFdAB768JSWQRJn3O}w6Yzt-~TSHr;p$j}*VlzDTKZrvkim4Box2tUUB`Ut(5s1#nf z2ji*C-lTX!@*f{7UH`v9UHtfK-dlg@X_kDZk1XQ1Is%!bp2y{$XazjSZY60?uJ4>L zxrR&o>q6($+eYd#aW3uLH}0>N3NrzK6q2TEMeQX>U|AeEsld~0aceknjHAlVo-lQ_ zJP!x$xl+DVE4Tix%=s9R5*^&$doQ~rr7Vh zHZhpE5+=vig{Ch}9BqVXP7mrv4+i6tjWH_kI#A+mF|PPLRij_bf%Us2Xmn2is!q;w zIoY5l*~6~`?Wr!|+QdH{enrEz#u3K*V(P41GmEPNw#~wJgNn_O{v53pTn`RM0BX_4 z5t6nB1m^hj^g+3NZJ*J$>f}XIzF(25O=LXMH4m@A$<5+0*BQ+_2U;#iYZ}CxmFZOv zUvhdt;*(1WkPlu+EZCRxlMd648_HEbWzt5rcIj1H8r!Da!EZVHo{!@JdUccm)hzX> zyQD@_Fpq8*#z$5uPm6~K#wfdcG&6He1+6t+ET8F`X9^jQtlEW7L1SD3v8hV#a50_i z%abvQGp~P-82+A|746FSDo$LT<7#|HIM0E1JZO8YJev=^c{X#r9<^t8sB83^v!`Uj zz36rajL~o}P=Ua^4TJRo&N9V4ePut5pRGlt@RJZX@6SlaVB3bd4?h%lsicH%Q6K77 z7g}J)VJIzF|76(ca*NcMai`kIv_Z;a3TC`zouNQ?wyJwxX8CSKGArl)flw|eMO)Ao zG{|td2=z6$oVJsXq<*B?V+$^+A#-p13^Ea;LdZ81n5LGtaz>O1BZ5@acSm4*0zsoD5}(BZ|6|wq_o!bgP?vI{cEm zfR=h=39>pD@?=Q?zdZbMmsXvD!!5=t7#>?+*c-Ah_r|u@R%s4BFLtQ9yKq%>R&z{$ z7rP#@dA8!BE`UEgjbF|Sq-ssmR{N&&p=y_ws3|af$SSZ2OzToseMpt)dtY%_SH)lt ziVz7cL7H}ZKY^7^qH~v%#U77MW}RVe?r>2Ze5D=NPGUU){_LV--VHoV#S`Rw8xK9) zw<+ElL5d|Ha^T23LuuPYGDAYCIRZVmk*p9fxhR)+EtmXau=7Z3yHU*Ja-k4l!|I@t zVqu;dRo2Sm-YPsd408ZRocVoO!<%jVzVQ@jg0=ugHadpQns~-o3f3^sT%F1_~k|iev(V zatac__>$Ealk5l_9f_?fUj}P*X3UN(&3+3YRw^G%6yie#W#r`gVUsnFjeu2v#3PY6 z{~ZVW21x~J0wr(mnga6y0cMUzCT}*F1(O2-{vR{L=EZq1Xkbvr9|6SO1+aDCRHtz; z%I5tgFlFFWu30enRCY*kre^0AFbMQ`B#PABOEAsUt|c(o|GLiD%ytE4js?QV`qOD@ zKLog0s>dZ5T=UN@D74hqpVN3;+*A`>aFx_-2=M=YgsJ%v7kmIcl?L*sUZDtzo4U;e z&XdZI3l7&z!3>T9nfebG93d4R9}F**R1Tc!M}zvm@kbIcW=1BK|DsUf0oa(B|M!+) zleUf127AJnZ+F16qQ!o&W zX&_ATN$C(t1O@T!@KhX>3>_4~GHeThdk-lPH)_uLA#mP5#sF$*Holt1EEH=o7x{Fm z6w-!v6UAT#fB?s4KeOf3kEBktfCQPV>^y`JXJ0_rqtP`Ck3_qPy6;B~5uj!%SZKR2 z2kLp)3QmS7Zo8`=Wx|w%9VIbI$D7I}PxP}ho0F(T&OoI-;dUPI|Nj`Zc zm=RH6g~2AtH8A!g#wyfZAmv2#{*CN8C-Y7WcDa(hpbyK#jXFB^_p)4FguTQtqru;0 z&Mz9V=WjiopLg+S1+orKT!&el3iO6P-)Qh`z%)YhsD|?>7duW?3_-%lf%Wv3DjC&n z07S$F;O0Pikb)9hi2NQT=QqD$jpdRQ4@qF(0^hfQ!H{<}134k_wn-Rbt_C3SqPLG) z8UlDHdx^A(`CCc;D+iK))W}n$fK(45&9lVxK>E~;tkVx){3b$OvR^6De581Wn=2i< z_!Xr#mQ~$SqYF8awv1ywb?}IS3Noc#OGLaD;5`)&+(TvSfe}Z~$hkR^GLwB-0>=?p z#Q%}V+Xq4so-g&r!k|LJ{|zC=52~aGnw6`%qbiwd0jG~mWNu5|kKGkx-O`gfOD=;G z?hFrF%i=GR0xv5&PD;ZL3Z1<%s6fw%Hd@+?3dAVTmq`Q7~eLWM)t((%0w7qi`b%hHF9rO?ys@DqGnL3&)qS5{+SxV2-Bn_*)g3a*|Af zPgk2eq3N6i6?ZKp@t2qfnr{|iHCOT{cXn!j=emjO+gt_MOpie^InaL{AWHh#rg@-HhH#l<2*W8ofjpexgJhMDGOALWn+k z??D97Mjveu(L2xot@qY@@6-Eo?_GD@4|lD5&ROf6d-mQg9`ly3-~Rrr%_vCHHU}*C zGsT*$P%AMhrsmXuc5?rEM(L|QSr?uwqH5c$!$!U;WXwW_k*4GZ43NrDPl9b=eZLK{ zq#;$)d;EP))lwNa859k@ID!eU2mG@6m>ai41fy?}U;vT_<>V=ej`WVJWeiD>X9RTi(!mypfd-8Q;B?-RCZz*0C>Mf}r6SrPZheP2v zs#bGD3duLZm>LD2NSCJsNSCn?@k$ImLr6ouSlF@9NU>H$@cW0G97i3a^m_bys%-=# zQoNmvdJ+zv@J{@=H&u06e~K3Np(5bVj4bUFV@~(XK%G3=qkym!snABw0eigi0$Y6J zYY*tFIlkmTugq%Si_cFCt1&TnLN>Px-zh7xZ`gQ$w;h=f$ox$TVul$jBx2{TcY)J8ZQd35?Hu2y!6F)xV@Ayv!T`wWKlioVSd-Jajj=`Fot-m9!il z-Jv(HnjqsnlOS&IWiw>l%z4=(%^sf?SJwe^`^3k0hiiSn38_VwbeOgyMvMik4BdtV zBVGur{Y+Rkll7%UTXUP$p$YLpIsRROa`Az89NPF|XEbE@?d_b|&+G!cliaFd3SVTMOl{((6C(o=QjN-NGY z(Nx-pYRrP1SF?42F^7o2eOiyaf7Mj7)ym}HmV5+@yH;XU{CncVH4(e}$dDv&UHB(? z_$PJ+cX5T=kl{o4Yot9q)p}d`XBjs2YNk;1^okBzQcNhDz-*1Be*b6P?*?lrS%pS) zg~g9o29OtIFH@3#NDp^Ep!|e4*LzRi9CODs?pg;@G^^P&FaT z1(R-;@8N?LXVZriqjqfNlfJB_@`-0R!x9n0WZk>j95tiN`m?7d)#+zzO_#`+AjP5g zX|;?iLc2gBD=0iprcCMF|67c1-pH|lc!ryD+{<$R0^@^=;k?+R7?X`LR=TSwk8eEO z>RY#3@S-#M4JQLNrWjvN5~U1-G3MnTs_lg6&Zh34OjNf$Nwi^-uS5?DPN+#7)~+%U z?XN1$&P`2-&P^~$sFrEc!u%2a#54n7w;1z$BU4~|17&!GE}*v%t2Q8mR0LsjJod+jun{Sn4EB?Xv5^GN@3w;*52*x3u><0#6s(BTA(fBqZMQwKJ;v0k}{u(*i6ce&N%f4yLvdL>nqDiKufb2YL0m(;DsK} z2HZ31?naAybJ_55S&X}}j8nV&Ap+MlB3vO4B%t|vRhU7#Me1jjp>4BxX)(=mlYF7L z8z6iU{wfGM!v%Id;*d%j=xh5xXl|r8G+;v<)G4_M2Tr;r5nYlT<)uj)y1Ex2x{DNC zE$)Slc1#wVki+M6;L0`owIvIZfPOIW<44q-1xe!sI0w%imbU)qTZwY{^iy%z6lP#a zw|}R?UwEt=jIpIlAt)>M!<+!y^t!v5Ah$c(CVzu67>3c zel&v7aBSyvPV_(@&oxeoFU^=nyew z->zNKgsJF{$v!BiN9I7zu9meCUAh_U*rZdTNnY1*z^D z-nAe##y|%4iYRvL$ru^69;_}EO|e`Tuc&E+vm9y*>!nqbP?=o#?Ukf@M&m60|Z|~tjz9mk=J~rtqJ+HL;{1={>wx6~f{@uLY z-|&S-N(Np0bNR}!1h2hncJeh@o{VYlO`bvvg)VpqB_=FBL#kSkk@oomfA$cRPl{~5p#N6Xs(2L9*~R<1$>e2q^zh(hGQ`G0Tl zM~|~g6kfpB_?3ek_)#o1vcd}^u-Gl>As;!sn#~RRkn`3<;!hK|kw$g+wBro&npZeWDHwBr-*;MM9o?8)k6bc zbg{dgB09gD5!NnC4L|wykD_WoOGD*QjEv3~;uZYup98@+Et}E@rDQE=j>{ zk;h+Jgz0B6>L=!@fnXc?`Uv~LM~~-b9tL?;D>VcJ*gF2 z91I$qMw;$1&Ov6KU9ScGvQf(>h3{?cxyG+uKGpr0-qqE`)dIvqr{fQslmF2(izXAB zeif~6+@D{lWBcPBX|?Vh8AnP0kwy5`VpzkTSMKRoe6Q^EB2j73dcIUy`AijeqDpO< z77}{xeFZ@{PcuLUX;Z~9-41zge%OR68f+@(Te8^VtQwgX1O*WstQrKEpSvRyo`9rB;w$}H5`qrd)wOcId3=|cu9BA?}!AHNiH zK_!X`-H{H0NyBRNt!~zb>9Zkz%Hr1G%rO=E$nq1ossB8L-bzJXR55^a zA=^-K*B{<8=C;QXZ&%LoWF43DydpZ}|>-XNhQb5D|P6N#zR)f*5=VRHS*6_9| zn8m9m2cqrnwLo-DcFg9hLR13&SCe9Xc2qL@VQzH1{=KMB^Ju6_pW*Y$nc~O2E?Qo{ zWGX?#?*I$_u(@VY>vA&2)By?w@emm6!T@WZkj&hsHxK{Miuo3E3Q;FP(du(_H2zy8 z!CZA?_$ZN{4}J4-onum1POap8y&vUu{WhC{DEEUZ!8*dBk;vxY<@bPRbgF(9;0kt8 z)UYn4VlG?0@hDGa7R*GO^NS-t9KXEPElBihIe;=*NL8$YhF*FbFMY4<`sJg0_AR{E z>-TBQtajx07$5!D>l(R>`Hk_b;mI4@!;Di$VV#m5c4cB7nj4hSKYWR=r{r!6kd0+^ z?8CejH3I3Q7Wp=1dHBWCTPd@Eg&*G1SZI*AU_X~*V3y)$LB_lz$BVrKATOkz<3$HA z8*u5cg|jh1+W&navz@EpYn)=)I&X4L;g~kfp6l2(q<39p4*uJi0j@=ZQ%hqxj5lRg zm^=}96hU6GD120IIC8?VPx7YCYim%SZD7!$``s-?)rgfZd}9<|gR?6Z$=_`J`RO#; zN|eZ5w72!%*}|FG>JcXeaFODeWA*?QPvGs5SzR1sDaR=J(5+RWCp`b%-33~K0EaBDoV7AlVJlx(1niCGmne*;{R_>Kk4Frjz z{uHq2*nCO=Nx|#mS7Te_EQecQ8-gSWf6qbULCUbn1t}7;ab=3B6FC?EuTU}#%mSya zJ1;Ra(+?kx7jn60A4Dmqo)+dsu>Q+K#(~bEjq2^q7qFYW)ja)kNkYlV$i~L?-IADd z^0%jY{(c)RAL^}wGM68Ft@i!B2F@jKmwo-r{WCAijXI8mP0rtMV>+DXq_DDc-!1`75X%ZWi)27u@O3Op0>W9v>exA)+X8x+-)?j(y2>* z+&I_1KRmJD9Go6KEswcA94f#6s)EeXxGcPtkFdj^V4hdzh~@iM_1@ReAAb5zZYjf9sdG= zqY(8SiH_Ofd|pL;yfk%y>o51xQmfKI!ROD|OKi&lnFimzuLe&Q?Iu4FyAr3V z=^bW?V^+2FQrcy2GCUuN_HAq;eJ&99z}K-m}O2JU+wspgx_#q zpuncZi%GwdBpGaMUYgn%7WW)GXqqPPURT-Bf`Ey;MuOLlbHl$b+ZZw^x$E&F!PyY@ zbFVMKwJ^%J&r-)Gf!m>tld2O(i-`FZ@jTEqX&yg|O{vlEg5@Zzf&MDEWCo*uZE0K3WA`>U1flw^x!@YnKRsWKd!eYsxv&M{hBT-<)L+4yP z-%G3&f1u*@SjZqx(c+IacE2Y)_ve=$2CP2y&uN_BDC?p`faf*@kiRVb+2G9R*Vt9( zWQN5jKTyg_gf!o%2j4AZ$WT5$bM|HpaU?sZDMtPLVU1h;%E~77qB}8~;p&+-o@@{y zKY0b~68920ca7W5SfB{PW3&1mN0OmBEsH~~h$9)~9~hP1VaMumIyZv5PsBj?b1rLc z^8JEK0;Att6p%Lg;spNKUd+Qlg4S0s!3b=PvGc)gj`cEgBv@kM*OsWP9{p^EVmtHj z=iz-vO#*G+8@AU^5xuSI+P9#p3#hq9OFZ}Ae$P{&_t_7%g92xj}CN8@@yb{pq%bf_v2#xWfeA8TK$MbOWFfqc{6nG z(9lH^W8Zr@$pqMfE|dlPonQK%Zf<>;95G+M@%8#Tj5NyoA+Z^Kmr+!6Pbq0Sy%WFT z@FQXo0O?$qn6|eD9eYzx{he$Cx&FrIoM)H0m+HGw)ZsB*M#j>W9hqyy+?z$lnRuV> z5BhxYKlMg+RI-XXIn5vDc35g~pw$T$w#)4L9*;{b-_D$Fj3#8)l#ki;&~U$jQ<;$t z|Jfmg0xj(YEdwVJw&O$9YS|t48y&z6YFbN*dTsE!5!853h)m#CWdHjv90(JA(K02i zt&RQ4YrSJUmaVY4q z;x1Ph|7z3xnp@$sUZ8!Fl_lKy0s)cdn&74l#?2@LBqkVgluQV7a@RJz8UIxblEu-w zBvYp2bR{83buW(AXLNbQ4Wke2jM+A2$cyTUb|W}b_0l6viX0qA4MEbx+r-C_qTkNt;2xXS*C;kU_r(6Tw^yOihg%u9yNvRH?MfIw%}!HavviHKO^~F( zxvV!3NWW)4Ni=$A#NH(SlBF+Js6Wr<)mJ?~opP~10nU&N#Eh(*LZJgtXk1LCJ7{`P zFC%8kkVVqU_u?#M)0TiX>l>LRGH@?gCiOXOTwSF~uQ0*dsL9ve7SpkdbN>`Dnm{u` zPvtGRZ&p)wV$9XE_?0LpBWeTaIeGD*aSr_48jgErr_n-YX-su==kn(y8GU zAA?AOZUTcIne`DykV|$t2ARA&wDNcvl)k*F7_eN!#_Q~npq?K?Shl(Y8;~4Sb0t|g z0UquA#yETAf8|a2lQ0x?aar|pds9?bP`72o)N0f>byy42r&=y9RI+dMTAO)&q>ml0 z7IM6e{do)i=zyg_*ezaEkPa0^P~*dkIEXTyl*v>nfc5;*k2%NyuzVwV{TC(p*A867 zw8JFo7^zhZZxU>;*FOcT$z#wU(yRKrw3e#XqB;HWuRk9D3_9_eX;e3!f#%0Q$CQ~r zZ&VXONn)Rf^^eBSrGKX3`LaJJwfY6$FAtv(Qe!3UGaj-;gikwfS$dCL1gyq^`eYzC zyYKKngE!^n6{hE*ceQlD6BFas@pH4`Hqf%MQ z1$k}xd8}bP{5IB>mJ9c^y(|6C!y``KP4P^6nrXX&QYKA|0d;DU=k1Jbw;Fx=ZWm5IxS%&Jg>m zRh>8`_BJt7>@B;JE_+HQeDz*DEloY=YH=NC&iWg0eP)nq6L{-jJodn1bV1OvklJgo z0|MVqN+19Z{eTgM#-tHpb4t+fVy8T6G40C+e z37I8dfYCP$(C-D)RFhy?+3kfe%#z9SK5L2Ez=hh8jy1_?_zcjN+Rfm5)j<_3>GonY zX9=&@AHn-6s3Svp$g!=c3A%h$JLRaOfgT>*2rItg6&&iQLLM^3bqdvjcQC79KhuV4aq; zvkgIbzW!J>ax3(CP(kSD|G%WVw7mDCSNqpCH=cMUd@@P$5R|OP#Gi+ax=`O9#?P%GZC2WRkM!qh9QGaf5}G_%te@mtbLeK7)!(yrgXKM9VqcV7Zb1((uT) zy4^YxMXeNK09-mH9y3K*-nRV&m_Tn%2s&8*R4IWwcSJ1VG9&?p9yxJFsuyi;)H7s=(=VN6_@L-`DzmNmr` z`KmKXcAp?i>=NG`j)VQA61hBf%)x$d_;kN|Rx8masFL{c_b|mPokRryua5QulhP8# zNiQb}!G6koil~w&Nn(scM3?6(Nn$KRq?hK$r+(4}>hFZxd-1A`AD3^|M{} zU_iD~-G#ZGOq6{Bj*hOC+k@SKCsN+z?Xp*p2Y94X-gcHs>(CU zr(n#}95t`FH}4E~Rceo&|DU!Pb$k7CaxQa>X6mselqPf2dC*6*vCo?#a-a9j-n|L` zEzR~gXrSscO*C`GQwZv- zQ240gbQY>Ys3faOmTAZjEwN_8&F5!+6f>o{CMNdoCY|OYS9iV<+)n?=uj`ma2Tw zC`_+!2tz>d^4*b#`^;;BA&Nviv;cL?x|cM(m@pFtFZntTEr}az4rRJhV%U(-6Gedc z8*tmiWaa)ut!qapDc5hxpWHv&pXuSi_=gYCcl+dSDIX>XjbV?_Hw~Yg zX|OBIPXT3<@FoP-@wQ>q9Mi5l$5{WfY?b)CZn3HRR5Rzc z+?buAe6wgn#PidK3~s{8AlA{H#-%g1lw;9hs&El*>?TUSV&;G1bG91vU;IeV6+<)n zlE%1Wn|)D?N4+xZ9`VJjS`!qRDWpC2YW>KJAUS3G#qS?Ieu$Y`xWteJlydwdYgcH4 zdU`GWG;dkgWw!?q9* z_y5!4PN*ba$SV{nH_n`A%|pO&8TNcO@=BB(@4u*hvbPF4QKE|*N)KV&p18JVitE;) zj5KLmF5H6Yy}b@OAa{)Eb`xuziDc#L%57jNGjB0-A-N!>L-rzzJx2Rc#R`dtdv}RUWoH`ZmjIZJX z@zj%q;OHw+46pDU`Eg3LaC(%@1suqF6K_@o6Qb7EIH-Jol3ggF{x>wqjlJ-o8t-&p z(=OLxYPtjKl-f~qndMY9!1Ad=fkeKD`DxEPcppRQdDfUMiRQB3Z_R_Yb>Bp_K<;Tu z^{St2)G_{aFZmyuRcEsPo}Z>pr|68Ws7~?~x72f%$rF)XEu{xWG-WFP zz-TJhNzwh+2T<8_SXom9=r9hXmJ=SMnYU4dc&Wd_H>K;vY`f0>v&-aXk0644W?cjs zM&-tcHYut7?=?mEcai0_yK2bra0HcCDo1Bq>HFj{Kn7dGjyUj&U=_wG6POymswoyx z@`FNsMM*65YHC~DJQ}2Nupi@#%e_uEb-l`-y7FK}&_nX2eo{C@wWo}#a=uQWhUyMN0YWSqd@{%QKocjou-YZI-yIZ;Qsddf? z;g_t-m;}Ffm#CBpM5kFoxO&KZu^KJ7&@=OZ7!tng&PcwZhl~?-CnR4r6N87BF0;pf z31XW*$Ve$&#yaIF3W}IIh`1^`7l0kxAg$Q8D3Qf3n_j~fg4qts1 z+74XxLVdwMDWG?fs}DujN0MRb(NWyoi>hCXI-`we86AK(kd9xpr=9{1!(p6hh9;P_ R_z+$}emrJod36Q6{{eH5ff4`! diff --git a/docs/manual/fr/user-manual.tex b/docs/manual/fr/user-manual.tex index 73033416..8b716f99 100644 --- a/docs/manual/fr/user-manual.tex +++ b/docs/manual/fr/user-manual.tex @@ -361,7 +361,7 @@ \subsection{Suppression d'un compte} \label{sec:plan-comptable-archivage} \begin{keshwarning} -\textbf{Avant d'archiver un compte de produit, pensez aux avoirs.} Un compte de produit imputé par des factures \textbf{déjà validées} reste nécessaire tant qu'un avoir peut être émis sur ces factures — ce qui n'a pas de limite de temps. Une fois le compte archivé, l'émission d'un avoir sur ces factures est \textbf{refusée} (le motif est expliqué au \S\ref{sec:avoirs}). +\textbf{Avant d'archiver un compte de produit, pensez aux avoirs.} Un compte de produit imputé par des factures \textbf{déjà validées} reste nécessaire tant qu'un avoir peut être émis sur ces factures — ce qui n'a pas de limite de temps. Il en va de même du \textbf{compte débiteurs}, du \textbf{compte de différences d'arrondi} et du \textbf{compte de TVA due} que ces factures ont mouvementés. Une fois le compte archivé, l'émission d'un avoir sur ces factures est \textbf{refusée} (le motif est expliqué au \S\ref{sec:avoirs}). Ce n'est pas bloquant : la marche à suivre est de \textbf{réactiver} le compte, d'émettre l'avoir, puis de le \textbf{ré-archiver}. Les deux mouvements étant journalisés, la manœuvre reste traçable. \end{keshwarning} @@ -377,7 +377,7 @@ \subsection{Rôles des comptes} \end{keshwarning} \begin{keshnote} -Dans cette version, l'indicateur \emph{postable} \textbf{bloque la saisie manuelle} : Kesh refuse une écriture saisie à la main dont une ligne vise un compte non imputable — un compte de regroupement, le compte de résultat de l'exercice ou un compte de clôture. Les autres chemins d'écriture le vérifient aussi~: la validation d'une facture contrôle le compte de produit de chaque ligne, l'enregistrement d'un règlement son compte de contrepartie, la saisie d'une facture fournisseur son compte de charge~; le rapprochement bancaire — manuel, éclaté, ou par acceptation d'une proposition — refuse un compte de contrepartie non imputable, et une règle d'affectation ne peut ni être créée sur un tel compte, ni y être déplacée, ni être réactivée s'il l'est devenu. Les réglages de facturation refusent un compte non imputable \textbf{au moment où vous le désignez}, et la fiche d'un compte bancaire refuse d'y lier un tel compte. Un compte \textbf{désigné dans les réglages de facturation} et devenu non imputable \emph{après} sa désignation --- la créance, la TVA due, le compte créanciers, la TVA récupérable --- est contrôlé \textbf{au moment où une écriture automatique veut l'utiliser}~: la validation d'une facture et la saisie (ou la complétion) d'une facture fournisseur sont alors refusées, avec un message qui nomme le compte et renvoie à \emph{Paramètres} → \emph{Facturation}, où un administrateur doit désigner à sa place un compte imputable --- l'un de ses sous-comptes, si vous l'avez scindé. Deux exceptions, voulues~: l'\textbf{avoir} relit la créance et la TVA due dans les réglages au moment de l'avoir, sans les contrôler, pour qu'une facture émise reste toujours annulable~; et le \textbf{compte de produit par défaut} n'est pas contrôlé à l'usage. +Dans cette version, l'indicateur \emph{postable} \textbf{bloque la saisie manuelle} : Kesh refuse une écriture saisie à la main dont une ligne vise un compte non imputable — un compte de regroupement, le compte de résultat de l'exercice ou un compte de clôture. Les autres chemins d'écriture le vérifient aussi~: la validation d'une facture contrôle le compte de produit de chaque ligne, l'enregistrement d'un règlement son compte de contrepartie, la saisie d'une facture fournisseur son compte de charge~; le rapprochement bancaire — manuel, éclaté, ou par acceptation d'une proposition — refuse un compte de contrepartie non imputable, et une règle d'affectation ne peut ni être créée sur un tel compte, ni y être déplacée, ni être réactivée s'il l'est devenu. Les réglages de facturation refusent un compte non imputable \textbf{au moment où vous le désignez}, et la fiche d'un compte bancaire refuse d'y lier un tel compte. Un compte \textbf{désigné dans les réglages de facturation} et devenu non imputable \emph{après} sa désignation --- la créance, la TVA due, le compte créanciers, la TVA récupérable --- est contrôlé \textbf{au moment où une écriture automatique veut l'utiliser}~: la validation d'une facture et la saisie (ou la complétion) d'une facture fournisseur sont alors refusées, avec un message qui nomme le compte et renvoie à \emph{Paramètres} → \emph{Facturation}, où un administrateur doit désigner à sa place un compte imputable --- l'un de ses sous-comptes, si vous l'avez scindé. Deux exceptions, voulues~: l'\textbf{avoir} n'est pas soumis à ce contrôle, pour qu'une facture émise reste toujours annulable --- il crédite la créance que la facture a débitée, lue sur l'écriture de la facture et non dans les réglages, et relit la TVA due dans les réglages au moment de l'avoir, sans la contrôler~; et le \textbf{compte de produit par défaut} n'est pas contrôlé à l'usage. Trois cas échappent encore à ce contrôle, parce qu'un compte peut devenir non imputable \emph{après} avoir été choisi — typiquement quand vous lui créez des sous-comptes~: (1)~le compte comptable d'un compte bancaire \textbf{déjà lié} reste accepté, et continue de servir au rapprochement et aux règlements~; (2)~une règle d'affectation active dont le compte est devenu non imputable reste affichée «~Active~», mais Kesh ne la propose plus~; (3)~le compte de produit d'une \textbf{fiche article} n'est pas contrôlé sur la fiche — il l'est sur la ligne de facture, sauf s'il s'agit du compte de produit par défaut. @@ -1231,7 +1231,7 @@ \subsection{Avoirs et notes de crédit} \begin{itemize} \item crée un avoir reprenant toutes les lignes de la facture, avec son propre numéro issu d'une séquence dédiée (par défaut \keshcommand{AV-2026-0001}, configurable dans \emph{Paramètres → Facturation}) ; - \item génère automatiquement l'\textbf{écriture de contre-passation} : l'inverse exact de l'écriture de la facture (la créance client, le produit et la TVA due — une ligne par taux — sont contre-passés), de sorte que le \textbf{solde du client revient à zéro} ; + \item génère automatiquement l'\textbf{écriture de contre-passation} : l'inverse exact de l'écriture de la facture (la créance client, le produit et la TVA due — une ligne par taux — sont contre-passés), de sorte que le \textbf{solde du client revient à zéro}. L'avoir crédite le \textbf{compte débiteurs que la facture a débité}, et contre-passe l'éventuel arrondi à 5~centimes sur le \textbf{compte que la facture a mouvementé} --- même si les comptes par défaut ont été changés dans \emph{Paramètres} → \emph{Facturation} depuis la validation : c'est ce qui ramène ces comptes à zéro. \textbf{Limite connue de cette version}~: la TVA, elle, est contre-passée sur le compte de TVA due \textbf{actuellement désigné} dans les réglages~; si ce réglage a changé entre la facture et son avoir, la TVA de la facture reste sur l'ancien compte et l'avoir débite le nouveau ; \item bascule la facture d'origine au statut \textbf{« annulée »} (sa propre écriture reste intacte). \end{itemize} @@ -1267,6 +1267,16 @@ \subsection{Avoirs et notes de crédit} \noindent Cette manœuvre n'a rien d'officieux : la réactivation \emph{et} le ré-archivage sont tous deux inscrits au \textbf{journal d'audit}. Un réviseur voit donc exactement pourquoi le compte a été rouvert quelques minutes, et dans quel ordre — ce qui préserve la valeur probatoire de votre comptabilité (CO art. 957a). \end{keshwarning} +\begin{keshwarning} +\textbf{Avoir refusé : compte débiteurs, d'arrondi ou de TVA archivé.} L'avoir écrit aussi sur trois comptes qui ne sont pas des comptes de produit : le \textbf{compte débiteurs que la facture a débité}, le \textbf{compte qui a reçu son arrondi} à 5~centimes, et le \textbf{compte de TVA due désigné dans les réglages}. Si l'un d'eux a été archivé, Kesh refuse d'émettre l'avoir avec le message «~Impossible d'émettre l'avoir --- compte(s) archivé(s)~: \ldots{} Réactivez le ou les comptes concernés.~», qui \textbf{nomme le compte}. + +La marche à suivre est la \textbf{même procédure en trois étapes} que ci-dessus~: réactiver le compte, émettre l'avoir, ré-archiver. Si vous aviez entre-temps donné le rôle \emph{Créances clients} à un nouveau compte débiteurs, retirez-le d'abord de ce nouveau compte~: la réactivation est refusée tant qu'un autre compte porte le rôle (\S\ref{sec:plan-comptable-archivage}). + +Pour le compte de \textbf{TVA due}, il existe une autre issue, et c'est souvent la bonne~: ce compte est celui \emph{des réglages}, non celui que la facture a crédité (limite connue, ci-dessus). Un administrateur peut donc désigner dans \emph{Paramètres} → \emph{Facturation} un compte de TVA due actif --- de préférence celui que la facture a crédité ---, plutôt que de réactiver un compte que la facture n'a peut-être jamais mouvementé. + +Ce refus est distinct du précédent~: celui du compte de produit nomme une \textbf{ligne} de la facture, celui-ci un \textbf{compte}. Les deux peuvent se suivre~: le compte débiteurs, d'arrondi ou de TVA est contrôlé d'abord. +\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 \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} From 6477cee511f7836a2e5b13a4c006ebf087473337 Mon Sep 17 00:00:00 2001 From: Guy Corbaz Date: Fri, 9 Oct 2026 02:03:52 +0200 Subject: [PATCH 06/10] =?UTF-8?q?docs(15-6a):=20Dev=20Agent=20Record,=20st?= =?UTF-8?q?atut=20review=20=E2=80=94=20gates=202895/2895,=20Vitest=201091/?= =?UTF-8?q?1091,=20E2E=20247=20/=207=20KF-029=20(refs=20#473,=20refs=20#52?= =?UTF-8?q?3)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- ...6a-avoir-credite-la-creance-de-la-vente.md | 125 +++++++++++++++++- .../epic-15-choix-autonomes.md | 23 ++++ .../sprint-status.yaml | 4 +- 3 files changed, 144 insertions(+), 8 deletions(-) diff --git a/_bmad-output/implementation-artifacts/15-6a-avoir-credite-la-creance-de-la-vente.md b/_bmad-output/implementation-artifacts/15-6a-avoir-credite-la-creance-de-la-vente.md index 350c0b80..8f42cfc6 100644 --- a/_bmad-output/implementation-artifacts/15-6a-avoir-credite-la-creance-de-la-vente.md +++ b/_bmad-output/implementation-artifacts/15-6a-avoir-credite-la-creance-de-la-vente.md @@ -2,7 +2,7 @@ ## Status -in-progress +review xt() z)qM)4GRoUEK(_)5-SrUS8u~0s_F-0Y8|hPtY1;z!QYf>0x??WrtDpl zF7IQ{Hytp^5o>BKG5xa6l<4vGU&Z~!J0^)EFS^Tr@IUo-khbiv%KU5Dxryp)REGva zkc+nSi%ig9svOr+9#I{^y9e`^c zMAw%tYXg&x>qbNZycQ$YkA3y3Dq-k9d|@nJ(&N!MM3xpu`}3!<*JO5{(Bs&z)HE|1 z^O()Tp||FyUD$rVXptz#-DC2uOk77s!mnB;jx2D-hS^z~nq+81*|@=`+nO_I(669; z+WXA2;>Y)*ND-6Q&~1Z_LG@>jq>c~d>t^yF4eJh-cc)}A)eD1+?Y<5^u{M1JctDx) zhVjTX15pD~JA@zBLk+{UO>)LMQ(smSK-Xd1R_=;kK38GKDhF1)o7G=pEJH55ROB=U zANmB=1VUNoXk?Pov!ODqG{)pQ$2HwYhr+SN65^cC9vyYs7p{+3ketj!C|Wap|EtqM z;fTkuG;5)pe&f0YL8Q|`zxUs)k;ys=j~cFYiTNI=fb6Bj9UN?>U%muMfTX3s_Ts=7 z4w90R4$=rE)TD<5ps zXeMg&Xj^O;f;(e~gYUnhRihG;atriVc8i)`9pVA{1OWQ&yajj>cD(&VcJ4>|;sGn? zaP;+YrlTVMI&H!Jyai8qpJq?Eu7$|7H&}rb{2N4=Y>L5%Oh{9#@H3HwTQ0lAtij0S^Aw- zeG@Rb{C=?4uaOP7tUe!x1W+|B8_!3~0oVhU^$_#nEnZany&;Y4RLjc39V)_Is;0um zrTjkhkutG2@<{mhb7Q-*nfG0j_icNx$MA!^!;&N3K5o3mI|$t^8d-3Utffm*A$N`K zyUUEC6mGq*vwtPCJlCSGyeZa7NZxLH&pTwW8$5p zszdM!@cqJHrkEt7p1bc_5xE?-Mq?7x;0}oUmv8{Rf zUH2&KRqiT-Dc@P6QYK1lreegeZTd^7PSt@}NZ+#_tCNSx?YvWbSMk$LLA(g=1NWHBVIvGitj=$WgsIWEQ;*^}>6IDJln?BSWw*;8h($;Bg4XtxFRy~v?a zYh3kRT7}J%b)$zi93;_%et<&f39g8e29L4i^-xA5n|bQH4BX-&J%^W0SKZr7&-F5@ zwPurW45h&lTC<5PMebsHuyFvwmqU!2Z{u0AFSh`#<0@aWFPFd*zT-A6+2j<1$k^m; zSMQJ!w&H}2s#RgUO}#&O7l7SgU6-^R(L7pP#pT!~v2ok4vRssEL6cHqjp!Ycthh^i z$~C35X#1J4i>q(dkgZggGFbHL#H%mznb)n-9va9Z_I@7h%_?l}=Q@Juhgj_KRy&eA zVm(Is_9j)!R!gw=(z#A+RkO;F%I@vsP7bfot1mf$Q1_U*rk%1UniV~Jhs9bXiSti@ zH+ONMljlp=xZGct^hYKw6|?c~uY-Bn^=>}P(r7Ge>U0^dIza9MCY~rg0haDmX>yk5 zvx)4_awUJ_tfm86>xd)L)&))-rT;IRtX)>OFVv0A3`_%eiksb5r`D zvVM-_7*WQLmOEdR7bsf5+-EkGY+E`>rYup9r5PiaUm#>1SKN|bu|yZX2uDFp>=NTJ zZ*?%`(%Cs~E($6N5oTrhkpf4`MUA%VibDYQx4Rzdwlj$PK4Tc?^bc$ACxXFzx7G;gQ5h1< zK>{Xo3%{CN{KwquSrB8rYFl!E;dzmxoS#@+*W&a;{8+L+Ydqnb5~YpD63#Ltu_u*p&ib3IljypjEzH3srcq#l!S!m{2#0MI`*z<@%4bOxpQXjZ6=7%m=4 zAghTgV(+t&r&JF)-%(Ja;R7*a&2f(udM7Y{9>9kI)7xV{8MFVTI55km6T2nWPR(0| z>ua8W>2;B3sp|_Us)oG8%GDrV`g8sQ$97arQ=C?yOSYs>&0!-qBDB8S0^Lu!*; z_0>W~u=s86Q=P2%^FR07eKGxIS4Su%LV|fI&vDBo59FxBo z*?VHKjN8md(^yPY5{MNT$`9@&3z%#oC_bU!2IqJ`8a{5NL0EOs4q>Xc;jyASKg(@B zuFiyPvYGx!&w*cV2OH(C_GW~-ddXn%zD!jesg0y2Ys?m(ni?sAt0WVK(5LQw3ys7L z1quDKSZrg(^U{Pfgsc>OeuBr{?zx-r+KBQ5pom)gkea?wv6rW8WRqqBZH9ehjD~^~i@GF80Zp#$LT$?8ckM zQytPwE`wq5BYz&>o%b&OvIgC)64Y(k%Da4N*?gh_S%tW_En6SuxD7L!E31Voo#atr zaf*-bbUijs@g9-k)`6@!m2$RyLys}ze=!=?&C+pOcFO&e?9+o7*|kYCe`)NX>|cHx zueQu&!M~aQ9ZvK!uJi$AUt(E&ICu+&`TdN`UN+K5GyAbhviea3$(MixhkKZ$DHxVv zgMCen2-4H?k&*xIC!cPK|mtf+REnNEtn;9sL>R!SWadOM*4Ym6baP=?*;%$9+QH;Ds z1+qSA7?HPzQRw zBseV#D?dMsi>tGlksXZZ=8TSv11<+r@2kcYA=1>?9XJ*@nkDE{+G<-Nq))O7xHMqw zc+6n=Ad_Q}E}k*kFfCe4yL*E^KJUkKZ~Luj5AR=uLbl8v9`C7ij>4jLcctfBNZSZ4 zLS4T%f241p*Wg?f?0wAb$cDgj*01lE+xxpy$m}HUvbIvqKsI{a;vEYkjE<_*A&Tpp zIsL7V-%AF}{$1I!BrwY?+4o1Zm;hiHDZ@?OHb3_+3T$1m#T%}ivPUA7OErvz@acgl zsd0yG`T26NpQqtV#dSHC`Z|pfHo#)@!uz=H`1SaZBmd5R8;oYmZ}^%F?Sd#h*~7DH zW}HnI-_u>6(W_Avc`brmcb0iQ9^FFeE)>?-_uLC6W^X4kCydc0P4LrB>+94b5$nW%BIMm^(O#k(DPlYRgQh`Ls>Pc}Saef>}D47^i8_-2^ zIDx$1NvvdWnO(3lZwYW|A+_7I(e0~cE)DS^5n27KxYyU(NL?yl zSrFtjK29xYr1#ealwX|8!;)0dR~kWEI~^%?>0LAHwjWn>2WARr{-8wg)75%x&M22% zDIZl%bvetba+DE9?T^axI%+ShDkBDFkj#f(lf;*bUi5a&-Ni$K)RVcylmcVOsn31^ zv}?gZ8?NB(2?!9DhPTA%V=^t2I)HBuYH3~QfNdjlWIZTha$Pze#*q%GhC;~Dl=B*c z3sN$8FyuB6PkYFX*%Q8S;3sIH&|r9u?4~Cdgm|>`OkQV3&S8Tl@d+pUt}}JUXk3aS zQ(#8A-FB$=r2j8m25}8p0I6PPwH%uS)5Zgurb-x*DxI>Stf9stXFEg&Ji_*lBKMRgA>ht`~(*a*(|*; zI^r(QRLC4Nop$D-xQiovjh&bp+o~WRs+b*o6v?<+mdY;16QJFh1|W`+K5QuXSr~wI z`2zhEFgoJ5g1yy8@(>k;v3IWlog{YR%$<)>AVouNc=ij{0DpMljWyaR*XD0UHrF>X*s&e0s&0nXC&_hK5`@{a&p+lQJ=m@D(N0xHvxDR^T30S}3%4c%r4ccl zDTS6qqkaEHeb-(f+o|5B9i-GK*P-5qBM3E<^XY)Tbry8kN0yG(;)lkwy% zpvt#PlO$n6nHPw<<&@`)Q3lt4M*POnl1Ro9XG==W^rr=X-Pr4Ofnls0n&mdCGPU1z zC#frfyZ87T`w}Jc2G5Sr<4)GtsoYBmhWain_NNYPpU4S!IP^TJ@!%#*N> zr(Wmo9x@yNoCm_Qo*W9t1Nig*Hj9)49UD^bBkc#`u%|(BIih61FnkK`LBhE#r&GW$ zpkKy^y3Gg0e6pL$`_bH+wu@imy{aU=53^PAcMM|)vk1Sze&a`*%4j-F;aUNeVf|ri zE;#u2>)hd)6603@Iqp-TBIQ?AxrFB<_$Q7~KMimTK#3-mcj@WwW{km_#IXMwarg52 z{LWOqRu`}M9o(flPEtOnLO1q17=0@W3N!d=^c?;mb=JvS_wVt4?gKZwI_PU!;Y*MH z^?WybzXvy&9<&m$29}_Irm;*s0W9$v9)=fxp{a5<9p8@K`>J1ldSa2sabrL{4M(mT zdjTH8LyWXyFNmxGI2!v_n-}V$e4Jw>F)XKe z7Ixt!yG#vWBq3{mm1yTMP-CQTr0XCoqXEDt2EeZaIQ`3G|?Vst6ZT^EeHfxAC6+y~6VfrE11T&q_S*)wQUiUjx{QwG%9k}*e zEB&RjVNH*ENZaqKpDMMNk718ymkRP?1Soj$NygFzA^KFEN5g8dKH~gn8GJ5U#9!!r zl4>bn)d`U9SCv`*#0Jwq1mk80Ny&r7a20ySV_Lhyh=R3uXBlqId`%-&I)I7td<2Lz zW&CkMN@H5!A>5Wko0Rl%U^V$W3dCtwg!9UaTw?BkT?Zw=u1mH5#Zf>1r$O~E)(Vf% zHWB)yrKf5Z`TPOz^OF5}<<4T(weB*IJEA8j*|}3p6T1{L;jy@tD;2vaNeX;&ZM&w( zcebG)z>hfM-ZmEZ<}f)22@~*$Ii~ANidIk4e}ri!PaEB`bKbs>+qrOZ05nzGPAB4x9j023g~U zH$)x;i8i<1*Lv$MO?x%aIN#FV(C&zd0$K|sy!;)5;&O0tr-Gas5fdN>0?*m?-Wvi+ zUo52)6^Q`K4uS|s$%9Nn{PPoXG#~&J@0nfxTOcm@K{UI81&<+vQ&4G#J*%-NwH56E z_Fxje)BwD8asX&fHEwSp)?&KbL$H0HH$~}X#%}_m7g+{H$C)|bQOTvj>RE!3?1a;o zY4Ys;I7aM-Y+scSjUO;{2&#nW{gq{yU+c?s!GecW`7+I-JnKW`IQ1eZKBe#Mg(d7r zpmuC29kp$i_>Jss2oC9o8xUUJiI!WUypRGnLrbC;9*;=@&S#h|8yS(b&%Y_6`fYZ4 z(|pxk2%UpZ;}RZ>Fqx+!o;tatgPCXg zUW1!w*m$rOXvDAAr%Js-0AgPR&qQ{+z5Xv#j2uGYgK=a>TYtypR5f>1^X$KTh=c=X zTB@;mH8VqJ^*Z+qUn>#N$Bgjk!Z55q&OYs*{3)o86W!;UPBS&+9iVCybcN9&UaQ+Q z>RIxJedE8;*x3M+ep~r(X(zc5SI}uk8%o8v<{CE9G`1@wuGr~lVD+RIHY2bl1g2SP z05x@wz$z)0(iDZ_tX~aZ86%u86uh@8Od0Bart*UFX0kKSQ%re{n_^r$nPy|()_w=P z)OC*9;^7@Zhu=PWMgPB5u6OlGy!5%Iw=qS~Cb;t0N_+q!i-Z{-_TQ9M)Ckf9i0?z- zh>(l4=oCAMnct**c@&;vj;kP4EIUy{X>d_4nJ-pxtbBJmVJ;GEu`6r@KKcr9PI@mB zb$@lNF9$eA9XXY8JCuUzQo&^OeQl%+hbJEv<=?+jB+CiqRnQd6k;!Z~lU7jlK(JuC zy=bu5SMY%Fy5`g`UEkDePqC9P!oat7=)nzfIExf`*0y097f_L96a+NPFT>?sx0C@4 zlC_I1bu1-=DXT0-(=FeHYu7ewzAurJ+aU3(O(hd&(;iHiyA;PB| zgX73^qIND2&fPE4J+()z&iWz*(=W70GUa8Fo|XW)g91?u&_-%^EQUxFuezQzg5JH1W&O?B zXKs!XL3T$_Z;Y~)HS;XkSo$2JHu^kGN$ltgt4YupWk%uNm3H-fne7E%B2 z8-px_HbC;g5o+=)8Z|ImGBG+e;%Mekipb|r_{ACuU!w<0vKG4MAKmYN{h{sea|Z|d zYstULmV>iU-`)(7|MV(V=_0yvh?8l z3mE6n9$m(Ckh;6ygLI{Nx2{%;>GFQ&l4Wxz$Lm3Hp!Dp_tW*5D##4Im#)2{{mm5~F<=Hss5A9D!`_rBlDIY3q&`tHL=tdh zdaeMdH9jN(ORYdbwEpB(3{e2&(>4@lGymosPT(t2IrKJSB=n629X1PubBD!tX~^U) zNcQ5|{Ksek8xKXtRlXQ5T_V{af^4@>H1GdTehQtl0fo8VLs}hi#*dB&FdX`Fa{?-O5e;B5E5xTu)s1AE^ znEk9YzUIbnrw-1dGHZ*8v!`Mp-hV0}$tidXmUo-KCjFV;1)i;(AObIujAZ5Qmon<` zyxqhnvE~m>=VO=5G`<9B5iVHyq!$DU*ApB2GxC(%M zid}F^?1q_*90(t+f{dCvg{wo?tK9JS;~N`W)@7`f;tsSJZP<`waC!96)e1pY4mUKR z9kDa`Et|VWUDwoT)OjE$ymi=1&|!whF>xQaN&^Icpo`t;MjD1h`-UI_jn7qKM2#_h z-W{tR@16YUGh8oPdHU(`rxfC#B@&>>-nGcqqkl0W?^*T9Nf!|OJ0BR->JTtY#+=3+ z7&q6kx6|u(s3#XZkcfk01#x5f?@Uz-YBAY%#r}!!yZ&`j*w<%>c8wm{``J$ykp5<;}%M}R(t25Bn{2cyOocjg7! zxT?+Qx&hh4&!*UwD)8BIbfP8VlC|sG%L$So6KPsH!Ve1a;Y@CCiL8OWu`E17iz4C$ zpi;F4oW>RQh~B}-r%Odria2va(Fr7$bX^ll++ABy4rZb{lO77ruWcp^#!`5tvg zp4z*T;H|lY{$|FVTf&a2OB+gkhcjb7eCx}iuEX!ijq6?{{OwcX!~y`OA?jPBq>Ow{C~Hs} z@SvYV16#4->J`FYNTogtp{PM5PXn`aeNv%hmR<_k7?ejd zls*;3q2>xi=h}-yz^A-^fjz9kcxRIO9@ibP%zCijN#iw;6|tN24h|^~&%`p>uCSC< z6Yxq1ELu>dyU1SZlJyE-k{*03aZ%mtXMuw$P?e4I%^d`Hh6Iz11CCORuXd6{@W}0N z-q`p+!{H;Zw=%ESp>Uwx>wFyiazG*I$v|9udi}jSGyi-VZ)_7!08xhoURfvlR~&N}0yG3(x8Bqr}=>pFRx} z8I~LXvZWMX-8e{`VAn-{*mT9-a3wC^_=i9fHdTNeL4yle>z|2{3mCybG7ri_c{wpJ z{#Q5;gNJ>=y_aGZb_wiw!i>sEBJK$=o|Ec%KoLT@SO!HpiSP7JeYgiS!_{f!7(|C5 zi$yReFQJ++@_=$MFS@m34;7=4ZqKqfks2@WDnxQi)i1)z1IGgCe5GS+ubEFl=H3&m zvJ8+6`M|BJ5ZimmI!Dekn;S=v)&#>TD%M0Fz=$YbE>x8Z`4bybpT*EQhEW&Sa;l%B zVOoUIF=sHG->YqL$RB-AkzC2%Q~hi5W@qzP@Spcyfy6UZm4A!lsZz=vDjl+}XS z(RF11Ncl*DJ4G@n`@$pg1cSX_Cr|!FLgGM2A_?b2RfQhjI*H|>6Icj&z-PLA2yj}U zKnI}~naQl~r-K`$;+|B7gP4{+FT2*wK6@iLctYzcgxi9zSX@Ez4jn)5G0uuDa_+g$ zN!(ek$#|oab+JNYE(S{cXf&q*r_nZwNyvv*iDX55VM-kA#6ybv@Lf9&1wdnLt5P>L zT}J&QcAP&Dt?Z|DZmad0J|d5g@;Oi}m8W1H9FydeP`i=EmI}}wQZwed*d9r$D=wY9 z>U=5j=7H^PLRtXdSJpm}Z79A z^wpZK!R;4l(=Bmc?oeQG1&lj*jv&paRPi@T@KF@5*lfzo265_JS!1oWGKNBvjH+d5 z=+RghGmi*fd6*EX7Qn)bTAOEMV~;O0XH9mnC0HV8J!VV?k9tYC&PkA|fmRT>oHQa~ zd*i9!6Fqm=U(5hOblXB-*`e3t{KKXGmXSwtX_LtlG(mjC$3{|m0|p?!58n37OnBSQSZZ3>(kbf^kV@SqR_tkEdl(r+p|&~DI1}6RAZ3kIqOv+Vw&bjsT{$U6e!E845 zh&Rcp$(~zgs2OHrd339t7-x7~hP}3vWE_UAYMUGASZFgVKzE9)Q=)Q|>+E`em3AA} zTLzC$ZQmr>Gs;~NB|}kdK2$X2)CzL*D}s_yEjrM@q;S#I<2(fU{BPlRIs3|kV|j-# zmYGYYl$fjZN-=vjS3#;q6Eavgx~eE;_!@6iJs+z}aVUv95F6R!LGGzM{^o|;B!`jB zHoxM}QH_EqAPx-W-;Ki`pOoF%-9}Ni0;v+76|L)jL7rk~c$OS=D}Ib&dYIt=6SqSi zina9Ur8RM69-J{d6$-?w@OTZE!|%$A=EcZs>?1NlFfKndCB zX(yyWCm*r$2nz2m6UH5T+_m9&LaC}Xr0#ZfZr0QKestCJhVGw&)5yP$==3^dYX z1@hAftY>+T(bWEk2W}A*a4I1SSuA7K#Z3uXz~6+wnAUmXvG?KwF|4BfuwV6=@^C1XYzcy;lzSx zved`A`h7lF7`DJSDa`6WZ9iY;7PBEV^`%tmG^GV}&iXPlm-O+g+%%7mmSQ_(xrE=U z7Q6Am#VNKNKL7d_?9Uul|0mqL7lfp`hyE~pL^ayc_{G0I@rnBX?gNUG?eG34+ndnI zH8((+I5^UQAwe;d!+#I};s_?O-CWhd>s&{#DyC|QV(cXg-eWdn1KxCOS@D&|JzC2z zOQmi2Kj-v+>BFj(cNqi*4>|5$f%Sbs0e2O)?00}uzOR2gec#tvVuy2tt$p49dOtSp zwT=x3Uw*>ns@gASM9;EYUm5@9yzF%Od3gPMhV|8-Q=kyHWW)3XnB*Tj`#+SV3+TK- z&8;uY6WPoG0KeBSYp)K#*)zcXO{AU-+1LIwv;!A+r|uAcK)q-7}*s90{3hpolT9VxL=? zu3lF9_`mK>r}bGb?YJ+gUz&9%Nb@E_)ZluX(W z+J%$!22eWd&sZXcw)in|IG-qFybE9#A&((z4l}cx6&V~v>TuIjC7|bG@H~wcJ1sBC zY&7CB-V45cxYt^YxqfQ1Vr-k0HA+6=v|r~bk*bBJ9a-oNNwUGt}@4OAl1YmTX$ z6Jk?X7SOgcCRmXV8tIK8(}IdkJZ8cY5dGElt}x<$tEslKBXJl5(1g!jl(1Ufv1f(f zWF}VFLojp6J3T>^hI#qf@Ni@L9BM;-+#>UwjTw4?O_`lo;_8wmXr~&RKhLgdNV@kt zixvE9@0rRclcZ4m-p*@rLhpyMm$Z8j8j=`rCO|ATJ;e>w^%8*9?(o1nI!<;hT>WRc zQa;Ta6R?>*mD7n+7YrY9I*o=AEt8Vf4nc#^vKzCLXJcKJLc<%EA?^i!lR1PI5hy6G zZS!P6?52>pQWrGIGsEfT%{Z_dkJ=W*E-i)=1Ho0p5;BGlC~3U5ZtM5Ql@gQ7x5;5 z(Dj);S~e?;)+m#kj3YO#SjuTYn{Diu)nYfR(!b3V1z6JI{wPY~wdKlmCUl$GBM`G5 z<~(3t)sxm09DP}mYo7SLI3*l>aT7Dp@?{Qazx{c`A8Iw-XyR%%la zzfBWj6G(&Mj!30wtaIa?~JL_7Hbi6MM5Ldh)kX>nQ#M^DBizisHH}P9+=zMh4S7E5yWcg zf&OpFVw=YzbEDLYHdFO1xM_^4F9#~RmxE)lGbyMG3-l)q92+V-YDV2~AnkUyp3 zOvK>Rsnf9n0O>g#J|J5qU=dDazB8~t7!ruYhP}i593Z}*N>nk%@~K@F#K_kK_*37oO5#MwUCu~M#<4{OgYdoNj4pn~zJ zgvSDNF>u}&uQs?H4{T1(1xO_V@Ebk5F=!tvxoc7HUX5dY`!}G8ihUH(u0W4O1pZPP zcIwO@q|hq#Mf9mZ@OQ+E<6z||v#isK+%`)OMyS!86O^mNizz0gX=ezZri9+bQr;KX z2H8s?REt^FATZQs5UW+Q1{uWaq^z1;4g+ses-`1D@`>|?Ly>e^eQ zXLFY@Im)?*4h4)4ZWLoxNXoU`lM}4U_Ab9MVL|G5AEioU z9?KUj)->d&=Ww#>vtSr;>T(SxP!r*V^Ln9sq#bSz+hK6YNcBOGy&ij#jMSFHjD_9u zT2Ai*Gf?<4>zoxbj!*&vWZUuYGgr=OfoF*w2rh=XNEVbtxq9G%akh{B^(>9qQ|t+Q zC?C?NId^=x5ae#C*r}qlFj*v2=y#>D=KDRu^k(qe@a` zkgUE`dF`ceV@BZDF1|S~xWrP}k*oRh#-JLXs59&v{7(zE)^?G zbk~g2HT3~{CC`=N>)R8 z-Yfw+^BEqGN<&Qer2PY>;acF?Tg@URF47L$0{ucUg5$vor7wZ((g@_-4a4$+w3u4b zfux+;3{5MXE!o8m0DEs9`gLKRX)2^?_M;sJK1GGe+UqhXkRJ*Y>8vppWIVl2Ww;|6 zZo{F#&V@cW$-iN1io>`SxLhQn-o<}VV}73Jyr~_g+8!r+8IkJH7^zs^3`MbV;Y=^0 zMd_SwqTUN*)b_cDs+nYEe zU4;@91CN!~b-n8oI%uOwe3KJvP6^T~j2poI(JUPkKbG58uK0ZWW#a848SRri)i(2$ zuU)gD*3oULbxvjP0)1DXwZN24+ZxN>;`E1Thy)@4NLt$3F-bH}TGaX3MC)Jgs@0Ze z`oPxdog9ekpB7n~Xl@>XFSc@10})8V1dP?krOs`;p?o(&vP?8Dt_B%GH##Mg>DfFQ zn&6MuD)~GVP>F&^!E&>GI1LF3+S6%@ETb#G^dQeOMA1HN_(YQtb4a5&;|?uZP)6{; zEfYxsCghluhQg6c<~qJ1tOyx+Kb5^(i|MnY7N!Q94)4)6;U$cZ?<6hl(IkZVJRyVy z>v#G~tHt(+L=9#;@#7rLP3e%43cBeSd+*;VE=>9BKBuJs0Ni-=aP8pbvz1xGSLDv1 zE%3x94UFQD#{ml&iDqvG#d@hZGt5^+HA_*zln>eCcp%~PAsiL|y^uDx)~+LSb$v?5 zq<-v%tpMX-Tj4`N3sIf(g-9AD*KE#_8pK4T5kW|*KVPplbS?j@GkIF9y+B+AO5_gsl!v6?47};oLx2jI#Efq@_(#v<9mK;};;|>lm3Gtoj z876z|<0NnxIAx~$zUmdc8w<0ei6{NBEo8m(BlY)fg4=P52q(*$%Cwf-X;L5K_{7Z$ zFUKG1U3#d32J0f zX}-UWSgBdO;fUj1rpgwS>x>-wrp5urju{?Bs z)UuZnz=+d*?}ZHU9$>6o{u{jO-v0j7Rlciy~Id*{mzlac-yVX=fY?=`TJK)K%rE1Q-2N#6~g z^u~C>^tt_diY}({Us^OK^p&cbX&HH^w}~!edoj^#ONG4aIZpi8KOx~$7P(SE!7gfeKzv*XBvOCLI?Wb)q`ztNm0SQG*m(pkl5Z8nKl$Cc_;xC%RTQ!aw9Ji>(=cA z59_at8vY)#ses^r7EhcW%TyY-u&)(x*CbbIi{$Z#-pC<;;$m)=-hr_RO;3uqm7`d_ zM+w0v?K&$lM37r#+7C_!477f*)t2|LWmj!<-%c>7=WwK7y|FFjea=hIj4-u^A^xK^ zf*Q1{Z*j9=?z05cq6D*RG~B&ubQ`Vnn=N@(aQy4jTzd;3wnd0b7LU{Fme_A!Y=6D) z{IZMTqO?rGXjI!+`K$9_6g~y9N^mm{#noIRz?B?66}Ku~WOdqp#m#MALoj_IrW%sW zM`GgUnd)S7dUc?BI4pRLvM-{SN9a;5f87!OsdiB7n+O8%ITVA6#b&I0XqqID4%SGYOkBcf>Gd)kN&Rw%XE}&Qu%^XGQyc5XOI6xkg)j0D zeNVs4DQ}tCNyM|>>oVCvO6%iZa@$DCK}Zr?YL>Mtq9|n2ZO=n1=?)Dyq+~E~b$S|3 z<=WoK1?~g9yKuDu-C`noOA-ONiFaVC$7q#&Z^hMHvkJ!R%+yW(*3p;{E&&f!&CKlZ zsam*n5SOBOk4a1-;6Brrw;U-@MU6!h$9Y%ppUwj=r5`Z%fB= zBMN6p>{R#gW-sb{6KXt2H3JmUTbCN&F(`Yz?2`d(uZ~H2-#07u*V@=IqeD8O z|FVp3k&3<7r%_@Rrn3^i8p71pYhQL~y*LzVXu|E%?bdN|w?fIq-`#Rm=bmiaS~EuO z@BhL^X+>Pyz~Arl@nNkEP$!ff$B4Pval47ELo_~ye=9%<52td8kYOf z6-P0GBbGuIt&-H(=vF`NW$u%KcfQgw7d&JiF z&{EhXq%qHHpEV`(LB7<-EM1#REhyAQxr@yvJ=@e5>(+cp_h& zpnJq~f_nz`rY9x64eh4^Tn%h$C((F$C2E3#soO~HZ*f*&LO<i?-WcLX9Fy%>v*sD-u0W`_RFLY77Ac#VLc%|snaRp6 zC5_EJs8&wD^|q$~Xk0L*jj%t|*T7>rFHT zB-Qoc!qST4HJhqCM-h-n>pIUl5qv>ETqIg?AkN^jN-@oTWQ`A~U+~nStr1JMZ3{&vd)X#WOgN?}0EvnHw-r67DR z%98bFYOeX?w+52jA{B4BS~n|Og{mG(;7YMMFLts?r+Os+OE*T1R-y~zT0a($VF>#j zrnzwWzLfKG3F5!3TU^RA*T0SRY%9?8*Y|&C`}WS;{7LcWm;X?eu3MlJui2H{MhOJH zu8D}K1)$p`F?Roiq-z9JR1Chh!>)KNa}m2;Q!0kL9~0UUxeZh?an4d{ygYj zv)4&Cy3PiczFnyPL7h%v9lRDOEte#Gx8R8rfUbrDkPg?381g#^l|+k8u%e)OP`Lo# zIvE?pY@Uq26PeRwYx($W^d-t1MH0Q?*RGTMsWEjnrDT?q#;-myt|6Cg)Lisb`0|`M zN^TzZIqikqC#g_tAD8y8wl$UrHibY2sFh6rbV6(he*V_fh<~nBvc6Wu|5D=4U>?0S zRaff+Qey}5Q&Y`XHq?Y!vcC8$LX@m5O^TeNWuHE=ba;H_r4LcPqy+qEW&AvI&~~J^ zCcE`ikg``@FEgbbueLEU4mk5Pu@@J)=Tcf1sy}Ua_mB9z6Tv%ch8WnEJ zbQJ7#2Zc6f6Hj7A%Z2L5)v%eDrme~)6asXBGq8xzOq*0$!H6^%)U6F$u8SP$cBqDg zXr!qGf{PwyS=MC`3TZlNd^Z1tg%2OPf5_KFD|HTLdYfX?7d<(`g79{MdPAx$%mJ-2 zp%JZ;aR4$QNd;38q7r-v+y@vVKG6hBpPOKK41sGRhQnKpKa{`afNtfof4jq!ep2oL z&C(T|W7`uCCpzk{i=<0M&;!A?8oV2%GP{D7{ zo0z+$Q2G*#!;6QtZY~2O^ezw-iU{DRA|Hd{_*qc?!^Wj~CGLP8r%hRv z6(Z6&Ji-WtHa=gwJk@FcOJr!nzMa~0%1mtDiXOi}1!6Xk6)2mQrI|)RehvmR* zxe<%fe*onhVxI+l0U25ReX<(RN9f<2W8cv{5GmP+6p9h2C*Q8d#gME^Jb;G|Vi^V$ zypz9HvihGKOvcK`#${!sjE4gVBTw304ICT78apblgBzyDWpBfBgZ;PO5*3UySk;kQ zlE!zl?x;dX&7#e!_~gAglmLi;Wct~4J?>Vp5&a1SrpCQrsaG%?q>25$f{I;uGBaM( zFvsbiW1M}yEX=E;0nOnRI>w-NjWYbQiO!cA_-o3qVbMKaV3D$!)iVy@{ar5S74xc! zA_!ojs@KX3pra|QV_lp=Ng69Z z##d~7(9Yv_*U>5}1M!Nr1pDZv88dLZ2a$#v4x^TT>&ZGU&YA6oWBZ)7eMmoibBS4? z9So6zOqDSY915{IL}ma~VW(wg7%3N&ThJI`Y0#QtVX9R(H_t{N4=?l?WF&x%8xHz< zTNIl!85!$PR!tcS)9jNB3o{7QiFlX4O8;57vl|Mjvo%K?3@M~z*Km)bqLt;ukT)6( zyozF03>*B!5%MrPq5E@nT))@*>$}qqJGl@sdw@mTTM} zyZS*SRe~*K_+co2QM}RQqTGOMSQi_o><0~tPQV~Yq;m`?7bQPN2gAUc)&F~A;c`=w zf+MsPBwqJ)ioXB-n!-pzssF(B^Z^{P$y(WL6T*=*!XFiRA9YE4+f~wxagF*&CWQrx z$jE(7@3(@tR>-o3zjVNpkYdVPnuSvHA1D$NMHYE?{M}nS3ra3X~m8+v>x0y-5YT8y;PmW@w0&NN~~@9g%>q~f==}~b3bqpXf|?f z!!m7QMi*OGGU{O#ji#KeVP4&dH0_q$EaPfLRw&Ixz@_co?dpsya`X`!t2*6G^{nml zzuIf9U)pNLl`fKi>=KH036>{4y*cU57x13dg*5=L;e=Ie*e)!^hIv_~+P9p%jy;0+ zLxJn>K8pI*Qua@wyT$Lc?17$79N8td)5(fHQ-$4`rW?T=Qlp+rdOH^@ppbAh+no5} zzJc~@7g;-MR4TqB?^g+b&Cqdo@X<$%JjyDq&_t|#p@JVF)Jid+to9^!fAp@uI|VXD zx!?i)b=hdb0qHvM;*U$ap{V&C0@@5!iBzt#OSB>!^04t*(gc8rF?mt7u{bMGZ-K17 z>4B$ZC9#oOjk_fYH!*$3QB_JzKM4k_yab@7|aIR-i?-K zAaP%=E!NXjCl7PG3fz-qbO_GazgieGOCW&4BV5()+vN4f*+~4JkAGeN%3 zS|#SE1wTd z#_|%zv-@fNV>(MT)uQ-JY#bV&s?=iKfB&?L28cl#5wHxg4({DG53G*AaCEydYwZB7 z;Dxm@Ijp@H58deNcfxJKZ;=BPm`?<_c%3t+yXookWjtgl8%a_AsmhPGksBc*g{oZ@ z>w&VyvI^yJO{~@WRkX#DE1$3lW;>x3`g!hXK55`dfnUC8l3ti@&#BlP%5^jdz+jt9 zJS|4>s6DYBQ@JGzd~j($1ZX(Pqi_K7Sh@8j;~B5<9FAD98|v$u-Ao;kY(tNLlq3x&;K*TI(oVyPM!vIp9yRs!L0IN^6s8oRX{l|6IqvIAFDNLh||F zbgUIZ&xa8$?gKk_xfEWM#$ig*FJ`eFoqJvUc5an56a;;TolQS%*jpKTe~%(VuExk|~kypS(sqN&AWTdbb@l*l!=axn;3lpgSIYZGELm53=4 z9DDOn+yxX837lSk6DNn1v-}X)2XAM_mtWDmKiVgyBL?R`I+TcBGQtukFWpvOXT^ne zM)RrS+|Z(A!cGU_%HV9q$iu7g^uh-9UM*^NvD(Z~_WGk|s5+O@M|RMc>B1OwGfgV`awkSlGbqg4T>))jF*JM3A`F zN>;X22E(OO%2q2L8VrH~pAe1rI^-flxLSTNJjyW&#ik(CJ}^QslSzMW8HdM8WLWrq z?+hAfYmv?v;rTSbHjo?-GccFc#38bR${kr|#>!d7& zbDtqKDHA6jP+7drY)F!+eVeagqY2amE5bZ}8dBr_qJO)g;af^s=KrOR`fDtwbw{}o z4&dQ9HvKUL+v1y^y;+dFU?|0dinUpy^mhm|7Tc*S_6DRMr%HTcJ5B z!DCuTL-%nm;qN~?RvXu&=>1qhh>Jhnn=adK6$v5Q`piQMs9$&|y}I|!D84GHb8&Si zA}WBvRf1$gxngyABNw(>^M#?|sWrVSv31bUcDOWK;3v@@&e8eysuq*=L0ti5sKcAv zit>7al>_Ju^q!ue8)r01*vq<|)>Y2cDSC5ug>5-;Aqqq(+uA%m=2U1<-H{9q;xz(S zbW_J~qrK_Q8RiCLc|YgDw~tS^o!y#D!XS6uIvf#46sF@BcmWzfkrcO7**uarst$2U1VX8u$wNjl z2vuq3K3FCKEPsr*P1#%sh=iA>Z@m)CRc83eog{Uqk7-kT-B!s3N55HIV|N#uX6GZr z_eC{YS>sPa-8M3}2g*b6r7QyCVO<@c>Bu}?gOt&8PAxwyjx8bX9WyW8c~%WReX2XL z4FQh~vE)@lQOsDgBh)$)C*=7Nnzj2gtny1fd~>cuw3HJtH|A;9^8 zns_h*)rjuKePxvOE;Q1y_iY;&c1N=DQwwdQCr7fF2;W9e>2*5P9mbr+wgCH0bhm)gH2Oz-Q_ zOK&0uRtKy>ku8AIV_4RoPoV!2oC}?>?P3sQ2WhoHNA~s6cevN)O2NCo-zG&(2{c4~ z^!z-R@^hs!TuR)W&m;ohwxUkOPbmltjeX7xwlSnG+OS3|RdFys&r{r)vpj>M^;8JB zSsNV3T;sHO=TI+I*F@g!$6zAx7cK9SDe3ioz5>P&0X_Uud!u8&Xtm>P;@}ToHt#rB z)?`B#t_cnJ6Z#l}go9Hm>*V3L;PBw7*;oCKT+~Eg~cR*uw%I!Wrri5!2*B-Wd zP1G!(=gi*cOR-b`oDa$5S&x<<+olm24@mPbXg`#kz|h|GxNvHK*7VT1(UPw(2wpH9 z8UgeH^<*+lAMKJIlakKa)~bF~MnYh`#CCp*?&#uberNVlauzc_i^xy;A$irc_`W!L zuCA5GN94IOVilmOtWr|mwxy;Yv?(}$E?-plW-Sr!8Y)VGI%mH>b+8#X0$J5*rb-Xg zyJi)t>H8|`bc=9M#;nE?lV*Qzd?rq7Spis};7tYv5Su}?(<_)!mB-ke5Y;g9^wqJw z`m*HIf2ib0GEv;J;9+J`BX%+rkgdRf$k3N~TKo*><7Fy%x;2XFwa~Gw{l!B^Tl;^yf$1R~I-0eN{WO};C?S;=H6#(R= zRUFei@Mbiae!~=z)zgRPja1kg=YlN<#k(q}k!S47HBm}G?Ds;l1rnPy>t=V{I>Exa zFf6f{R+Fme+{jkWH=||JgIN-CF?OT}+W!lJKz+YY-JTQ9t6wsdF>H-XrJQ1$t9(oI z&z={$H=5+L)mPFo+HqS~nJS&z>YTw-Ni)5FvQ|&2Ke=UADde376o;f*umVt=g@toc zU8#9pw@UG~i8;x)Y^cy(594WxC$m{YI|NswQRKn9oWRGF^5mv20E2 zS0l%*nI$EnJE}u&k~aY|+@p@mWcbi?nr~^83ci`4s>^#T?m4w@HV)6k+&ts>{Hy+d zK@R-*xyo#FRW`RSxZ=={g}=X~W;S&|nqNV*4KsBT{90qt_jj&%_0ACkK4S~?E-KnulD+I|)77;(1DTuDb}*h;YDXbvhCh#h zZi$;3TqW%4G)`=Ds4zwTPewU^I3C+Ua=5ZHosx`)m89L5bU4--wzxOmRU0ZCGxAM& zGgJH?&oFx8Ebdrxk6t*p=(A>w@#qBLaB=pu^gzSh__rh(#T&e+?Ah8*f!K4C9OO=Q zABOclRj!_tg0r)1(KcnrXq(AS-7U?Md7}7WD|q+>U7;*TP4iAxSgNpp8fCllK^+!Z zZc_L$TN>S)DRtYa`~2xa8Og;nH|tClS7xNhuvt4b&xU7G!3~t^+dL&BDVu*=ZGyz6 zg@ss~#OB8IV84rfTs1pr@ePS*_CzNw32y6r_B5k|8=K8m!E@0Jp)P$9ydwIzB)A3C zuBQ&&f*zGbXF6q<5lEbWnl-0wR)?hp3!xrk4VqLW^!cDjSVB4qbVLrO@CB`+kK>5XhP# z2$TyZ4%kHCtjn<}E^l^wkSs2hHFu&Y_4&PpJGGj2_vnmIbKJ&%8XnEch^*9G#%r zK^=XBjUMbQJDuTwbqi1t{roIHG2`v9zBz&$r&q1j;~}TrLq8kwZo`*gPEo;*_s;<^ zM@afC(CJIfxu8z3J?YKdzaAE7vwGz~D`u?a(JXiX@+N~?K_h5a@UT<>rcS6}2p*Df zSQg;zHiW2^To(pBwc?CdAv)EH8L&m8@G>Oj`e(xXIu27~8BM>!a*aud+kG=keiYF7 zhi=rANTHECrH4P60Rl|~en?30>HmrOdA%8bisFU6Xd~=JRJ>-@$@?L_>sG1KX+W;V zM&nZO-TD}*9ez*y)~(8cKBBU;?ZPUYPs+KLnFwx|#wh_K0Uej>DFGUPzDBSvq*;UB zf=a|cEa(`Bg=yAsaxoH|eJX@$Yf$&D_1;Q23$c*15X+FB)_bM6(NGH*E3pVVqWR!N z>*rX$!`j2y8(&JzIxr)!F}>y48(Y{+WMOmJ3PouOQZ*Jbq-q1$AlZmEQW!Gfgc;XY zcxGp3H=c{P^Z5|%G2(`QpV{fxx6pfeIvvn9TepN=8`w7uRnZKfZvhYG6-($C7ZXDS zeS(ehk|k)A9w_SSHN)KuCI>uS3=JszRE^p}j=uRWIU#qo7h$P&S;WuX#{83M8i+R_ z^sW!{dbJbhXzbx}jymN{6lv4Ze$0+KM7D2sqp5M+ z3fEpm2F0|#1}!Q+QGH4b;u^t?kP-KcP<}d7PW8$2%1LRa#hQ^RJsI7D>12R%qkD^E zcq(K0c4%=rtelz|L93AW_mx-Y3am2kkOHH`kc|@6g*^p=a4h6qzEvuChHr(P6B#@= zbPH&>ir^A|a?79?maJTmjGS4q<6#tJ#WA#qQh1(PE(+BewII0^xJ z&IV>JMAPa1UGK+jO>8sOoY$lPadBKj*V5t?^~+|hc^-uA#K_*a)Al^K{K^iA;iZ7! z4|i0kZFouk7!5pXPaBVp7mo>98E1DFGhJ*kB_m^h^A^XLU_`V1L>bNc;0(0p5O-@f zNtq!%9kE$GYt&|AqEIO%I9A7*<@>%n$N!cvJ36(@K#@sC{dhU2q_UO<= zyGq+KB2aT-)>_)i^Q83?Hr;hUq#Ydnk}D`AZkvjecFBfaQd$QxaL+CO6kb{T@7GW1JM8Kv^JIm?^bQ%$@neS4v5s@hV-K|71EuRb~U z-44EfmP(gk-D*^dSAy0Tja{GJ70!LKOU?2jCFA{>7C<5Uhv^4K>cWjF~8H z^-*+QtJTXG8#=G`8Zwwe=zK~skcz>ot)vHzskjG@shFX`Gzu?6Qob&9J_M1vkJjsd z4AUza;>+-P&SNSg1|U99Y$AOATVeAbh0i~9r&fGEac2(Cf8z#7`vkdu9$5WtnV(mj z@h7R?2jcTyrhIVU;qzXvHd@*6d2dsAHVHNM+)5a@Ooa?wZWSvn)On(mM*nv4`Nlwo z&wB+rAVh}G!?cs$V)%S3;Vi^L&O$7ILwZ{8mEuOj*Nx9VSj`^9=Zo1ebz4<@ek`2g z^EHzm$*z%8N9n`YOS{L{;9NZMu`}1>@%nvr@)>yjtkbV=;XfI#w{UV&8rRXO8kA9X z?)3%bYwE2UhFv^XqKL+FW_Ud)x?QV7}ZMI}{&s4gaSG;pf$ z3IWNTSpMu1D;yRh-Srgju5(g`jf)H6$02`g~2w&13iqc>t$beZod7S-KmTufji(*fMwRv{p=UX6O@%3$dn zL#1?erCnhup=P=q#lSR%c=Z>krq_q+w_|A-W4s;Y#<}h)c9sQ@+WhH%Z|vT#=XVI3 zjK~HB>NCHQ-7`kURy|F98p8*xAk-McPa>ACZ0D=n7GJi2an0_!wh!ZVZ6D}m2_;6n ztxwTf4xRKbA`lq(aurx|KT98>dXW zPi{RL0f+e%v?0{IC6c9Q)`W?cF_0Etc)!y2~ zVcy0NB26B<+FdGgqfwV(b1|B3;oBp(@DbD@XmIo&CT|eRSTiZ5h9H>=O>q>7s|R{f z07sqnF3$TA=fD5TqWM+!3OOCT%8nyc&*%3(!7W$SuKlrVNv{~3%Y!usi z;Vbf9=YN}*j1op2F;S{)A(gsWrRp9wqqYhW>(m<479rVl+af>t7Pe^A==~NWRZ=NM zEG63_ujEp;2>sIeohqB}n>gsa9J%y4ZHb>kg;Z<}6CFp;P#XTQNxER}MZDte_| zyh>|Q=T$Y3B7wp=@G5*~s-VD@Bd<_SBnm`x=6}^>U? zD)b6fB;+$*L1qOKHI=PUWvhHsMemg3G9YhA7en-jwnW-fIz@cls;3C;t$`v`O1Biz zuaTeVI7roiP%D?F-wQuA{_tuJJa0Tgn6>i==9(VyKB8})_BG>@_O-|*L>rG{=bIv# z{(qpVR^Y4guR@%zXk2WFsCyr`&Z9u*c3mP=n$*?)p1Gv`=ua=ygXnvu9zAL>4~gEp zln90Jk^q1CNCy1nLwD(~)YGG6wP8o8cn#$>UjcUJYs!8nDR7uG>)rLR(}Rc@@Fu~l&M^2T$p;Xdo+90)D0~hJobo$Od8e` z?KkyA&s|q4SBC|bacsjR>WxmTB`@fxX8IU9UcX`leGIa~SYDs#6&nUMV=heZ`y$&b zV~Ua0ui~sDaYHsn_rewZYrE~HFAYVaOcJ*kJd7{zBX;hy%2mc623g$7%715-gK3QO z;5O(giCevgaWjieSK?cCOt!_Wh$FpavPDK8(Uzv!BB?EmM607MqM_I| zm0$eg^grL-{N~Zk*at|=i`#mO87axCo z`u+9Q(?=IKpPzpJ>j$ULF21`tJ^cFdmlvPxU%%SF9{%a$kAFf1pZ)*yi_Xva>9?ncr%yf)`^_(T?a}EsSAU;foqqk(7Z*=& zo?TtK@G~fX%FX9Bx=K?@Y0jj!tvF^nW{>DFN#`Q_&67LI_h|R%A}Uu>cYUaLaWb5x zWS3)KLE9$UF}Tr{!JLL2R>pOUD`>}wHZ)7q;HxfU&Wbg7DQE6#d%df3>rS=>8x9+| zBH8(x&fj92dpg(PWPg+~8BT^dqitB}Gtz#A{5=MkmUj+vb}|_e*~3a6j4I`a`D@<`YG&spCXs6d{ zr8$fJ2`W>7EYm2 z^5$ARv$WIe(#H39y$;vcg*|$8xi3)eG)p^TMvA;3NDfvochSz=;ykt3FfVdAJ>+oj z@c5kf3x-raXn(O!Zg)T7MNVMoj$m^I?Mc6IV9~20_uBQ{0fm=#W^tF-#f|6gh`khf zF=1x;+pe~9{w9}M-1yCdckqI%MQ?SFF-F#d@dYUN-%3M~E z6e6}vnKU|Jjlidwl7Q89>?UESQ}|8z2rqpF?P;ncaDV4pBHwK|5v1#RNq0|EWxy9x z@-kwxtGb``g_6F*>Y8kG|tDEC`yW3t#opVD`-!)tK!x- zD=klBtb{u4O2Eu)2cwoH-~M z>DI<91q)fWC9I%4XAYPTl?)C&6H1SDGS}8fJk9wM>9rEsd1UA^`fjzXpgq}?96asa zV-BSbGA?P!eMzROy%Ox)W~vevWh^2nnx+S`m#*hC53xQa5Gu}?R#iqyI&P`qj4iOG z{eK{TkclnJSQ;C_PAd4rZB2HtALb_$l=E0h2}HSYfM?o5U&g_lpG?FXek|*B=W7`p z#n6^<5N8fXKAZs`FtvLUr-iaY0S=Nl40uJmnA(QlmKc0?*5K{@}N1G?^&9Pi=WYkyCEO6f#KyjGrRd&Jbmq!GVj0mvWZbhHvs z$MPxn#DGTQ&pN-nO8=nGtt;fWkz47J_EWCen)c7B4v3bz=N*!kWO;6V4e>eIfwS(* zNc1R$Xf?UgYzgf&XP0=W)$OcA!vGS&OI|`e&j}`snvKPL{HmfwYM5zDh?hCVM1L0x zagaI|c3=x!i(!?@mjjhorYQ|0R|hac%OYh->*s_EeuXewa%<9_X9}^bYJ5FvJZ?r6 zuAnMP?jzmQtF2>Up5;Ttx$;;$Vu=)XmR2m3$db)jr|t$UMdnbDqR0SDOKA6rHdcO5 zYZEQU0o-zN$$G!PKheFU-f*qPf`5M)S)ooyeYf8KK4!ku$T#P{7~F|~ZCUP1)Zdu> zvZ4j&e(vnoE$InBQ-jthu%)?<6mrF<2}`v42tgy{jOJ0L+X$Iz(CpHd5T7A5I1mDQ zMI2UB?hC<(u!Qyup~=nl0V~J|K*=w9VUg6+oc+S15!g-N0_ElmGi#(ir+*xAAgYiB zS|-75E$DqLXZO`-^iXH^6iSlDYPq7I_hE^zdW0Wp`;0Nx~0Z$oIOW2j)B=W~~cIN|b~MU`C-!UDf`h2ssNp zCbVzU0=NM6%roBBv2f4@cz+RP--F-;9mSn=eFX*~96+grAreMjQ9AL;mT)8Dn(m*W zRLrUaU!LW`lr5qD%&_OSIy0z<$))cAQ9O&JbgZoi6OS-OTFO#i3)eI47;Zr?ilE^nWP-9CoBt&ufSu z=ESPJ*`lTLGIC-ut$KR$Hcf=_ntGt_>u-FRp3ZR1_p6tYhIo z_k40yuzaj+={8vM3xyC55_I0}TLcKya~K@u6>F@}pHoE@-&4-LfNyQ=UvS>e4g3ld zw9ry>p{4=I$pSVOp?|SfBxw$?g~p`b#wIO-3#~ii;4g_G(?Ld5Ay(g!NH)xFS%Y8= zX|^|^8*!xe|I|Gl>$T9lM(Q*Ei>}wU%OI~#>-w$3OkYBL27ocE3%DWIvT5H~#nmjf zg!qgAi$(AOZM$+~)@L?^03n7}G)^RycqQ-yyM5&|Z&aKrCp- zHzBcP!?ol1yhyBD!V^?kiKwWQ2;>nru7X=)I1iA@w|S^LXwI@c)B#xeHV-8PtpI=P z@=ynG<=Z@zV1KL3m1j;vy+V6Vzpl^zJt?oW>$S{+&a(3=U z&sZcht0?+W@UIPvO2~v2$(AEDQf)3;C*HxncO6C{?|&>TA#O7is=FF=!iG8R)J`=E z9*pm&K601PH~x5LLsVLGp0PD_iJQ6?-spVh5m~D6o%CG{MB^BnWM)^YQEgpH@k&F=Ojg4b9XF z>wA-nyr9tstE5`+flvR+!fmr;&W0V$qDXb)ncez(GqP~LVSi2M!v1@%l zS>$fV>boR^|Jr~bR20ilZ-Niw#^6$UCoqt9Ms*zxP~_>n=biuKRPrCMoju;=s-q{A z;(roQH^!mqCd{zT?YhfHyyt`Z{tmox`oq{l9AD(dSfK(gqMC?vLYW zsD$(C$C0B#*_5@N9PyZSM5SVdPDq$=+lR-jy%{;Z_XAufY5d@L=~uVAWo=~LvJe#t zQM62CHt>C+u2V8ao<-*(j^Vo>X47=2jem~aTeE5X-q7;ql8r+#T^T2hC^GHi#1X6i zd57Pb@gMhu@{T%fKm0WNBlA5a zccAuvcAc$m-i?qdk3Brvf?k%ut6$kcqzTZ+yt}zt^cxAwaX6F@_1#d*RomlYi+|3N zHuD&w?qlP-a8RXbfgpT%p4k13p<412<}M57NslLNrQo(Y8n@;AR`$iFFt*%hZ8Xv` z;dNhj%_gvCdK)YxUz|^6t7AvUe(r;g=-2Uc{r#MU&*2UJD>BEbZsAtufB5vFK0p2Q z&CT-{KR=zm{q|e;;@Ni}eR+NL&3|+E^y=%=#pUVw_0<>OJiU4G(Tj`gzdd_;@#6I4 z@#SBiU4HrfgBLfCFTZ$v{l&AtKUTM${_pyuC(pk6%KiD~>#x2azbOvrVAMWxqBiW8 zD`AJPhfv;%{*1$%wVOGNqqH-wy^rH$eb-`nSMuV^_V~u6^GfR2P#){PQ(hp9gNw9V zqrK-o<(ud1*mmTsPM{S&h-NEkAq&>-@In82v+s@F_tcvSRi_)25m-n4*9SV$sg3`r z09zkFM2F(ua1dH)oM2=EHESkAI`mRXCb09n=Gy)PV-$&_m+|5P6Svl$0#-zSG%!9s z3UhRFWnpa!c-rM0%Z?+sEg>J}715qxJBupOrFK zrNSrp+~A`hxw$kstCTnJeS5r^7~vQH1iz^Al;O!(?BjCzJpS$DpSM4L{2D0R8~|xM zBJIMXRRwvNF_c*qV-*2c4>I$)cCY<^E3V0J|Jh12 z{-<_D&-B*^`sIQ!>8nEeaB9XE2deGQ+h6|%U(W}DXfTbjdv=?SX?LbTXcT-n5Pt@x z5A=_S`)$(f_iW8KBAV$=UV-$1u!ay~q25?;i@pvQjf~NI&IY9V zCw#O%?8)iv$8B>i?kQ$}7uStEaA<_dvr!RR!k$yKsRDJ*?v$TV`l{b z^vsnzh)uz+&XR?HGZi|WG4(KPY#7c*nzuu{l4^P7`j7Z`>mhftYVxq+eXGQv@GgKM}Fpu%?XJK&2Hb4_k#gfI4%e&dNfFfM18* zP9x$`FD-U|PEkvB#|w3qWq>fPhGe0vZW-Hn=WcCk<{FRW6=PvFF%A4`TBg^3IM)fS ziyc|Q5%?tGmAAQRvQSOo%7X9yr2CTtIAYB1v>}dohkLN#0Jw-kC2f5XButuE+*X=@ z1q@h^Ymz0yr;uX~o75Tam0N~ny3Wl|-L;LA%{26XG>d)0vk=xu|8R1eV0Vy5-~;MX zBRJ9exE|p;X~Z)ncP9mFhB9wau?XQ)?&%YWQFV+Cb~u95ZQeNPubF3?-;0Kx}dpO3OCv4(Y#@zrcw8|fN8PI#SpXOCrfbigZfzZy28bOEJx@X+C zkf30|Ew_yC^_>#bLjl=gRZbS4=!9x%t@R0i5FLu+(u9XqU{!rAeS-?@^f?(!w@%;0 z+*#8oAt$V9ZPPd7bpi)TGpnE8$?5?J?Nz6n5y-0Y+@6JnOL?_t<2mz{NVr7MrVh|E z3S6(uI`8vj6kFFUTd}O!4zayPCBxXVY0g@R&4@r|81{gP#275wyqZ>Y09VVshUdh8 zvqGp1g@vj!>Q3OU(ID2#CvLS8yraK$-3VGm*${{dw8$|yxN_ExPLoYyA)!SfOqtbW zYwb9`GTjUrmt~Ms3Dw{k_3Tt6%2Ct=NjBH81O4b84x(6iD$W!^t2S_X$`3qDUu_da z?AA?&($ls%c4%mfnCVek9XWdyh(+apiaaPDE+WyJ=d5Y!Bwy*Mu&^2t433M@2`ni0 zj-$d_IB919$kf@mbh66#{D9s8wsr*rIYDNV1u|iS-**RyXIeE40i)_7ndSm^zO3f0 zgF7|!fdtuJX%#Q2P{s4NC*1%-X7HJK-dA-(3kKliRzmfYzBV=jqnZhdho69d_I}t( z7xLFN8}mreBqjo)tq^(i{8>QuN3iXXnH{G{cw>pB9+I0_o)l*L{D%=RH{~ z{1^eY)4p-0_fwaOxQwiBhfShsA(}og zNLp_QXVs1|3hX<2zjkwW44Ke>;i4I-ab}hrq@IETbp?slN43vE9{6begs}sT<&Yp%4^3jfPR}-hI0i3PYlq;SL;^Ht8@74J$ev7>qHfcs!ev9M8qR5da0m_ko(v>IG>)-Ko z6q|X7mqgkw%?3{gE8F=)z?`@$_9915*zqt=b&?j}>GgF?4})0gXU~+5yAnIjfpCiR z^s&&=nq~b()T<24!KrGvqB;D`PSDjfO%K8IYEUZwAKras_pKQIhgKt zj%zjNpsMGEQ8t7|X?$;giGLDG7bj0lYr7MK!R-EUlDIAoe9*E5o|csPZFR1CvuEFk zK8jk)K%fMvF;3hiITk#Pe)qAtLtemy=ep~v=;+CPh`R0O{#3=02Yd1pgD9vlkIhvO z7CY@xk3|K&9Xmz!wLhFks1rNmj?6gfEaIZWNwZ1g;YI+Oa+PF%QC)Ry((a9(R-I(8 zQ&C2(R1`cPbSmOHw^doGz1_8ws@tg2B(9j#m^CsvuVPKL^*FsC*7=p4;;GQl6sg?| zZ>0k@u1(?(!RLV+O^fpgQL=f?FUYZYV*vS>K`NFIs-c$B9Bp-BQ#-sayNPy+k$z!3 zezME*G3O}3IXD)7P~S+S7(G03O~s(zFTsX#QnMWyR{<7I8h5FP(ekr1?+z(8&+RD<^z z5;&032xONtA~fi&rDq{G_AKN!#-~9ij2jNr(6N$#n*fl|!-UfJ+if2DK#75 z>~MRGzAwH!eD8RyIoehZb0~A;GNB5LD5V>WHbqMUy8`-(z7a)|{FES?f-sBp>-_yy z)XD5Ed?C=^-ooE2yL}<4y<0GXE)MoyNSw%m)vLM8Wz|!nfZN3{Go;0Z)fB3uzh28# zKtF|lPA^9Ooj9cHEC~k`^c-B$tpLnPpox2OqEp7v)U|lB_^`FeK|O|y93ZUjCG|Na zAhl^@e$asCP@zT15;)iD;Y3A6R?7qkV>*}nHTpv16Xpqx-h-Z#5ELr#Q?%Zu{DxxL zlovr2Y?VY#W!@o%nR1^yk$w`qd><+;m6cC_)a@etXTdmk39~VQm~An>TLD>zlUoY1 zpmR=;nfyaqHFeP@jN}q3bJBNNNy9Zh7vmKL46$x?jTi?GClO9+kl?!I+{+9D{8u|yZXX^ zW+QX=!A+N5c)oh&Y<5&I3xEO;jq~fr`rqv%UgASSs^2} zypvX5s(Wx2VrS&s#u@~Br*`-~#VgtDq02gQ(WCLUhJ>dKXW}wL-{q~R<(5*ws`aV* zsH=;bQ`^?9YL@(XP|qAP3H(Byz4JJ+0OcZY(1aP*L3yeu@|dgtp%p*D0!hxmJg29=HnY zOhK+$o7MUy&z0(?M7Kx|cP`wFnJSp`RM~W=)OxCYqKP;cewKD~mUb&uyKZtiyrcP5O>wLP&Jw`DsTb_H~Pj9~JCT3F0n6qpgYt7QS0Q+m- zl(DKr20_32VE6ajld(E~@7d`QK$q;l*jg4n8#fLwb$9hK7oB?R0H03qR;M@?NzC3= z+>PokpRJ-h4Mum#a=sB`_Gs+j0u#nmlY&ouUAJ4^v*y|QN>`ooeJR84ZaQ1}RFhfR z+r;j`xaJDAt%AMTQ=Ydy?+Tic3S4L<+;A#%Xq)GLwcfIJRG|ca0%T{aZM>p>u_pB1 zcb+WK;m84hS9w2`HuF7uGC^7q?-ey{=cB&jq6RQXlpo{(!HUIOTjCmD`sMhpfiS#l zAfA2KK;R-UVbH5@5C~Mw!w0Fm&L>q5u3z(B?iz(JTY#brnE6tT$ACEUAG zGS>_8^e&&ynD6>1Ju7Lcs}o7LJQ!h>vH-M^zZ8W>UY^|dAePFf@ zD|%Qj<5gDe-rE=ARW{6Tcd84P$}fQ{|3c`etWQ&oAKj9F!By`Jt!9q$RLgygAm@D? zhJB3o^oe~;6fuA^`?w4NQfW5|fp@CoafUUzBbUxQXfR;9>!9x4aySIZXVYjxY_4O^ z=lDoQ)KqfbtgogFs>-@||AWBXd0C5*0{G<_I@SVYY2P7d=cZzLKn|omPP*oVBy4 z7IvTwc^}L3M>W5Et3ujy(8pU+M$JABXnh^@3-|EWB1_?ew6<>WT*WiC^orwdn+VJ$ z#dki7uyUBwUf)gC2j44dC59#e#S|AW*uj4N9$OiIme@<;;pvqy8}a2?&|oJho%Oqt ze}_vh6i&(HcCBg5BX$1KvH1AzE4E&*O!(rrBo^rnuDkLcjoZD5=Z)*Q>nj4r5HGK# zsJmmr^{zIIAG6Jm6{p2aCU)>HCNe zv9xhKbLLK~)rNpv|7}UFQ-AY>YOu@y76j*i#VX2k`Sz~-a+ilJA8Dj_M<%@5Pb@zz zKmP(Bj~j7lLn@`$Xcp<#;3SUF2za+4{QySq%0Gp)H%YNwqgJm&GEp6@0Ko$}b{H3qLk8rB%QhTzin0nRP8FFt-=YA<}uf9;u?La%=<%g07%3_;-V(Ryw0Q%O)j z6FNc4f|Z=q0x5LKDYf!;)WV{B?;KHUptqN~ZNY>F8c-c*9_aeU!Ys||;}U>2N0N*U zibj;MAR@}9#cA%W5r0uPyQ-T(`GIc4rvOhbp6(>7OD|3im9;$AhDm{*G`4y`phH~_ zKg)a4_vJ{CCvy)Svx=8OvZ<7`<@_)O5t&i2aZmBS+ic1SKc3i}|1MwZAv1h#Mv_LZ z&br%c_aif2?f4w^Zq({PP%XS_hQ8m0E;;1L(}s(jY&>Cvp(E8BE=u&Z*$+1jrH&I?oR6`BqNQvVg&&WJhz z0~Hj`Z|XQzCY_>QdTbe^md`p&8D9xfDK7_JtW29uk-;qifaD2dfj>aw0uhz#{`_VIQ zTL=3blu(7?t_yIOc-T|~GjX-b3)Mz$5SkmuA|TE4nZXE`I(XXuG<(_p)Uj-T>O-29 zKU33T}6qZr&V2eu(vlQf^Rm^gh&lkS3pmfPZCj*$Yq##EkrRN7D!T<+M z#_CRcILo{P*#<2S>Nt;8!yK1hv~W0#o8SnIQ>Qx}1q+n8vMAVEM|p_-GZtw9M?#}d zV1G~sMur(4Vx6;Kfx{+&DDiUmORekB9qWK-WqaAmNYk*_Kscd z4Rm8_W=+p5@eF45yDi{(pxSPO2>|iTgENJ+>UPg-9|G7$!_vdjO5tgV0Vm@so9T%Q zs}h~C&bS>wmo+_LJq_bDkHGQ4GanPeUVjoFc>^w-WiZZ1;}7ab);A_3q$%@agpc-L z&;G0dw8$@JEHR;kNv4!&()-Wm<7}~`UUl2F{SudxCWUN?uijRH97NiJD}M>p zvHf3nH;F(}|2pd{I?UWu2Azh(+;lr0nT5MF>2t(M>BnmItTMUmDtYTJxNLEaz~5&l zw{l<}t1#MJiFJj2`e8J$Cc^{=z5QkwKZ`oQsx`JXM~^b+nQWBkt-%puxZw(l4WlWI ziG2d}3s%9g~F3Hh+ykJ348b7)Ht~VKegHrZNSdxA!C&hFr2r^qa_NY`2d!Yjt9jiwSSc6Jhe^C zDt)i^de)#-o1eJK&uwCp#$w*tB=-GLK~iXKTQk>OLb9Aw*#oh;**ema#mq8sG{7OT zDoQ-k5b6UnUuSb{C8Dmk(-W_UXZ}ph{Q3LNvT&O1#n=o6vKvc}2Ra^#b2Kwm!NvnN z*EN&pLVzg7wr)oiV3d9H-pUvuz$*cl22=FqJ-Ob z!{%?5A-w5U%^AYX9XU-Ae&gK!oS zkh73rgijm2VBBzc+kZL28|&GFIl_Eyn6j-(j&KA{bA-SM=^NU`Np$4*pgY5SGrLWb za7X#JIm3N*@`9YjP;AB|%J-4c9mh@Cpcjp##w`!UUOx9Y>c1cx7ROg&^8h;Zq*0KmsA-p7sQeZuG zo~Eoakxyp~^Az@+Lvya{PQkw#P^#^DT7t50MBR{zDXChIJa(c5gJuy3IKqP2B+Fnj z758J16Goc=np}5*a!yN4g4tLYnxseaI1o#1T*~*7HU&$uCQXQ@>(Rb4TkaP(IleNE zWF(RdrZs7^Tz~c=c}l~!kt}oez;_4Zd}io{g>oqC6{evYU?htPA$w|ar=nG2Z6zJ& zR76)U}3dDdCoeFfI!;e=jPxpMUPSfrh~INDrKG-O|)vWog71 zKMjpu=p==2OzlbvCa)?~7GAP2C2>E`7GunonfjEnbY&SR9p4g%EL_Rv-IoUn!ZO5E z-Ft;VS!0bOFG}q8PDK{gG(0I150#5pw+7RalC<)q9WvTeg{8ZWybL0m8ZvLIn-RsF zctNxkZhut4kU4cujd45VZ?ys?Q>%$~cRx{+4~_T@PU#FN=hjK3bD`)zQq$vxPvP=3 zQO|OI{ute(#%Sa??J}T+pjBPDo%1#S$TrDvAl(5sR~IQu?OI)eq`CwWdD6Hwn=>~o zNBs(Bt&p8TpuFS>iIiPNs5+XI;6vc-{zKg?v469AnZB!?xB)cD=Toz`E!CvlqCl^0 zR+*?J?Vtwv^0g`^Zsxiq0i|jshBg^RrCsbxA8wLvK5ob#wsUoqYw%%X=N?I>x_jPg zCsoTUW>`{bQ#w}_EPEqOk5AV!nk9`~c7L^+=4EjPsCFztsK&Ks^G_75w%|(2nWeas zt$&^>UAJ1*88=V8B6p{iS)~JgpunvURMJScpbFx-DOf94%M?J%z^aeO67W)o-7Kv0 zAgyYXh0SQT+CnkO!m1#trmTCVu&++(lx5JXlx;~-!I$&hWGjnGCZ)J~;7xQ@4NvcSr-wO`>?WdQ^D~AbkkOdj*yFXf_PU^ejIWCMje8d7 zwo1jZ17c7;553uOX*L)w6*qEg`4zs!OKw6}bE&!V^+T;dr=3S%ycxpeWGNL^jDO1B z(xC6(CBAOdS6Ul`NIbTOw$HZb=BkM#WNV7-MOSW`7+}9&sSGk0to9TiI)B(Z-6sR! z+kR#op{tANHiQAwyw%8jO}Pf+(;K(d-$R8zO5 z|ANKpM{xR@airv`oB6U6XT8%#@iM9hg78>CS0*KzvApAs%M`HIyhMqnsWsP%37a;W z%Mi&TvEeOdY!_c8`FG~OKP4Z`nHv+H)Vm8U@4o3?>8xPYYtQ<1cxtX z0h6P`YC#3($6j$1{=M-FYD#(2cxw#W=6GuiDk|7YTB$4Lp@q!u$`}#0cgDE!&KN!3 z8KbAv>M6B)ORd{bD}Tb1M?_3j0q0`GPNN4zyXyR9;`AgK$SfdHPw|A!1 zXa;oX1FO*l%r&c>q-WSkN}%bdf~-I+<`Wh#l2H`~3Jv!oYBL&}foS?P96?!;A!}L? zsuLi(4TW!3smT~vVIJ#d9jnxY2fKn4xqi zd*$p^)ge`$pyzTY3=0gIF5T_NY-MtQQ=+SRLo682YV2O2Ms#1By`V;PHt5?Ew=cO@ zCcT^PFck)7L4R@a49hy#Wjj%cN_quSIvygHM{igtEr(01RsH2i`Cn|FObSW{z|ky{(nj|-LKLh@=Sj`QfjEM+=AUqWo&<5noCbQUEz}_>uZ~7(oQqvm9Q;$RT2>V3Wqzw zVq5cF)Wodz$E{YV{hDIgmAmGrUE-((FWzw%ULac{;&P0LCtXNPZ<9`Ivn8+H&_~sF zo}%75BqK98s9&WqN0Mb=5!}|JdwoHM4Klp6FV^~YhDdAZuY+oNsSD=@E0to#&9)};uUC{DtZ^XlLVw>EGs2m*4X&A4EuKfLky+B6pnGkPotdJ z7thnUUY(0qwlliw(970fKLzNit?(H)F`UkoBw?J`1j*`1=RTT-$E57QqmyL zzm`X1L{Q3XzigYeLBx=3e^>*l;{cRk0kjMN@gl`wVn%AM6EXU%J3zLr^~J|uw$=+D z%U^${rqJu>ZTr|4nY2&%k!J><*68ITLBgU_e4Q0iR;ANwtuyqR#R9`a@SYZHp?6TF zZJh}rnx;A}d0N+37Gz~bNLz%pH7Ujh)fy^bh)k+ma2mUsa4^&Ge{VM5XoKOuVj0yT zpp|s#l>K|V(~ZT{UeJ+$*DuZ1PoF2(;54XGI&IN$&Tqa`yeBvTWr5kX6D;t-)=nl? zzlGmT?K})?oUqcj&giMs6Zj327!89us8s!4_GOixOFu|GOY1y1L9uUH3qUNc47roB zv^F?ZoHF7Z#(l1Ce;DApjpJMyxjJVCg(vu$r(afI8{qGRw*lMi`r2`3c}&cYh34~k~>bG5`kbl7oTZ5 z*d-W5VKJ$o1>oX*tCJ!f%w3;5 z%8;l9DyDK>f8w)X;p$~uSSqquo~nkb4~IYQ&CQ68e{E<{3Q_olO?_~?uD%)q7i zz~CaOS%5G!rZCxloP}wDB}A~gGm^mBn5opBh)0brdsaLc4|)u4yqE#srHVEk+1$AX zLJDX9mTeS8`-rh?eDQk+WsBX@ao zAfd(1+nZYiWk4;U`Rh%>h~Wa8(8-V$9qsA+w9;4y8&}mWbVkRmg)B4zD%;0qG-l?g zW|^P4RR*V}5@Rp14cf0vvrPDXQ1QmLs=y7OawEPvBj#Kyk;q3jhc!6KBA4V}i(JM- zI!oK?f94B)P4zhqmuWldH{ZEod7$1so~hA>D&BZW4hXeP=p9B*E~if3-j87cErUlx z%EUX#x=kY3aE+`F7%r1_Tck~AV-i-z#CGXiXhFoNK=(L>q`pYLBH{JbVz^@sNt#7- z-J;c}szrl&B)8|6eit@#Li;c&fF0QEE{J}+efq~ z39L-j@y6`45mQn^vlwtN))^J-;&htxa(T!sdBrw$ex8r?gBKxo*`=+GJ}xC4VwB{E ze+aWo$<=*IQ>+XnwX1ZiQ?o-G3~4c9<6Ee_r70Mm4r3R6iKRb3s}@Y;*__}@ZQHDO z@q70u5%wEjG=t)sBEi}vlpVf2>{ zZ1RJOm~Kgnco@I$)5Qx^(THfn4ppckf09Xz|M*yzzRXy=A%-0oLHiFNx$=fHZYMge zA7fT)gYuXoof<||B-tY8rerzn5Cw6|fh}oEL+J{uc6&FP$kReT9u#c0VlY#<@4zeS zkNFG&`l!bidKbw~EUhJ*^ksi_q=_kVD$nF!JF3wu)!dLh&JAA&0zXd5es1^02hr=D{x|v&E%U<02=kVvZ;F zugv(@tPlbt5sDdHleMAkxG1JnJ$xw0$W_($LlUQHNEYlmINOm^V~#N0e`@Qly)a-x!P*Dm!@^W~f9iB=oe1y{E=<^H4`12V zHXem#B&TJeuSk$CJi~*r4i@cZfhxOtSI4)^hgx|msbs+f2S4mHv)WnnA_u8eHdfX= z_Bgg1Ir?R%))|Uquq?e;^1OQC>?m_oe!avhm4+-zHbj|holR>q^I}fGf95DCx8%0W zw?$ODpA}{tsCGkRLKNxUE~ZsB{gq84!%rXJatFw>*|~WS#qJp3YoCO2*X%b349&Q9 z<91PipM*|!&bLmfu{kexN>#iTHbuVk&ph|fa|-Q*c=CXDUkQLBN3As%G!o%^GAjlV z!Zo|G0mCKC>WMH$#1#vHf9lCPwnwXm!I2)9Vd&UyOd(P9pAF2KsY!U>?cW`UYa}?K zbWN6V!0>s*eId&@5qB^f@4LR38m#s(EdQO+mEeRk?yh9jPDpvpdCq|0GO`-;$Q^Z$ z|H`j=Z59@UPoOR=rwW9v3DIsETH4BRS4r|tW~=Qb(o4rjDG4V7f8P-faaSJZMsXLg zQ6y((CV5waEt6gjwzLf}mY-3$Km^HAhcUxBtYPQD+`d~DW|#BZmKtE^5#~%5&gl6# zx(r6gqeVvO{5Gf7+zS!`zj~ppo}BtwQCO*UKey>Skkob{E$wc23B#^1!J?;(cc|Gq zSqi~MJlpYeinfdue>T2Y!F0)#uBzJlQfn?{TlZe{ZtdR=31I0#dvrFzr(08?5y4>| zO?Vry?nNYJz758tFH2~7K>5G1~_i4YO6tWtrt~xHE1vY zY6CD3&EfcMRmxdgD&+#UObHzS=M>6u$`Yupy;id|`cSpoe;Boiz}FrfA8|Dk}_bTgc8rK&7;V&`RO?p7nTW@9=2`Y zL2U!VvJF^y-?4WHCm{hj2?<8?>DcRx`y48Ok&-}wsI7+i@$jw07$f?C%o2FgUj=e$$e|BB92!qnzuIJ(woALScCe~xF zoxT$3x5wyG*RDW1fh!A6uW}>?9PopcpnZz?=F=fbo^5BTaOYoJzYTgbo_iEjQ{nJ9 zR$T7W54`BXn|YWtR2Z}Bim|d8_5?=tD%mrKW3HbJRP=OCR-VqCler+zpOdYsE1U&a zFw{8He@Od^k5bUbk3Y&*wY9J04!MK_*q4khxGqO9NRLAKg#^RU( zm(uO%cRb^s^8LD(Vb5p2B=nf zPjT8^&3;cd#n%*J?(fOm)!5IUJ5K)phu&;T_t|XRc+DlV zb?-&rHnVk85?F;BfAAhrcpYy|f*V}QOt8n-fWB?HBsA7u_51`vl8U?jYO0(`a{t7Yvs3Ys+}djm5Jk*`K>>=)4}6A zKEe}+7tdeHD+F~>#3V<|49k`Fw^r2}4zW+|=$ z7zju}Tr@D_8cj1{?+`{nf^h;8jHc6(*9rG2eA^kW8{64SGhERZy~%~nco6307=`VYl)F>`>Z3~HLBaT&N!e~93J zWg@1jO-n`L(|lRWUYP1C^>^-RncUG@Xm)|!*OZBo{Qg!Q`{fHRWy-A! zCLvN!65!+FDr1ZgYq|M4lXjIX^-@B9Q4zn+RHoQ!#Pbv{zs?k`a5jv$(xu(eo3cLT zxAC4iwc%x%bW6s_6Blp_BCLO@e*>_K5<0nD_c_%^n@S7E%4SQw^jtk?yjm367_VP0 zw8;eU@{#hMtUSx~j9qBx&RhTjHM3sx3qV?4egP;67w>TYM@xr?FJJ}0%Q=0`{U2?# z2*&SF1!6#cn}FBss&T&oN>?LZyaZZMov#G1!|mS6(Ik{!lF+%!EB&cAf3E>=H#c(Y zY5;28SG8b+bn;qwYe+5hqISO6hHg^m$qgol4A`9X%r*UqaC>>#TTepk!B(=9*9eiM z-d&CU3=_L5(DnIhSXY8xURRdhD7E|XdPM$`0V~-}nZ=tmf7V|8MmhV-W7vKC zeyKb9OvhE~R$}01*$|is<@jq{n5()1y|Y5P9exwK?VIGSy>tuJOhpzL&WDbcS(=r3 ztDU>T6u|j6$g~R)dEGIVnwy-iRAt!{?Ds2Po*6~*cvru1ApqrE)0Jm>QHa9ZzN6`+ zH#9VfUZ0iT<~^AjG?*vB+f4H_KrLgh9;Z{mN+W{a(E;DJi9r;8;?2Z-=b{Ey_&xvT z_Vc)mdvE{te?bnFZn3UhRFWnpa!c-rk;S(6;Mk$&G_ zQIBdzuo_1mz=nfiOIm3e>sUKPZG?9jd>9T}s=HCwv}nzU7OjVw zLM4tozDyv2s_N)sj$*Z7yK=)*AqHBV4U>rjeJjfAag}`7Hxd}00 zz-Y1=uc%EZFO4c2wqZ}}5@(nuz(?kt>8y?zn zbXG_Wld*u@%*FzvvzQi{8&0N0)`qjGl5-;t5<;j(d{Pt`h&U57H9{~ydDjS|F##kI zMH{b;ByWPFDx2UTHtH}u0SgU=ZqP_QHGcVswx-~l+=WV1S~Yee|`ik zl2b4R7AZg$in&RKCsAmUbtR%EaWo1V3tItgR0(GDa32_f;A`1gFASPPG>cM`qktE% z&kQ3FV9G*9Nli|GkzmK1p&*;{Ww654+?UV~C2OJ)rGWngDZPuPzc(2C-60( zgpxYQl+BeB3c8MBJo%p(rAQk#09nw7*#h;aqRUOFd-EW*G; z$>t$7UnXmpQKRVZ)p;wSe;o$3i4GE|g}KcQUM;p%t(}$63 zMJWhs;WMzF@h}fdg6K@&<6E5i>?~;DgFXkylFY-h1$HtI%P=5H)=nT> zGCZ>RRYG84`UJif9>JOP2_6;@0T=~R1V`|n1U!Nh%sdGw1eYLNe^RuALzmFBx!3rj zSMat7m}DqfysFL2HlZbG9^eu->y;8&l-Vd`-f8o7i3Ngx1OpQhW$iGxAr`Wg%yg;_l9k{|Z2I~PJ#1D*2{vId zZZH-w!YD8iFoG_@e+*-jo0ZH~!Dxtnl#GTLK#8qn7(t$ZhOjd{DwL>Ah#8a&57-c; zf{~44%bZQ;C|Q(c>tVD0Z-2Y~`mZ;e_3y8)u6OI_cdvGK_lJwCZ`Yq*Z(nb=FC3O+ z_j3K8>u2lFUij|j_vrR~{pI!7*Xz$exxIUJyE)%oTwk@Ge_CBVeY*Y%_b)I3w;4i7 z_HD4WgZ-oQEP_Qkv=-e56p?U z_2$AUv|lmw7oumzS+H4|LOBD)?aUa*sY&kp1s+8)?GgBF3%p8=b!)h z>i?lA8a}(UnJ)h$vOI+K2k=XK#Gf?4wOk7; zR+}x39kl`ToUpdYv_7B%JaE8;qslqA0h6nUZzb6@e+8e%GK1`|jZPog;1<_q8jsQn z7A~Zstu2knSK1)*$lL=(%ssZq#nzHaOu=m=TYNHRA9+By*I%e52Ok)DAbYfYbgiZF zNYjA7ZFjg0Y#V{xVnKRb<^+SYt<0QT%c_tZwzjk@w^1HhZ>($GQVy)KXk&ga6HG9B zIQmFJf85s^TPz`!5yY4!6j5VKNLhq2$sJbokH^agAx@UgU=JqhT1Bo;md@CA0k(+z z=-CXov+cnWOpSeFU`7&Udo68|#s3@&YDDf5Z6QTm+i8XwL`o`*M2qO*Eud9G6%prU zvyb&ulO6u#GT{=|Qv}|ln?J+zEw+(G+VOy~f46A|TbkA>|Dy7_D#&{FA0mhRCy8)h zIXHG&ZBE&ap^!pEJHsf`9rlz+A%0ETKO$NFnRYO<*(|bGNp?(~N-iA(T+`bLGF7?s z$F`R{?xd;ONMZkuowQ)4^>&B^;YU7G+b9sQ0xwXS94?8p2p%|vhM88e~AI}o}uYPoNJhZI#lSpQ5Q-^#{o!VoX zR_!4M=3~skAGaY=m2Z2M7O4=Y)Y>yx4|_O!V1hI}Y5dg*ANe%NwdGMU_BQs!L9pj> z*&Zkx2)}&e|4Z=vz#E*!ZM=_2DWk%he-ztz6F0S4u%pJ-R_Os!%Y)j(b9{m%r( zf!9Eq55DZ*YocF5T>MnWygA#Rz1g1Ky!}n}oG?&oyLSg#hh2Bjj9s@qwdYoZKkh?C zpIvJY;Kgg3vpr_UI&05NV{9e(k8ppHZF^x4ffeZ58!fiI@QsGph7=$Du`JAsf6e2b zI|;bjyBCkf9h=wc5tVTZhX>;1ka^lWAdmJyyyi~!c!n}uOIzmhPE$*o8sXS_&5vyP z;H7oJHwF($`Hz^H$zDB62vX@z;Oqq;e{}&l`akm20|`gt_w4`Yt7rE2$=luT=Jwy$>+iq+ zzWw&{u04Bwd2!XAzg^>I{mpj$_WE+OzCHWy`eOU!W_z}~_{-g9{msSI+12^xc71+* zd9yovb@A?Ew|TPJJ=t!)ySO!7-tI2n9aKr7&E=&idy41nCDqp6lw>4=e<{Yt&}4JF z`|ha9L=c-H$_u@|n3RL9{PHM*{BX0`?r!fcZf`epqEM#RUgGpDwh%xx2hs9lh_c&m z?%tkVy?%0!G}wIGn{{wGdpDM_UgQ)WLzvr(AD+C~Uff4K}H%G$le-6^0ZC_vf?abn3{g>?%c;35NU$e(^YDrzN$9KVAg@tDP zfCpzkVa|An1pa+_-n{wdX8Yz98lUZUo9)&5^Nm^ct1mx2Hu&B7nQtA+=Vy(7`n03} z;o|aQcl+X#&#%v)K$V}w+{DI-CI3Z z_W}1s&zD2iU2WVm)+6J64N0oTA>8Nco>$Zvt-lxC`|4hC9~Swho^SADzG-FedLGBg zxUckqIx5vI)JLQr@1ee?CAJ~#*~*A5YA99e1gM`Xbpq5+6$%0BrwRrPi1lNJ?-=32 z`M)k+-@dR{%F({Oe^B0Cb9YU=D8KfjL^qZn=1#R8V8&Fz7huLz!3Xtnr7p&^?-mx= zM!#pvBetlaT$v%ljJZND8lMWi2s7piJ`6}%7x_xhm&R;i?Obc4D|WRy3HKFxF|X_u z^}{`_xO|*4%az=o>sVXV*xSd)Vf@NIBRJlNm3>8WypOoAe?TDPTZs(@Sej0PUF7Io zCWp>Nav8OJPf)IY5j)!3aH5Q??9-W}FtDMopd;AO*UG|%zEaj?Uqp`gHsmpMs9|5( zfMLT>LC3IRsGwumFjUZCKn{!8G4J{2F<;0VDs>p@hguz|A1dq^>W2zD42X9k{Px}baiq$1j~6nv0Er$WGt zJkZj{A1Gqe+Ht3!89>y$X(hrBm>VbO&F4aYM(3(M+cc_$naH_`$Y`}*8zcMMJ)|k(+nHa zAy4Tb_bJwZ4AuHfYs8gPI3+8OM9mAL!r9Y|o#66m#!hhj&i#gDI8V|k87`F4k9EpU z#4c94bh&(fSFT+?Sw6j+j&_4>2XO7L@u+8Cf5Bb)*53l?E>-C7tv*)wfTB;+_TzEp zMXt1Go5pMbQkTB<_po!9n)LTDaG$4j_qvikPumYD&M)%KJ>L+R?Wm zK2PiJ{Yv^gZ9kqkU1E#5xi5^>MUA*h9n9I{D|Ijpi?3int}niV0U4e6T5PEA=@i&S ze~iv!y41ORe%W2SNm2YFc42SBIJO~b#8=osH(x=A9f9~tS7iDUDqS(2ghlA$-iA>} zY*8bjvH^Ak5-RMlBal#Ghg4reg&hWzu!tQJ|E@rO%onx}wLVZkR{B7USV4zWUt$Fv z#*-MA*dp=o3uARrBe7BkTLOudI%Iq+e-x1GORQi(LMO2j8w`ZpNwn#(kV@!%CG@`1 z<(K8-+a+!GM&z*(Q6p4=hdqH%1s;T_N?GLkLY1-@PiPT4-W!o+#1=I|l@YKf5U#Mp zoPv-!Kz*si1_PNr0ve(%hm#4R-dDKyl`c^%pI_g%&W(oQ+;{W+ zv_-uuN?J6^9t#~~ff~^Ya{(4w8wprwWh84}*$Cr_E>5+&$3l6;7B!+3ayV93GUd zj>RgyJXULev~_&422vW5tW_S8!bTumc5# z?~lX|Bp5ynEdnElp+R7^e{i1%@KKH#qr<>qXeZb=4Dp9~!w`O0HxAK=kfDEhk2WbN`jM8Ugqv1i9IXX-|drCHW!aSnzytt*Q_6gnO+iUF) z(1sO8_cL_p(jTS`>42Hy3>^Rxop8g64xL+v-koFb&i!GG5}!1Uf2<~7Xf0Ru z4e4;I{=9BThilQCA>m*=wPW!VKIM3RY=y8SZ*eh>Gkkyx(+nNBFwf9|3r~G76nRg% zUKq<8>vPg^OXWPn2l9%Wp+jDo`du`+B+Sq;{_H8=uvJ`*-qw%v^2Q<;&+sw1WQGnq z%G7_T$eU*9CU2ghe}nJrdEUK#-RaRq3-?jNeUx;$dHJcIdvc(K(Iof1L%+08AB(dO zSH~Yhj0kDo*49#$W^4hQD$mdnt12^e#H!9=fLNVd;jxkzRsqA3|RBA$|= zElnIH7eRGOHkRrATsK$Y(7)`EVjl+mA!MQxUY7S`4mTqUma*1}kG?kC+L>w`<}1rQ zP;Deef9$N1mMUQyUh=`=USgkH4Kw+%KTi52);Zpx?64E3`O30JR(p+6C3;pHj)_D` z{Arb9dsuJ=Yx6xCbzPFgdDdl87h0;cp1f2E?5i!wSfC_&QlT)XPj17qK0O99XXezc zMLK8Y+{vVVp$=neSg3=Z8fWVY{_Xt!@GKPmbnbH#;`wSQqQ z^{+ss{{56Rcpaj9Y9alxv-Bxu|9WQ91Bx#~H&3nq{J;O#Xl%8~aFE zf87C29*?dg-ITfIWA|7Ihkof~cSo%|R(E@-u7AMFc9Ol==dgOH&K3XiJ6C0xs==f` zJ5zFxT$i5q{O#NM#;06kSJ|gCE|BLV^4(%ys)OxT?U%I;qJw8t=8eel>^Ew$*-pm> zHM-7z8;{t2NoC)FTpTG7?H9!stJ!t~PqJ7Y?L~h-XTILAQ|0zP%ar#~xk+`jUsbHC z^QgBS*)3Ox7%Jd{8}nA!?Pz#6>diS;yeHL>k+vRTmoyq2A$JsQQcZBW1L%JTXpQef zm(HyOa|1y_2$w{z1SNmuqh&c<_|RkM(d#%w%h5d_QjMR>Aw53rP^o;@L(Q?bLtEoB zCTT}4ev}fQeCN0Bb{JDu%ZHViuO8;kcR6f?A7i3d;~BzND@Ps1XFXh*hZyc*ZzPJ% z`q8u=y^%}BUP-xkKFL|}uf!nV%3tqcuPlev^>Pes6`v97;7@!v z5qwyS2#NNh44NK|FQP!vSqktl4|$8S$j%qh0Wz#WiULik42ahZQ(@K)Fw92?i$RSL zJZwE`zfRG6>n6+iR2KhABuQ@;nTe(3ndl zK#+c@1_;Dc4G<`lYJk98S|n1%R0!HF1`(8^sS&VS3<882G$Y>*AGGEuIX}sQAQlYPAWesG>Xlezsop^YN^64$J$M_rRLG>Q!z^4m^%NQnw6lr7fjLEPEkq+t-!hr%I%b@ z49e}1*dTJCZ&@OG44`LZV3~D0&`@ongO~{{Xz{SXN>^k|>h~@_u5>!ac+@Wsb_yt`SOl- zk;ln7G*617D;c=4OoE`ga-~c1DfXiCTp*@B<`SDjNgC(E%383Yspy^BeGw6!o?>{V zF^cMTx3JzK=L%7aZ|rOoeP_>Q>(8E@dVTB@&d#Zy+ixi=uuSr{+9GnHAJy+f_6v4G z=Sc&{)w`VXl9$YYbv$c}`0eNhK4eW_34(bo+OP~UxEy7U43ie2p8CYRVX*@>C0 zT?CcKBb&I1rPd#vIgHPAS-qvSR44C+=#7o}oSZA(HSy+;-rPs#KDe%{o`;eP7ko$( zdvxaRG|hc!LD3$Y^3!>U>scI2p3GAFbRLT9%w=8mZ2vxSoJ}Im-IITKT)bs?s%L65 z8ScDSSlDRv^qy7m#(V5j8{61rcRpn|-TB5VmW`QP-%?IA(NB`+ZDMA5B?J3szZLtN z)aJeWX7Y>T#R)L$#-1%zy_1epk-Kc{($oD0eUshQ8@)c4F5~ySxo=Ie^*3X4L7igj z@5YA88{G-y^PR?Gak16g)AcvJi}|!meVnduy!RW) zWq*x3-MRDY#av~&OzD1~xxOc1Yd_CJy1plQou>NZZ7Hq|(jI@`vczSoKi`y*|BqKV z>#GnBk>|QE`>SxSXtzB6x?+y$C^q&q9@}b{tb6meyUTtPE3$2&qGfO1$1Z#Cz1Utb z75C}td^L+rcY=?@*4OR#<8r(n6XRXs`OfWXj*Hy$2^BS%VqeXL18jLT3%B~Lk1UsN z@ml7Ir`@M_C0BoE&J^d{z~Z*Ptr*ksx9s<~>tQU86n3i|-#Co9aWZeW(r~97wm0v2 zyx$5wV7W8zY2NlUZ@%A#`uYOTr+33s1NPTl!_nLG{&{1M_fNL5(DArE zOq=BPDCb7U5sY5T`Ri|tF{{V6fBeS%u8)6Q%oe7Wz&?#-7|m5a5#eHFX4YyCe-UGb{`0jYNN;G-=mEetTV;Y&;;`9r5R6A=(R$nnzf}hR>Z^CZzkmAt z>BdW7{OtXEe)acnK7V`v>T!QOu#VA6(LH}Ef2ABgv6jLTRVk0pfTe$cZ0RGuo9!L{ zF67^p@Af~(?|t0fnaBHgzr}oid-uoh|Ni~E-~am4kN^2}fBF2)_unmg(Z6~B{@q_b z{BZy7^IA4w6o0<|@AFz!nA1v`R{ZmijgCMKU1|GXVNf*&&zSIge?PGJmn*bWhxWhx ze{lcrS{e~R{0R_~nP)a2Dy|-CNLl86K&6xNZTAg%)#1C^K>G?*y}4&D6c`%fZAE^Oz3yPemBJ z+NDF~f>6QT=Fgc(9VTYmNEeuRQLDTaj?`zV+k;6`8<=4$1J^!dyQbQBk}#eefAu%x zp#xr!I-*bD(X*>6ybn=AoRL_wH%RtY3QH%%WQ%cy&n7E~+P+toH{t!Nj1LhUq+HZt zovSiVd2fcmHpUiU<^)(K1gnu9*xR{n$3#iLxo(loh{_ys{@KfyG@+Q?ZsL?p*>$-7 zlKUkQ<%-Yq*=$ca?#AF`E|J^`e>gAnI%ck>jmtg4y{?~&FWeY2BiN4#o(Ul5IV1mB zg2$n*gV0LSJ4ERnobV2@4XxQaE0&J38uRQ4SC+auMy_q^Z%FB)@Yxb8c4uV|h24*l zWsliz>adtsM;S*v!9f>r&^eAh{5_7$&70R?pbmztI$?t0aws3Dj1elce@#?WCiVkL zr7~4?83j+1#!|0Ib zwaD2eG!mL^2a>RdBvM{yuaYQVHc9BdB&5>wHfW!8*GS?Q4Xyb%Ir-h$mUzywR9q#M z46WcaCpe{XzhrFYa8B%Ve{B-UdI?JScaOE--Hkhl#Qo#3G%LKS*345})WNk1Gbx(j zVw#(E`_Gqp7GkApl2lD|?j$xtDk2+&LEosR7war{EH|E(4HtZ?Z&b)idi)7Jbf}B} zAl7cSzf5^cR__~Cuu>JABsQE7YdQfc<%COm!hz2GbRcF7q;gR4e*}m*a)G%! zm7fKK9fJrGL^=WC4lnz#B37B&O54&vO=vIMlf=3)v0}BwXI$pQmK6ErDp8$qklieU z4OY}Ra=U@Fj5_|6V7|WB;(Zgu*LEuHGiLZPGo%yJ`h*#|?!mF4059QtX8565Gwkm~ z9$&5W8mBgkgMJA^e{h>{3phFf7llX5IZP#45{n-<26k8T0JE1vysP26OtgQ_+Xvpd0Df@-OBIALdlfXGR}&=*y}ke=`8>Ply%u`;ZPV*Co<_ z3Bk=knK3AtDd=zl%ARBj(h)q4MNQ}h$rok97bh z$yAcr0=W}#rmo_QmKR-MZzkT1iLW%mpAc`GQd`62l8XIwF!MFoY-theAX|f#Sp|g? zux?-mBDcGz=b(GOTz_TlP-1Heh?g)34=S^qs*ZWJaxM3}ZK+Zpn+c&)4eu_lbKg3L zp9xhKf9)trw-3$Xo-Te7#~mdCBM}6F!1o<@wIZQWmXqFoY-Cz&x(ldg zAgM!GsX5xgw{{NV4P7LjULX9QRQQSTLGZ7Mrw;L(7>ryD8*>qfxln+SZ2x#+AHSy9 zj48gt?)E^}6_y>d3%$<#+4S0&-U+Czy@iMNe}p{xGOFC&(M%UqnHp8TLT_QG7OZ>` z8!ql-r!`$+J4v?7qKRR2Jz^Fm=xSl_mD8Rr^9s4PO*dmsI`wjQs{DH!clWT{^jHl zzN)s7tjsv9n>orXIk|wb{sJS~Eu~j%Jx*)jVv~T6#q>6^%NbD;M`AW6ok_gee@L>j zs&C^fP?ONtr$d|1yUJz7y6@RMEz+Ai7?X6r>}1dKqo*8?#De}IPuHGvj5-|4N)s+} zET=~M4Ub3O-I|p1&r+oJ9>ryS5YVK-1Zgnc(}<11$ikdW7Z?%iDl{*SOB%K3a_w%| zmvh>2?6jp3xGM=$YD8K}0HJeTf5p`z+6_Vy%$H_E(YG@uIA9kng6K})s{tce`g(JBG)sE zokY3%l674tt4DfJ7OiSYZkAr@^8+exjTGKDx8UThI&53D%KaD@E}#_Wov(QJUl^$wBdua!ynxh-w0BrM>)fWHW}#BbDE~T&3xwL}}G@_UY_V%m`JUH06XFbK*?IGw0fZyrh`@ zh#F4SXXKb=S<&(cIVFOn;`9I@E50@^q2f6DsHYJT9g17#w?Tf3BMyC!zB*%`ckh z>e6r~KP#s1Uf;;-SEOSv;HBrv!@fr&*4Mke$(!$ba( zhThL~F5|4+L%0Cif7ExXOCV(GPa`gt$POo3JzOpT&a(0BobZPu|L`k|>kzdbFg z)K|nC3eQ-3W+l`twDqwr7@n=ym1@ZCZy(Rk<5g+CpCwFYupwBMkU{Lc6qSibyzgFi zEOAuE$nNdh+Nw<(|AO?efnI?^-bXX->B7mu?T$*#$|BOgf9;YtqaEyX^wdGW2{h=7 z=$jm;I$tU3x5HdVi$6m0m|fhQ7?Y`DX^_ersPEV0dOMg4Sm_I39sJRwvr8S?G{Hw6 zAgFcLUO-}+^6E={F22pIXS=xaZ|&@ray@qNl&|jwRaf?>nUZOod4*o?#sqf(z5R0g z^v~tiGmEp0f4`AsgAiUpz3?|d)%-lWPFnesPQm!xZ`j6fI4KZWADI2+vnNPxuFz2> zc|2Ec?o`L<2EUU+?vHs6+l*mf6<%>6PfYI|>GAoW%$R;&H!A$|_DOEq}QFlk*@;XX07dBSX?8&c9DpcQx7U z!=`AhfzhzVLw;SCuP)u>QHpLdQ;L2$lf{tl4U_eNGK!Nkn9s4~#Fv0!eVj}A98E46xsEUPkWqjpPbhN$ zOx{MBm#6e0%L0D#Da!|83V``soB}zK1(H}LpR-d4j1nVGAqJLl3MuP=G7{t1DWVZ1 zlo6k?af;66Yy5u{J6ESZE-5MEY9ucbwj<3f&k7x2` zRl!FFm$@LYyHP9#F^iQ?MrI8Po0#}w4$%ct%%AfVC?`;O!n(2%ggZ=d7Q>5&(Pb&+ zBr#i%%P!NSXF8$SjIWo-lAA&f8?hcnFCsiF{$;k(~453FCPVW)hzi?@-90DRzINn5iblqL`^BhN8sskQjqf$d~f; zR%RAk@+gYQypt@&h7jlTD1=Nt9*PS{xMoG1Y;_hm1Mx)GW;=<7)^Tu{sTWrBO(?mERn#mMq?$;5Aoi}6Y~U@* zIr-G49P~Vymr`Se3oNAAT}bA=D@j4J9Q%Jc*8?9ISjrAZCbPf_F*#40oHAs{B}c*r zg2}2$Ru4vUF3Jc@zS7Gu6Im1vnCUO155)Ic$!~$H&SZs{p;%T5Ul7Y$3G(1jY|K>= zf@C!-RN#Qi2}`CAOnxh&8G@8l;Xap#{IT4ga&U>wqRBFe4`VjER8EL&vPqISN+^Hv zF^Ujb{3!JmGFg%oHXcgOwW1X#IZm*dW1O*g=uql4TM5||Y5M&0o9{k6|9JWQ{pJ08 zzF8_GI|wg)bbMr4tlTd(_D+xdnJ50y&oA;hTlbQda~!~V97uoXV?e#8!wB^5<_nhN zuQ&hw@ZH1BS8tv^egAa1d;ak7@hyMYx9+od?{2=meY|{p&drCJ{&Mr}&d{7F>}kOQRZYNuQ-w4LpxIT$X!Z*1(T7aIFUMfCfP| zu!~fpiN9wlOp&bm!136z#uj#^3uEbgD_uGuUDTPq+KGytX7=bg`8`XTRQi95+TE4& z^Eei*vae9>zHjR|*5)Piir`B1Y|5*N7b;(wTogoAa+46Vgd*P`K0H2&c#y^W`u1tI z@6DGFpIGu(RgflfQNgFiA<69M{vJFsea=T#e zI?BC;eP>XUQPZ|c6BJOSOR)q{q?ZtSM^KO^1f_S7-XWBmB9R^eDbf*;q6Cl@30(-% z1rmCZ4xvd25bDSK{P^a1f4y^Nc6QF3UpsrQJ+o(bugFnd)wD8Z4js+Nsz)Vb0B5o8 zyRjSgXolDWJ6QbB&zU(Njjo#M?rqi>2hvyM`Vu95=pG89^#WRMxu zWB1&*;BOYdP!t$iLzs41jj7vQcB!ZeuiZh9src3V-`HWY61NI(a)-Uq+*2W2o~QaqT18#KCgH1(QL$+}l`7oiUwr+|+WcaFwt^SSBYy!b7P z6tL5J8pGhWV&!-Fy^yitPsfpoO1`keTb_!!@$?#d&3X|#h(+JzA}YbLMXB2$)GW2R z1nL|)C$?GLHWgZd`9e4m5Um{*u|0q1VxJcI+<4y*)8d3L!ao+To7wOAF-o?7F2&OF z_W^qKL5ru3%wHfco&jfo%HSq@&GSB9k8;DK$+h!Exmf<=jy}VNfmw>Ug~K>HLp6;n zg_hO!j{x$v7f?g;&eKW1%s#FT&FO6{$lSfd^0ZFvhF7cle0rkNj8bX&()ka#*|KWO zw6QXpHGoFjAttV-)T2LXvX5Xa;WCcU>$`=y6^IFp2K24C&0oBah<9!9o!kPex(q25 z~S?rAtxm;J=G&4i$`o7+FHqYTkVPI$?@A@L^01 z&UjOF1oYPu*6Ms;*s&X@w!O2ww#CStU{r?>Y1L zGpav&H_2w<|;T6qcS$(~f z4NwUdFHDqP^Tg7|O~YxgAFAX~8~6Vb_n=}Ps{bR% zI+^Irb%GRk$vT1muQ7evBlV#oUEC3mTiO&@6@n(%Tlf=4 zdy)kdy(LJMRjH8Uk*l(b%U##pg}0V&`XkT^2XUs1 zFl&MqDJ}&|xv^(q#`$;D#(`Gb@NK^DkDBhcG6M4OPUd$Hv0o`1_03p&oX~u5Y_F5V zy~lbGnsr2ysMr!Vg>k#O^r8J{wVhEK#*MNH-#p$KpUP6K@rgx+HmkxP%bT$zHvzVG z>IZPuKZiei0ZgQN(X*}S#Wf)#M|NWDyl$|M||^79mB{s z&jeTfd{-sQ?}6ep=40fz^PBn-Y;oDK(8Y@yjX|-(;tdi}(;=_ulNtts&EF@G&vN*g zY3oEV5Lbvcv^iU`**&%iR$c`M^06K*HE+CeSns~#OHU1HOuKjHp)=OKKF@4wstg%M zX4Cj>A+-t{PNNz|)b~@rxQUO_)&@SBev7_K)0?_;Bc;+DO_aR#(UmT#&Y|X~hWg^g zG3-YUO~`B-*Ch6Rf|C+714U*cUTS>lv(w@VT%;Wq<_1j%oaC%c27r^V2vHEyL(kM> zDKfq0Y8=Jn@u5)nkKWRsy))A*UHt-ECLRo-_z$bby7O10BY?YJ?4hGeKwhWSy)5x`KPa0^qgH!r5%N($9J8ygGGW+_s5k5|xn$RzJL=~LI z?OD*_%hdVVOC(-RIs!hF6xb>5lPW4mJPoJz}gAir}6>G(0I znyG!oWaq!fx*1dSk~S6mMrwA8X66eJrZ+f@`iaWl-98d8lzmZUmm77UNY5puUiAm8 zf6|xkEv#BqH`)IpR&QKvRA>exSE%1|GX`bLCp}BO^RRqkh`I9k^^T8LiLfZ! zXT$A`MVtQjPN{C6ly~{Ni!)_@vi7+@nQ_1>R|uldZb^bXUytY3xVY1uL9t-Z*( z%3X%0d1x)Pq+ep}fNS&2&Woc3O+m&6}Pk1)U=!rWgr`G~j%He)poZXL2yW z?=RPOx!W&%@GGEY#y(TfN%Af5()QxV_)J)Z?(9}j{JATUYH)?AB2zw9C~&wLO+4J4 zzZjWW-t8B>YHjAqTmt*v#Qow`PWd1kaBugO4O-5Q zJC1_)*Ncmjy9MV$2;1z~>&XWznNzuY_oKKJu~TVKXD+eSwVwIC<*ofK#MO8$x6Vq% zfgkn&mB~`}zP)F_+Vz1{{KVN9)&|`Wvec(>;aLy_l90?i@;gorD$2>ZG4~J}Se7pO z@$5Gj!0;NgCwUn{vrSJFAr=@Cz@I_w8rOSqGJqq6^D5GUkr7-IglYDu`LS5NvgKc!~c^U|1WquG1sAyuMEsq=9IgU7$& zJzYkKr-3C9xLy8)#x^M2D__c1n|Rgno5K$1%I!RS=6#sHSB3It9Ha2?%1&9JZ2H{9 z4^->3zqU0EH0BHP?X<$hU~?~X73Zm=}Yo;`Y|qH=imsA_qQ3TKyE3G+0yyR|S8DH2in6(Y23P324;wMri^ z(`!~;V*BSI-usgz+B<{AE8Bs!E{i0K^+(^jWq(Qr2l;Es>Y)VDNYNWPC!0D~nYk4) zix=Iuol=FKQ5jlll<+w+-;mZUy-gGc`i!c+z*=C&O;6gFpJ@~SZhRiQ5DHPF6ZFgV zPnMr=6lzF|COUn!g6@Q54GmECwr!j7<(Of%qu3^T&6=_hH*b-z5XUW-{Zl*jI3|p4 zlP>>_)$Orwz6Q7YrGt!94O@%hSWYU`X9eD=N?vZyrAIOz{#Q0KZF-E9vi`9)88@kt zu(gDZowb;)q_w0iSOP3!CoTqt*hq*7gKexVZ6t-|Wd5%cX|eib>Hrx0-{g?u<9<4f z8w_2$vgp(B2U?x84D`}kWYSUWd{JMlb{ICBVom8QEl3i-SP6zcU{t|Mdx>88h*y`! z&t#iz`R=}cExSG&k*5-kbNk1G_5){!jlJ7HPB-Ncwr@tYgs%S}ChHZK=4WH*T(EV4Uy3HMg0X4`!fnu+-WYir_y?nfaV2)?T+;6>hg% z+-_qWr!%&EQilPLGfr6sZicc~-Sfo=j2rN2OL(cxK+2X`a&%F?!2#ltwTukJ-ENl^JT@aLnu}kWcpf(sB26Mk4)lTx<3{8wT!v} zUEno|cXH=P{vudKURVtHk*t2Z%+~B{k4I5TEHX~VOu0*Mw`QYm`7Lrx$DX>&bF@bI z12<_G@AhE|{BI)`DP^LI|J4{}OJ`O?e!aQ9FyRcBvx+?ZTrTMxPyei}?X)i;%Q0tp zWsS#hdroupjZ{HLgC`cWs{W<3?6_XTQ^TsGY{Dr?Ch4IS4w?bVRoTJqTb=WDEHV9F z>^uR_fS5Gk(S&m-_fHiOlkc3I>4l*xz|Lx~)wx@Gcg07}vT6}jkF0pWiN$f{nkG7CXIA~7(k*bNTfu!Y0~S&VZkl{Ve?ge**x@4e;s{RZU* zULmy|s7DuWRsEvL%#nhn#>yUNYnUai>7P-V$I5y%PMpBYPfW&QWea{Uom>ptH!5!0 zYH$P3k=ot`<8S6ZpUM7|o8jyL}%~-sew&NP{hJA$0opmlHE^z@1XZf=rT~N`&S<^IV2xPl(CeaNN2Gy5Xvlu7|sZT2))>$;Q zBT?BcY`8l~=mq$kShr46-~z@j6O^!Y-KP4`WRKOu4-o10Ph!FX_uL=7YVU@SC3ngiScIgs zSKvGPPpdUDn$@!Sb|jSbO%+XJY>i&YMVv6sVx&vX{nj;#nm@ogxAn&6UnYIVR{Rrh zZi5BA)Mg!cH|fm=cjt-f)rvL!jhbv|VN zunWl=_z6J(HlPFyDO;~$(=!ouZcC9@U1C{UgF)|Pm1N;ROHK##?61oyHdU1Ulf>g( zUzK0Bse-OEzkfJR=M!YC$sSW)MO|mRp=_cw%pq;@eDgi*JWbE9C?rn1ijKg6Y_mnN zK`r>3Eog6UN1Ksx!b?tOPvHSfgJ%{y4d(d)V9;a&| zwzr;eblvl(8vIjWpB?cN#Z-}(unLnI`zOU{b$7PaN{WDAPn_$ZoBn{%*)f)_us4b} zFaqLXJR5JuEco$FRk@cH^0Lw;nbr@;t#3I;o{Y{$qunI}jGYlM9?raNtw9d_&CQ75 zD$c_z-zQvSi;56RYFdYO$R&Z2ltG;8UxeT)Ey@QKw3vJlCov`RWXt>Gq}u-=I3afJ zPT-%_VEP1%aJm9wmOmQ8NVs{0wpc*o_uW3bTq)QZq%safN_e^c#OFy}oA3B_=^?MU zIhu9{ws0w$4fHyMAcn117L03cE*ZNd%>l7Q=|Y$nuWDprgW7HMS@07^>6e3!c$vn~ zGzj^9Q`mscX`UeXmj{G{(6G*wu}x$ns$|7Ig^<_}CCB=dxTyv6__n!%0GRVk6~&=S zQ`yd1cX0@UbD||OoJMwa_eS)GS4{(sB|C3|IZX?5BKH3YUyT)#64@|d4J&ZJjh``4 z(7sNv^_0(wDRmBxcl#$|)&|46g_2{voK@Jf4>W&ONgTF?IOBAkuHp4rVwH7UqP#U< zs-5qBPB#rx#gHyG z_nhzHklW-`iMh#E$;(Rm-b$0{Q}J0M)S&DqMe1HrvANvIEVkHR64}aU5^*AD8t>%6 zj_jgNkIwXX?Al(ABJ=q)3!mD3u-#!=V*MkbK?;fgU0TqT5YM9vbk+azC-w0j-d$zJ zGkf0f+NIA?3y$&9$?dN$IVzT83P?w+>hA2v8ig|WGgSN)Z%l|Zdf!>%r}}iZgO2Z) zXJcgjE{$9}+MkvXdvsl{gB4r6CoI>-dj;7>@q@eUqn7;5t!c+pa|=#E5rpy9rX@Xz zg+Oe5Ci2>vR9zInBR7Br-AcTH{!11`>S?zj;|^6Y*@KjL*LeJn=Cs z5k={yzi&v8V)vmApnLVpCM#kcax3kxRTkowhb&&dzOc^CK)O~ewZFqNd z$2=cdz25K{S+@Sps2pXU;@|bFLL%^-$W<}d5pa$R_5#3BryFyD$!;5ccx2S6W97uj zt7%SO#q?>HvP;K3wcTSA(VrFmL60>r%w*V~r%$((BEdw8T@`~&86%mYEzJvEnWvZU nPBwB%9qpX*{Pkp034qtVMD4T>revH{U Date: Fri, 9 Oct 2026 03:29:25 +0200 Subject: [PATCH 09/10] =?UTF-8?q?docs(15-6a):=20revue=20de=20code=20P1=20c?= =?UTF-8?q?lose,=20statut=20done=20=E2=80=94=20rebas=C3=A9e=20sur=20ec745d?= =?UTF-8?q?0c,=20gates=202906/2906,=20Vitest=201091/1091,=20E2E=20245=20/?= =?UTF-8?q?=207=20KF-029=20+=202=20hors=20branche=20(refs=20#473,=20refs?= =?UTF-8?q?=20#523)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- ...6a-avoir-credite-la-creance-de-la-vente.md | 28 ++++++++++++++++++- .../sprint-status.yaml | 5 +++- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/_bmad-output/implementation-artifacts/15-6a-avoir-credite-la-creance-de-la-vente.md b/_bmad-output/implementation-artifacts/15-6a-avoir-credite-la-creance-de-la-vente.md index 0f8841b8..2256316d 100644 --- a/_bmad-output/implementation-artifacts/15-6a-avoir-credite-la-creance-de-la-vente.md +++ b/_bmad-output/implementation-artifacts/15-6a-avoir-credite-la-creance-de-la-vente.md @@ -2,7 +2,7 @@ ## Status -review +done