From b8dc3cb696f63f900c42ba652a6d3444299ebf65 Mon Sep 17 00:00:00 2001 From: tristan Date: Mon, 8 Jun 2026 14:40:18 +0000 Subject: [PATCH] =?UTF-8?q?Correctifs=20=C3=A9cran=20Client=20(ERP-115)=20?= =?UTF-8?q?(#76)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Lot de correctifs sur l'écran Client (M1), + un retrait de règle métier et une petite fonctionnalité. ## Formulaire client (création / édition) - Boutons « ajouter un bloc » (Adresse, RIB) désactivés tant que le dernier bloc n'est pas valide. - Onglet Information : bouton Valider désactivé si aucun champ rempli (création) ; onglet Contact accessible dès la création (Information facultatif). - Champs « Relation » (Distributeur/Courtier) et « Prestation de triage » masqués par défaut, révélés seulement si une catégorie ordinaire (≠ Distributeur/Courtier) est sélectionnée. - Bloc RIB affiché uniquement si le type de règlement est LCR (création, édition, consultation) ; plus de RIB fantôme soumis. - Alignement du bas du textarea « Description » sur les autres champs. ## Recherche d'adresse (BAN) - Une erreur de l'API ne bloque plus définitivement la recherche : chaque frappe réessaie (le mode dégradé restait verrouillé). - Garde minimum 3 caractères avant l'appel à l'API. ## Répertoire client - Titres de colonne en noir 16px, corps + tags de site en 14px. ## Navigation - L'onglet actif est conservé au passage consultation ↔ édition (via history.state, hors URL). ## Règle métier - Retrait de RG-1.04 : l'onglet Information n'est plus obligatoire pour le rôle Commerciale — facultatif pour tous (back + tests + docs). Tests : suites front (Vitest) et back (PHPUnit) vertes hormis flakes d'infra connus. Reviewed-on: https://gitea.malio.fr/MALIO-DEV/Starseed/pulls/76 Co-authored-by: tristan Co-committed-by: tristan --- README.md | 2 +- docs/specs/M1-clients/cahier-test-back-M1.md | 9 +- docs/specs/M1-clients/spec-back.md | 9 +- docs/specs/M1-clients/spec-front.md | 16 +-- frontend/app/layouts/default.vue | 7 +- .../components/ClientAddressBlock.vue | 49 ++++--- .../__tests__/ClientAddressBlock.spec.ts | 73 ++++++++++- .../composables/useClientReferentials.ts | 4 +- .../commercial/pages/clients/[id]/edit.vue | 109 ++++++++++++---- .../commercial/pages/clients/[id]/index.vue | 26 ++-- .../commercial/pages/clients/index.vue | 29 ++++- .../modules/commercial/pages/clients/new.vue | 120 +++++++++++++----- .../utils/__tests__/clientFormRules.spec.ts | 98 ++++++++++++++ .../modules/commercial/utils/clientEdit.ts | 6 +- .../commercial/utils/clientFormRules.ts | 94 +++++++++++++- .../modules/core/pages/admin/audit-log.vue | 7 +- frontend/modules/core/pages/admin/roles.vue | 4 +- frontend/modules/core/pages/admin/users.vue | 2 +- .../modules/sites/components/SiteDrawer.vue | 2 +- frontend/modules/sites/pages/admin/sites.vue | 4 +- frontend/package-lock.json | 28 ++-- frontend/package.json | 2 +- .../components/audit/AuditLogDetail.vue | 6 +- frontend/shared/components/ui/PageHeader.vue | 11 +- .../shared/utils/__tests__/historyTab.test.ts | 33 +++++ frontend/shared/utils/historyTab.ts | 22 ++++ migrations/Version20260601000000.php | 14 +- .../ClientAccountingCompletenessValidator.php | 6 +- ...ClientInformationCompletenessValidator.php | 77 ----------- ...pplierInformationCompletenessValidator.php | 2 +- .../State/Processor/ClientProcessor.php | 45 ++----- .../DataFixtures/ClientFixtures.php | 2 +- .../Core/Application/Rbac/RbacSeeder.php | 4 +- src/Module/Core/Domain/Entity/User.php | 4 +- .../Contract/BusinessRoleAwareInterface.php | 1 - src/Shared/Domain/Security/BusinessRoles.php | 14 +- .../Database/ColumnCommentsCatalog.php | 14 +- tests/Module/Commercial/Api/ClientApiTest.php | 4 +- .../Commercial/Api/ClientRBACMatrixTest.php | 32 +---- .../Commercial/Api/ClientSecurityTest.php | 4 +- .../Commercial/Unit/ClientProcessorTest.php | 88 +------------ 41 files changed, 652 insertions(+), 431 deletions(-) create mode 100644 frontend/shared/utils/__tests__/historyTab.test.ts create mode 100644 frontend/shared/utils/historyTab.ts delete mode 100644 src/Module/Commercial/Application/Validator/ClientInformationCompletenessValidator.php diff --git a/README.md b/README.md index 5388528..b01b52c 100644 --- a/README.md +++ b/README.md @@ -141,7 +141,7 @@ Disponibles uniquement après `make db-reset` / `make fixtures` (état « avec s | `bob` | `bob` | ROLE_USER | — | | `bureau` | `demo` | ROLE_USER | clients : view + manage | | `compta` | `demo` | ROLE_USER | clients : view + accounting.view / manage | -| `commerciale` | `demo` | ROLE_USER | clients : view + manage (Information obligatoire — RG-1.04) | +| `commerciale` | `demo` | ROLE_USER | clients : view + manage | | `usine` | `demo` | ROLE_USER | aucun accès clients | --- diff --git a/docs/specs/M1-clients/cahier-test-back-M1.md b/docs/specs/M1-clients/cahier-test-back-M1.md index 33fe309..f0b58e4 100644 --- a/docs/specs/M1-clients/cahier-test-back-M1.md +++ b/docs/specs/M1-clients/cahier-test-back-M1.md @@ -10,8 +10,8 @@ trous, zéro duplication »). ERP-60 n'écrit QUE les tests des RG non déjà couvertes par la stack, et mappe ici l'intégralité des RG (existantes + nouvelles + déléguées). Les tests dépendants -des **rôles métier** (matrice RBAC bureau/compta/commerciale/usine + RG-1.04 -fonctionnel) sont **délégués à ERP-74 (#493)** : ces rôles n'existent qu'après le +des **rôles métier** (matrice RBAC bureau/compta/commerciale/usine) sont +**délégués à ERP-74 (#493)** : ces rôles n'existent qu'après le merge de la stack. ## Mapping RG → test @@ -21,7 +21,7 @@ merge de la stack. | ~~RG-1.01~~ | _(supprimée V1 — refonte-contact)_ contact inline retiré du Client ; complétude couverte par RG-1.05 / RG-1.14 (`ClientContact`) | — | refonte-contact | | ~~RG-1.02~~ | _(supprimée du Client V1)_ téléphones inline retirés du Client (testés sur `ClientContact`) | — | refonte-contact | | RG-1.03 | distributor/broker exclusifs + type catégorie | `ClientApiTest::testPostWithDistributorAndBrokerReturns422` ; `::testPostDistributorReferencingNonDistributorReturns422` ; `::testPostValidDistributorReturns201` ; `ClientProcessorTest` (unit) | ERP-55 | -| RG-1.04 | Onglet Information obligatoire pour rôle Commerciale | `ClientProcessorTest::testCommercialeIncompleteInformationIsUnprocessable` ; `::testNonCommercialeSkipsInformationCompleteness` (unit, dormant). **Test fonctionnel + durcissement → ERP-74** | ERP-55 / **ERP-74** | +| ~~RG-1.04~~ | _(SUPPRIMÉE)_ Onglet Information désormais facultatif pour tous les rôles (validation de complétude retirée) | — | ERP-55 / ERP-74 | | RG-1.05 | Contact : prénom OU nom → 422 (CHECK) | `ClientSubResourceApiTest::testPostContactWithoutNameReturns422` | ERP-57 | | RG-1.06/07/08 | Adresse prospect exclusive de livraison/facturation → 422 (Assert\Callback + CHECK filet) | `ClientAddressTest::testProspectAddressCannotBeDelivery` ; `::testProspectAddressCannotBeBilling` | ERP-60 / **ERP-76** | | RG-1.09 | Code postal `^[0-9]{4,5}$` → 422 | `ClientSubResourceApiTest::testPostAddressWithInvalidPostalCodeReturns422` | ERP-57 | @@ -60,8 +60,7 @@ merge de la stack. - **Matrice RBAC différenciée** par rôle métier (Bureau / Compta / Commerciale / Usine) : 200/403 par verbe et par onglet selon le rôle. -- **RG-1.04 fonctionnel** : PATCH onglet Information par une Commerciale avec - champs incomplets → 422 ; même PATCH par Admin → 200 (+ durcissement code/spec). +- ~~**RG-1.04 fonctionnel**~~ : _(SUPPRIMÉE)_ l'onglet Information est désormais facultatif pour tous les rôles. - Raison : ces rôles métier ne sont seedés qu'après le merge de la stack M1. ## Gaps & suivi diff --git a/docs/specs/M1-clients/spec-back.md b/docs/specs/M1-clients/spec-back.md index fb30551..c5706d2 100644 --- a/docs/specs/M1-clients/spec-back.md +++ b/docs/specs/M1-clients/spec-back.md @@ -310,7 +310,7 @@ CREATE TABLE client ( distributor_id INT REFERENCES client(id) ON DELETE SET NULL, broker_id INT REFERENCES client(id) ON DELETE SET NULL, triage_service BOOLEAN NOT NULL DEFAULT FALSE, - -- Onglet Information (Commerciale obligatoire — RG-1.04 — null sinon) + -- Onglet Information (facultatif pour tous — RG-1.04 supprimée) description TEXT, competitors VARCHAR(255), founded_at DATE, @@ -864,8 +864,7 @@ Cf. § 2.6. Pattern Shared standard. ### Onglet Information -- **RG-1.04** _(durcie — ERP-74)_ : Pour un utilisateur portant le rôle métier **Commerciale**, **tous** les champs de l'onglet Information (`description`, `competitors`, `foundedAt`, `employeesCount`, `revenueAmount`, `directorName`, `profitAmount`) sont obligatoires sur **POST et sur tout PATCH**, **indépendamment des champs réellement envoyés** (la condition d'intersection avec `client:write:information` a été retirée). Pour les autres rôles, ces champs restent optionnels. Implémenté via un validator custom `ClientInformationCompletenessValidator` invoqué systématiquement par le `ClientProcessor` quand le user porte le rôle Commerciale. - - **Conséquence** : le POST n'exposant que le groupe `client:write:main`, l'onglet Information n'y est pas renseignable → une Commerciale obtient **422** sur tout POST (cf. § 8.1). La complétude se fait donc via les PATCH `client:write:information` ultérieurs. Un Admin (non gaté) crée normalement (201). +- ~~**RG-1.04**~~ _(SUPPRIMÉE)_ : l'onglet Information est désormais **facultatif pour tous les rôles**, y compris Commerciale. Les champs `description`, `competitors`, `foundedAt`, `employeesCount`, `revenueAmount`, `directorName`, `profitAmount` restent optionnels (colonnes nullable, aucune validation de complétude). Le validator `ClientInformationCompletenessValidator` et le gating par rôle métier dans le `ClientProcessor` ont été retirés. ### Onglet Contact @@ -883,7 +882,7 @@ Cf. § 2.6. Pattern Shared standard. ### Onglet Comptabilité -- **RG-1.30** _(ajoutée — correctif incohérence spec-front/spec-back)_ : à la **validation complète de l'onglet Comptabilité**, les six champs scalaires `siren`, `accountNumber`, `tvaMode`, `nTva`, `paymentDelay`, `paymentType` sont **obligatoires** (alignement sur spec-front § Onglet Comptabilité). Colonnes `nullable` en base (l'onglet est rempli dans un second temps, et l'onglet principal ne les envoie pas) + validateur contextuel `ClientAccountingCompletenessValidator` invoqué par le `ClientProcessor` — même parti que RG-1.04 (Information). Déclenchement : uniquement quand **les six champs sont présents dans le payload** (le front les envoie toujours ensemble via « Valider ») ; un PATCH ciblant un sous-ensemble de champs comptables (édition ponctuelle) n'est pas soumis à la complétude. Chaque champ manquant → 422 sur son `propertyPath` (mapping inline front, ERP-101). `bank` reste hors complétude (conditionnel RG-1.12). +- **RG-1.30** _(ajoutée — correctif incohérence spec-front/spec-back)_ : à la **validation complète de l'onglet Comptabilité**, les six champs scalaires `siren`, `accountNumber`, `tvaMode`, `nTva`, `paymentDelay`, `paymentType` sont **obligatoires** (alignement sur spec-front § Onglet Comptabilité). Colonnes `nullable` en base (l'onglet est rempli dans un second temps, et l'onglet principal ne les envoie pas) + validateur contextuel `ClientAccountingCompletenessValidator` invoqué par le `ClientProcessor` (parti pris : nullable + validateur d'onglet plutôt qu'un `Assert\NotBlank` sur l'entité). Déclenchement : uniquement quand **les six champs sont présents dans le payload** (le front les envoie toujours ensemble via « Valider ») ; un PATCH ciblant un sous-ensemble de champs comptables (édition ponctuelle) n'est pas soumis à la complétude. Chaque champ manquant → 422 sur son `propertyPath` (mapping inline front, ERP-101). `bank` reste hors complétude (conditionnel RG-1.12). - **RG-1.12** : Le champ `bank` est visible et obligatoire **uniquement** si `paymentType.code = 'VIREMENT'`. Validation server-side dans le `ClientProcessor` : si `payment_type.code = VIREMENT` et `bank IS NULL` → 422. - **RG-1.13** : Les champs RIB (`label`, `bic`, `iban`) sont obligatoires si **au moins un bloc RIB est présent ET** `paymentType.code = 'LCR'`. C'est-à-dire : - Si `paymentType.code = LCR` ET `client.ribs.count() = 0` → 422 « Au moins un RIB est obligatoire pour le type LCR ». @@ -938,7 +937,7 @@ Cf. § 2.6. Pattern Shared standard. - [ ] ~~RG-1.02~~ _(supprimée du Client V1)_ : plus de téléphones inline sur le Client (téléphones testés sur `ClientContact`) - [ ] **RG-1.03** : POST avec distributor ET broker → 422 ; POST distributor seul → 201 - [ ] **RG-1.03** : POST distributor référençant un client SANS catégorie de code DISTRIBUTEUR → 422 (validation custom `ClientProcessor::hasCategoryCode`) -- [ ] **RG-1.04** : PATCH onglet Information par un user Commerciale avec champs incomplets → 422 ; même PATCH par Admin → 200 +- [x] ~~**RG-1.04**~~ _(SUPPRIMÉE)_ : onglet Information facultatif pour tous les rôles (plus de validation de complétude) - [ ] **RG-1.05** : POST contact sans firstName ni lastName → 422 (BDD CHECK lève une exception) - [ ] **RG-1.06/07/08** : POST adresse avec isProspect=true ET isDelivery=true → 422 / CHECK - [ ] **RG-1.09** : POST adresse avec postalCode invalide (3 chiffres) → 422 ; CP/ville incohérents → 200 (pas de validation stricte côté serveur) diff --git a/docs/specs/M1-clients/spec-front.md b/docs/specs/M1-clients/spec-front.md index 88a19ab..466d020 100644 --- a/docs/specs/M1-clients/spec-front.md +++ b/docs/specs/M1-clients/spec-front.md @@ -13,7 +13,7 @@ date_redaction: 2026-05-28 # === LIENS === maquette_figma: "https://www.figma.com/design/jRYgT0T9c03VsEbjGhCwwS/Composants---Design-System?node-id=1132-31898" -regles_metier: [RG-1.01, RG-1.02, RG-1.03, RG-1.04, RG-1.05, RG-1.06, RG-1.07, RG-1.08, RG-1.09, RG-1.10, RG-1.11, RG-1.12, RG-1.13, RG-1.14, RG-1.15, RG-1.16, RG-1.17, RG-1.18, RG-1.19, RG-1.20, RG-1.21, RG-1.22, RG-1.23, RG-1.24, RG-1.25, RG-1.26, RG-1.27, RG-1.28, RG-1.29] +regles_metier: [RG-1.01, RG-1.02, RG-1.03, RG-1.05, RG-1.06, RG-1.07, RG-1.08, RG-1.09, RG-1.10, RG-1.11, RG-1.12, RG-1.13, RG-1.14, RG-1.15, RG-1.16, RG-1.17, RG-1.18, RG-1.19, RG-1.20, RG-1.21, RG-1.22, RG-1.23, RG-1.24, RG-1.25, RG-1.26, RG-1.27, RG-1.28, RG-1.29] roles: [Admin, Bureau, Compta, Commerciale, Usine] lien_spec_back: ./spec-back.md @@ -105,13 +105,13 @@ Saisir les informations de l'entreprise. | Champ | Type | Obligatoire | Règle | |---|---|---|---| -| **Description** | `` | Conditionnel | RG-1.04 (obligatoire pour rôle Commerciale) | -| **Concurrents** | `` | Conditionnel | RG-1.04 | -| **Date de création** (de l'entreprise) | `` (exception Malio — pas de composant date couvert) | Conditionnel | RG-1.04 | -| **Nombre de salariés** | `` | Conditionnel | RG-1.04 | -| **CA €** | `` | Conditionnel | RG-1.04 | -| **Dirigeant** | `` | Conditionnel | RG-1.04 | -| **Résultat €** | `` | Conditionnel | RG-1.04 | +| **Description** | `` | Non | — _(onglet facultatif, RG-1.04 supprimée)_ | +| **Concurrents** | `` | Non | — | +| **Date de création** (de l'entreprise) | `` (exception Malio — pas de composant date couvert) | Non | — | +| **Nombre de salariés** | `` | Non | — | +| **CA €** | `` | Non | — | +| **Dirigeant** | `` | Non | — | +| **Résultat €** | `` | Non | — | **Action** : « Valider » → PATCH partiel `/api/clients/{id}` (groupe `client:write:information`). diff --git a/frontend/app/layouts/default.vue b/frontend/app/layouts/default.vue index 0fa7ab9..ee46cf8 100644 --- a/frontend/app/layouts/default.vue +++ b/frontend/app/layouts/default.vue @@ -2,7 +2,9 @@ Valeurs en dur issues de la maquette Figma (design Starseed) : - sidebar depliee : 232px (w-[232px], repli laisse par defaut 72px) - marge horizontale du contenu sur desktop : 170px (xl:px-[170px]) - - bande blanche sticky sous la navbar : 47px (h-[47px]) + La marge haute du contenu (44px) vit desormais DANS l'entete (PageHeader, + pt-11) et non sur le
: sinon, l'entete etant sticky, ce padding + laissait un trou blanc entre le SiteSelector et l'entete. A faire evoluer uniquement avec une mise a jour de maquette. -->