Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
adc8d357c9 | ||
|
|
950539472f | ||
|
|
e01048fe3b | ||
|
|
c5b0896bb4 |
No files matched your search
+13
-1
@@ -171,7 +171,19 @@ async function syncAppro(force) {
|
|||||||
suividatemodif: r.SUIVIDATEMODIF,
|
suividatemodif: r.SUIVIDATEMODIF,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const count = await fullRefresh(pg, 'foucad', rows, FOUCAD_COLS);
|
// foucode est PK côté PostgreSQL mais la source FOUCAD peut renvoyer
|
||||||
|
// plusieurs lignes par FOUCODE → on déduplique en gardant la plus récente
|
||||||
|
// (suividatemodif), sinon l'INSERT viole foucad_pkey.
|
||||||
|
const byCode = new Map();
|
||||||
|
for (const row of rows) {
|
||||||
|
const prev = byCode.get(row.foucode);
|
||||||
|
if (!prev || (row.suividatemodif ?? 0) >= (prev.suividatemodif ?? 0)) {
|
||||||
|
byCode.set(row.foucode, row);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const dedupRows = [...byCode.values()];
|
||||||
|
|
||||||
|
const count = await fullRefresh(pg, 'foucad', dedupRows, FOUCAD_COLS);
|
||||||
await logSync(pg, 'foucad', count, 'ok');
|
await logSync(pg, 'foucad', count, 'ok');
|
||||||
console.log(`[foucad] ${count} lignes`);
|
console.log(`[foucad] ${count} lignes`);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|||||||
+73
-1
@@ -11,6 +11,48 @@ const STOCK_COLS = [
|
|||||||
const PA_COLS = ['artnoid','pa'];
|
const PA_COLS = ['artnoid','pa'];
|
||||||
const PV_COLS = ['artnoid','site','pv'];
|
const PV_COLS = ['artnoid','site','pv'];
|
||||||
|
|
||||||
|
// ARTFOU2.PRIXACHAT empile l'historique des prix d'un fournisseur sous forme
|
||||||
|
// "[01/01/1901,0.990:16/05/2018,1.010]". Le dernier couple porte le prix courant
|
||||||
|
// et la date à laquelle il a été appliqué.
|
||||||
|
function lastPriceChange(s) {
|
||||||
|
if (!s) return null;
|
||||||
|
const body = String(s).trim().replace(/^\[/, '').replace(/\]$/, '');
|
||||||
|
if (!body) return null;
|
||||||
|
const pairs = body.split(':');
|
||||||
|
const [d, p] = pairs[pairs.length - 1].split(',');
|
||||||
|
const m = d && d.match(/^(\d{2})\/(\d{2})\/(\d{4})$/);
|
||||||
|
const price = parseFloat(p);
|
||||||
|
if (!m || Number.isNaN(price)) return null;
|
||||||
|
return { date: Date.UTC(+m[3], +m[2] - 1, +m[1]), price };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pour chaque artnoid en doublon, retrouve le prix dont le changement est le plus
|
||||||
|
// récent. Égalité de date de prix → on départage sur SUIVIDATEMODIF de la ligne.
|
||||||
|
async function resolveDuplicatePa(ms, artnoids) {
|
||||||
|
const ids = artnoids.map(Number).filter(Number.isFinite);
|
||||||
|
if (!ids.length) return new Map();
|
||||||
|
|
||||||
|
const res = await ms.request().query(`
|
||||||
|
SELECT f1.ART_NO_ID, f2.PRIXACHAT, f2.SUIVIDATEMODIF
|
||||||
|
FROM ARTFOU1 f1
|
||||||
|
JOIN ARTFOU2 f2 ON f2.IDARTFOU1 = f1.NO_ID
|
||||||
|
WHERE f1.ART_NO_ID IN (${ids.join(',')})
|
||||||
|
`);
|
||||||
|
|
||||||
|
const best = new Map();
|
||||||
|
for (const r of res.recordset) {
|
||||||
|
const lp = lastPriceChange(r.PRIXACHAT);
|
||||||
|
if (!lp) continue;
|
||||||
|
const key = String(r.ART_NO_ID);
|
||||||
|
const modif = r.SUIVIDATEMODIF ? new Date(r.SUIVIDATEMODIF).getTime() : 0;
|
||||||
|
const prev = best.get(key);
|
||||||
|
if (!prev || lp.date > prev.date || (lp.date === prev.date && modif > prev.modif)) {
|
||||||
|
best.set(key, { date: lp.date, price: lp.price, modif });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return best;
|
||||||
|
}
|
||||||
|
|
||||||
async function syncStock(force) {
|
async function syncStock(force) {
|
||||||
const ms = await getMssql();
|
const ms = await getMssql();
|
||||||
const pg = getPg();
|
const pg = getPg();
|
||||||
@@ -57,7 +99,37 @@ async function syncStock(force) {
|
|||||||
// === Cube_PA (full refresh) ===
|
// === Cube_PA (full refresh) ===
|
||||||
try {
|
try {
|
||||||
const res = await ms.request().query(`SELECT ArtNoId, PA FROM Cube_PA`);
|
const res = await ms.request().query(`SELECT ArtNoId, PA FROM Cube_PA`);
|
||||||
const rows = res.recordset.map(r => ({ artnoid: r.ArtNoId, pa: r.PA }));
|
const raw = res.recordset.map(r => ({ artnoid: r.ArtNoId, pa: r.PA }));
|
||||||
|
|
||||||
|
// artnoid est PK côté PostgreSQL, mais le cube peut sortir deux lignes pour
|
||||||
|
// un article livré par deux fournisseurs → l'INSERT violerait cube_pa_pkey.
|
||||||
|
const groups = new Map();
|
||||||
|
for (const r of raw) {
|
||||||
|
const key = String(r.artnoid);
|
||||||
|
const g = groups.get(key);
|
||||||
|
if (g) g.push(r); else groups.set(key, [r]);
|
||||||
|
}
|
||||||
|
const dupIds = [...groups].filter(([, g]) => g.length > 1).map(([id]) => id);
|
||||||
|
|
||||||
|
let rows = raw;
|
||||||
|
if (dupIds.length) {
|
||||||
|
const best = await resolveDuplicatePa(ms, dupIds);
|
||||||
|
rows = [];
|
||||||
|
for (const [key, g] of groups) {
|
||||||
|
if (g.length === 1) { rows.push(g[0]); continue; }
|
||||||
|
const b = best.get(key);
|
||||||
|
let chosen = b && g.find(x => Math.abs(parseFloat(x.pa) - b.price) < 0.005);
|
||||||
|
if (!chosen) {
|
||||||
|
// Pas de prix fournisseur exploitable : on retient le PA le plus élevé,
|
||||||
|
// qui sous-estime la marge plutôt que de l'inventer.
|
||||||
|
chosen = g.reduce((a, x) => (parseFloat(x.pa) > parseFloat(a.pa) ? x : a));
|
||||||
|
console.warn(`[cube_pa] artnoid ${key} non résolu → PA le plus élevé retenu (${chosen.pa})`);
|
||||||
|
}
|
||||||
|
rows.push(chosen);
|
||||||
|
}
|
||||||
|
console.log(`[cube_pa] ${dupIds.length} artnoid dédupliqués`);
|
||||||
|
}
|
||||||
|
|
||||||
const count = await fullRefresh(pg, 'cube_pa', rows, PA_COLS);
|
const count = await fullRefresh(pg, 'cube_pa', rows, PA_COLS);
|
||||||
await logSync(pg, 'cube_pa', count, 'ok');
|
await logSync(pg, 'cube_pa', count, 'ok');
|
||||||
console.log(`[cube_pa] ${count} lignes refresh`);
|
console.log(`[cube_pa] ${count} lignes refresh`);
|
||||||
|
|||||||
Reference in new issue
Block a user