# Phase 2.1b — implémentation préventif et entreprises

## Baseline

- Version avant : `0.1.0-dev.10` ; version après : `0.1.0-dev.11`.
- Commit de départ : `e65e653`.
- Le dépôt était propre ; les services Docker local (Flask, MariaDB,
  Adminer) étaient démarrés et `/health/` répondait.
- Les tests de référence restaient `152 passed, 1 failed, 1 error` ; l'échec
  et l'erreur historiques concernent le lookback/contexte GMAO et son
  teardown, hors périmètre.

## Ce qui est implémenté

### Durée et périmètre

`LotTask` expose maintenant :

- `duration_mode` : `unit`, `fixed` ou `zone` ;
- `scope_mode` : `dynamic` ou `manual` ;
- `executor_type` : `internal`, `external_company`, `department`, `mixed`,
  `undefined`.

En mode `unit`, la durée d'une cible est `duree_minutes × quantity`. Une
quantité de groupe (par exemple 30 chaises) est donc respectée. En mode
`fixed`, la quantité ne multiplie pas le forfait. Le périmètre dynamique suit
les équipements actifs du lot ; le périmètre manuel utilise la sélection
explicitement enregistrée. Les tâches sans durée ont `estimated_duration=0`
et le statut technique `needs_duration`; elles ne sont pas proposées par
`DayPlanner` comme tâches planifiées.

Les valeurs historiques ne sont pas réintroduites comme vérité : le catalogue
reste idempotent et ne remplace jamais une personnalisation existante. Les
propositions de durée du document de référence restent à valider métier avant
d'être injectées en masse.

### Espaces verts

Les zones disposent de quatre durées facultatives propres à la zone : tonte,
bordures, haies et ramassage/évacuation. Elles sont éditables dans le
formulaire normal d'une zone et ne créent aucune durée générique dans un lot.
Le raccord automatique d'une règle de lot à ces quatre champs reste à faire
dans une passe ultérieure après validation du vocabulaire des tâches espaces
verts.

### Entreprises et passages

Les champs existants `company_id`, `contract_id`, `periodicite` et
`jours_entre_interventions` sont exposés dans la fiche de tâche, ainsi que le
type d'exécutant. La migration `i4d5e6f7a8b9` ajoute les modes `appointment`,
`date_only` et `open_window`, les heures/fenêtres, la date réelle, l'arrivée,
l'accompagnement et le technicien. La page **Passages & contrôles** permet
les actions Réalisée, Non venue et Entreprise arrivée. Un rendez-vous avec
heures est transmis à `DayPlanner` comme contrainte fixe ; les autres modes
restent informatifs et ne bloquent pas une plage horaire.

Le fichier `preventive_duration_proposals.py` embarque les **132** valeurs
numériques effectivement présentes dans la proposition documentaire. Elles
sont appliquées uniquement aux nouvelles lignes de catalogue dont la durée est
vide ; les personnalisations existantes ne sont jamais écrasées. L'écart avec
les 147 valeurs minimales annoncées doit être clarifié métier avant d'ajouter
des valeurs.

## Migration

`h3c4d5e6f7a8_phase21b_preventive_scope.py` et
`i4d5e6f7a8b9_phase21b_contract_visits.py` ajoutent sans perte :

- les trois colonnes de qualification à `lot_tasks` ;
- la table d'association `lot_task_equipments` ;
- les quatre durées d'espaces verts à `zones`.
- les données de passage entreprise à `contract_visits`.

La migration est additive et possède un downgrade. Elle n'attribue aucun
horaire, lot ou exécutant à `alban`.

## Tests ajoutés

`tests/unit/test_preventive_scope.py` et
`tests/unit/test_external_maintenance_visit.py` couvrent le calcul par
quantité, les modes de passage, le forfait par cible et l'absence explicite de durée. La compilation Python et le
chargement des modèles passent. La suite intégration locale est actuellement
bloquée par l'erreur historique de teardown (`equipment_lifecycle_events`
absente de la base de test), déjà présente avant cette passe.

## État de validation

Cette passe est **partiellement validée** : durée unitaire, périmètre, durées
de zone et workflow de passage de base sont persistants et exposés dans l'UI.
La sélection en masse avancée, le raccord automatique espaces verts → lot et
le scénario intégral sans intervention technique restent à renforcer. Phase
2.2 non commencée.
