---
suivi: 1657
date: 2026-09-22
sujet: Diagnostic — assujetti_tva et regime_tva de la fiche foncière (indépendance, montants, combinaisons)
chantier: Foncier / Faisabilité
type: diagnostic
statut: poussé
hash: f2276726
fichiers:
  - docs/suivi/SUIVI_1657_diagnostic_assujetti_et_regime_tva.md
---

# SUIVI #1657 — diagnostic assujetti × régime TVA (fiche foncière)

🔴 **Lecture seule.** Aucun fichier applicatif modifié, aucune migration, aucun
test écrit, aucun rebuild Vite, aucune donnée touchée. Les trois requêtes SQL
fournies sont en `SELECT` pur et seront exécutées par Robin en production.
La base MySQL locale est inaccessible (constat récurrent #1626 → #1656) :
aucune mesure chiffrée n'a pu être produite ici.

La question comptable (que *devrait* faire le statut du vendeur) **n'est pas
tranchée**. Le code dit ce qu'il fait. L'arbitrage appartient à Robin et à
la comptabilité.

## PROMPT ENVOYÉ

Diagnostic. Fiche foncière d'un scénario : deux champs TVA (`assujetti_tva`
case, `regime_tva` liste à trois valeurs). Sur le scénario 5 (DF-2026-0012 V1)
la ligne unique est non assujettie et porte pourtant « TVA totale ». Robin
constate que cocher ou décocher Assujetti ne bouge aucun montant. Établir
ce que chaque champ conditionne, s'ils sont liés, fournir les SQL de
production, projeter les deux issues d'arbitrage, formuler la question à
poser à la comptabilité. Ne pas coder.

## BASE GIT ET FICHES LUES

