---
suivi: 1435
date: 2026-09-04
sujet: Avis d'imposition — fix édition inline + scroll horizontal
chantier: foncier
type: fix
statut: poussé
hash: 317a10c3
fichiers:
  - app/Http/Controllers/Foncier/AvisImpositionController.php
  - app/Models/AvisImposition.php
  - resources/js/Pages/Foncier/Patrimoine/AvisImposition/Index.vue
  - resources/js/Components/Foncier/InlineEditableField.vue
  - tests/Unit/AvisImposition1435SourceTest.php
  - tests/Feature/Foncier/AvisImpositionInline1435Test.php
  - database/data/journal_mises_a_jour_post_2026_06_12.php
  - public/build/
---

# SUIVI #1435 — Fix édition inline avis d'imposition + scroll horizontal

## PROMPT ENVOYÉ

Production : l'édition en ligne écrit l'objet JSON `{champ, valeur}` entier dans la colonne
(id=312 interlocuteur corrompu ; SQLSTATE 1406 sur `tlvh` varchar(10) ; Array to string
conversion L427). Corriger le contrat front/back, refuser un payload malformé, nettoyer
via requêtes SQL (sans exécuter), rendre le scroll horizontal accessible. Ne pas toucher
import #1423, droits #1420/#1434, pièces jointes, totaux, tri, schéma.

## FICHES LUES

- `SUIVI_1420` — contrat PATCH + whitelist `champsInlineAutorises` ; overflow-x-auto déjà posé.
- `SUIVI_1422` — colonne Adresse sticky (CSS `left:0`) ; ne pas remettre le fond bleu.
- `SUIVI_1423` — montant vide = NULL (pas 0) ; ne pas retoucher les 311 lignes importées.
- `SUIVI_1271` — `overflow-x-hidden` interdit (masque sans raccourcir le contenu).
- `SUIVI_1434` (après rebase) — GS_GEMME_PDG : Avis déjà dans GROUPES_AUTORISES, ne pas y toucher.

## SYNTHÈSE

### Cause A — écriture de l'objet entier

Contrat réel (inchangé côté front) :

- Front `InlineEditableField.vue` : `JSON.stringify({ champ: props.champ, valeur: draft.value })`
- Back lisait `champ` correctement, puis assignait `$validator->validated('valeur')`.
  En Laravel 13, `Validator::validated()` ne prend **aucun** argument et renvoie
  `['champ' => …, 'valeur' => …]`. Cet array était passé à `setAttribute` : MySQL
  recevait le JSON `{"champ":"tlvh","valeur":null}`. D'où 1406 sur `tlvh` (varchar 10)
  et la corruption silencieuse sur `interlocuteur` (varchar 255).
  `valeursEquivalentes` L427 faisait `(string) $array` → Array to string conversion.

### Contrat retenu (identique des deux côtés)

`PATCH /patrimoine/avis-imposition/{id}` body JSON :
`{ "champ": "<clé whitelist>", "valeur": <string|number|null> }`

Le back écrit **uniquement** `valeur` (scalaire). Un objet/tableau est refusé en **422**
avec message lisible (plus de « Server Error »). Whitelist `champsInlineAutorises`.
Type : montant (`is_numeric`, sinon refus sans conversion), date `Y-m-d`, texte + max
varchar. Vide → NULL (pas 0). `tlvh` reste varchar(10), trop long refusé.

### Cause C — scroll horizontal inaccessible

Le wrapper `overflow-x-auto` prenait la **hauteur du tableau entier** (~300 lignes).
La barre horizontale était donc en bas du tableau, sous la fenêtre. Pas de
`overflow-x-hidden`.

Solution : page bornée `h-[calc(100dvh-11.5rem)]`, conteneur `.avis-imposition-scroll`
avec `overflow-x: scroll` + `overflow-y: auto` (barre collée en bas de la carte,
toujours visible). Colonne Adresse toujours `position: sticky; left: 0`. Pied de
totaux sticky en bas du conteneur.

## DÉPLOIEMENT-TEST

```
git pull origin main
php artisan optimize:clear
php artisan view:clear
php artisan journal:sync
```

Pas de migration. Puis exécuter les requêtes SQL de détection / correction id=312
fournies dans le livrable (à la main, pas par l'agent).

## LEÇON

`Validator::validated()` (Laravel 13) renvoie tout le tableau validé, pas une clé.
`$validator->validated('valeur')` est une ArgumentCountError, pas un extrait.
Toujours `$validated = $validator->validated(); $valeur = $validated['valeur']`
**ou** (retenu ici) extraire `valeur` avant toute écriture et refuser `is_array`.
Un overflow-x sur un wrapper aussi haut que le tableau cache la barre sous le pli :
borner la hauteur au viewport, ne jamais masquer le débordement.
