---
suivi: 1494
date: 2026-09-09
sujet: Export grille de prix Excel et PDF
chantier: commercialisation / grille de prix
type: feature
statut: poussé
hash: 4d271ab2
fichiers:
  - app/Services/Commercial/GrillePrixExportService.php
  - app/Support/Commercial/StockSyntheseCalculator.php
  - app/Http/Controllers/CommercialisationController.php
  - resources/views/pdfs/grille-prix-table.blade.php
  - resources/js/Components/Commercialisation/LotsGrillePanel.vue
  - routes/web.php
  - tests/Unit/GrillePrixExport1494Test.php
  - tests/Unit/GrillePrixExport1494SourceTest.php
  - tests/Feature/GrillePrixExport1494HttpTest.php
  - database/data/journal_mises_a_jour_post_2026_06_12.php
  - docs/suivi/SUIVI_1494_export_grille_prix_excel_pdf.md
---

# SUIVI #1494 — Export grille de prix Excel / PDF

## PROMPT ENVOYÉ

Exporter la grille de prix d’un programme en Excel et PDF depuis
`/programmes/{id}/commercialisation#grille`. Modale validée par Robin
(colonnes, statuts, Fermer / PDF / Excel, PDF/Excel ne ferment pas la
modale). Étape 0 bibliothèques. Aligner sur l’existant. Pas d’acquéreur.
Même droits que l’onglet Commercialisation. KPI même source que l’écran.

## BASE GIT

HEAD attendu `7b2215c6` (fiche #1492). origin/main à jour, pas de rebase.
#1493 non présent sur main ; fichiers dépôt facture non touchés.

## FICHES LUES

- **#1158 LEÇON** — séparer lignes affichées et agrégats programme. Un KPI
  qui suit le filtre est pire qu’un 500. Conséquence : les KPI du fichier
  sont ceux de `StockSyntheseCalculator` sur **tous** les lots du
  programme (hors annule/dation), comme le bandeau. Le tableau / totaux /
  nombre du titre suivent les statuts cochés. Proposition alternative
  (KPI recalculés sur le périmètre exporté) **non tranchée** — non
  implémentée pour ne pas diverger de l’écran.
- **#1064 / #1066** — grille lotissement : colonnes différentes. Export
  propose les colonnes de l’écran lotissement si `enseigneAModule(grille_lotissement)`.
- Zones communes : `LotsGrillePanel.vue` disputée ; seul ajout = bouton
  Export + Dialog. Pas de refonte de la grille.

## ÉTAPE 0

Déjà dans `vendor/` (pas de `composer require`) :
- `phpoffice/phpspreadsheet` ^5.7
- `barryvdh/laravel-dompdf` ^3.1 + `dompdf/dompdf`
- mpdf / tcpdf / maatwebsite : absents

`package.json` : pas de SheetJS / jspdf / exceljs.

Mécanisme repris : `AvisImpositionExportService` /
`AoLotsTableExportService` (PhpSpreadsheet `streamDownload` + Dompdf
`setPaper('a4', 'landscape')`). Excel : montants/surfaces en **numérique**
(pas en texte, contrairement à certains exports existants).

## SYNTHÈSE

Bouton Export à gauche d’Historique. Dialog PrimeVue 4. Routes POST
`commercialisation/grille/export-excel` et `export-pdf` : même groupe
`auth` que l’index, **sans** `feature.permission`, **sans** `foncier.prix`,
**sans** m365 extra. `composer require` non lancé. `vendor/` non modifié.

TVA sous le n° de lot : **non exportée** comme colonne distincte
(proposée, non tranchée). Seuil PDF : **10 colonnes** (jeu par défaut ;
au-delà message non bloquant).

## DÉPLOIEMENT-TEST

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

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

## LEÇON

KPI écran = programme entier (`StockSyntheseCalculator`). Export tableau =
filtre statuts. Les extraire dans une classe unique évite un second calcul
qui divergerait. Ne pas ajouter `authorize()` ni `foncier.prix` sur un
export si l’écran n’en a pas. Bibliothèques déjà dans `vendor/` : ne pas
`composer require` sur OVH. Excel : type numérique + format, pas
`number_format` en string.
