From 6134fc31070b829ae7556592631eabd8c84f242f Mon Sep 17 00:00:00 2001 From: tristan Date: Mon, 4 May 2026 11:29:13 +0200 Subject: [PATCH] docs : plan saisie information bovin post-EDNOTIF Co-Authored-By: Claude Opus 4.7 (1M context) --- .../plans/2026-05-04-bovine-info-saisie.md | 598 ++++++++++++++++++ 1 file changed, 598 insertions(+) create mode 100644 docs/superpowers/plans/2026-05-04-bovine-info-saisie.md diff --git a/docs/superpowers/plans/2026-05-04-bovine-info-saisie.md b/docs/superpowers/plans/2026-05-04-bovine-info-saisie.md new file mode 100644 index 0000000..e566c08 --- /dev/null +++ b/docs/superpowers/plans/2026-05-04-bovine-info-saisie.md @@ -0,0 +1,598 @@ +# Saisie information bovin (post-EDNOTIF) — Plan d'implémentation + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. +> +> **Mode utilisateur :** L'utilisateur souhaite valider chaque étape avant exécution (cf. memory `feedback_step_by_step_validation`). Avant chaque task, présenter ce qui va être fait et attendre OK explicite. + +**Goal:** Ajouter un écran de saisie post-EDNOTIF (poids, prix/kg, bâtiment, case) accessible depuis le tableau "Entrées validées", structuré en accordéons-par-bovin. + +**Architecture:** Un nouveau composant `UiAccordion` réutilisable. Une nouvelle page Nuxt `entry-exit/bovine-info/[id].vue` qui charge la réception et ses bovins, instancie un accordéon par bovin et délègue la saisie à un sous-composant `bovine-info-form.vue`. Pas de nouvel endpoint, pas de migration : on PATCH les `Bovine` existants (`receivedWeight`, `pricePerKg`, `buildingCase`). Mini ajustement backend : exposer les ids de `BuildingCase` et `Building` dans le groupe de sérialisation `bovine:read`, sinon on n'a pas de quoi pré-remplir les selectors. + +**Tech Stack:** Symfony 8 + API Platform 4 (annotations Groups) ; Nuxt 4 + Vue 3 + Tailwind ; pas de tests automatisés (cohérent avec le reste de la feature entry-exit, cf. spec). + +**Spec source:** `docs/superpowers/specs/2026-05-04-bovine-info-saisie-design.md` + +**Branche de travail:** `feat/entree-sortie` (déjà créée). + +--- + +## Synthèse du file-mapping + +| Fichier | Type | Responsabilité | +| --- | --- | --- | +| `src/Entity/BuildingCase.php` | Modify | Ajouter `bovine:read` au groupe de `id` | +| `src/Entity/Building.php` | Modify | Ajouter `bovine:read` au groupe de `id` | +| `frontend/services/dto/bovine-data.ts` | Modify | Ajouter `id` à `BovineBuildingRef` et `BovineBuildingCaseRef` | +| `frontend/components/ui/UiAccordion.vue` | Create | Composant réutilisable, header en slot, body en slot, v-model boolean | +| `frontend/components/entry-exit/bovine-info-form.vue` | Create | Sous-composant : 4 champs + bouton Valider, émet `saved` | +| `frontend/pages/entry-exit/bovine-info/[id].vue` | Create | Page : header, fetch, tri, état d'ouverture, rendu liste d'accordéons | +| `frontend/pages/entry-exit/index.vue` | Modify | Ajouter `row-clickable` + `@row-click` au tableau "Entrées validées" | + +--- + +## Task 1 : Exposer les ids `BuildingCase` et `Building` dans `bovine:read` + +**Contexte :** Quand l'API normalise un `Bovine` avec le groupe `bovine:read`, l'embedded `buildingCase` ne contient que `caseNumber` et `building.label`. Pas d'ids → pas de pré-remplissage possible. On ajoute le groupe `bovine:read` aux deux propriétés `id` concernées (zéro changement de schéma, juste un attribut PHP). + +**Files:** +- Modify: `src/Entity/BuildingCase.php:42` +- Modify: `src/Entity/Building.php:36` + +- [ ] **Step 1 : Patch `BuildingCase.id`** + +```php +// src/Entity/BuildingCase.php — remplacer +#[Groups(['building:read', 'building_case:read'])] +private ?int $id = null; + +// par +#[Groups(['building:read', 'building_case:read', 'bovine:read'])] +private ?int $id = null; +``` + +- [ ] **Step 2 : Patch `Building.id`** + +```php +// src/Entity/Building.php — remplacer +#[Groups(['building:read', 'building:summary', 'reception:read'])] +private ?int $id = null; + +// par +#[Groups(['building:read', 'building:summary', 'reception:read', 'bovine:read'])] +private ?int $id = null; +``` + +- [ ] **Step 3 : Vider le cache (les groupes sont compilés)** + +```bash +make cache-clear +``` + +- [ ] **Step 4 : Vérifier que les tests existants passent** + +```bash +make test +``` + +Attendu : 9/9 tests OK (aucun changement de comportement, juste une exposition supplémentaire). + +- [ ] **Step 5 : Vérification manuelle rapide** + +```bash +curl -s -H "Authorization: Bearer $TOKEN" \ + 'http://localhost:8080/api/bovines/1' | jq '.buildingCase' +``` + +Attendu : la réponse contient `id` (numérique) en plus de `caseNumber`, et `buildingCase.building` contient `id` en plus de `label`. Si le bovin n'a pas de buildingCase, ce sera `null` — prendre un id de bovin qui en a un (sinon ignorer cette étape). + +- [ ] **Step 6 : Commit** + +```bash +git add src/Entity/BuildingCase.php src/Entity/Building.php +git commit -m "feat(api) : exposer BuildingCase.id et Building.id dans bovine:read" +``` + +--- + +## Task 2 : Compléter le DTO frontend `BovineData` + +**Files:** +- Modify: `frontend/services/dto/bovine-data.ts` + +- [ ] **Step 1 : Ajouter `id` aux deux interfaces de référence** + +Remplacer le bloc en haut du fichier : + +```ts +export interface BovineBuildingRef { + id: number + label: string +} + +export interface BovineBuildingCaseRef { + id: number + caseNumber: number | null + building: BovineBuildingRef | null +} +``` + +- [ ] **Step 2 : Vérifier que TypeScript ne casse pas** + +```bash +cd frontend && npx vue-tsc --noEmit 2>&1 | head -40 +``` + +Attendu : pas d'erreur (les autres pages qui consomment `BovineData` ne lisaient pas l'`id` depuis ces sous-objets ; ajouter un champ ne casse rien). + +Si erreurs inattendues, les corriger en touchant seulement les call-sites pointés par tsc. + +- [ ] **Step 3 : Commit** + +```bash +git add frontend/services/dto/bovine-data.ts +git commit -m "feat(front) : id dans BovineBuildingRef et BovineBuildingCaseRef" +``` + +--- + +## Task 3 : Créer `UiAccordion` + +**Files:** +- Create: `frontend/components/ui/UiAccordion.vue` + +- [ ] **Step 1 : Écrire le composant** + +```vue + + + +``` + +- [ ] **Step 2 : Vérifier l'auto-import** + +Nuxt auto-importe les composants de `components/ui/` avec le préfixe `Ui` (cf. CLAUDE.md). Donc `` sera utilisable sans import explicite. Pas d'action ici, juste validation mentale. + +- [ ] **Step 3 : Commit** + +```bash +git add frontend/components/ui/UiAccordion.vue +git commit -m "feat(front) : composant UiAccordion réutilisable" +``` + +--- + +## Task 4 : Créer `bovine-info-form.vue` (sous-composant) + +**Contexte :** Encapsule l'état local et le formulaire d'un bovin. Reçoit le bovin et la liste de bâtiments, émet `saved` avec le bovin mis à jour. Permet à la page parent de rester lisible. + +**Files:** +- Create: `frontend/components/entry-exit/bovine-info-form.vue` + +- [ ] **Step 1 : Écrire le composant** + +```vue + + + +``` + +> Note : on utilise `application/merge-patch+json` comme content-type côté API Platform pour les PATCH (la convention par défaut). `useApi.patch` a déjà ce content-type par défaut — la ligne `headers` est ici **à supprimer** si `useApi.patch` le pose déjà. Vérifier dans `composables/useApi.ts` à l'étape suivante. + +- [ ] **Step 2 : Vérifier le content-type par défaut de `useApi.patch`** + +```bash +grep -n "patch" frontend/composables/useApi.ts | head -10 +``` + +- Si `useApi.patch` injecte déjà `application/merge-patch+json`, **retirer** le bloc `headers` du composant ci-dessus. +- Sinon, le garder. + +- [ ] **Step 3 : Commit** + +```bash +git add frontend/components/entry-exit/bovine-info-form.vue +git commit -m "feat(front) : sous-composant bovine-info-form (4 champs + valider)" +``` + +--- + +## Task 5 : Créer la page `bovine-info/[id].vue` + +**Files:** +- Create: `frontend/pages/entry-exit/bovine-info/[id].vue` + +- [ ] **Step 1 : Écrire la page** + +```vue + + + +``` + +> Note de style : `BovineInfoForm` est référencé sans import — Nuxt auto-importe les composants `components/entry-exit/*.vue` avec un PascalCase basé sur le nom de fichier (à confirmer ; sinon, ajouter `import BovineInfoForm from '~/components/entry-exit/bovine-info-form.vue'`). + +- [ ] **Step 2 : Vérifier l'auto-import** + +```bash +cd frontend && npm run dev +``` + +Aller sur `http://localhost:3000/entry-exit/bovine-info/` (id d'une réception validée). Si erreur "BovineInfoForm is not defined", ajouter l'import explicite. Si rendu OK, continuer. + +- [ ] **Step 3 : Commit** + +```bash +git add frontend/pages/entry-exit/bovine-info/'[id].vue' +git commit -m "feat(front) : page saisie information bovin (accordéons)" +``` + +--- + +## Task 6 : Câbler la navigation depuis le tableau "Entrées validées" + +**Files:** +- Modify: `frontend/pages/entry-exit/index.vue` + +- [ ] **Step 1 : Ajouter la fonction de navigation** + +Dans le `