feat(transport) : export XLSX répertoire + prix (ERP-162) #118
Reference in New Issue
Block a user
Delete Branch "feat/erp-162-carrier-export"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
GET /api/carriers/export.xlsx (mêmes filtres que la liste : includeArchived, search, certificationType) + GET /api/carriers/{id}/prices/export.xlsx (tableau Prix regroupé Benne / Fond Mouvant). Controllers Symfony custom avec #[Route(priority: 1)], génération déléguée au service Shared SpreadsheetExporterInterface. Tests : 200 + en-têtes fichier, respect des filtres, regroupement par contenant, 404 transporteur inconnu, 403 sans transport.carriers.view, 401 anonyme. Lien ticket ERP-162.
Code review ERP-162 (export XLSX répertoire + prix transporteur) — relue contre spec-back § 4.6.
Verdict : PR solide, aucun constat bloquant. Deux controllers custom bien faits :
priority: 1présent et documenté sur les deux routes (/api/carriers/export.xlsxet/api/carriers/{id}/prices/export.xlsx) → évite le conflit avec l'item API Platform{id}.{_format}(règle CLAUDE.md respectée).transport.carriers.view, soft-delete → 404 (cohérentCarrierProvider), génération déléguée au service SharedSpreadsheetExporterInterface(le COMMENT générique séparé du QUOI métier), cross-module via contrats Shared (aucunuse App\Module\Commercial\…).Tests = les meilleurs de la stack : ils chargent réellement le XLSX (PhpSpreadsheet) et assertent le contenu des cellules (colonnes QUALIMAT, regroupement, libellés cross-module), + RBAC 403/401 + 404 + filtres.
Quelques 🟡 mineurs en inline (sémantique colonne APRO/Sites à confirmer, cohérence
archivedOnly). Rien qui bloque.@@ -0,0 +45,4 @@private readonly SpreadsheetExporterInterface $exporter,) {}#[Route('/api/carriers/export.xlsx', name: 'transport_carriers_export_xlsx', methods: ['GET'], priority: 1)]🟢
priority: 1correct et bien justifié (sans lui, API Platform capterait/api/carriers/export.xlsxcommeGET /api/carriers/{id}.{_format}avec id="export").IsGranted('transport.carriers.view'), génération déléguée au service Shared, et surtout les mêmes filtres queGET /api/carriers(viacreateListQueryBuilder) → l'export reflète exactement la vue liste. Colonnes conformes à spec-back § 4.6. RAS.@@ -0,0 +54,4 @@// - includeArchived : reintegre les archives en plus des actifs ;// - search : recherche fuzzy sur le nom ;// - certificationType : filtre repetable (?certificationType[]=A&...).$includeArchived = $this->readBool($request->query->get('includeArchived'));🟡 Cohérence filtres (lié à mon constat #112). L'export ne lit que
includeArchived(miroir deCarrierProvider, qui n'expose pasarchivedOnlycontrairement aux 3 autres répertoires). C'est cohérent avec le provider actuel — mais si tu ajoutesarchivedOnlyauCarrierProvider(pour le toggle « Voir les archivés » du front ERP-164), pense à le répliquer ici sinon l'export divergera de la liste affichée. À traiter ensemble.@@ -0,0 +133,4 @@* - branche FOURNISSEUR : l'adresse d'approvisionnement, identifiee par la* raison sociale du fournisseur (cf. note de classe sur les contrats Shared).*/private function formatDeparture(CarrierPrice $price): string🟡 À confirmer (sémantique colonne). Le controller mappe « Adresse APRO ou Adresse Sites » ainsi : CLIENT → site de départ (86/17/82), FOURNISSEUR → adresse d'appro (fournisseur). C'est business-correct (un prix CLIENT n'a pas d'appro) et c'est bien testé (
testExportRendersGroupedPriceRows). Mais le texte littéral du docx p.10 dit « Si Client → Adresse APRO sinon Adresse Sites » — soit l'inverse. Le docx est vraisemblablement imprécis et ton interprétation est la bonne ; juste s'assurer que l'écran Consultation onglet Prix (ERP-170) appliquera exactement le même mapping pour la cohérence export↔écran.@@ -0,0 +59,4 @@* cross-module (le prix CLIENT livre chez le client, le prix FOURNISSEUR part* de l'adresse du fournisseur).*/public function testExportRendersGroupedPriceRows(): void🟢 Très bon niveau de test (le meilleur de la stack) : le test charge réellement le XLSX via PhpSpreadsheet et assert le contenu — regroupement Benne/Fond mouvant, état du prix (« Validé »/« En cours »), et les libellés cross-module (
TESTCARRIERREF CLIen colonne livraison pour un prix CLIENT,TESTCARRIERREF FRNen colonne départ pour un FOURNISSEUR). Ça verrouille à la fois le mapping de branche et l'intégrationresolve_target_entities. 👍f0c09d6961to15c6207c78GET /api/carriers/export.xlsx (mêmes filtres que la liste : includeArchived, search, certificationType) et GET /api/carriers/{id}/prices/export.xlsx (tableau Prix regroupé Benne / Fond Mouvant). Controllers Symfony custom avec #[Route(priority: 1)] pour éviter le conflit API Platform {id}, génération déléguée au service Shared SpreadsheetExporterInterface.15c6207c78toc0fa00c9c5