`HEAD` local = `origin/main` = `dfabe00e` (chore hash fiche #1656).
`git fetch` sans nouveauté, aucun rebase nécessaire. Schéma relu sur
`hectareenvol.sql` (`scenario_fiche_lignes` L4079-4095, `scenarios`
L4041-4056, `dossiers_fonciers` L1442-1461) **croisé** aux migrations
et aux modèles. Aucun nom de colonne inventé.

Fiches consultées :

- **#1655 LEÇON** — `assujetti_tva` n'alimente aucun montant ; il alimente
  `mode_prix_affiche`. Conservé à l'écran pour cette étiquette. Le présent
  lot **reprend** ce constat (grep à jour) et le prolonge : MODE n'a
  lui-même aucune conséquence sur un montant.
- **#1650** — graphe moteur : `mode_prix_affiche` = convention assujetti
  (L768) ; frais notaire et base PR ignorent ce champ. Confirmé.
- **#1635** — `REGIMES_TVA_FICHE` appartient à la FICHE (acquisition) ;
  la GRILLE porte cinq couples mode × taux pour la **vente**. Les deux
  référentiels restent séparés. Confirmé ci-dessous.
- **#1636 LEÇON** — ne pas confondre `scenario_fiche_lignes.regime_tva`
  (3 valeurs d'acquisition) avec `lots_commerciaux.regime_tva` (un **taux**
  de commercialisation). Non confondu ici.
- **#1656 LEÇON** — la base PR n'est pas le coût d'achat HT. Conservé :
  `cout_achat_foncier_ht` (Σ numéraire, tous régimes) ≠
  `base_prix_revient_foncier` (Σ marge + Σ totale + notaire).
- **#1143** (moteur, via tests `FaisabiliteLotissementMoteur1143Test`) —
  fixture : une ligne assujettie + TVA sur marge, une ligne non assujettie
  + sans TVA. C'est le seul couple « suggéré » par les tests, **pas une
  règle d'écran**.

Anti-collision : dernier numéro consommé = **#1656** (`dfabe00e`).
**#1657 est libre.** Lot concurrent possible sur le dépôt : ce diagnostic
n'écrit qu'une fiche `docs/suivi/`.

Tentations écartées (notées, non faites) : forcer `sans_tva` si non
assujetti ; diviser le prix par 1,20 ; relabeller « Prix num. » en HT ;
supprimer la case Assujetti ; réafficher `rappel_sans_tva` (computed
morte, cf. § 1.4).

---

## 1. CE QUE FAIT EXACTEMENT `regime_tva`

### 1.1 Référentiel

Trois valeurs, constantes `FoncierReferentiels.php:447-458` :

| Code | Libellé écran |
|------|----------------|
| `tva_sur_marge` | TVA sur marge |
| `tva_totale` | TVA totale |
| `sans_tva` | Sans TVA |

Validation PUT fiche : `Rule::in(array_keys(REGIMES_TVA_FICHE))`
(`ScenarioFaisabiliteController.php:197` et le recalcul à blanc `:313`).
Persist : `ScenarioFicheFonciereService.php:108-133` — régime invalide →
422 « Régime de TVA invalide. » Aucune autre contrainte.

Colonne DB : `scenario_fiche_lignes.regime_tva` varchar(32) NOT NULL
(migration `2026_07_31_250000` L37 ; dump `hectareenvol.sql:4092`).

### 1.2 Ventilation d'une ligne — moteur `calculerFicheLigne`

Source : `FaisabiliteLotissementMoteur.php:765-811`.

Le prix ventilé n'est **pas** le seul numéraire. C'est le **prix d'achat
total** de la ligne :

```
prixAchatTotal = prix_achat_numeraire + prix_achat_odf_ttc     (L767)
```

Puis **un seul** seau reçoit 100 % de ce total, selon `regime_tva`
(L786-810) — les deux autres seaux restent à zéro :

| `regime_tva` | `prix_tva_sur_marge` | `prix_tva_totale` | `prix_sans_tva` | surfaces (même aiguillage sur `surface_achetee`) |
|---|---|---|---|---|
| `tva_sur_marge` | = prixAchatTotal | 0 | 0 | `surface_tva_sur_marge` = surface achetée |
| `tva_totale` | 0 | = prixAchatTotal | 0 | `surface_tva_totale` = surface achetée |
| `sans_tva` | 0 | 0 | = prixAchatTotal | `surface_sans_tva` = surface achetée |

**Aucune conversion HT/TTC.** Le montant saisi est recopié tel quel dans
le seau. `assujetti_tva` n'apparaît pas dans ce branchement.

### 1.3 Totaux fiche — `calculerTotauxFiche`

Source : `FaisabiliteLotissementMoteur.php:818-857`.

Somme arithmétique des seaux de chaque ligne :

- `sum_prix_tva_sur_marge` ← Σ `prix_tva_sur_marge` (L847)
- `sum_prix_tva_totale` ← Σ `prix_tva_totale` (L848)
- `sum_prix_sans_tva` ← Σ `prix_sans_tva` (L849)
- et les trois équivalents surface (L850-852)

Plus, **indépendamment du régime**, pour toutes les lignes :

- `sum_prix_achat_numeraire` ← Σ numéraire (L844)
- `sum_prix_achat_odf_ttc` ← Σ ODF TTC (L845)
- `sum_prix_achat_total` ← Σ totaux ligne (L846)

### 1.4 Où ces sommes vont ensuite

**A. Base du prix de revient foncier** — `FaisabiliteLotissementMoteur.php:49-54` :

```
base_prix_revient_foncier
  = sum_prix_tva_sur_marge
  + sum_prix_tva_totale
  + frais_notaire
```

Commentaire du moteur L49, littéral : « SANS TVA exclus ».

Pourquoi `sans_tva` en est exclu : **parce que le moteur le dit**, repris
du tableur Excel #1143. La phrase métier livrée au front est
`rappel_sans_tva` (`ScenarioFicheFonciereService.php:254` et
`ScenarioRecalculABlancService.php:29`) :

> Les propriétaires en régime « sans TVA » sont exclus de la base du prix
> de revient foncier (seuls TVA sur marge + TVA totale + frais de notaire
> y entrent).

Ce n'est **pas** une déduction de droit fiscal dans le code. C'est une
règle de produit copiée d'Excel. Preuve chiffrée par la fixture
(`FaisabiliteLotissementMoteur1143Test.php:113-126`) :

- ligne marge 300 000 + ligne sans TVA 80 000 numéraire + 12 000 ODF
- `cout_achat_foncier_ht` = 380 000 (les 80 000 **sans TVA y sont**)
- `frais_notaire` = (380 000 + 60 000 ODF grille) × 0,023 = 10 120
- `base_prix_revient_foncier` = 300 000 + 0 + 10 120 = 310 120
  (les 92 000 sans TVA **n'y sont pas**)

Nuance importante, **constat de code**, pas un jugement comptable : le
numéraire d'une ligne `sans_tva` **reste** dans `cout_achat_foncier_ht`
(L42), donc dans les frais de notaire (L44-47), donc la part notaire de
ce numéraire **entre** dans la base PR. Seul le **prix** de la ligne
sans TVA est exclu, pas le notaire calculé dessus.

Le computed Vue `rappelSansTva` (`Show.vue:425`) **existe** mais n'est
plus interpolé dans le template (grep : une seule occurrence, la
déclaration). Depuis le retrait du pied de fiche (#1656), l'utilisateur
ne lit plus cette phrase à l'écran. Le payload JSON la porte toujours.

**B. Coût d'achat foncier HT et étude Fi** — `FaisabiliteLotissementMoteur.php:42`
puis `calculerEtudeFi` L197-205 :

```
cout_achat_foncier_ht = sum_prix_achat_numeraire   // TOUS régimes
totalAchat = cout_achat + viabilité ODF grille + notaire + commission + hon. montage
```

Une ligne `sans_tva` **pèse** donc dans l'étude Fi (poste Achat) et dans
le notaire. Elle **ne pèse pas** dans la base PR (hors la part notaire
ci-dessus). Les deux agrégats ne sont pas le même objet (#1656 LEÇON).

**C. Prix de revient au m²** — L57-60 : `base_prix_revient_foncier ÷
surface_cessible_hors_agricole` (surface **grille**, pas fiche). Ce m²
entre ensuite dans le calcul de TVA **des lots de vente**
(`calculerTvaLot` L864-909 : `(prix_ttc − PR×surface) × t / (1+t)` pour
la marge). Donc `regime_tva` de la fiche, via la base PR, **modifie la
TVA calculée sur la grille de vente**. C'est le seul pont quantitatif
fiche → grille. Il passe par un scalaire (PR au m²), pas par une copie
de régime.

**D. Surfaces par régime de la fiche** (`sum_surface_tva_*`) : calculées,
sérialisées, **jamais relues** par un autre calcul (grep : moteur totaux
+ payload fiche + tests #1144). Elles n'alimentent ni la grille, ni
l'étude Fi, ni le PR au m².

### 1.5 Lien avec les cinq couples de la GRILLE ?

**Aucun. Confirmé — les deux référentiels sont distincts**, comme le
disait #1635.

| | Fiche (acquisition) | Grille (vente) |
|---|---|---|
| Table | `scenario_fiche_lignes` | `scenario_lots` |
| Champ | **une** colonne `regime_tva` (3 codes) | **cinq paires** surface/prix (`surface_tva_marge` / `prix_ttc_tva_marge`, `_social`, `tva_totale`, `_sociale`, `surface_agricole` / `prix_ttc_agricole`) |
| Référentiel | `FoncierReferentiels::REGIMES_TVA_FICHE` | couples MODE × TAUX du scénario (`taux_tva` / `taux_tva_social`) |
| Saisie | une liste « Régime TVA » par ligne de fiche | deux listes MODE + TAUX par lot (`Show.vue:522-541`, `MODES_TVA_LOT`) |
| Code qui copie l'un vers l'autre | **aucun** (grep `regime_tva` dans `ScenarioGrillePrixService` et `ScenarioEtudeFiService` : 0 occurrence) | |

Troisième objet, hors sujet ici : `lots_commerciaux.regime_tva` est un
**taux** de commercialisation (`tva_20` / `tva_10` / `tva_55` /
`exonere`) — piège #1636. Non lu par le moteur de faisabilité.

---

## 2. CE QUE FAIT EXACTEMENT `assujetti_tva`

### 2.1 Grep exhaustif à jour (périmètre `app/` + `resources/js/`)

Occurrences **applicatives** (hors tests, hors journal, hors ce diagnostic) :

| Fichier:ligne | Rôle |
|---|---|
| `FicheLigneInput.php:22,40` | DTO — lit le booléen, défaut `false` si absent |
| `FaisabiliteLotissementMoteur.php:768` | `$modePrix = $ligne->assujettiTva ? 'HT' : 'TTC'` |
| `FaisabiliteLotissementMoteur.php:799,801` | recopie `assujetti_tva` + `mode_prix_affiche` dans la sortie |
| `ScenarioFicheFonciereService.php:115-126,305` | persist (nullable) + sérialisation affichage |
| `ScenarioRecalculABlancService.php:133,248,255` | recopie payload + `mode_prix_affiche` live |
| `ScenarioFaisabiliteController.php:190,306` | validation `nullable\|boolean` — **seule**, sans croisement |
| `ScenarioFicheLigne.php` | fillable, cast boolean, audit, `toMoteurArray():166` |
| `Show.vue:384` | défaut nouvelle ligne : `false` |
| `Show.vue:404,469,1475,2337` | map / payload / checkbox |
| `Show.vue:2441` | affichage `mode_prix_affiche` (colonne Mode, grisée) |
| `FaisabiliteFixtureReference.php:92,104` | une ligne true, une ligne false |

**Zéro occurrence** dans :

- `ScenarioGrillePrixService`
- `ScenarioEtudeFiService`
- `FaisabilitePromotionMoteur`
- `Dossiers/Show.vue`
- le modèle `Proprietaire` (l'assujettissement n'est **pas** une qualité
  du vendeur en base, seulement un booléen de **ligne** de fiche)

Ailleurs dans l'ERP : **nulle part**. `assujetti_tva` n'existe que sur
`scenario_fiche_lignes` (grep modèles : un seul hit).

### 2.2 La colonne MODE a-t-elle la moindre conséquence sur un montant ?

**Non.**

`mode_prix_affiche` est une **chaîne** `'HT'` ou `'TTC'` posée L768,
renvoyée L801, affichée `Show.vue:2441` via `ficheCalculee` (overlay live
#1652, clés calculées `Show.vue:1462-1463`). Aucune formule ne la relit.

Preuve par construction de `calculerFicheLigne` : après L768, `modePrix`
n'est écrit que dans le tableau de sortie. `prixAchatTotal`, les €/m²,
les trois seaux `prix_tva_*` n'en dépendent pas.

Conséquence observée par Robin (scénario 5) : cocher / décocher Assujetti
change HT ↔ TTC dans la colonne Mode, **aucun euro ne bouge**. C'est
exactement ce que le code fait.

### 2.3 Le prix saisi dans PRIX NUM. est-il traité différemment selon ce champ ?

**Non.**

Chaîne complète, sans conversion :

1. Saisie `row.prix_achat_numeraire` (`Show.vue:2402`) — InputNumber brut.
2. Persist tel quel (`ScenarioFicheFonciereService.php:131`).
3. `toMoteurArray():164` : `(string) ($this->prix_achat_numeraire ?? 0)`.
4. `FicheLigneInput.php:38` : `Dec::of(...)` — aucune division, aucun
   `match` sur `assujettiTva`.
5. `calculerTotauxFiche` L844 : somme brute.
6. L42 : `$coutAchatFoncierHt = $totauxFiche['sum_prix_achat_numeraire'];`

Le nom de variable `coutAchatFoncierHt` est une **étiquette de code**.
Le contenu est la somme des numéraires saisis, **quel que soit**
`assujetti_tva`. Confirmé par le #1655 et par l'absence de tout `div`
sur ce champ (grep `prix_achat_numeraire` dans `app/` : persist,
DTO, somme, affichage — pas de `1.20`).

Colonne « Prix ODF TTC » : même constat — additionnée au total de ligne
(L767) sans regarder Assujetti. Le suffixe `TTC` est dans le **nom de
colonne**, pas un traitement conditionnel.

### 2.4 Implication — cœur du sujet (constat de code, pas un avis comptable)

Si un utilisateur saisit, pour un vendeur **non assujetti**, un prix qui
est en réalité un TTC (parce que la colonne Mode affiche « TTC »), ce
montant **entre dans le prix de revient et dans l'étude Fi comme s'il
était un HT**. Le moteur ne retire aucune TVA. Il n'y a pas d'autre
lecture possible du code.

Que cela soit **juste ou faux** au regard de la TVA immobilière : **NON
ÉTABLI par le code**. C'est la question du § 6.

### 2.5 Défaut d'une nouvelle ligne

`Show.vue:384-391` : `assujetti_tva: false` **et**
`regime_tva: 'tva_sur_marge'`. Toute ligne ajoutée naît donc
« non assujetti + TVA sur marge », la combinaison que Robin trouve
étrange, avant toute saisie. Ce n'est pas un oubli utilisateur : c'est
le défaut usine.

---

## 3. LES DEUX CHAMPS SONT-ILS LIÉS PAR UNE RÈGLE ?

**Non. Aucune.**

Cherché et **absent** :

- `required_if` / `prohibited_if` / `Rule::` croisé entre les deux champs
  (les deux PUT : fiche L185-198, recalcul L300-313 — validations
  **indépendantes**)
- règle dans `ScenarioFicheFonciereService::normalizeRow` (L108-133 :
  régime ∈ liste, assujetti booléen-ou-null, **pas de `if` croisé**)
- signal moteur (`signaux` L64-78 : uniquement `tva_marge_negative` sur
  un **lot de vente**)
- message, alerte, infobulle, désactivation d'option Vue (le `Select`
  régime prend `regimes_tva_options` complet, L2432-2436 ; la checkbox
  n'a pas de `@change` qui force le régime)
- watch / computed liant les deux

Les deux champs sont **librement combinables**, y compris dans des
combinaisons qui n'ont pas de sens pour Robin. L'ERP n'émet aucun
avertissement.

### Les six combinaisons — ce que l'ERP en fait aujourd'hui

Même prix saisi P. « Seau PR » = seau qui alimente
`base_prix_revient_foncier` (hors notaire). « Étude Fi Achat » = P
entre dans `cout_achat_foncier_ht`. MODE = étiquette seule.

| # | Assujetti | Régime | MODE affiché | Seau PR | Étude Fi Achat | Montant ≠ si on inverse Assujetti ? | Cohérence métier |
|---|---|---|---|---|---|---|---|
| 1 | oui | TVA sur marge | HT | `sum_prix_tva_sur_marge` ← P (+ ODF) | oui, P brut | non | À ARBITRER |
| 2 | oui | TVA totale | HT | `sum_prix_tva_totale` ← P (+ ODF) | oui, P brut | non | À ARBITRER |
| 3 | oui | Sans TVA | HT | **exclu** de la base PR | oui, P brut | non | À ARBITRER |
| 4 | non | TVA sur marge | TTC | identique à #1 | identique à #1 | non | À ARBITRER |
| 5 | non | TVA totale | TTC | identique à #2 | identique à #2 | non | À ARBITRER |
| 6 | non | Sans TVA | TTC | identique à #3 | identique à #3 | non | À ARBITRER |

Toutes les six : **À ARBITRER**. Le code n'établit la cohérence métier
d'**aucune**. Il n'encode que deux axes orthogonaux (seau × étiquette).

Lecture utile pour Robin, **sans en faire une règle** :

- la fixture #1143 **illustre** #1 et #6 (assujetti + marge ; non
  assujetti + sans TVA) — suggestion de couple, pas un contrôle ;
- le défaut usine d'une ligne neuve est **#4** (non assujetti + marge) ;
- le scénario 5 cité (DF-2026-0012 V1) est **#5** (non assujetti + TVA
  totale) — identique à #2 au centime près, seule l'étiquette MODE
  diffère. C'est exactement le constat de Robin.

---

## 4. MESURE DE PRODUCTION — requêtes LECTURE SEULE

À coller telles quelles. Ne rien substituer. Base locale inaccessible :
les totaux ci-dessous restent à **remplir par Robin**.

`dossiers_fonciers.deleted_at` existe en prod (migration
`2026_09_17_140000_add_soft_deletes_to_dossiers_fonciers_and_satellites`).
Filtre posé. `scenarios` n'a pas de `deleted_at` (dump L4041-4056).
`assujetti_tva` est **nullable** (dump L4085) : le CASE isole le NULL,
à ne pas fondre dans le « non ».

### 4.a — volume par combinaison, toute la base

```sql
SELECT
    CASE
        WHEN f.assujetti_tva = 1 THEN 'assujetti'
        WHEN f.assujetti_tva = 0 THEN 'non_assujetti'
        ELSE 'assujetti_null'
    END AS assujetti,
    f.regime_tva,
    COUNT(*) AS nb_lignes,
    COUNT(DISTINCT f.scenario_id) AS nb_scenarios,
    COALESCE(SUM(f.prix_achat_numeraire), 0) AS cumul_prix_achat_numeraire,
    COALESCE(SUM(f.prix_achat_odf_ttc), 0) AS cumul_prix_achat_odf_ttc
FROM scenario_fiche_lignes f
INNER JOIN scenarios s ON s.id = f.scenario_id
INNER JOIN dossiers_fonciers d ON d.id = s.dossier_foncier_id
WHERE d.deleted_at IS NULL
GROUP BY
    CASE
        WHEN f.assujetti_tva = 1 THEN 'assujetti'
        WHEN f.assujetti_tva = 0 THEN 'non_assujetti'
        ELSE 'assujetti_null'
    END,
    f.regime_tva
ORDER BY assujetti, f.regime_tva;
```

### 4.b — scénarios portant au moins une ligne douteuse

Douteuses au sens de Robin : « non assujetti + TVA totale » **ou**
« assujetti + sans TVA ».

```sql
SELECT
    s.id AS scenario_id,
    s.version,
    s.libelle AS scenario_libelle,
    s.statut,
    s.gele,
    d.id AS dossier_id,
    d.reference AS dossier_reference,
    d.libelle AS dossier_libelle,
    SUM(
        CASE
            WHEN (f.assujetti_tva = 0 OR f.assujetti_tva IS NULL)
                 AND f.regime_tva = 'tva_totale'
            THEN 1 ELSE 0
        END
    ) AS nb_lignes_non_assujetti_tva_totale,
    SUM(
        CASE
            WHEN f.assujetti_tva = 1 AND f.regime_tva = 'sans_tva'
            THEN 1 ELSE 0
        END
    ) AS nb_lignes_assujetti_sans_tva,
    COALESCE(SUM(
        CASE
            WHEN (
                    (f.assujetti_tva = 0 OR f.assujetti_tva IS NULL)
                    AND f.regime_tva = 'tva_totale'
                 )
              OR (f.assujetti_tva = 1 AND f.regime_tva = 'sans_tva')
            THEN f.prix_achat_numeraire
            ELSE 0
        END
    ), 0) AS cumul_numeraire_lignes_douteuses
FROM scenario_fiche_lignes f
INNER JOIN scenarios s ON s.id = f.scenario_id
INNER JOIN dossiers_fonciers d ON d.id = s.dossier_foncier_id
WHERE d.deleted_at IS NULL
  AND (
        (
            (f.assujetti_tva = 0 OR f.assujetti_tva IS NULL)
            AND f.regime_tva = 'tva_totale'
        )
     OR (f.assujetti_tva = 1 AND f.regime_tva = 'sans_tva')
  )
GROUP BY
    s.id, s.version, s.libelle, s.statut, s.gele,
    d.id, d.reference, d.libelle
ORDER BY d.reference, s.version;
```

Le scénario 5 / DF-2026-0012 V1 **doit** apparaître dans ce résultat s'il
est toujours en base avec cette combinaison. S'il n'y est pas : la ligne
a changé, ou le dossier est soft-deleted — le dire.

### 4.c — enjeu total des combinaisons douteuses

```sql
SELECT
    COUNT(*) AS nb_lignes_douteuses,
    COUNT(DISTINCT f.scenario_id) AS nb_scenarios_concernes,
    COALESCE(SUM(f.prix_achat_numeraire), 0) AS cumul_prix_achat_numeraire,
    COALESCE(SUM(f.prix_achat_odf_ttc), 0) AS cumul_prix_achat_odf_ttc
FROM scenario_fiche_lignes f
INNER JOIN scenarios s ON s.id = f.scenario_id
INNER JOIN dossiers_fonciers d ON d.id = s.dossier_foncier_id
WHERE d.deleted_at IS NULL
  AND (
        (
            (f.assujetti_tva = 0 OR f.assujetti_tva IS NULL)
            AND f.regime_tva = 'tva_totale'
        )
     OR (f.assujetti_tva = 1 AND f.regime_tva = 'sans_tva')
  );
```

**Chiffrage de ce diagnostic** : NON ÉTABLI (requêtes non exécutées).
Les trois totaux ci-dessus **sont** le chiffrage. À reporter dans
l'arbitrage du § 5.

Périmètre plus large si l'arbitrage porte sur **tout** vendeur non
assujetti (pas seulement la combinaison #5) : relire la requête 4.a,
lignes `non_assujetti` + `assujetti_null`, **tous régimes**. Ce volume
est l'enjeu d'une conversion TTC → HT, distinct de l'enjeu 4.c.

---

## 5. SI LA COMBINAISON EST ILLÉGITIME, QUELLE SERAIT LA CONSÉQUENCE ?

Projection soumise à arbitrage. **Pas une décision. Pas une
implémentation.**

### 5.1 La comptabilité confirme que le comportement actuel est CORRECT

Les deux champs sont indépendants. Le prix saisi est toujours un HT.
`sans_tva` reste exclu de la base PR (règle Excel).

À écrire à l'écran (proposition, non faite) :

- en-tête **Prix num.** : préciser « HT — toujours, même si Mode affiche
  TTC » ;
- colonne **Mode** : « convention d'affichage, ne convertit pas le
  prix » — ou la retirer, puisqu'elle n'a aucun effet quantitatif ;
- colonne **Assujetti** : son utilité se réduit à alimenter cette
  étiquette. Si Mode disparaît, Assujetti n'a **plus aucun lecteur**
  dans le moteur. Le conserver alors relèverait d'un souvenir
  d'information (quel vendeur est assujetti), pas d'un calcul.
- le journal #1655 dit aujourd'hui « la case Assujetti reste, car elle
  indique si le prix s'affiche en HT ou en TTC ». Cette phrase est
  **factuellement vraie** (étiquette) et **facile à mal lire** (on croit
  que le montant change de nature). Si 5.1 est retenu, la relire.

Aucune reprise de données. Aucun scénario recalculé. Aucun euro ne
bouge.

### 5.2 La comptabilité dit que le statut du vendeur DOIT influencer le traitement du prix

Ampleur : **changement de moteur**, pas un habillage.

Le point d'accroche unique est `FaisabiliteLotissementMoteur.php:42`
(`coutAchatFoncierHt = sum_prix_achat_numeraire`) et L767-810 (seaux
alimentés par le brut saisi). Toute conversion (ex. diviser par 1,20
si non assujetti) modifierait, pour chaque ligne concernée :

- le coût d'achat de l'étude Fi ;
- les frais de notaire (assiette = ce coût + recettes ODF grille) ;
- la base PR (via les seaux **et** via le notaire) ;
- le PR au m² ;
- donc la TVA calculée sur **chaque lot de la grille** (formule de
  marge qui soustrait PR × surface).

Ce n'est pas local à la fiche. Un scénario « non assujetti » verrait
ses KPI, son étude Fi et sa TVA de vente **recalculés**.

Quels scénarios : **tous** ceux qui portent au moins une ligne
`assujetti_tva` = 0 ou NULL — volume = requête 4.a, pas seulement 4.c.
La combinaison #5 n'est que le cas le plus visible. Le défaut usine
(#4, non assujetti + marge) est probablement le plus fréquent
(hypothèse, à confirmer par 4.a).

Reprise de données : **oui, à trancher**. Deux sous-cas, tous deux
NON ÉTABLIS tant que 4.a n'a pas tourné :

- les prix déjà saisis étaient pensés HT → il ne faut **pas** les
  convertir, seulement changer le moteur pour l'avenir (et les
  scénarios déjà saisis resteraient faux au regard de la nouvelle
  règle, ou il faudrait les figer) ;
- les prix déjà saisis étaient pensés TTC parce que Mode affichait
  TTC → il faut convertir les stocks, au moins pour
  `assujetti_tva` ∈ {0, NULL}.

Chiffrage : coller ici le `cumul_prix_achat_numeraire` de 4.a
(`non_assujetti` + `assujetti_null`) et celui de 4.c. Sans ces
nombres, l'enjeu financier est **NON ÉTABLI**.

Une règle de cohérence de saisie (interdire #5, forcer `sans_tva` si
non assujetti, etc.) est un **deuxième** chantier, distinct de la
conversion du prix. Elle ne recalcule rien à elle seule ; elle empêche
les combinaisons futures. Les lignes 4.b resteraient à corriger à la
main ou par script, après arbitrage.

Formule exacte d'une conversion TTC → HT (taux ? quel taux ? le 20 %
des paramètres du scénario ? un forfait ?) : **NON ÉTABLI**. Le code
actuel n'a pas de taux d'acquisition — les taux `taux_tva` /
`taux_tva_social` sont ceux de la **vente** (paramètres du scénario).
Les plagier pour l'achat serait une décision métier, pas une lecture.

---

## 6. LA QUESTION À POSER À LA COMPTABILITÉ

À Sabrina Bartoli ou Rudy Benor, telles quelles :

Quand on achète un terrain à quelqu'un qui n'est pas assujetti à la
TVA, le prix qu'il nous demande doit-il entrer tel quel dans notre
coût d'achat, ou faut-il le traiter comme un prix TTC et en retirer
la TVA pour ne garder qu'un HT ? Et dans ce cas, a-t-on seulement le
droit de classer cet achat en « TVA totale » ou « TVA sur marge »,
ou bien un vendeur non assujetti ne peut-il être que « sans TVA » ?

---

## DÉPLOIEMENT-TEST

Sans objet : aucun code livré, aucune donnée écrite. Robin exécute
les trois `SELECT` en production et rapporte les totaux avant tout
lot d'implémentation.

## LEÇON

Deux champs côte à côte sur le même tableau ne sont pas une règle.
`regime_tva` aiguille le prix d'acquisition dans un seau (et exclut
`sans_tva` de la base PR) ; `assujetti_tva` n'écrit qu'une étiquette
HT/TTC. Le prix numéraire est toujours additionné comme un HT, y
compris quand Mode affiche TTC. La colonne Mode, lue seule, **induit**
l'utilisateur en erreur : c'est le mécanisme du scénario 5, pas un
bug de calcul. Ne pas « corriger » avant l'arbitrage comptable : une
conversion TTC → HT recalculerait la TVA de vente de tous les
scénarios concernés. Mesure prod = les trois SELECT, pas la base
locale.
