Files
Starseed/docs/specs/M1-clients/refonte-contact/M1-ticket-01-back.prompt.md
T
Matthieu 83508d0c5a
Pull Request — Quality gate / Backend (PHP CS + PHPUnit) (pull_request) Successful in 1m39s
Pull Request — Quality gate / Frontend (lint + Vitest + build) (pull_request) Successful in 1m5s
docs(commercial) : refonte contact — suppression du contact inline (specs M1 + M2)
Le contact principal inline (firstName/lastName/phonePrimary/phoneSecondary/email)
est retiré des entités tiers : il vit désormais uniquement dans ClientContact /
SupplierContact (onglet Contacts).

- M1 (spec-back / spec-front / cahier-test) : contact inline retiré du modèle Client ;
  RG-1.01 et RG-1.02 marquées supprimées (équivalent RG-1.05 / RG-1.14) ; décisions
  D1 (recherche) et D2 (export) décrites ; version V1.
- M2 (spec-back / spec-front) : contact inline retiré du modèle Supplier dès la
  conception ; RG-2.01 et RG-2.02 supprimées (équivalent RG-2.04 / RG-2.13) ; version
  V0.2. Fichiers M2 introduits ici car non encore versionnés sur develop.
- docs/specs/M1-clients/refonte-contact/ : décision (README) + tickets (M1 back/front/
  specs, M2 specs) + prompts d'implémentation + amendement des tickets M2 existants.
2026-06-03 15:11:45 +02:00

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

  1. Explorer : Client.php, ClientProcessor.php, DoctrineClientRepository.php, ClientExportController.php, ClientFixtures.php, et ClientContact.php (pour confirmer que la cible porte bien les mêmes champs).
  2. 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 contact position minimal. Ne pas inventer un autre comportement.
  3. Migration (src/Module/Commercial/Infrastructure/Doctrine/Migrations/) : backfill INSERT INTO client_contact ... WHERE NOT EXISTS(...) puis ALTER TABLE client DROP COLUMN ... (les 5). down() best-effort documenté. Voir le SQL exact dans la spec § 4.
  4. Entité : retirer les 5 props + getters/setters + #[ORM\Column] + #[Assert\*] + #[Groups(['client:read','client:write:main'])].
  5. Processor : retirer de MAIN_FIELDS, changedBusinessFields(), normalize() ; supprimer validateMainContact() et son appel.
  6. Repository : applySearch() selon D1.
  7. Export : buildHeaders() / buildRows() selon D2.
  8. Fixtures : alléger ensureClient() ; garder addContact().
  9. 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).