96 lines
3.7 KiB
JavaScript
96 lines
3.7 KiB
JavaScript
/**
|
|
* routes/modernisation.js
|
|
* Vue "Succession des marchés Modernisation"
|
|
* Croise table 856 (marchés actifs, nature=Modernisation) avec table 872 (AO en lancement)
|
|
* Lien : region commune entre les deux tables
|
|
*/
|
|
const express = require('express');
|
|
const router = express.Router();
|
|
const { getMarches, getPipeline } = require('../services/baserow');
|
|
const { normalizeMarche, isCloture, selectVal } = require('../services/calc');
|
|
|
|
const ALL_REGIONS = ['Gabes','Gafsa','Kebili','Medenine','Sfax','Tataouine','Tozeur'];
|
|
|
|
function isModernisation(r) {
|
|
const nature = selectVal(r.nature);
|
|
return nature && nature.toLowerCase().includes('moderni');
|
|
}
|
|
|
|
function pipelineRegions(r) {
|
|
const v = r['Regions'] || r.regions || [];
|
|
if (!Array.isArray(v)) return [];
|
|
return v.map(x => (typeof x === 'object' ? x.value : x)).filter(Boolean);
|
|
}
|
|
|
|
function phaseAO(r) {
|
|
const now = new Date();
|
|
const limit = r['date-limite'] ? new Date(r['date-limite']) : null;
|
|
const ouv = r['date-ouverture-adm-tech'] ? new Date(r['date-ouverture-adm-tech']) : null;
|
|
const clos = r['date-cloture-evaluation'] ? new Date(r['date-cloture-evaluation']) : null;
|
|
|
|
if (clos && now > clos) return { label:'Attribué', code:'attribue', color:'#6b7280' };
|
|
if (ouv && now > ouv) return { label:'Évaluation', code:'evaluation', color:'#8b5cf6' };
|
|
if (limit && now > limit) return { label:'Dépouillé', code:'depouille', color:'#f59e0b' };
|
|
if (limit) return { label:'Ouvert', code:'ouvert', color:'#10b981' };
|
|
return { label:'Préparation', code:'preparation', color:'#3b82f6' };
|
|
}
|
|
|
|
function fmtDate(d) {
|
|
if (!d) return null;
|
|
const dt = new Date(d);
|
|
if (isNaN(dt.getTime())) return String(d);
|
|
return `${String(dt.getDate()).padStart(2,'0')}/${String(dt.getMonth()+1).padStart(2,'0')}/${dt.getFullYear()}`;
|
|
}
|
|
|
|
// GET /api/modernisation
|
|
router.get('/', async (req, res) => {
|
|
try {
|
|
const [rawMarches, rawPipeline] = await Promise.all([getMarches(), getPipeline()]);
|
|
|
|
// Marchés modernisation actifs par région (non clôturés)
|
|
const modActifs = rawMarches
|
|
.filter(r => !isCloture(r) && isModernisation(r))
|
|
.map(normalizeMarche);
|
|
|
|
// Pour chaque région, construire la chaîne actuel → suivant
|
|
const regions = ALL_REGIONS.map(reg => {
|
|
const actuels = modActifs.filter(r => (r.region || '') === reg);
|
|
const suivants = rawPipeline.filter(r => pipelineRegions(r).includes(reg));
|
|
|
|
return {
|
|
region: reg,
|
|
actuels: actuels.map(r => ({
|
|
ref: r.ref,
|
|
projet: r.projet,
|
|
entrepreneur: r.entrepreneur,
|
|
taux_phy: r.taux_phy,
|
|
taux_fin: r.taux_fin,
|
|
date_fin: r.date_fin,
|
|
delai_restant: r.delai_restant,
|
|
montant: r.montant,
|
|
statut: r.statut,
|
|
})),
|
|
suivants: suivants.map(r => ({
|
|
num_ao: r['num-ao'] || '',
|
|
description: r['Description du projet'] || '',
|
|
estimation: parseFloat(r.Estimation || 0) || 0,
|
|
duree: r['Duree'] || '',
|
|
date_limite: fmtDate(r['date-limite']),
|
|
date_ouverture: fmtDate(r['date-ouverture-adm-tech']),
|
|
date_evaluation: fmtDate(r['date-cloture-evaluation']),
|
|
phase: phaseAO(r),
|
|
jours_limite: r['date-limite']
|
|
? Math.ceil((new Date(r['date-limite']) - new Date()) / 86400000)
|
|
: null,
|
|
})),
|
|
};
|
|
}).filter(r => r.actuels.length > 0 || r.suivants.length > 0);
|
|
|
|
res.json({ count: regions.length, regions });
|
|
} catch (err) {
|
|
res.status(502).json({ error: 'Erreur modernisation', detail: err.message });
|
|
}
|
|
});
|
|
|
|
module.exports = router;
|