diff --git a/index.html b/index.html
index fb6f7a2..7a28d30 100644
--- a/index.html
+++ b/index.html
@@ -741,10 +741,16 @@ function buildRegionOptions(selectId, includeAll = true) {
}
/* ── NORMALIZE API FIELDS ── */
+function buildRef(r) {
+ const base = r.id_marche || r.reference || '';
+ const reg = r.region_csc || r.region || '';
+ return reg ? `${base} - ${reg}` : base;
+}
+
function normalizeMarche(r) {
return {
...r,
- id_marche: r.id_marche || r.reference || '',
+ id_marche: buildRef(r),
region: r.region || r.region_csc || '',
taux_phy: parseNum(r.taux_phy ?? r.avt_phy ?? 0),
tot_marche: parseNum(r.tot_marche ?? r.m_max ?? r.totmarche ?? 0),
diff --git a/routes/export.js b/routes/export.js
index 49d0708..124faa4 100644
--- a/routes/export.js
+++ b/routes/export.js
@@ -13,6 +13,7 @@ const {
isCloture, normalizeMarche, parseNum, formatMontant, selectVal,
getDelaiRestant, niveauAlerte, niveauAvancement, niveauRisque,
DELAI_CRITIQUE, DELAI_ATTENTION, SEUIL_STANDARD, SEUIL_CRITIQUE_PCT,
+ buildRef,
} = require('../services/calc');
const pdfGen = require('../services/export-pdf');
@@ -50,7 +51,7 @@ async function buildViewData(view, req) {
const alertes = actifs
.map(r=>({...r,_d:getDelaiRestant(r)}))
.filter(r=>r._d!==null&&r._d<=DELAI_ATTENTION)
- .map(r=>({ref:r.id_marche||r.reference||'',projet:r.projet||'',region:r.region||'',entrepreneur:r.entrepreneur||'',delai_restant:r._d,niveau:niveauAlerte(r._d)}))
+ .map(r=>({ref:buildRef(r),projet:r.projet||'',region:r.region||'',entrepreneur:r.entrepreneur||'',delai_restant:r._d,niveau:niveauAlerte(r._d)}))
.sort((a,b)=>a.delai_restant-b.delai_restant);
return {
total: rows.length, actifs: actifs.length, clotures: clotures.length,
@@ -315,7 +316,7 @@ router.get('/pptx', async (req, res) => {
const alertRows = alerteItems.slice(0, 18).map(r => {
const alColor = r._d <= DELAI_CRITIQUE ? 'EF4444' : 'EA580C';
return [
- { text: r.id_marche||r.reference||'', options: { fontSize: 8, color: 'CBD5E1' } },
+ { text: buildRef(r), options: { fontSize: 8, color: 'CBD5E1' } },
{ text: r.projet||'', options: { fontSize: 8, color: 'CBD5E1' } },
{ text: r.region||'', options: { fontSize: 8, color: 'CBD5E1' } },
{ text: r.entrepreneur||'', options: { fontSize: 8, color: 'CBD5E1' } },
@@ -355,7 +356,7 @@ router.get('/pptx', async (req, res) => {
const t = parseN(r.taux_phy||r.avt_phy);
const res = classify(r);
return [
- { text: r.id_marche||r.reference||'', options: { fontSize: 8, color: 'CBD5E1' } },
+ { text: buildRef(r), options: { fontSize: 8, color: 'CBD5E1' } },
{ text: r.projet||'', options: { fontSize: 8, color: 'CBD5E1' } },
{ text: r.region||'', options: { fontSize: 8, color: 'CBD5E1' } },
{ text: r.entrepreneur||'', options: { fontSize: 8, color: 'CBD5E1' } },
@@ -564,7 +565,7 @@ router.get('/docx', async (req, res) => {
rows: [
tr([['Référence',2500,navyFill],['Projet',3000,navyFill],['Région',1200,navyFill],['Délai (j)',1200,navyFill]], true),
...alerteItems.slice(0,20).map((r, i) => tr([
- [r.id_marche||r.reference||'', 2500, i%2===1?altFill:undefined],
+ [buildRef(r), 2500, i%2===1?altFill:undefined],
[r.projet||'', 3000, i%2===1?altFill:undefined],
[r.region||'', 1200, i%2===1?altFill:undefined],
[String(r._d||'—'), 1200, i%2===1?altFill:undefined],
@@ -594,7 +595,7 @@ router.get('/docx', async (req, res) => {
...regActifs.map((r, i) => {
const phy = parseN(r.taux_phy||r.avt_phy);
return tr([
- [r.id_marche||r.reference||'', 2500, i%2===1?altFill:undefined],
+ [buildRef(r), 2500, i%2===1?altFill:undefined],
[r.projet||'', 2800, i%2===1?altFill:undefined],
[r.entrepreneur||'', 2000, i%2===1?altFill:undefined],
[fmtPct(phy), 700, i%2===1?altFill:undefined],
@@ -620,7 +621,7 @@ router.get('/docx', async (req, res) => {
rows: [
tr([['Référence',2200,navyFill],['Projet',2500,navyFill],['Région',1000,navyFill],['Entrepreneur',1800,navyFill],['Phy %',700,navyFill],['Résultat',1300,navyFill]], true),
...pilotItems.map(({ r, res, phy }, i) => tr([
- [r.id_marche||r.reference||'', 2200, i%2===1?altFill:undefined],
+ [buildRef(r), 2200, i%2===1?altFill:undefined],
[r.projet||'', 2500, i%2===1?altFill:undefined],
[r.region||'', 1000, i%2===1?altFill:undefined],
[r.entrepreneur||'', 1800, i%2===1?altFill:undefined],
diff --git a/services/calc.js b/services/calc.js
index 5287927..29e70a5 100644
--- a/services/calc.js
+++ b/services/calc.js
@@ -139,6 +139,12 @@ function niveauRisque(r) {
// ─── Normalisation d'un marché ───────────────────────────────────────────────
+function buildRef(r) {
+ const base = r.id_marche || r.reference || String(r.id || '');
+ const reg = r.region_csc || r.region || '';
+ return reg ? `${base} - ${reg}` : base;
+}
+
function normalizeMarche(r) {
const obsValue = selectVal(r.observation);
const natureValue = selectVal(r.nature);
@@ -152,7 +158,7 @@ function normalizeMarche(r) {
return {
id: r.id,
- ref: r.id_marche || r.reference || String(r.id || ''),
+ ref: buildRef(r),
projet: r.projet || '',
region: r.region || r.region_csc || '',
region_csc: r.region_csc || r.region || '',
@@ -197,5 +203,5 @@ module.exports = {
formatMontant, formatDateFR, formatPct,
isCloture, getDelaiRestant, niveauAlerte,
niveauAvancement, resultatFinancier, resultatPhysique, niveauRisque,
- normalizeMarche,
+ normalizeMarche, buildRef,
};
diff --git a/services/export-xlsx.js b/services/export-xlsx.js
index 8ab9db3..11d7d35 100644
--- a/services/export-xlsx.js
+++ b/services/export-xlsx.js
@@ -2,7 +2,8 @@
* services/export-xlsx.js
* Génération XLSX comprehensive — Situation des Marchés RLA Zone Sud
*/
-const ExcelJS = require('exceljs');
+const ExcelJS = require('exceljs');
+const { buildRef } = require('./calc');
const C = {
NAVY: 'FF002D62',
@@ -244,7 +245,7 @@ async function buildSheet1(wb, actifs) {
subtotalFin += finDT;
const rd = ws.addRow([
- r.id_marche || r.reference || '',
+ buildRef(r),
r.projet || '',
r.entrepreneur || '',
nat,
@@ -422,7 +423,7 @@ async function buildSheet2(wb, actifs) {
const result = classify(r);
const rd = ws.addRow([
- r.id_marche || r.reference || '',
+ buildRef(r),
r.projet || '',
r.entrepreneur || '',
r.region || '',