Commit Graph

3 Commits

Author SHA1 Message Date
matthieu a5de67940b [ERP-56] Exposer les 4 référentiels comptables en lecture seule (#34)
Pull Request — Quality gate / Backend (PHP CS + PHPUnit) (pull_request) Failing after 54s
Pull Request — Quality gate / Frontend (lint + Vitest + build) (pull_request) Successful in 1m11s
## ERP-56 — Référentiels comptables en lecture seule

Expose les 4 référentiels comptables (M1 Commercial) en **lecture seule** via API Platform. Aucune nouvelle entité ni migration : seule la couche API (`#[ApiResource]`) est ajoutée sur des entités existantes (ERP-53/54).

> **Stacked PR** — base = `feature/ERP-59-declarer-permissions-commercial-clients-rbac` (la security `commercial.clients.view` est déclarée par ERP-59).

### Endpoints exposés
| Méthode | URL |
|---|---|
| `GET` | `/api/tva_modes` · `/api/tva_modes/{id}` |
| `GET` | `/api/payment_delays` · `/api/payment_delays/{id}` |
| `GET` | `/api/payment_types` · `/api/payment_types/{id}` |
| `GET` | `/api/banks` · `/api/banks/{id}` |

OpenAPI exposée automatiquement.

### Détails techniques
- **Opérations** : `GetCollection` + `Get` uniquement. Aucune écriture déclarée → `POST` / `PATCH` / `DELETE` renvoient **405**.
- **Security** : `is_granted('commercial.clients.view')` au niveau opérations **et** ressource.
- **Tri par défaut** : `position ASC` puis `label ASC` (spec § 4.7) via `order:` sur `GetCollection` (provider Doctrine par défaut, aligné sur le pattern `CategoryType` ERP-46 — pas de provider custom car référentiels sans filtre).
- **Pagination (ERP-72)** : pagination serveur conservée sur ces collections autonomes. `paginationClientEnabled: true` par opération pour activer l'échappatoire `?pagination=false` (alimenter un `<MalioSelect>` complet). Note : `client_enabled` est `false` globalement, d'où l'activation explicite par opération.

### Tests (`tests/Module/Commercial/Api/ReferentialApiTest.php`)
`make test` → **364 tests OK** (dont 21 nouveaux, 70 assertions) :
- 4 endpoints → **200** avec le seed (`CommercialReferentialFixtures`) ;
- tri **position ASC** vérifié + départage **label ASC** (lignes de test purgées en `tearDown`) ;
- `GET` item → 200 ;
- `POST` (×4) / `PATCH` / `DELETE` → **405** ;
- user authentifié sans `commercial.clients.view` → **403** ;
- anonyme → **401** ;
- pagination serveur active (page 2 vide) + `?pagination=false` cohérent.

`make php-cs-fixer-allow-risky` : clean.

### Review
Reviewer souhaité : @tristan
À **squash merge** (sélectionner manuellement dans l'UI Gitea).

---------

Co-authored-by: Matthieu <contact@malio.fr>
Reviewed-on: #34
Co-authored-by: THOLOT DECHENE Matthieu <matthieu@yuno.malio.fr>
Co-committed-by: THOLOT DECHENE Matthieu <matthieu@yuno.malio.fr>
2026-06-01 19:28:18 +00:00
Matthieu 0f8fc48df0 fix(commercial) : robust gating + strict category denormalizer + provider via EM (review ERP-55) 2026-06-01 14:50:45 +02:00
Matthieu d3d00425f7 feat(commercial) : add Client API Platform provider + processor + business rules
Branche l'API REST du repertoire clients (M1) sur l'entite Client preparee en
ERP-54. Operations GetCollection / Get / Post / Patch (pas de Delete au M1 :
l'archivage passe par PATCH isArchived).

ClientProvider :
- liste paginee (Paginator ORM, aligne sur la convention ERP-72) + echappatoire
  ?pagination=false
- exclut archives + soft-deletes par defaut (RG-1.24), ?includeArchived=true
  reintegre les archives (RG-1.25)
- tri companyName ASC (RG-1.26), filtres ?search (fuzzy companyName/lastName/
  email) et ?categoryType=<code>
- detail : 404 sur soft-delete, embarque contacts/adresses/ribs

ClientProcessor :
- normalisation serveur via ClientFieldNormalizer (RG-1.18 a 1.21)
- 409 sur doublon de nom de societe (RG-1.16) ; 409 dedie sur conflit de
  restauration (RG-1.23)
- gating par onglet : champ comptable -> accounting.manage, isArchived ->
  archive, mode strict 403 sur tout le payload (RG-1.28) ; archivage exclusif
  (RG-1.22) + pose/retrait archivedAt
- regles metier RG-1.01 (prenom/nom), RG-1.03 (distributor/broker exclusifs +
  controle du type de categorie), RG-1.12 (Virement -> banque), RG-1.13 (LCR ->
  >= 1 RIB), RG-1.04 (completude Information pour le role Commerciale)

Lecture comptable conditionnelle : ClientReadGroupContextBuilder ajoute le
groupe client:read:accounting selon commercial.clients.accounting.view.

Resolution des references categorie : CategoryReferenceDenormalizer resout les
IRI vers Category quand la propriete est type-hintee par le contrat
CategoryInterface (denormalisation impossible sur une interface sinon).

Contrats Shared :
- CategoryInterface::getCategoryTypeCode() (implemente par Category) pour la
  verification de type sans import inter-modules
- BusinessRoleAwareInterface (implemente par User) + BusinessRoles::COMMERCIALE
  pour detecter le role metier ; le code de role sera seede par ERP-74 et
  reutilise par ERP-59/60. RG-1.04 reste dormante tant qu'aucun user ne porte
  ce role.

Coordination stack :
- chaines de permission commercial.clients.* referencees ici, declarees en
  ERP-59 (tests RBAC complets en ERP-60)
- config globale de pagination (itemsPerPage client, max 50) portee par ERP-72
- referentiels comptables (PaymentType/Bank/...) exposes en ERP-56

Tests : 31 tests Commercial (integration admin sur les regles metier + unitaires
sur le gating, RG-1.04/1.12/1.13 et le context builder). Suite complete verte
(339 tests).
2026-06-01 14:50:45 +02:00