From 7ca0eccb9667c9f4c2a5a2b56ad0dc8f11671e50 Mon Sep 17 00:00:00 2001 From: Nabil Derouiche Date: Sun, 19 Apr 2026 10:40:07 +0100 Subject: [PATCH] =?UTF-8?q?feat:=20ajouter=20region=5Fcsc=20=C3=A0=20toute?= =?UTF-8?q?s=20les=20r=C3=A9f=C3=A9rences=20march=C3=A9s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implémente buildRef(r) dans calc.js : ref = id_marche + ' - ' + region_csc (ex: AO 58/2024 : Lot01 - ULS Gabes) Propagé dans : - services/calc.js → champ ref de normalizeMarche() - services/export-xlsx.js → toutes les cellules référence - routes/export.js → PPTX + DOCX (3 occurrences) - index.html → normalizeMarche() champ id_marche export-pdf.js déjà couvert via r.ref (utilise normalizeMarche) Co-Authored-By: Claude Sonnet 4.6 --- index.html | 8 +++++++- routes/export.js | 13 +++++++------ services/calc.js | 10 ++++++++-- services/export-xlsx.js | 7 ++++--- 4 files changed, 26 insertions(+), 12 deletions(-) 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 || '',