237 Commits

Author SHA1 Message Date
gitea-actions 6a83adc00a chore: bump version to v0.1.120
Auto Tag Develop / tag (push) Successful in 7s
Build & Push Docker Image / build (push) Successful in 42s
v0.1.120
2026-06-15 09:29:53 +00:00
tristan c76c447aa2 feat(front) : consultation + modification prestataire (ERP-145) (#107)
Auto Tag Develop / tag (push) Successful in 8s
Empilée sur ERP-144 (#106).

## Périmètre ERP-145
Écrans **Consultation** (lecture seule) et **Modification** (édition par onglet), peuplés depuis la **seule** réponse `GET /api/providers/{id}` (embed contacts/adresses/ribs + refs comptables — pas de N+1).

### Consultation — `pages/providers/[id]/index.vue` (`/providers/{id}`)
- Ouverture par défaut sur **Contacts** ; tous champs readonly ; onglets **Contacts · Adresse · Rapports · Échanges · Comptabilité** (navigation libre). Rapports/Échanges = placeholders « À venir ».
- Flèche retour → répertoire. Bouton **Modifier** (si `manage` OU `accounting.manage`). Bouton **Archiver** (Admin seul, `archive`) → modal → PATCH `{isArchived:true}` ; **Restaurer** si archivé.
- Comptabilité visible seulement si `accounting.view` ; banque/RIB affichés selon le type de règlement (VIREMENT/LCR).

### Modification — `pages/providers/[id]/edit.vue` (`/providers/{id}/edit`)
- Pré-rempli ; **bloc principal éditable** (Nom/Catégories/Sites, PATCH `provider:write:main` via `updateMain`) ; onglets Contact/Adresse/Comptabilité en **navigation libre**, PATCH partiel par onglet (réutilise `useProviderForm` en `editMode`).
- Onglets sans permission `manage` / `accounting.manage` restent **readonly** (pas de bouton Valider / suppression). Accès réservé à `manage` OU `accounting.manage`.

### Composables / helpers
- **`useProvider(id)`** : charge le détail (ld+json) + archive/restore (PATCH isArchived seul, puis rechargement).
- **`useProviderForm`** étendu : `updateMain()` (PATCH principal en édition) + `editMode` (completeTab ne verrouille/avance plus).
- **`providerDetail.ts`** : mapping embed → brouillons + options role-indépendantes (libellés depuis l'embed) + règles d'actions (Modifier/Archiver/Restaurer).

## Conformité
- `useApi()` only ; `Malio*` only ; `usePermissions()` pour boutons/onglets ; aucun texte FR en dur ; pas d'import inter-module (règle ABSOLUE n°1).

## Vérifications
- Vitest : 470/470 (16 nouveaux : mapping détail, actions par permission, updateMain + editMode).
- ESLint : OK · `nuxi typecheck` : 0 erreur sur les fichiers source du ticket.
- Golden path navigateur : **Consultation** (ACME) — bloc principal readonly + libellés catégories/sites résolus depuis l'embed, 5 onglets, Modifier+Archiver visibles (admin), Comptabilité readonly. **Modification** — bloc principal éditable pré-rempli (Site « 86 17 »), 3 onglets navigation libre, onglet Contact pré-rempli.

Reviewed-on: #107
Co-authored-by: tristan <tristan@yuno.malio.fr>
Co-committed-by: tristan <tristan@yuno.malio.fr>
2026-06-15 09:29:44 +00:00
gitea-actions 19ac8833eb chore: bump version to v0.1.119
Auto Tag Develop / tag (push) Successful in 6s
Build & Push Docker Image / build (push) Successful in 41s
v0.1.119
2026-06-15 09:19:42 +00:00
tristan c25c33116d feat(front) : onglet comptabilite prestataire (ERP-144) (#106)
Auto Tag Develop / tag (push) Successful in 8s
Empilée sur ERP-143 (#105).

## Périmètre ERP-144
Onglet **Comptabilité** de l'écran `/providers/new` — gated par permission + blocs RIB conditionnels.

- Champs (`Malio*`) : SIREN / Numéro de compte / Mode de TVA (`/api/tva_modes`) / N° de TVA / Délai (`/api/payment_delays`) / Type de règlement (`/api/payment_types`) / Banque (`/api/banks`).
- **RG-3.07** : Banque visible **et** obligatoire **seulement si** Type = `VIREMENT` (affichage conditionnel + payload `bank` forcé à null sinon).
- **RG-3.08** : blocs RIB (Libellé/BIC/IBAN) affichés et requis si Type = `LCR` ; « + RIB » gated (dernier RIB complet) / Supprimer (modal). À la validation, **POST des RIB AVANT** le PATCH des scalaires (le back valide RG-3.08 sur le PATCH).
- **Gating** : onglet présent uniquement si `technique.providers.accounting.view` ; **éditable** uniquement si `.manage` (sinon lecture seule). Masqué pour Bureau/Commerciale.
- « Valider » → PATCH `/api/providers/{id}` (groupe `provider:write:accounting`) + sous-ressource RIBs (`/providers/{id}/ribs` + `/provider_ribs/{id}`). Erreurs 422 inline (scalaires) et par ligne (RIB).
- `useProviderReferentials.loadAccounting()` (chargé seulement si l'onglet est accessible). Helpers purs `utils/forms/providerAccounting.ts`.
- i18n `technique.providers.form.accounting` + `confirmDelete.rib`.

> NB : les placeholders **Rapports / Échanges** relèvent des écrans Consultation/Modification (ERP-145) — le flux de **création** ne porte que 3 onglets (Contact/Adresse/Comptabilité), conformément à la spec.

## Conformité
- `useApi()` only ; `Malio*` only ; pas de masque email ; aucun texte FR en dur ; pas d'import inter-module (helpers ré-implémentés côté Technique, règle ABSOLUE n°1).

## Vérifications
- Vitest : 454/454 (18 nouveaux : helpers compta RG-3.07/3.08, workflow VIREMENT/LCR, ordre RIB→scalaires, 422 inline + par ligne, lecture seule sans manage).
- ESLint : OK.
- `nuxi typecheck` : 0 erreur sur les fichiers source du ticket.
- Golden path navigateur : page compile, onglet Comptabilité visible (gating accounting.view OK pour admin). Contenu de l'onglet gaté derrière le déverrouillage des 3 onglets (multiselect `Malio` non pilotable en a11y) — couvert par les tests unitaires + typecheck.

Reviewed-on: #106
Co-authored-by: tristan <tristan@yuno.malio.fr>
Co-committed-by: tristan <tristan@yuno.malio.fr>
2026-06-15 09:15:20 +00:00
gitea-actions 17aa61d014 chore: bump version to v0.1.118
Auto Tag Develop / tag (push) Successful in 7s
Build & Push Docker Image / build (push) Successful in 40s
v0.1.118
2026-06-15 09:14:47 +00:00
tristan 3d4ae391fe feat(front) : onglet adresse prestataire (ERP-143) (#105)
Auto Tag Develop / tag (push) Successful in 7s
Empilée sur ERP-142 (#104).

## Périmètre ERP-143
Onglet **Adresse** de l'écran `/providers/new` — saisie multi-adresses (blocs ajoutables) via la sous-ressource addresses.

- **`ProviderAddressBlock.vue`** (miroir `SupplierAddressBlock` **simplifié**) : Sélecteur de sites (≥1, RG-3.05) / Catégories (PRESTATAIRE, RG-3.09) / Contact(s) rattaché(s) (depuis l'onglet Contact) / Pays (défaut France) / Code postal / Ville / Adresse (autocomplete BAN) / Complément. **Pas** de type d'adresse, **pas** de bennes, **pas** de triage (différence M2).
- **RG-3.06** : `useAddressAutocomplete()` **réutilisé tel quel** — CP → liste des villes (BAN) ; cas dégradé (API down) → ville/adresse en saisie libre + toast unique.
- **`useProviderForm`** étendu : `addresses`, `canAddAddress` (RG-3.05/3.09), `add/removeAddress`, `submitAddresses` (POST `/providers/{id}/addresses` + PATCH `/provider_addresses/{id}`, groupe `provider:write:addresses`), erreurs 422 **par ligne**.
- **`useProviderReferentials`** : ajout des pays (`/countries`) pour le select Pays.
- Helpers purs `utils/forms/providerAddress.ts` (`isProviderAddressValid`, `buildProviderAddressPayload` — relations en IRI, requis vides omis au POST).
- « + Nouvelle adresse » / Supprimer (modal) / « Valider ». i18n `technique.providers.form.address` + `confirmDelete.address`.

## Conformité
- `useApi()` only ; `Malio*` only ; aucun texte FR en dur ; `useAddressAutocomplete` non réécrit ; pas d'import inter-module (helpers ré-implémentés côté Technique, règle ABSOLUE n°1).

## Vérifications
- Vitest : 436/436 (18 nouveaux : helpers adresse, bloc — BAN dégradé/allow-create/mapping erreurs, workflow adresses POST/PATCH/422 par ligne).
- ESLint : OK.
- `nuxi typecheck` : 0 erreur sur les fichiers source du ticket.
- Golden path navigateur : page compile, onglet Contact OK. NB : l'onglet Adresse est gaté derrière la validation principal+contact (multiselect `Malio` non pilotable en a11y) — couvert par tests unitaires (montage + BAN + mapping) + typecheck.

Reviewed-on: #105
Co-authored-by: tristan <tristan@yuno.malio.fr>
Co-committed-by: tristan <tristan@yuno.malio.fr>
2026-06-15 09:12:50 +00:00
gitea-actions 04c794addb chore: bump version to v0.1.117
Auto Tag Develop / tag (push) Successful in 7s
Build & Push Docker Image / build (push) Successful in 39s
v0.1.117
2026-06-15 09:09:56 +00:00
tristan c1e45cd582 feat(front) : onglet contact prestataire (ERP-142) (#104)
Auto Tag Develop / tag (push) Successful in 8s
Empilée sur ERP-141 (#103).

## Périmètre ERP-142
Onglet **Contact** de l'écran `/providers/new` — saisie multi-contacts (blocs ajoutables) via la sous-ressource contacts.

- **`ProviderContactBlock.vue`** (miroir `SupplierContactBlock`) : Nom / Prénom / Fonction / Email / Téléphone (x1, +1 révélable, **max 2**), erreurs 422 par champ (prop `:errors`).
- **`useProviderForm`** étendu : état `contacts`, `canAddContact` (RG-3.04), `addContact`/`removeContact`, `submitContacts` (POST `/providers/{id}/contacts` pour les nouveaux, PATCH `/provider_contacts/{id}` pour les existants, groupe `provider:write:contacts`), `submitRows` (erreurs collectées **par ligne**, non bloquant).
- **RG-3.04** : « + Nouveau contact » désactivé tant que le bloc courant est vide (≥1 champ parmi prénom/nom/fonction/tél/email — aligné back).
- **RG-3.12** : onglet non validable vide ; une amorce vide est soumise pour déclencher la 422 `firstName` inline.
- Suppression d'un bloc → modal de confirmation.
- Helpers purs `utils/forms/providerContact.ts` (`isProviderContactBlank`, `buildProviderContactPayload`).
- i18n `technique.providers.form.contact/confirmDelete` + `toast.updateSuccess`.

## Vérifications
- Vitest : 418/418 (16 nouveaux : helpers, bloc, workflow contacts).
- ESLint : OK.
- `nuxi typecheck` : 0 erreur sur les fichiers source du ticket.
- Golden path navigateur : bloc Contact rendu, « Nouveau contact » désactivé tant que vide puis activé après saisie, révélation du 2e téléphone (max 2).

Reviewed-on: #104
Co-authored-by: tristan <tristan@yuno.malio.fr>
Co-committed-by: tristan <tristan@yuno.malio.fr>
2026-06-15 09:05:07 +00:00
gitea-actions a6f01400ba chore: bump version to v0.1.116
Auto Tag Develop / tag (push) Successful in 7s
Build & Push Docker Image / build (push) Successful in 40s
v0.1.116
2026-06-15 09:02:10 +00:00
tristan d0e9f48983 feat(front) : page ajout prestataire + formulaire principal (ERP-141) (#103)
Auto Tag Develop / tag (push) Successful in 8s
Empilée sur ERP-140 (#102).

## Périmètre ERP-141
Écran `/providers/new` — création par onglets + formulaire principal (POST).

- **Page** `modules/technique/pages/providers/new.vue` : en-tête + retour, formulaire principal (Nom, Catégorie, Site), barre d'onglets **Contact · Adresse · Comptabilité** (pas d'onglet Information ; Rapports/Échanges absents en création). Contenu des onglets = placeholders « À venir » (ERP-142→144).
- **`useProviderForm()`** : POST principal (groupe `provider:write:main`, IRIs catégories/sites), pré-check front RG-3.03 (≥1 site) / RG-3.09 (≥1 catégorie), 409 doublon (RG-3.10) inline, 422 mapping par champ via `useFormErrors`, orchestration des onglets (verrouillage + bascule auto sur Contact au succès), `patchProvider` (PATCH partiel mode strict pour les onglets à venir).
- **`useProviderReferentials()`** : catégories type PRESTATAIRE + sites (`?pagination=false`, Hydra).
- i18n `technique.providers.form/tab/toast`.

## Conformité
- `useApi()` uniquement, composants `Malio*`, aucun texte FR en dur, bouton « Valider » toujours actif + erreurs sous les champs (ERP-101).

## Vérifications
- Vitest : 402/402 (dont 9 nouveaux tests `useProviderForm`).
- ESLint : OK.
- `nuxi typecheck` : 0 erreur sur les fichiers source du ticket.
- Golden path navigateur : page rendue, catégories filtrées PRESTATAIRE, sélecteur site, onglets désactivés avant validation, erreurs inline RG-3.03/3.09.

Reviewed-on: #103
Co-authored-by: tristan <tristan@yuno.malio.fr>
Co-committed-by: tristan <tristan@yuno.malio.fr>
2026-06-15 08:59:39 +00:00
gitea-actions c1206fa29c chore: bump version to v0.1.115
Auto Tag Develop / tag (push) Successful in 6s
Build & Push Docker Image / build (push) Successful in 43s
v0.1.115
2026-06-15 08:51:28 +00:00
tristan 090ea5eb49 feat(front) : page repertoire prestataires (ERP-140) (#102)
Auto Tag Develop / tag (push) Successful in 9s
Page d'entree du pole Technique : repertoire prestataires (route /providers).

## Perimetre (ERP-140)
- Page `modules/technique/pages/providers/index.vue` (route /providers, titre i18n technique.providers.title).
- `MalioDataTable` branche sur `usePaginatedList<Provider>({ url: '/providers' })` : colonnes Nom / Categories / Site (badges) / Derniere activite (updatedAt, format JJ-MM-AAAA).
- Clic ligne -> /providers/{id} ; bouton + Ajouter -> /providers/new (gate technique.providers.manage).
- Drawer Filtres : recherche, categorie (type PRESTATAIRE), site, inclure archives. Etat 100% local (jamais dans l'URL).
- Bouton Exporter -> /api/providers/export.xlsx (memes filtres).
- Pagination standard 10/25/50.
- Composable `useProvidersRepository` + cles i18n `technique.providers.*`.

## Garde-fous
- `useApi()` uniquement, composants `Malio*`, pas de `<table>` brut, aucun texte FR en dur.
- Cloisonnement par site laisse au back.

## Tests
- `make nuxt-test` : 393/393 verts (dont 3 nouveaux sur useProvidersRepository : ciblage /providers, enveloppe Hydra, exclusion archives par defaut).
- ESLint clean.
- Note : `nuxi typecheck` non concluant dans l'env (develop produit deja ~303 erreurs d'auto-imports non resolus, independamment de cette branche). La page et le composable sont type-clean.

Reviewed-on: #102
Co-authored-by: tristan <tristan@yuno.malio.fr>
Co-committed-by: tristan <tristan@yuno.malio.fr>
2026-06-15 08:51:19 +00:00
gitea-actions ee1f344764 chore: bump version to v0.1.114
Auto Tag Develop / tag (push) Successful in 8s
Build & Push Docker Image / build (push) Successful in 54s
v0.1.114
2026-06-12 14:44:56 +00:00
matthieu 3fe0f676f6 test(technique) : couvrir RG-3.x PHPUnit + capturer le contrat JSON (ERP-139) (#100)
Auto Tag Develop / tag (push) Successful in 11s
Ticket Lesstime #139 (M3 — Répertoire prestataires, position 1.9). DoD back avant le front : suite PHPUnit consolidée sur la matrice § 8.1 + captures JSON réelles dans la spec § 4.0.bis.

## Contenu
- **Fix réfs comptables** : `provider:read:accounting` ajouté sur `TvaMode`/`PaymentDelay`/`PaymentType`/`Bank` — sans ça elles sortaient en IRI nu dans le détail prestataire (réplique du fix ERP-92 du M2, piège #1 § 4.0.bis).
- **`ProviderSerializationContractTest`** (13 tests) : gating RIB/scalaires par omission, réfs compta en objet `{id,code,label}`, `isArchived`, embed categories/sites liste+détail, sous-collections, enveloppe AP4 ; `testDodReferenceJsonShape` dumpe le JSON réel (`PROVIDER_DOD_DUMP=1`).
- **`ProviderAuditTest`** (5 tests) : create/update/archive (`technique.Provider`), iban/bic dans le diff (`technique.ProviderRib`, pas dAuditIgnore), trace M2M `sites`.
- **`ProviderListTest`** étendu : `?pagination=false`, anti-N+1, filtre `?typeCode=PRESTATAIRE`.
- **`ProviderRbacGatingTest`** étendu : restauration en conflit de nom → 409 (RG-3.14).
- **`ProviderFixtures`** (§ 8.4) : démo idempotente (complet VIREMENT+banque+RIB, LCR+RIB, CHEQUE multi-cat, minimal, archivé) répartie sur sites 86/17/82 ; skip en env `test`.
- Helper `seedCompleteProvider` ; spec § 4.0.bis : gabarits remplacés par les captures réelles (liste + détail avec/sans accounting.view).

## Vérifications
- `make php-cs-fixer-allow-risky` → 0 fichier
- `make test` → OK, 677 tests, 3328 assertions (garde-fous globaux verts)

## Notes
- MR stackée sur ERP-138 (base = sa branche).
- Fixtures démo exercées en dev via `make fixtures` (autowiring vérifié).

---------

Co-authored-by: Matthieu <contact@malio.fr>
Reviewed-on: #100
2026-06-12 14:44:43 +00:00
gitea-actions d5462bcf42 chore: bump version to v0.1.113
Auto Tag Develop / tag (push) Successful in 8s
Build & Push Docker Image / build (push) Successful in 48s
v0.1.113
2026-06-12 14:34:35 +00:00
matthieu 54d8327fa5 feat(technique) : entités + repositories Provider* (ERP-133) (#91)
Auto Tag Develop / tag (push) Successful in 9s
PR **empilée sur ERP-132** (#90) — base = \`feature/ERP-132-migrer-schema-bdd-m3\` (ERP-132 pas encore mergé dans develop). À rebaser sur develop une fois #90 mergée.

## Périmètre (ticket Lesstime #133, M3 § 3.3/3.4/2.12/4.0)
Entités Doctrine + mapping ApiResource (squelette) + repository avec hydratation anti-N+1. Miroir des entités `Supplier*` (M2), **amputé de l'onglet Information** et **augmenté de `provider.sites`** (M2M direct, RG-3.03).

### Créé
- `Provider`, `ProviderContact`, `ProviderAddress` (simplifiée : pas de `addressType`/`bennes`/`triageProvider`), `ProviderRib` — `#[Auditable]` + Timestampable/Blamable.
- `ProviderRepositoryInterface` + `DoctrineProviderRepository` : `createListQueryBuilder` (filtres + tri seuls) + `hydrateListCollections` anti-N+1 (catégories puis **sites en relation directe**, requêtes `IN` bornées séparées — § 2.12).

### Contrat de sérialisation (RETEX M1 — 3 maillons)
Groupes posés sur l'entité (source unique) : liste = `provider:read`+`category:read`+`site:read` ; détail = +`provider:item:read`. Piège booléen `isArchived` traité (`#[Groups]`+`#[SerializedName]` sur le getter). Embed `categories[].code/name` + `sites[].name/postalCode` (objet, pas IRI).

### Consommation cross-module (§ 2.1)
- Site/Category via contrats Shared (`SiteInterface`/`CategoryInterface` + `resolve_target_entities`) — comme Supplier, conforme règle ABSOLUE n°1.
- Référentiels comptables (`TvaMode`/`PaymentDelay`/`PaymentType`/`Bank`) en relation ORM partagée directe (décision § 2.1, remontée Shared tracée HP-M4-2).

### Garde-fous / infra (requis pour le vert)
- Mapping ORM du module `Technique` dans `doctrine.yaml` (sinon les 9 tables `provider*` vues orphelines → DROP).
- Tables `provider*` ajoutées à `ColumnCommentsCatalog` + ligne `dbal:run-sql uq_provider_company_name_active` au makefile `test-db-setup`.
- 4 libellés `audit.entity.technique_*` (fr.json) ; `ProviderAddress::postalCode` whitelisté dans `EXCLUDED_LENGTH_MIRROR` (Regex CP {4,5}).

## Hors périmètre (→ ERP-134)
ApiResource **sans** `ProviderProvider`/`ProviderProcessor` ; sous-entités **sans** `#[ApiResource]`. Hydratation effective, gating accounting, cloisonnement par site, normalisation, 409 doublon, RG-3.07/3.08 → ERP-134. Sous-ressources POST/PATCH/DELETE → ticket ultérieur.

## Tests
- \`make test\` → **589/589 ✓** · \`php-cs-fixer\` → 0 correction.
- \`schema:validate\` : mapping OK ; « not in sync » résiduel strictement homologue à supplier (COMMENT via catalogue + index FK auto-Doctrine), non régressif.

---------

Co-authored-by: Matthieu <contact@malio.fr>
Reviewed-on: #91
2026-06-12 14:25:27 +00:00
matthieu 09a4b9d464 feat(technique) : migration schema repertoire prestataires (ERP-132) (#90)
Auto Tag Develop / tag (push) Successful in 9s
## ERP-132 — Migrer le schéma BDD M3 (provider + sous-collections)

> ⚠️ **MR stackée** sur `feat/erp-m3-technique-module-taxonomie` (ERP-131, module Technique + type PRESTATAIRE). À merger **après** ERP-131. Base volontairement ≠ develop tant qu'ERP-131 n'est pas mergé.

### Contenu
Crée tout le schéma Postgres du répertoire prestataires (1 migration, namespace racine `DoctrineMigrations` — FK cross-module user/category/site + référentiels comptables M1).

**Tables (9)** :
- `provider` : company_name + bloc Comptabilité (siren/account_number/n_tva + FK tva_mode/payment_delay/payment_type/bank ON DELETE RESTRICT) + is_archived/archived_at/deleted_at + Timestampable/Blamable. **Pas d'onglet Information** (≠ supplier).
- M2M formulaire principal : `provider_category` (RG-3.09), `provider_site` (sites du prestataire — RG-3.03, **nouveau vs supplier** + `idx_provider_site_site` pour le cloisonnement par site).
- Sous-collections : `provider_contact` (CHECK `chk_provider_contact_name` : ≥1 champ parmi first_name/last_name/phone_primary/email), `provider_address` (**sans** address_type/bennes/triage), `provider_rib`.
- Jointures adresse : `provider_address_site` (RG-3.05), `provider_address_contact`, `provider_address_category`.
- Index partiel unique `uq_provider_company_name_active` (LOWER(company_name) WHERE non archivé/non supprimé — RG-3.10) + index FK.
- `COMMENT ON COLUMN/TABLE` inline sur **toutes** les colonnes (règle ABSOLUE n°12).

### Décisions
- **CategoryType PRESTATAIRE non re-seedé** : déjà créé par ERP-131. Migration purement structurelle.
- **COMMENT inline (pas via ColumnCommentsCatalog)** : tant que les entités Provider* n'existent pas (ERP-133), `schema:update --force` du setup test droppe les tables non mappées → les référencer dans le catalogue ferait planter `app:apply-column-comments`. Catalogue + ligne `dbal:run-sql uq_provider` différés à ERP-133, exactement comme supplier (ERP-86 après ERP-85).

### Tests
-  `make db-reset` (dev + test-db-setup)
-  `make test` — 589 tests, `ColumnsHaveSqlCommentTest` vert
-  Index partiel vérifié partiel (clause WHERE), `idx_provider_site_site` présent, 0 colonne sans COMMENT
-  Cycle `down()`/`up()` OK
-  `make php-cs-fixer-allow-risky` (0 fichier)

---------

Co-authored-by: Matthieu <contact@malio.fr>
Reviewed-on: #90
2026-06-12 14:19:46 +00:00
matthieu d97b9ce6d0 feat(technique) : module Technique + taxonomie categories prestataires (#89)
Auto Tag Develop / tag (push) Successful in 11s
## M3 — Ticket 1.1 : module Technique + taxonomie catégories prestataires

Prérequis de tout le M3 (répertoire prestataires). Spec : `docs/specs/M3-prestataires/spec-back.md` § 2.1 + § 2.4.

### Contenu
- **Nouveau module `Technique`** (`src/Module/Technique/TechniqueModule.php`) : `ID=technique`, `LABEL=Technique`, `REQUIRED=false`, `permissions()` (5 codes `technique.providers.*` : view / manage / accounting.view / accounting.manage / archive).
- Activation dans `config/modules.php` → `/api/modules` expose `technique`.
- **Layer front** `frontend/modules/technique/` (auto-détecté).
- **Seed taxonomie PRESTATAIRE** : nouveau `CategoryType` (code `PRESTATAIRE` / label `Prestataire`) + 3 catégories (Maintenance industrielle, Nettoyage, Transport).
  - Migration racine idempotente `Version20260612080000` (`ON CONFLICT DO NOTHING` + `NOT EXISTS`, jonction M2M `category_category_type` — schéma courant, pas l'ancien `category_type_id`).
  - Fixtures `CategoryTypeFixtures` / `CategoryFixtures` étendues (survivent au purger `db-reset`).

### Critères d'acceptation 
- [x] Module + permissions déclarées (`app:sync-permissions` → 5 codes en base)
- [x] `TechniqueModule::class` dans `config/modules.php`
- [x] Layer front
- [x] Seed CategoryType PRESTATAIRE (migration + fixture idempotente)
- [x] ≥ 3 catégories PRESTATAIRE
- [x] `GET /api/categories?typeCode=PRESTATAIRE` filtre correctement

### Tests
- `TechniqueModuleTest` : identité + jeu de 5 permissions figé.
- `CategoryPrestataireSeedTest` : `?typeCode=PRESTATAIRE` ne renvoie QUE le type PRESTATAIRE + pagination Hydra préservée.
- `make test` : **589 tests OK** · `php-cs-fixer` : 0 correction · `make db-reset` : type + 3 catégories présents, idempotent.

### Hors-périmètre (tickets M3 suivants)
Section sidebar « Technique », personas RBAC E2E, et entités `Provider*` (l'écran `/providers` n'existe pas encore → pas de lien mort introduit ici).

---------

Co-authored-by: Matthieu <contact@malio.fr>
Reviewed-on: #89
2026-06-12 14:19:14 +00:00
gitea-actions b36520d3b1 chore: bump version to v0.1.110
Auto Tag Develop / tag (push) Successful in 7s
Build & Push Docker Image / build (push) Successful in 1m17s
v0.1.110
2026-06-12 08:45:47 +00:00
tristan a340d8139a feat(commercial) : amélioration et validation stricte des champs date (ERP-148) (#92)
Auto Tag Develop / tag (push) Successful in 8s
## Contexte
ERP-148 — mise à jour @malio/layer-ui et amélioration des champs date (onglet Information, Client & Fournisseur).

## Changements
- **MalioDate v1.7.10** : le composant expose désormais son état de validité (`@update:valid`) et la saisie brute invalide (`@update:rawValue`).
- **Validation back-autoritaire du format** : `foundedAt` n'accepte plus que l'ISO strict `Y-m-d` (`#[Context]` DateTimeNormalizer) + `collectDenormalizationErrors` sur `Client` et `Supplier`. Toute saisie non-ISO renvoie un **422 porté sur le champ**.
  - Corrige un cas piège : `12/25/2026` (invalide en JJ/MM/AAAA côté front) était auparavant accepté par PHP en M/J/AAAA → 25 décembre. Désormais rejeté.
- **Front** : la saisie invalide est transmise au back ; le message technique de type-error est surchargé par une clé i18n via le **code de violation** (`resolveViolationMessage` / `VIOLATION_MESSAGE_I18N`), affiché inline par `useFormErrors`.
- Réorganisation des utils de formulaire sous `utils/forms/`.

## Tests
- Back : `ClientFoundedAtFormatTest` / `SupplierFoundedAtFormatTest` (dont le cas piège `12/25/2026`).
- Front : résolveur i18n (`api.test.ts`, `useFormErrors.test.ts`) + payloads (`clientEdit`/`supplierEdit` specs).
- Suite Commercial verte ; vérifié bout-en-bout en navigateur (PATCH → 422, erreur inline, submit bloqué).

## Note
Échecs JWT aléatoires connus du hook pre-commit (401/500 sur tests d'auth sans rapport) ; tous verts en isolation.

Reviewed-on: #92
Co-authored-by: tristan <tristan@yuno.malio.fr>
Co-committed-by: tristan <tristan@yuno.malio.fr>
2026-06-12 08:45:38 +00:00
gitea-actions 7d8a633eee chore: bump version to v0.1.109
Auto Tag Develop / tag (push) Successful in 7s
v0.1.109
2026-06-11 15:10:30 +00:00
tristan df9451a5f4 fix(commercial) : champ Fonction du contact sur 2 colonnes (ERP-147) (#88)
Auto Tag Develop / tag (push) Successful in 8s
ERP-147 — Le champ « Fonction » (jobTitle) du bloc contact passe sur 2 colonnes, côté Client (M1) et Fournisseur (M2).

## Changements
- `ClientContactBlock.vue` — champ Fonction enrobé dans `<div class="col-span-2">`
- `SupplierContactBlock.vue` — idem côté fournisseur

## Détail technique
Le wrapper `col-span-2` est nécessaire car `MalioInputText` (`inheritAttrs:false`) renvoie `class` sur son input interne et non sur la cellule de grille — même pattern que `ClientAddressBlock.vue`.

## Vérification
- `eslint` OK sur les 2 fichiers
- Rendu à valider visuellement sur les écrans Ajouter/Modifier client et fournisseur

Reviewed-on: #88
Co-authored-by: tristan <tristan@yuno.malio.fr>
Co-committed-by: tristan <tristan@yuno.malio.fr>
2026-06-11 15:10:21 +00:00
gitea-actions cb12490ba0 chore: bump version to v0.1.108
Auto Tag Develop / tag (push) Successful in 9s
Build & Push Docker Image / build (push) Successful in 43s
v0.1.108
2026-06-11 10:05:53 +00:00
tristan a442d124a3 fix(commercial) : conserver le RIB au changement de type de règlement hors-LCR (ERP-121) (#86)
Auto Tag Develop / tag (push) Successful in 11s
## Contexte — ERP-121

Le passage d'un tiers de **LCR** vers **virement** (ou autre) supprimait ses RIB en base : au changement de type de règlement, le front marquait les `ClientRib` / `SupplierRib` existants pour suppression puis envoyait des `DELETE`. Le métier veut **conserver** le RIB (coordonnée bancaire du tiers, découplée du mode de règlement) pour un éventuel retour en LCR.

## Décisions métier (validées)

1. **Affichage hors-LCR** : RIB **totalement masqué**, ré-affiché au retour LCR — jamais supprimé en base.
2. **RGPD / IBAN** : conservation telle quelle, hors-scope de ce ticket.
3. **Données déjà perdues** : acceptable, le fix ne vaut que pour l'avenir.

## Modifications (100% frontend — clients **et** fournisseurs)

- `new.vue` / `[id]/edit.vue` : `onPaymentTypeChange` ne marque plus les RIB pour suppression et ne jette plus la saisie ; ils sont seulement masqués (`visibleRibs`) et réapparaissent au retour LCR.
- `submitAccounting` ne (re)soumet les RIB que **sous LCR** ; seules les suppressions **explicites** (corbeille d'un bloc) restent en `DELETE`.
- Consultation `[id]/index.vue` : RIB dormants masqués hors-LCR via le helper pur type-safe `paymentTypeCodeOf` (+ tests Vitest).

## Back

**Aucune modification** : la seule règle est `LCR → ≥1 RIB` (RG-1.13 / RG-2.08) ; rien n'interdit un RIB sur un tiers non-LCR. Le guard `Client/SupplierRibProcessor` (refus de supprimer le dernier RIB sous LCR) reste inchangé. **Pas de migration.**

## Vérifications

-  Vitest : **384/384** (`make nuxt-test`)
-  ESLint : clean sur les 10 fichiers
- ⏭️ PHPUnit non lancé : aucun fichier back modifié

Reviewed-on: #86
Co-authored-by: tristan <tristan@yuno.malio.fr>
Co-committed-by: tristan <tristan@yuno.malio.fr>
2026-06-11 10:05:40 +00:00
gitea-actions 431d831c8b chore: bump version to v0.1.107
Auto Tag Develop / tag (push) Successful in 7s
Build & Push Docker Image / build (push) Successful in 41s
v0.1.107
2026-06-11 08:09:47 +00:00
matthieu 3f356f0679 feat(commercial) : referentiel pays (country) en base + branchement front (ERP-116) (#79)
Auto Tag Develop / tag (push) Successful in 9s
## Objectif (ERP-116, 1re iteration minimale)

Sortir la liste des pays du **code en dur** cote front et la poser en base comme **referentiel `country`**, source unique du select pays. **Perimetre volontairement minimal** : code ISO + libelle + ordre uniquement — **aucune longueur bancaire/fiscale** (numero de compte, IBAN, TVA, BIC, SIREN) a ce stade.

## Backend
- Entite `Country` (`code` ISO 3166-1 alpha-2 unique, `name`, `position`), calquee sur `Bank` : referentiel statique **lecture seule** (`GetCollection` + `Get`), gating `commercial.clients.view OR commercial.suppliers.view`.
- Migration `Version20260609100000` : table `country` + `COMMENT ON COLUMN` + seed des **6 pays** (France, Allemagne, Belgique, Espagne, Italie, Royaume-Uni), `ON CONFLICT DO NOTHING`.
- `CommercialReferentialFixtures` : re-seed des pays en dev/test.
- Garde-fous : ajout au `ColumnCommentsCatalog` + whitelist `EntitiesAreTimestampableBlamableTest`.

## Frontend
- `useClientReferentials` charge `/countries` (value = **nom** du pays : l'adresse stocke `country` en chaine libre, **pas de FK ni migration de donnees**).
- Les 3 listes `countryOptions` en dur (clients new / edit / consultation) sont supprimees ; la consultation derive ses options de l'embed.

## Hors-scope (iterations suivantes du ticket)
- Longueurs bancaires/fiscales par pays + validation associee.
- FK `country_id` sur les adresses + migration de donnees.

## Tests
- Back : suite complete verte (583), tests API dedies countries (200/seed/405/403/401).
- Front : Vitest vert (256), spec `useClientReferentials` mise a jour.
- Migration appliquee en dev + test.

---------

Co-authored-by: Matthieu <contact@malio.fr>
Reviewed-on: #79
2026-06-11 08:09:38 +00:00
gitea-actions c1ce940c98 chore: bump version to v0.1.106
Auto Tag Develop / tag (push) Successful in 6s
Build & Push Docker Image / build (push) Successful in 37s
v0.1.106
2026-06-11 07:27:54 +00:00
tristan c594a76d47 feat(front) : page Modification fournisseur (/suppliers/{id}/edit) (ERP-96) (#85)
Auto Tag Develop / tag (push) Successful in 7s
## ERP-96 — Modification fournisseur

Étape 7/7 (front). Dépend de #94 (Ajouter) + #95 (Consultation).

> ⚠️ MR **stackée sur `feature/ERP-95-suppliers-show`** (95 → 94, pas encore mergées dans develop) pour limiter le diff aux 3 fichiers d'ERP-96. À recibler sur `develop` une fois 94 puis 95 mergées. Squash au merge.

### Périmètre
- Route `/suppliers/{id}/edit` : champs **pré-remplis** depuis GET /suppliers/{id}, **PATCH partiel indépendant par onglet**. Bloc principal conservé (éditable via son propre PATCH `supplier:write:main`), pas de contact inline (ERP-106).
- **Mode strict (RG-2.16)** : chaque onglet n'envoie QUE les champs de son groupe de sérialisation (jamais de mélange → sinon 403). Builders de payload scopés (`supplierEdit`).
- Éditabilité par rôle (`resolveTabEditability`) : métier readonly sans `manage` ; Comptabilité visible/éditable selon `accounting.view`/`accounting.manage` ; placeholders non éditables.
- Collections contacts/adresses/RIB : POST/PATCH par ligne + DELETE différé des retraits ; 422 mappées **inline par champ** (`propertyPath` → `useSupplierFormErrors`/`extractApiViolations`), jamais un toast fourre-tout (ERP-101).

### Tests
- Vitest : `supplierEdit.spec.ts` enrichi (mappers d'hydratation `mapMainDraft`/`mapInformationDraft` avec `volumeForecast`/`mapAccountingFormDraft` + `resolveTabEditability` matrice § 2.7). `make nuxt-test` → 375/375 . ESLint .
- `nuxi typecheck` non lancé sur l'hôte (casse le conteneur dev-nuxt).

Miroir de l'écran Modification client (M1), adapté M2 (enum `addressType`, `bennes`/`triageProvider`/`volumeForecast`, pas de relation Distributeur/Courtier).

Reviewed-on: #85
Co-authored-by: tristan <tristan@yuno.malio.fr>
Co-committed-by: tristan <tristan@yuno.malio.fr>
2026-06-11 07:26:32 +00:00
gitea-actions 59bae8c5e6 chore: bump version to v0.1.105
Auto Tag Develop / tag (push) Successful in 7s
Build & Push Docker Image / build (push) Successful in 3m21s
v0.1.105
2026-06-11 07:17:24 +00:00
tristan 477f77a6b5 feat(front) : page Consultation fournisseur (/suppliers/{id}) lecture seule (ERP-95) (#84)
Auto Tag Develop / tag (push) Successful in 7s
## ERP-95 — Consultation fournisseur (lecture seule)

Étape 6/7 (front). Dépend de #92 (contrat JSON figé) et #94 (blocs/types fournisseur). Bloque #96.

> ⚠️ MR **stackée sur `feature/ERP-94-suppliers-new`** (ERP-94 pas encore mergée dans develop) pour garder le diff limité aux 5 fichiers d'ERP-95. À recibler sur `develop` une fois la 94 mergée. Squash au merge.

### Périmètre
- `useSupplier(id)` : GET /api/suppliers/{id} en Hydra (embed contacts/adresses/ribs + scalaires compta si `accounting.view`), `archive()`/`restore()` via PATCH `isArchived` seul + rechargement complet.
- `supplierConsultation` : mappers purs de l'embed (enum `addressType`, `bennes`/`triageProvider`, `volumeForecast`, gating compta par **omission de clé** → null) + helpers de permissions.
- Page `[id]/index.vue` lecture seule : bloc principal + onglets Information / Contacts / Adresses / Comptabilité (si permission) / 4 coquilles « À venir » ; boutons Modifier (`manage`), Archiver/Restaurer (`archive`) ; flèche retour → répertoire. Miroir de l'écran Consultation client (M1).

### Tests
- Vitest : `supplierConsultation.spec.ts` (mappers + permissions, gating compta) + `useSupplier.spec.ts` (GET/PATCH + propagation 403/409). `make nuxt-test` → 365/365 . ESLint .
- `nuxi typecheck` non lancé sur l'hôte (régénère .nuxt/tailwind en chemins hôte et casse le conteneur dev-nuxt).

Reviewed-on: #84
Co-authored-by: tristan <tristan@yuno.malio.fr>
Co-committed-by: tristan <tristan@yuno.malio.fr>
2026-06-11 07:15:28 +00:00
tristan d6790dd37d feat(front) : page Ajouter un fournisseur (/suppliers/new) + workflow par onglets (ERP-94) (#83)
Auto Tag Develop / tag (push) Successful in 7s
ERP-94 (etape front 7/7 du M2). **Stack sur #97** (base = `feature/ERP-97-suppliers-i18n-sidebar`, elle-meme sur #93) pour un diff isole. A recibler sur `develop` une fois #93 (MR #81) et #97 (MR #82) mergees.

Page « Ajouter un fournisseur » — **replique a l'identique le fonctionnement de l'ecran Client** (workflow inline par onglets, blocs reutilisables, validation 422 inline ERP-101), avec les specificites M2.

## Architecture (miroir Client)
- Workflow par onglets **inline dans `suppliers/new.vue`** (comme `clients/new.vue` — il n'existe pas de `useClientForm` monolithique). Helpers paralleles : `useSupplierReferentials`, `useSupplierFormErrors`, `supplierFormRules`, `supplierEdit` (payloads), `types/supplierForm`.
- Blocs `SupplierContactBlock` / `SupplierAddressBlock` (miroir des blocs Client).
- POST `/suppliers` puis PATCH partiels par onglet (mode strict, groupes de serialisation). Sous-ressources : `/suppliers/{id}/contacts|addresses|ribs`.
- Validation ERP-101 : 422 `violations[].propertyPath` mappees inline par champ (`useFormErrors` / `mapViolationsToRecord`), `{ toast: false }`, bouton Valider toujours actif.

## Specificites M2 (vs M1)
- Formulaire principal **sans contact inline** (ERP-106) : Entreprise + Categorie (type FOURNISSEUR, `?typeCode=FOURNISSEUR`).
- Adresse : **radio exclusif** Prospect/Depart/Rendu (`addressType` enum, RG-2.09), champs **Bennes** (stepper) + **Prestation de triage**, **pas d'email de facturation**.
- Information : champ **Volume previsionnel** (8e champ).
- Compta (Admin+Compta) : banque si VIREMENT (RG-2.07), RIB si LCR (RG-2.08) ; RIB sous-ressource gardee par `accounting.manage`.

## Tests (mirroir strategie Client)
- `make nuxt-test` : 338 passed (specs ajoutees : supplierFormRules, supplierEdit, useSupplierReferentials, SupplierContactBlock, SupplierAddressBlock).
- ESLint propre ; `nuxi typecheck` (lance en container) : **0 erreur**.
- Golden path navigateur valide end-to-end : POST /suppliers OK, companyName normalise UPPERCASE (RG-2.12), gating des onglets (Information actif, Contacts deverrouille).

## Note de revue
~30 `WARN Duplicated imports` au typecheck : les helpers Supplier exportent les memes noms generiques que leurs equivalents Client (`buildMainPayload`, `omitEmptyRequired`, `RefOption`...), tous deux auto-importes par Nuxt. **Sans impact runtime** : tous les consommateurs utilisent des imports explicites (qui priment). Consequence directe du miroir 1:1 ; une factorisation des generiques dans `shared/` pourrait etre un suivi.

Reviewed-on: #83
Co-authored-by: tristan <tristan@yuno.malio.fr>
Co-committed-by: tristan <tristan@yuno.malio.fr>
2026-06-11 07:14:51 +00:00
gitea-actions 3c1fc39eee chore: bump version to v0.1.103
Auto Tag Develop / tag (push) Successful in 6s
Build & Push Docker Image / build (push) Successful in 2m46s
v0.1.103
2026-06-11 07:10:24 +00:00
tristan 1b0339bf1c chore(front) : i18n écrans/onglets fournisseurs + sidebar fournisseur avant client (ERP-97) (#82)
Auto Tag Develop / tag (push) Successful in 8s
ERP-97 (étape front 6/7 du M2, parallèle). **Stack sur #93** (MR #81) : base = `feature/ERP-93-suppliers-list` pour un diff isolé. À recibler sur `develop` une fois #93 mergée.

## Périmètre
- **Sidebar** : `Répertoire fournisseurs` placé **avant** `Répertoire clients` (`config/sidebar.php`). Affichage conditionnel par `commercial.suppliers.view` (déjà câblé par #90), vérifié au navigateur.
- **i18n écrans/onglets** : bloc `commercial.suppliers.*` complété (onglets Information/Contacts/Adresses/Transport/Comptabilité/Statistiques/Rapports/Échanges, titres Consultation/Modification, actions, `comingSoon`, toasts). Scaffolding pour #94/#95/#96.

## Déjà couvert (vérifié, non modifié)
- Clé sidebar `sidebar.commercial.suppliers` : déjà présente.
- Libellés audit-log `audit.entity.commercial_supplier{,address,contact,rib}` : **déjà présents** (ajoutés côté back avec les entités `#[Auditable]`). Garde-fou `AuditableEntitiesHaveI18nLabelTest` : OK (43 assertions).

## Tests
- `make nuxt-test` : 284 passed.
- `AuditableEntitiesHaveI18nLabelTest` (isolé) : OK.
- Golden path : sidebar fournisseurs au-dessus de clients ✓.

Reviewed-on: #82
Co-authored-by: tristan <tristan@yuno.malio.fr>
Co-committed-by: tristan <tristan@yuno.malio.fr>
2026-06-11 07:08:03 +00:00
gitea-actions cc7a657df9 chore: bump version to v0.1.102
Auto Tag Develop / tag (push) Successful in 6s
Build & Push Docker Image / build (push) Successful in 2m47s
v0.1.102
2026-06-11 07:01:03 +00:00
tristan d72f67d374 feat(front) : page Répertoire fournisseurs (/suppliers) + datatable + filtres + export (ERP-93) (#81)
Auto Tag Develop / tag (push) Successful in 7s
Page liste `/suppliers` (ERP-93, étape front 6/7 du M2).

## Périmètre
Répertoire fournisseurs uniquement (datatable + filtres + export). Les écrans new/consultation/edit sont d'autres tickets.

- `MalioDataTable` branché sur `usePaginatedList<Supplier>({url:'/suppliers'})`
- Colonnes : Nom, Catégories (`categories[].name`), Site (`sites[].name`, badges colorés), Dernière activité (`updatedAt`) ; clic ligne → `/suppliers/{id}`
- Boutons : « + Ajouter » (manage), « Filtrer » (drawer : search / categoryCode / siteId / includeArchived + badge + Réinitialiser), « Exporter » (XLSX)
- État filtres/pagination 100 % local (règle n°6) ; pagination 10/25/50 ; `useApi()` + composants `Malio*` only

## Différences vs Répertoire clients
- filtre `includeArchived` (au lieu de `archivedOnly`)
- colonne Catégories = `name` (clients affiche `code`)
- catégories du filtre = `?typeCode=FOURNISSEUR` ; export `/suppliers/export.xlsx`

## Tests
- `make nuxt-test` : 284 passed (11 nouveaux : useSuppliersRepository ×3, page index ×8)
- ESLint propre ; typecheck sans erreur sur les fichiers suppliers
- Golden path navigateur OK (page + drawer)

Aucun mirror RBAC à toucher (sidebar + permissions posés par #90/#92).

Reviewed-on: #81
Co-authored-by: tristan <tristan@yuno.malio.fr>
Co-committed-by: tristan <tristan@yuno.malio.fr>
2026-06-11 07:00:55 +00:00
gitea-actions 26b1f2c39b chore: bump version to v0.1.101
Auto Tag Develop / tag (push) Successful in 6s
Build & Push Docker Image / build (push) Successful in 56s
v0.1.101
2026-06-09 19:47:49 +00:00
tristan 8490de99da ERP-119 : revue validation front clients + évolutions écran client (types d'adresse, 2e email, saisies manuelles, redirection) (#80)
Auto Tag Develop / tag (push) Successful in 7s
## Contexte
Branche ERP-119 — revue de la validation des formulaires clients (déclencheur : écran « Ajouter un client »), accompagnée de plusieurs évolutions de l'écran client (M1).

## Contenu

### Validation front (clients)
- Boutons « Valider » toujours actifs (retrait du gating de validité) : c'est le back qui renvoie les 422, mappées en rouge par champ.
- Champs requis adossés à une colonne non-nullable : la clé est omise du payload si vide (companyName, RIB, adresse) → 422 NotBlank au lieu d'un 400 de type.
- Onglet Contact : au moins un contact requis (l'amorce vide est soumise → 422 RG-1.05).
- Onglet Adresse : affichage inline des erreurs type / sites / catégories + RG back « au moins un type d'adresse obligatoire ».

### Nouveaux types d'adresse
- Courtier / Distributeur, types autonomes exclusifs : colonnes `is_broker` / `is_distributor` (migration + CHECK miroir d'exclusivité), entité + Callback, et front (select, drapeaux, payloads).

### Saisies manuelles
- Adresse : `allow-create` sur le champ Adresse → saisie libre si la BAN ne propose rien.
- Date de création : `MalioDate :editable` → saisie clavier JJ/MM/AAAA en plus du calendrier.

### 2e email de facturation
- Colonne `billing_email_secondary` (optionnel, max 2), miroir du téléphone secondaire. Bump `@malio/layer-ui` 1.7.8 (prop `addable`).

### Fin d'ajout d'un client
- Redirection vers la liste à la validation du dernier onglet remplissable par le rôle (Adresse pour Bureau/Commerciale, Comptabilité pour Admin) + toast « Client ajouté ». Dérivé de `tabKeys`, sans règle RBAC custom.

## Vérifications
- Back : suites Module/Commercial + Architecture vertes (Client : 124/124). Migrations appliquées (dev + test).
- Front : Vitest vert (272), ESLint OK.

> Note : le hook pré-commit flake aléatoirement (JWT 401 / timeout DB) sur des tests sans rapport (Supplier) ; les commits ont été faits après vérification des suites concernées en isolation.

Reviewed-on: #80
Co-authored-by: tristan <tristan@yuno.malio.fr>
Co-committed-by: tristan <tristan@yuno.malio.fr>
2026-06-09 19:47:40 +00:00
gitea-actions b3ab23ee8f chore: bump version to v0.1.100
Auto Tag Develop / tag (push) Successful in 6s
Build & Push Docker Image / build (push) Successful in 36s
v0.1.100
2026-06-09 08:44:19 +00:00
tristan 222338e5a4 fix(commercial) : validation onglet compta LCR + controle croise BIC/IBAN (ERP-118) (#78)
Auto Tag Develop / tag (push) Successful in 7s
## ERP-118 — Validation onglet Comptabilité (LCR / RIB)

### 1. Fix — 422 « Au moins un RIB est obligatoire pour le type de règlement LCR »

L'onglet Comptabilité envoyait le `PATCH /clients/{id}` des scalaires (`paymentType=LCR`) **avant** le `POST /clients/{id}/ribs`. Or le back valide RG-1.13 (LCR ⟹ ≥1 RIB persisté) sur ce PATCH, en lisant les RIB en base — vides à ce stade. Résultat : 422, et le `return` empêchait la création des RIB. Premier passage en LCR impossible (deadlock).

**Correctif :** inverser l'ordre — RIB d'abord, puis PATCH des scalaires.
- `new.vue` : `POST/PATCH RIB` → `PATCH scalaires`.
- `[id]/edit.vue` : ordre universel `CREATE/UPDATE RIB` → `PATCH scalaires` → `DELETE RIB retirés` (suppressions après le PATCH : le guard back n'autorise la suppression du dernier RIB qu'une fois quitté LCR). Corrige au passage un 409 latent sur le swap du dernier RIB en LCR.

### 2. Feat — contrôle croisé pays BIC/IBAN

`Assert\Bic(ibanPropertyPath: 'iban')` sur `ClientRib` et `SupplierRib` : le pays du BIC (positions 5-6) doit correspondre au pays de l'IBAN (positions 1-2). Un BIC et un IBAN valides isolément mais de pays différents → 422, violation portée par le champ `bic` avec message FR (`ibanMessage`), mappée inline côté front. Aucune modif front nécessaire.

### Tests

- Tests fonctionnels du mismatch (BIC DE + IBAN FR → 422 sur `propertyPath=bic`, message FR) côté client et fournisseur.
- Suite back complète au vert (garde-fou `EntityConstraintsHaveFrenchMessageTest` inclus), suite front Vitest au vert.

### Points d'attention

- **Durcissement de RG** (cross-check BIC/IBAN) hors spec initiale : des RIB existants avec BIC/IBAN de pays différents deviendraient non modifiables sans correction.
- L'orchestration de submit n'est pas couverte par un test unitaire (pas d'infra de test composant sur ces écrans) — vérification golden path recommandée.

Reviewed-on: #78
Co-authored-by: tristan <tristan@yuno.malio.fr>
Co-committed-by: tristan <tristan@yuno.malio.fr>
2026-06-09 08:44:12 +00:00
gitea-actions d4a5df50a7 chore: bump version to v0.1.99
Auto Tag Develop / tag (push) Successful in 6s
Build & Push Docker Image / build (push) Successful in 38s
v0.1.99
2026-06-09 06:07:03 +00:00
tristan 191fd42406 Correctifs frontend ecran categories + alignement boutons admin (ERP-117) (#77)
Auto Tag Develop / tag (push) Successful in 9s
## Contexte
ERP-117 — correctifs frontend sur l'ecran de gestion des categories et alignement des boutons d'action des ecrans admin.

## Changements
### Drawer categories
- Titre stable « Modifier la categorie » (plus de bascule view → edit selon l'etat « dirty »), aligne sur les drawers simples du projet.
- Bouton Enregistrer toujours actif : il sauvegarde a tout moment, meme sans modification (PATCH du payload complet `name` + `categoryTypes`, comme `SiteDrawer`).
- Champ « Types de categorie » : suppression du label « Selectionner un ou plusieurs types ».

### Alignement des boutons admin
- Ecran Categories : ordre des boutons Filtres avant Ajouter + gap reduit (`gap-8`), comme le repertoire client.
- Boutons d'ajout admin (categories, roles, sites) passes en `variant=secondary`.
- Boutons Filtres (categories, audit-log, clients) en `tertiary` simple : suppression des surcharges de classe, icone a gauche 24px.

## Tests
- `useCategoryForm` mis a jour (PATCH payload complet).
- `make nuxt-test` : 256/256 OK.
- `make nuxt-lint` : OK.

Reviewed-on: #77
Co-authored-by: tristan <tristan@yuno.malio.fr>
Co-committed-by: tristan <tristan@yuno.malio.fr>
2026-06-09 06:06:52 +00:00
gitea-actions edfb2b1619 chore: bump version to v0.1.98
Auto Tag Develop / tag (push) Successful in 6s
Build & Push Docker Image / build (push) Successful in 37s
v0.1.98
2026-06-08 14:53:01 +00:00
tristan c5c650c599 style(front) : marges PageHeader (38px haut / 30px bas) + ordre boutons Filtres avant Ajouter (repertoire client)
Auto Tag Develop / tag (push) Successful in 7s
2026-06-08 16:52:53 +02:00
gitea-actions e598a92f94 chore: bump version to v0.1.97
Auto Tag Develop / tag (push) Successful in 7s
Build & Push Docker Image / build (push) Successful in 58s
v0.1.97
2026-06-08 14:40:27 +00:00
tristan b8dc3cb696 Correctifs écran Client (ERP-115) (#76)
Auto Tag Develop / tag (push) Successful in 7s
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: #76
Co-authored-by: tristan <tristan@yuno.malio.fr>
Co-committed-by: tristan <tristan@yuno.malio.fr>
2026-06-08 14:40:18 +00:00
gitea-actions 843e4b0a0c chore: bump version to v0.1.96
Auto Tag Develop / tag (push) Successful in 6s
Build & Push Docker Image / build (push) Successful in 49s
v0.1.96
2026-06-08 09:47:21 +00:00
matthieu a9c14704b7 feat(catalog) : categories multi-types (M:N) + bouton Filtres liste (#75)
Auto Tag Develop / tag (push) Successful in 7s
## Contexte

Une `Category` ne pouvait appartenir qu'à **un seul** `CategoryType` (ManyToOne). Le besoin métier : plusieurs types par catégorie. Cette MR fait passer la relation en **ManyToMany** et ajoute le bouton **« Filtres »** à droite de la liste des catégories (modèle Répertoire Clients).

Slice vertical complet (le passage M:N casse mécaniquement le contrat inter-module `CategoryInterface`, consommé par la RG-2.10 fournisseurs).

## Volet A — Relation M:N
- `Category.categoryType` (ManyToOne) → `categoryTypes` (ManyToMany, jonction `category_category_type`). Au moins un type obligatoire (`Assert\\Count(min:1)`).
- **Unicité du nom GLOBALE** parmi les actifs (`uq_category_name_active`, remplace `uq_category_name_type_active`). Message 409 reformulé.
- Migration : table de jonction + backfill + drop colonne `category_type_id` + nouvel index. Validée **rejouable sur base fraîche**.
- Contrat Shared : `getCategoryTypeCode()` → `getCategoryTypeCodes(): array`. `Supplier`/`SupplierAddress`/`SupplierFixtures` revalident « contient FOURNISSEUR » (RG-2.10).
- Provider/Repository : filtre type via sous-requête `EXISTS` (ne tronque pas la collection embarquée), eager-load anti-N+1.

## Volet B — Bouton « Filtres »
- Drawer recherche par nom + types multi (OR). Compteur de filtres actifs. État local, jamais persisté en URL.
- Back : filtres `?name=` et `?typeId[]=` sur la collection.

## Front
- Multi-select `MalioSelectCheckbox`, `useCategoryForm` en `categoryTypeIds[]`, colonne « Types », clés i18n.

## Tests / vérifs
- `make test` : **582 tests, 2474 assertions, 0 échec** 
- `make nuxt-test` : **236 tests** 
- `make php-cs-fixer-allow-risky` 
- Migration rejouée sur base fraîche (`make db-reset`) 
- Nouveau `CategoryFilterTest` (name partiel + typeId[] OR + multi-type non dupliqué)

---------

Co-authored-by: Matthieu <contact@malio.fr>
Reviewed-on: #75
Co-authored-by: THOLOT DECHENE Matthieu <matthieu@yuno.malio.fr>
Co-committed-by: THOLOT DECHENE Matthieu <matthieu@yuno.malio.fr>
2026-06-08 09:47:15 +00:00
gitea-actions 43b2251ef1 chore: bump version to v0.1.95
Auto Tag Develop / tag (push) Successful in 6s
Build & Push Docker Image / build (push) Successful in 21s
v0.1.95
2026-06-08 08:50:41 +00:00
matthieu 9cda225bdf Correctifs post-review M2 fournisseurs (P1 + P2/P3 + alignement M1) (#74)
Auto Tag Develop / tag (push) Successful in 8s
Correctifs issus de la review lead du stack M2 fournisseurs (ERP-84→113), répartis en priorités. Base : `develop`. Suite verte : `make test` 577 tests / 2475 assertions, `php-cs-fixer` 0 correction.

## P1 — défauts bloquants
- **ERP-89** — Le message de complétude Information ne fuit plus le nom de champ technique (`(champ "%s")` retiré). Correction miroir appliquée aux deux validators (Supplier + Client), accent uniformisé. Le `propertyPath` est conservé pour le mapping inline front.
- **ERP-112** — La fixture fournisseurs résout désormais la catégorie en filtrant sur le type `FOURNISSEUR` (via `CategoryInterface::getCategoryTypeCode()`), évitant de rattacher une catégorie homonyme d'un autre type (RG-2.10).
- **ERP-113** — Tests d'export complétés : dédup F3 (fournisseur multi-catégories rendu sur une seule ligne) ; gating SIREN prouvé via un utilisateur minimal non-admin portant `suppliers.view` + `suppliers.accounting.view` (nouveau helper `createUserWithPermissions`).

## P2 / P3
- **ERP-86** — `maxMessage` explicite sur `competitors` (Supplier).
- **ERP-92** — Garde `skipIfSitesModuleDisabled()` sur le test POST adresse sans site (évite un faux positif si le module Sites est désactivé).
- **ERP-89 bis** — Nouveau test : Admin authentifié non-Commerciale + Information incomplète → 200 (distinct du cas `user=null`).
- **ERP-85** — `down()` de la migration fournisseurs en `DROP TABLE IF EXISTS`.
- **ERP-87** — Reset de la mémoïsation payload en début de `process()` du SupplierProcessor + documentation du filtre `?archivedOnly` de l'export (parité avec le provider liste).
- **spec-back.md (M2)** — Alignée sur le code (le code fait foi) : security PATCH `manage or accounting.manage`, gating accounting par ajout de groupe (`SupplierReadGroupContextBuilder`), anti-N+1 via `hydrateListCollections` (pas de fetch-join), types de colonnes réels (`IDENTITY` / `TIMESTAMP(0)`).

## Alignement M1 ↔ M2
- **ERP-86/87 (Client)** — Mêmes corrections appliquées aux jumeaux M1 : message `competitors` explicite + reset mémoïsation `ClientProcessor`.

## Décision actée
- **RG-2.10 (catégorie)** : court-circuit conservé (une seule violation sur `categories`). Les violations partageant path + message sont fusionnées côté front ; ERP-101 (toutes les erreurs en un aller-retour) est déjà respecté car le Callback n'interrompt pas la validation des autres champs.

---------

Co-authored-by: admin malio <malio@yuno.malio.fr>
Co-authored-by: Matthieu <contact@malio.fr>
Reviewed-on: #74
Co-authored-by: THOLOT DECHENE Matthieu <matthieu@yuno.malio.fr>
Co-committed-by: THOLOT DECHENE Matthieu <matthieu@yuno.malio.fr>
2026-06-08 08:47:43 +00:00
gitea-actions f031c70393 chore: bump version to v0.1.94
Auto Tag Develop / tag (push) Successful in 6s
Build & Push Docker Image / build (push) Successful in 19s
v0.1.94
2026-06-08 08:04:20 +00:00