fix: nomenclature CA - valeurs absolues, GenreMvt IN(3,4,9), traversée hiérarchie

- CA était négatif (MntMvtTTC brut) → filtre mntmvtttc < 0 + ABS comme le endpoint /ca
- GenreMvt=3 uniquement → IN(3,4,9)
- Niveau 1/2 retournait vide (articles au niveau 3) → remontée via chemin_pere
  chemin_pere = "no_id_niv1.no_id_niv2" → SPLIT_PART + JOIN NOMENCLATURE parent
- Ajout taux_marge dans la réponse

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
MichaelandClaude Sonnet 4.6 committed 2026-04-26 09:28:51 +02:00
1 parent c1d6f87a7e
commit 57e9449a58
1 file changed
+25 -14
+25 -14
View File
@@ -91,33 +91,44 @@ router.get('/hitparade', async (req, res) => {
}); });
// GET /api/performance/ca/nomenclature?dateDebut=&dateFin=&site=&niveau= // GET /api/performance/ca/nomenclature?dateDebut=&dateFin=&site=&niveau=
// Articles sont stockés au niveau 3. Pour niveau=1 ou 2, on remonte via chemin_pere (format "noid_niv1.noid_niv2").
router.get('/ca/nomenclature', async (req, res) => { router.get('/ca/nomenclature', async (req, res) => {
try { try {
const pool = getPool(); const pool = getPool();
const { dateDebut = '2024-01-01', dateFin = '2099-12-31', site = '', niveau = 1 } = req.query; const { dateDebut = '2024-01-01', dateFin = '2099-12-31', site = '', niveau = 1 } = req.query;
const niv = Math.max(1, Math.min(parseInt(niveau) || 1, 3));
// Pour niveau 1 ou 2 : remonter la hiérarchie via chemin_pere de la nomenclature niveau 3
// chemin_pere = "no_id_niv1.no_id_niv2" → SPLIT_PART(..., niv)
const joinNom = niv === 3
? `JOIN NOMENCLATURE n ON n.NO_ID = a.NOM_NO_ID`
: `JOIN NOMENCLATURE n3 ON n3.NO_ID = a.NOM_NO_ID
JOIN NOMENCLATURE n ON n.NO_ID = NULLIF(SPLIT_PART(n3.chemin_pere, '.', ${niv}), '')::bigint`;
const result = await pool.query(` const result = await pool.query(`
SELECT SELECT
n.CODE AS CODE_NOMEN, n.CODE AS code_nomen,
n.LIBELLE, n.LIBELLE,
n.NIVEAU, ${niv} AS niveau,
COUNT(DISTINCT m.ArtNoId) AS NB_ARTICLES, COUNT(DISTINCT m.ArtNoId) AS nb_articles,
SUM(m.QteMvt) AS QTE_VENDUE, ABS(SUM(CASE WHEN m.mntmvtttc < 0 THEN m.qtemvt ELSE 0 END)) AS qte_vendue,
SUM(m.MntMvtHt) AS CA_HT, ABS(SUM(CASE WHEN m.mntmvtttc < 0 THEN m.mntmvtht ELSE 0 END)) AS ca_ht,
SUM(m.MntMvtTTC) AS CA_TTC, ABS(SUM(CASE WHEN m.mntmvtttc < 0 THEN m.mntmvtttc ELSE 0 END)) AS ca_ttc,
SUM(m.MargeMvt) AS MARGE SUM(m.MargeMvt) AS marge,
CASE WHEN SUM(m.mntmvtht) != 0
THEN ROUND(SUM(m.margemvt) / ABS(SUM(m.mntmvtht)) * 100, 2)
ELSE 0 END AS taux_marge
FROM MvtArt m FROM MvtArt m
JOIN ARTICLES a ON a.NO_ID = m.ArtNoId JOIN ARTICLES a ON a.NO_ID = m.ArtNoId
JOIN NOMENCLATURE n ON n.NO_ID = a.NOM_NO_ID ${joinNom}
WHERE m.GenreMvt = 3 WHERE m.GenreMvt IN (3, 4, 9)
AND m.DatMvt BETWEEN $1 AND $2 AND m.DatMvt BETWEEN $1 AND $2
AND m.Site LIKE $3 AND m.Site LIKE $3
AND n.NIVEAU = $4 GROUP BY n.CODE, n.LIBELLE
GROUP BY n.CODE, n.LIBELLE, n.NIVEAU ORDER BY ca_ttc DESC
ORDER BY CA_TTC DESC `, [dateDebut, dateFin, `%${site}%`]);
`, [dateDebut, dateFin, `%${site}%`, parseInt(niveau)]);
res.json({ dateDebut, dateFin, niveau: parseInt(niveau), ca_par_nomenclature: result.rows }); res.json({ dateDebut, dateFin, niveau: niv, ca_par_nomenclature: result.rows });
} catch (err) { } catch (err) {
res.status(500).json({ error: err.message }); res.status(500).json({ error: err.message });
} }