feat: implement advertising data synchronization and API endpoints for performance tracking

This commit is contained in:
Michael committed 2026-04-07 10:34:36 +02:00
1 parent de69370d9e
commit 9e993b3559
6 files changed
+506 -5

No files matched your search

+5 -1
View File
@@ -10,7 +10,8 @@ const stockRouter = require('./routes/stock');
const mouvementsRouter = require('./routes/mouvements');
const performanceRouter = require('./routes/performance');
const syncRouter = require('./routes/sync');
const rankingRouter = require('./routes/ranking');
const rankingRouter = require('./routes/ranking');
const publicitesRouter = require('./routes/publicites');
const app = express();
app.use(express.json());
@@ -36,6 +37,7 @@ app.use('/api/mouvements', mouvementsRouter);
app.use('/api/performance', performanceRouter);
app.use('/api/sync', syncRouter);
app.use('/api/ranking', rankingRouter);
app.use('/api/publicites', publicitesRouter);
// 404
app.use((req, res) => {
@@ -72,5 +74,7 @@ app.listen(PORT, () => {
console.log(' GET /api/performance/hitparade[?dateDebut=&dateFin=&site=&limit=&groupBy=qte|ca|marge]');
console.log(' GET /api/performance/ca/nomenclature[?dateDebut=&dateFin=&site=&niveau=]');
console.log(' GET /api/performance/ca/gamme[?dateDebut=&dateFin=&site=]');
console.log(' GET /api/publicites[?search=&site=&dateDebut=&dateFin=&page=&limit=]');
console.log(' GET /api/publicites/:code[?site=]');
console.log(' GET /api/sync/status');
});
+154
View File
@@ -0,0 +1,154 @@
const express = require('express');
const router = express.Router();
const { getPool } = require('../config/database');
// GET /api/publicites?search=&site=&dateDebut=&dateFin=&page=&limit=
router.get('/', async (req, res) => {
try {
const pool = getPool();
const {
search = '',
site = '',
dateDebut = '2000-01-01',
dateFin = '2099-12-31',
page = 1,
limit = 50,
} = req.query;
const pageNum = Math.max(1, parseInt(page) || 1);
const limitNum = Math.max(1, Math.min(parseInt(limit) || 50, 500));
const offset = (pageNum - 1) * limitNum;
// Synthèse par pub (agrégat multi-sites)
const result = await pool.query(`
SELECT
e.tcr_code,
e.tcr_libelle AS intitule,
e.tcrd_datedeb AS date_debut,
e.tcrd_datefin AS date_fin,
e.datecalcul,
e.site,
e.ca_pub_periode_pub,
e.qte_vendue_pub,
e.ca_total_periode_pub,
ROUND(e.pourc_capub_catotal::NUMERIC, 2) AS pourc_capub_catotal,
e.client_pub_periode,
e.client_total_periode,
e.stock_datedebut,
e.stock_datefin,
e.ca_pub_30_jours,
e.ca_pub_60_jours,
e.ca_pub_90_jours,
e.ca_pub_180_jours,
e.ca_depuis_finpub,
ROUND(e.taux_sortie::NUMERIC, 2) AS taux_sortie,
ROUND(e.marge::NUMERIC, 4) AS marge,
ROUND(e.taux_marge::NUMERIC, 2) AS taux_marge,
(SELECT COUNT(DISTINCT d.artnoid)
FROM pub_ecoulement_detail d
WHERE d.tcr_code = e.tcr_code AND d.site = e.site) AS nb_articles
FROM pub_ecoulement e
WHERE ($1 = '' OR e.tcr_libelle ILIKE '%' || $1 || '%' OR e.tcr_code ILIKE '%' || $1 || '%')
AND ($2 = '' OR e.site ILIKE '%' || $2 || '%')
AND e.tcrd_datedeb >= $3
AND e.tcrd_datedeb <= $4
ORDER BY e.tcrd_datedeb DESC, e.tcr_code, e.site
LIMIT $5 OFFSET $6
`, [search, site, dateDebut, dateFin, limitNum, offset]);
res.json({
page: pageNum,
limit: limitNum,
publicites: result.rows,
});
} catch (err) {
res.status(500).json({ error: err.message });
}
});
// GET /api/publicites/:code — synthèse par site + articles
router.get('/:code', async (req, res) => {
try {
const pool = getPool();
const { code } = req.params;
const { site = '' } = req.query;
// Synthèse par site
const synthese = await pool.query(`
SELECT
e.tcr_code,
e.tcr_libelle AS intitule,
e.tcrd_datedeb AS date_debut,
e.tcrd_datefin AS date_fin,
e.datecalcul,
e.site,
e.ca_pub_periode_pub,
e.qte_vendue_pub,
e.ca_total_periode_pub,
ROUND(e.pourc_capub_catotal::NUMERIC, 2) AS pourc_capub_catotal,
e.client_pub_periode,
e.client_total_periode,
e.stock_datedebut,
e.stock_datefin,
e.ca_pub_30_jours,
e.ca_pub_60_jours,
e.ca_pub_90_jours,
e.ca_pub_180_jours,
e.ca_depuis_finpub,
ROUND(e.taux_sortie::NUMERIC, 2) AS taux_sortie,
ROUND(e.marge::NUMERIC, 4) AS marge,
ROUND(e.taux_marge::NUMERIC, 2) AS taux_marge
FROM pub_ecoulement e
WHERE e.tcr_code = $1
AND ($2 = '' OR e.site ILIKE '%' || $2 || '%')
ORDER BY e.site
`, [code, site]);
if (!synthese.rows.length) {
return res.status(404).json({ error: 'Publication introuvable' });
}
// Articles de la pub
const articles = await pool.query(`
SELECT
d.site,
d.artnoid,
d.codein,
d.libelle,
d.prmp,
d.pa,
d.pv,
d.prixpub,
d.qte_vendue_pub,
ROUND(d.ca_pub_periode_pub::NUMERIC, 4) AS ca_pub_periode_pub,
d.stock_datedebut,
d.stock_datefin,
d.ca_pub_30_jours,
d.ca_pub_60_jours,
d.ca_pub_90_jours,
d.ca_pub_180_jours,
d.ca_depuis_finpub,
ROUND(d.taux_sortie::NUMERIC, 2) AS taux_sortie,
ROUND(d.marge::NUMERIC, 4) AS marge,
ROUND(d.taux_marge::NUMERIC, 2) AS taux_marge
FROM pub_ecoulement_detail d
WHERE d.tcr_code = $1
AND ($2 = '' OR d.site ILIKE '%' || $2 || '%')
ORDER BY d.site, d.ca_pub_periode_pub DESC
`, [code, site]);
res.json({
tcr_code: synthese.rows[0].tcr_code,
intitule: synthese.rows[0].intitule,
date_debut: synthese.rows[0].date_debut,
date_fin: synthese.rows[0].date_fin,
datecalcul: synthese.rows[0].datecalcul,
synthese_par_site: synthese.rows,
articles: articles.rows,
});
} catch (err) {
res.status(500).json({ error: err.message });
}
});
module.exports = router;