Acte la décision refonte-contact dans les specs : le contact principal inline (firstName/lastName/phonePrimary/phoneSecondary/email) est retiré des entités tiers (Client, Supplier). Les contacts vivent uniquement dans ClientContact / SupplierContact (onglet Contacts). Garantie « >=1 contact nommé » préservée par RG-1.05/1.14 (M1) et RG-2.04/2.13 (M2). - M1 (spec-back/spec-front/cahier) : modèle Client sans contact inline ; RG-1.01/1.02 supprimées ; D1 (recherche) / D2 (export) décrites ; version V1. - M2 (spec-back/spec-front) : FICHIERS NOUVEAUX (non versionnés sur develop), introduits déjà corrigés (Supplier sans contact inline, RG-2.01/2.02 supprimées) ; version V0.2. - docs/specs/M1-clients/refonte-contact/ : décision (README) + tickets (M1 back/front/specs, M2 specs) + prompts + amendement des tickets M2. Lesstime : tâches #103 (M1 back), #104 (M1 front), #105 (M1 specs), #106 (M2 specs) ; tickets M2 #85-#97 amendés. --------- Co-authored-by: Matthieu <contact@malio.fr> Reviewed-on: #54 Co-authored-by: THOLOT DECHENE Matthieu <matthieu@yuno.malio.fr> Co-committed-by: THOLOT DECHENE Matthieu <matthieu@yuno.malio.fr>
3.2 KiB
Prompt d'implémentation — M1 · Ticket 1/3 (Backend)
Tu travailles sur le projet Starseed (Symfony 8 / API Platform 4 / Doctrine / PostgreSQL).
Lis CLAUDE.md et .claude/rules/backend.md avant de coder. Commentaires en français,
code en anglais, declare(strict_types=1); partout.
Mission
Supprimer le contact principal inline de l'entité Client : les 5 champs
firstName, lastName, phonePrimary, phoneSecondary, email. Les contacts sont gérés
uniquement via la sous-entité ClientContact (onglet Contacts), déjà en place. Le code est
déjà en prod → migration avec backfill avant DROP.
La spec détaillée du ticket est dans docs/specs/M1-clients/refonte-contact/M1-ticket-01-back.md.
Lis-la en entier, ainsi que le README.md du même dossier (décision + RG impactées + D1/D2).
Étapes
- Explorer :
Client.php,ClientProcessor.php,DoctrineClientRepository.php,ClientExportController.php,ClientFixtures.php, etClientContact.php(pour confirmer que la cible porte bien les mêmes champs). - Demander la validation des décisions D1 (recherche) et D2 (export) avant de coder —
défauts recommandés : D1 = LEFT JOIN sur
client_contact, D2 = colonnes export depuis le contactpositionminimal. Ne pas inventer un autre comportement. - Migration (
src/Module/Commercial/Infrastructure/Doctrine/Migrations/) : backfillINSERT INTO client_contact ... WHERE NOT EXISTS(...)puisALTER TABLE client DROP COLUMN ...(les 5).down()best-effort documenté. Voir le SQL exact dans la spec § 4. - Entité : retirer les 5 props + getters/setters +
#[ORM\Column]+#[Assert\*]+#[Groups(['client:read','client:write:main'])]. - Processor : retirer de
MAIN_FIELDS,changedBusinessFields(),normalize(); supprimervalidateMainContact()et son appel. - Repository :
applySearch()selon D1. - Export :
buildHeaders()/buildRows()selon D2. - Fixtures : alléger
ensureClient(); garderaddContact(). - Tests : mettre à jour
ClientApiTest,ClientFormulaireMainTest,ClientExportControllerTest,ClientMigrationTest,ClientFieldNormalizerTest(cf. spec § 5). Ajouter une assertion que le backfill crée bien un contact pour un client qui n'en avait pas.
Garde-fous
- Ne touche pas
ClientContact,ClientContactProcessor,ClientFieldNormalizer. - Respecte les règles ABSOLUES : pagination,
#[Auditable], COMMENT ON COLUMN (ici on supprime → pas de commentaire à poser, mais ne pas casser le garde-fou). - Les RG-1.01 et RG-1.02 disparaissent du Client : leur équivalent (RG-1.05 / RG-1.14)
vit déjà sur
ClientContact, ne le duplique pas.
Vérification finale (obligatoire avant de dire « fini »)
make db-reset && make migration-migrate # migration rejouable sur base fraîche
make test # PHPUnit vert
make php-cs-fixer-allow-risky # lint
Puis capture le JSON réel de GET /api/clients/{id} (avec un JWT) et confirme que les 5
champs ont disparu de la réponse et que contacts[] porte bien l'info (réflexe RETEX M1 :
on valide sur le contrat réel, pas sur les annotations).