---
suivi: 1505
date: 2026-09-10
sujet: UI saisie destination Projet + journalisation RGPD à la sélection
chantier: factures / depot / destination-projet
type: feature
statut: poussé
hash: 580370a9
fichiers:
  - app/Services/Depot/DepotProjetDossierService.php
  - app/Http/Controllers/DepotFactureController.php
  - routes/web.php
  - resources/js/Components/DepotFactures/DepotProjetDossierFields.vue
  - resources/js/Pages/DepotFactures/DepotFactureSaisieForm.vue
  - tests/Unit/DepotProjetSaisie1505Test.php
  - tests/Unit/DepotProjetSaisie1505SourceTest.php
  - database/data/journal_mises_a_jour_post_2026_06_12.php
  - public/build/
  - docs/suivi/SUIVI_1505_ui_saisie_projet_journalisation.md
---

# SUIVI #1505 — Étape 3bis : UI saisie Projet + journalisation à la sélection

## PROMPT ENVOYÉ

Reprise du #1504 (arrêt RGPD). Arbitrage DPO Robin : option A + journalisation
à la sélection. Activer le bouton « Projet », cascade dossier foncier →
poste budgétaire (nomenclature enseigne, actifs seulement), AutoComplete
PrimeVue, format `PONTEILLA — MARTIN, BLANC (DF-2026-003)`. Journal RGPD
`proprietaire_access_logs` à la sélection, pas à la recherche. Pas de
schéma, pas de visibilité, pas de comptabilisation.

## BASE GIT

HEAD de départ `ff53c776` (fiche #1504). origin/main rebasé avant commit.
Aucun lot en cours.

## FICHES LUES

- **#1504 LEÇON** — un nom dans un sélecteur de dépôt élargit l’audience
  (GS_COMPTABILITE, GS_ENVOL `!foncier.*`). Le journal existant ne couvre
  que la fiche. Impact : ce lot ne rouvre pas le RGPD ; il consigne
  l’arbitrage A + log à la sélection, et réutilise les faits INSEE /
  propriétaires / `code_interne` déjà établis.
- **#1492 LEÇON** — ancrer sur le TYPE (`poste_budgetaire_type_id`), jamais
  recréer `poste_budgetaire_id`. `required_if` / `prohibited_unless`
  déjà en place. Impact : l’UI poste `dossier_foncier_id` + type ; masse /
  Outlook restent refusés.
- **#1490 LEÇON** — `hasColumn` mort. Impact : aucune migration.
- **#1490 / #1448** — nomenclature ≠ ligne budgétaire (`pb_parent_xor`).
- **#1467 LEÇON** — dégriser Projet sans garde = pollution FG (`627000`).
  Impact : `numeroCompteChargeSelonDestination` retourne `null` pour
  PROJET (CLASSID = étape 5).
- **#1450 LEÇON** — ne pas assouplir l’enregistrement sans durcir l’envoi.
  `required_if` inchangé. `cloturerSiValidationEnAttente` (#1489) conservé.
- **#1448** — `typesActifsPourEnseigne` / `scopeActif` : actifs seulement.
- **#1107** — `proprietaire_access_logs` = accès (view / anonymise), pas
  les modifications. On **ajoute** `depot_select`, on ne touche pas aux
  deux existants.

## SYNTHÈSE

### Arbitrage DPO (à consigner)

La référence seule (`DF-2026-003`) n’identifie pas un dossier pour un
utilisateur qui saisit une facture. Commune + nom du vendeur sont les
seuls repères utilisables. Le nom sert à **IDENTIFIER** le dossier : il
ne donne accès ni à la fiche propriétaire, ni aux prix, ni aux parcelles,
ni aux offres (toujours derrière `foncier.lecture`). Seule une donnée
d’identification minimale est exposée (nom de famille, sans prénom, sans
adresse, sans montant). Restriction du module Foncier inchangée.

### UI

Bouton segmenté « Projet » (singulier) activé, valeur postée `PROJET`.
Cascade affichée seulement si destination = Projet : dossier (AutoComplete
PrimeVue) puis poste en retrait (`border-left` + `border-radius: 0`).
Poste vide et inactif tant qu’aucun dossier. Changement de dossier → poste
réinitialisé. Mention « Nomenclature {enseigne} ». Récap Enseigne + classe
Sage ; absente = « non renseignée », sans alerte.

### Journalisation

Action `depot_select` (12 caractères, garde strlen ≤ 32). Une entrée par
propriétaire **à la sélection** (`POST .../journaliser-selection`), jamais
à la recherche (`GET .../search`). Échec d’écriture : `Log::error` +
trace, la saisie continue. Dossier sans propriétaire : aucune entrée.
`ProprietaireController` inchangé.

### Mapping

| Composant | Libellé affiché | Valeur postée | Libellé back |
|---|---|---|---|
| Destination | Programmes | `PROGRAMMES` | `ocr_raw.saisie.destination` / Rule::in |
| Destination | Frais généraux | `FRAIS_GENERAUX` | idem |
| Destination | Projet | `PROJET` | idem — `PROJETS` hors Rule::in |
| Dossier | `COMMUNE — NOM1, NOM2 (REF)` | `dossier_foncier_id` | `depot_factures.dossier_foncier_id` |
| Poste | `code_interne — libelle` | `poste_budgetaire_type_id` | `depot_factures.poste_budgetaire_type_id` |

## DÉPLOIEMENT-TEST

Après `git pull origin main` :

- `php artisan optimize:clear`
- `php artisan view:clear`
- `php artisan route:clear` (routes ajoutées)
- `php artisan journal:sync`

Pas de migrate. Pas de `npm run build` sur OVH.

Vérifier à 1440 / 1100 / 800 px : bouton Projet, cascade, poste inactif,
changement de dossier, récap « non renseignée ». Dépôt en masse : Projet
toujours absent / refusé.

## LEÇON

Arbitrage DPO : identifier n’est pas consulter. Un nom de famille dans un
sélecteur de dépôt n’ouvre pas la fiche. Journaliser **à la sélection**,
jamais à chaque frappe (volumétrie). `action` varchar(32) : poser une
garde de longueur (`alertes.scenario` varchar(64) → SQLSTATE 1406).
L’échec du log ne doit jamais faire échouer la saisie. Ne pas utiliser
`Proprietaire::libelleAffiche()` (prénom). Réutiliser `DossierFoncier::commune()`.
`postes_budgetaires_types.code_interne` (pas `code`). Nomenclature filtrée
par `enseigne_id` du dossier : un filtre absent est une faute fonctionnelle.
Étape 4 (visibilité) et étape 5 (CLASSID) restent hors de ce lot.
