From c75fd5c56d5169eca39c34fc21943deecfebab7d Mon Sep 17 00:00:00 2001 From: Michael SCHAL Date: Mon, 13 Apr 2026 15:58:15 +0200 Subject: [PATCH] fix: utiliser FOUPORT2.SEUIL comme source principale du franco fournisseur MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit FOUCAD ne couvre que 53/153 fournisseurs actifs (commande auto uniquement). FOUPORT2.SEUIL est la source officielle de l'ERP et couvre 108 fournisseurs. 4 fournisseurs dans COMMANDE_AUTO_QTEPROPO n'avaient pas de franco visible (IMDIFA, MYCANDYSHOP, PTIT CLOWN, PUCKATOR BV). - Sync : ajout de FOUPORT et FOUPORT2 dans le pipeline de synchronisation - API : franco = COALESCE(fouport2.seuil, foucad.franco) dans les deux routes - Schema PG : création des tables fouport et fouport2 Co-Authored-By: Claude Sonnet 4.6 --- postgres/init.sql | 21 +++++++++++++ src/routes/commandes-auto.js | 32 ++++++++++++-------- sync/tables/appro.js | 58 ++++++++++++++++++++++++++++++++++++ 3 files changed, 99 insertions(+), 12 deletions(-) diff --git a/postgres/init.sql b/postgres/init.sql index b750a1e..355b23e 100644 --- a/postgres/init.sql +++ b/postgres/init.sql @@ -461,6 +461,27 @@ CREATE TABLE IF NOT EXISTS foucad ( suividatemodif TIMESTAMP ); +-- Conditions de port fournisseur (source officielle du franco) +CREATE TABLE IF NOT EXISTS fouport ( + no_id NUMERIC(20,0) PRIMARY KEY, + code TEXT, + sit_code TEXT, + suividatecreation TIMESTAMP, + suividatemodif TIMESTAMP +); +CREATE INDEX IF NOT EXISTS idx_fouport_code ON fouport (code); + +CREATE TABLE IF NOT EXISTS fouport2 ( + fou_no_id NUMERIC(20,0), + no_id NUMERIC(20,0) PRIMARY KEY, + seuil NUMERIC(12,2), + mode SMALLINT, + valeur NUMERIC(12,2), + suividatecreation TIMESTAMP, + suividatemodif TIMESTAMP +); +CREATE INDEX IF NOT EXISTS idx_fouport2_fou_no_id ON fouport2 (fou_no_id); + -- ============================================================ -- Publicités (FBENTPUB + ECOULEMENT + ECOULEMENT_DETAIL SQL Server) -- ============================================================ diff --git a/src/routes/commandes-auto.js b/src/routes/commandes-auto.js index 4a27443..1d27498 100644 --- a/src/routes/commandes-auto.js +++ b/src/routes/commandes-auto.js @@ -14,27 +14,31 @@ router.get('/', async (req, res) => { q.codesite AS site, q.fou_code AS codefou, fi.nom AS nom_fou, - fc.franco AS franco_ht, + COALESCE(fp2.seuil, fc.franco) AS franco_ht, COUNT(q.art_no_id) AS nb_articles, SUM(q.qtepropo) AS qte_totale, ROUND(SUM(q.qtepropo * cp.pa), 2) AS montant_propo_ht, CASE - WHEN fc.franco IS NULL OR fc.franco = 0 THEN 'N/A' - WHEN SUM(q.qtepropo * cp.pa) >= fc.franco THEN 'OUI' + WHEN COALESCE(fp2.seuil, fc.franco) IS NULL OR COALESCE(fp2.seuil, fc.franco) = 0 THEN 'N/A' + WHEN SUM(q.qtepropo * cp.pa) >= COALESCE(fp2.seuil, fc.franco) THEN 'OUI' ELSE 'NON' END AS franco_atteint, CASE - WHEN fc.franco IS NULL OR fc.franco = 0 THEN NULL - ELSE ROUND(fc.franco - SUM(q.qtepropo * cp.pa), 2) + WHEN COALESCE(fp2.seuil, fc.franco) IS NULL OR COALESCE(fp2.seuil, fc.franco) = 0 THEN NULL + ELSE ROUND(COALESCE(fp2.seuil, fc.franco) - SUM(q.qtepropo * cp.pa), 2) END AS ecart_franco FROM commande_auto_qtepropo q LEFT JOIN fouident fi ON fi.code = q.fou_code LEFT JOIN foucad fc ON fc.foucode = q.fou_code + LEFT JOIN fouport fp ON fp.code = q.fou_code + LEFT JOIN ( + SELECT fou_no_id, MAX(seuil) AS seuil FROM fouport2 GROUP BY fou_no_id + ) fp2 ON fp2.fou_no_id = fp.no_id LEFT JOIN cube_pa cp ON cp.artnoid = q.art_no_id WHERE q.qtepropo > 0 AND ($1 = '' OR q.codesite LIKE $1) AND ($2 = '' OR q.fou_code ILIKE $2) - GROUP BY q.codesite, q.fou_code, fi.nom, fc.franco + GROUP BY q.codesite, q.fou_code, fi.nom, fc.franco, fp2.seuil ORDER BY q.codesite, montant_propo_ht DESC `, [`%${site}%`, `%${codefou}%`]); @@ -58,27 +62,31 @@ router.get('/:codefou', async (req, res) => { q.codesite AS site, q.fou_code AS codefou, fi.nom AS nom_fou, - fc.franco AS franco_ht, + COALESCE(fp2.seuil, fc.franco) AS franco_ht, COUNT(q.art_no_id) AS nb_articles, SUM(q.qtepropo) AS qte_totale, ROUND(SUM(q.qtepropo * cp.pa), 2) AS montant_propo_ht, CASE - WHEN fc.franco IS NULL OR fc.franco = 0 THEN 'N/A' - WHEN SUM(q.qtepropo * cp.pa) >= fc.franco THEN 'OUI' + WHEN COALESCE(fp2.seuil, fc.franco) IS NULL OR COALESCE(fp2.seuil, fc.franco) = 0 THEN 'N/A' + WHEN SUM(q.qtepropo * cp.pa) >= COALESCE(fp2.seuil, fc.franco) THEN 'OUI' ELSE 'NON' END AS franco_atteint, CASE - WHEN fc.franco IS NULL OR fc.franco = 0 THEN NULL - ELSE ROUND(fc.franco - SUM(q.qtepropo * cp.pa), 2) + WHEN COALESCE(fp2.seuil, fc.franco) IS NULL OR COALESCE(fp2.seuil, fc.franco) = 0 THEN NULL + ELSE ROUND(COALESCE(fp2.seuil, fc.franco) - SUM(q.qtepropo * cp.pa), 2) END AS ecart_franco FROM commande_auto_qtepropo q LEFT JOIN fouident fi ON fi.code = q.fou_code LEFT JOIN foucad fc ON fc.foucode = q.fou_code + LEFT JOIN fouport fp ON fp.code = q.fou_code + LEFT JOIN ( + SELECT fou_no_id, MAX(seuil) AS seuil FROM fouport2 GROUP BY fou_no_id + ) fp2 ON fp2.fou_no_id = fp.no_id LEFT JOIN cube_pa cp ON cp.artnoid = q.art_no_id WHERE q.qtepropo > 0 AND q.fou_code ILIKE $1 AND ($2 = '' OR q.codesite LIKE $2) - GROUP BY q.codesite, q.fou_code, fi.nom, fc.franco + GROUP BY q.codesite, q.fou_code, fi.nom, fc.franco, fp2.seuil ORDER BY q.codesite `, [codefou, `%${site}%`]); diff --git a/sync/tables/appro.js b/sync/tables/appro.js index 81285d3..f4e5ab1 100644 --- a/sync/tables/appro.js +++ b/sync/tables/appro.js @@ -22,6 +22,14 @@ const FOUCAD_COLS = [ 'montantcdeauto','datecdeauto','suividatecreation','suividatemodif', ]; +const FOUPORT_COLS = [ + 'no_id','code','sit_code','suividatecreation','suividatemodif', +]; + +const FOUPORT2_COLS = [ + 'fou_no_id','no_id','seuil','mode','valeur','suividatecreation','suividatemodif', +]; + const PLAN_REAPPRO_COLS = [ 'id','magasin','codein','bu_no_id','stockmag','encours_palette_mag', 'attente_prepa_mag','colis_rea','colis_ajout_web','stock_dispo', @@ -171,6 +179,56 @@ async function syncAppro(force) { console.error(`[foucad] ERREUR: ${err.message}`); } + // === FOUPORT (full refresh — conditions de port par fournisseur) === + try { + const ms = await getMssql(); + const res = await ms.request().query(` + SELECT NO_ID, CODE, SIT_CODE, SUIVIDATECREATION, SUIVIDATEMODIF + FROM FOUPORT + `); + + const rows = res.recordset.map(r => ({ + no_id: r.NO_ID, + code: r.CODE, + sit_code: r.SIT_CODE ?? null, + suividatecreation: r.SUIVIDATECREATION, + suividatemodif: r.SUIVIDATEMODIF, + })); + + const count = await fullRefresh(pg, 'fouport', rows, FOUPORT_COLS); + await logSync(pg, 'fouport', count, 'ok'); + console.log(`[fouport] ${count} lignes`); + } catch (err) { + await logSync(pg, 'fouport', 0, 'error', err.message); + console.error(`[fouport] ERREUR: ${err.message}`); + } + + // === FOUPORT2 (full refresh — seuils de franco par fournisseur) === + try { + const ms = await getMssql(); + const res = await ms.request().query(` + SELECT FOU_NO_ID, NO_ID, SEUIL, MODE, VALEUR, SUIVIDATECREATION, SUIVIDATEMODIF + FROM FOUPORT2 + `); + + const rows = res.recordset.map(r => ({ + fou_no_id: r.FOU_NO_ID, + no_id: r.NO_ID, + seuil: r.SEUIL ?? null, + mode: r.MODE ?? null, + valeur: r.VALEUR ?? null, + suividatecreation: r.SUIVIDATECREATION, + suividatemodif: r.SUIVIDATEMODIF, + })); + + const count = await fullRefresh(pg, 'fouport2', rows, FOUPORT2_COLS); + await logSync(pg, 'fouport2', count, 'ok'); + console.log(`[fouport2] ${count} lignes`); + } catch (err) { + await logSync(pg, 'fouport2', 0, 'error', err.message); + console.error(`[fouport2] ERREUR: ${err.message}`); + } + // === PLAN_REAPPRO (full refresh — état courant) === try { const ms = await getMssql();