Fix sync: null bytes, BIT→SMALLINT, DECIMAL complex types, NbJours safe int, error_msg column

This commit is contained in:
Michael committed 2026-03-18 11:03:29 +01:00
1 parent 20a1396810
commit 984ed4da5f
5 files changed
+83 -37

No files matched your search

+2 -2
View File
@@ -61,7 +61,7 @@ app.get('/api/status', async (req, res) => {
try {
const pg = getPg();
const { rows } = await pg.query(
`SELECT table_name, last_sync, rows_synced, status, error_message
`SELECT table_name, last_sync, rows_synced, status, error_msg
FROM sync_log ORDER BY table_name`
);
res.json({
@@ -238,7 +238,7 @@ function renderStatus(data){
<td><strong>\${t.table_name}</strong></td>
<td>\${fmtDate(t.last_sync)}</td>
<td>\${t.rows_synced ?? '—'}</td>
<td class="\${t.status==='ok'?'ok':'err'}">\${t.status ?? '—'}\${t.error_message?' — '+t.error_message:''}</td>
<td class="\${t.status==='ok'?'ok':'err'}">\${t.status ?? '—'}\${t.error_msg?' — '+t.error_msg:''}</td>
</tr>
\`).join('');
}
+13 -13
View File
@@ -1,5 +1,5 @@
const { getMssql, getPg } = require('../db');
const { batchUpsert, getLastSync, logSync } = require('../utils');
const { batchUpsert, getLastSync, logSync, safeStr, safeBit } = require('../utils');
const ARTICLES_COLS = [
'no_id','codein','libelle1','libelle2','lib_ticket',
@@ -32,15 +32,15 @@ async function syncArticles(force) {
const rows = res.recordset.map(r => ({
no_id: r.NO_ID,
codein: r.CODEIN,
libelle1: r.LIBELLE1,
libelle2: r.LIBELLE2,
lib_ticket: r.LIB_TICKET,
tax_code: r.TAX_CODE,
ach_code: r.ACH_CODE,
utilisable: r.UTILISABLE,
actif: r.ACTIF,
suspendu: r.SUSPENDU,
codein: safeStr(r.CODEIN),
libelle1: safeStr(r.LIBELLE1),
libelle2: safeStr(r.LIBELLE2),
lib_ticket: safeStr(r.LIB_TICKET),
tax_code: safeStr(r.TAX_CODE),
ach_code: safeStr(r.ACH_CODE),
utilisable: safeStr(r.UTILISABLE),
actif: safeStr(r.ACTIF),
suspendu: safeStr(r.SUSPENDU),
suividatecreation: r.SUIVIDATECREATION,
suividatemodif: r.SUIVIDATEMODIF,
nom_no_id: r.NOM_NO_ID,
@@ -102,9 +102,9 @@ async function syncArticles(force) {
`SELECT IDARTICLE, GTIN, PREFERENTIEL FROM ART_GTIN`
);
const rows = res.recordset.map(r => ({
idarticle: r.IDARTICLE,
gtin: r.GTIN,
preferentiel: r.PREFERENTIEL,
idarticle: r.IDARTICLE,
gtin: safeStr(r.GTIN),
preferentiel: safeBit(r.PREFERENTIEL),
}));
const count = await batchUpsert(pg, 'art_gtin', rows, ['idarticle','gtin'], GTIN_COLS);
await logSync(pg, 'art_gtin', count, 'ok');
+3 -3
View File
@@ -1,5 +1,5 @@
const { getMssql, getPg } = require('../db');
const { batchUpsert, fullRefresh, getLastSync, logSync } = require('../utils');
const { batchUpsert, fullRefresh, getLastSync, logSync, safeDecimal } = require('../utils');
const ARTFOU1_COLS = [
'no_id','art_no_id','code','reference','ean13','itf',
@@ -58,8 +58,8 @@ async function syncFournisseurs(force) {
);
const rows = res.recordset.map(r => ({
idartfou1: r.IDARTFOU1,
prixachat: r.PRIXACHAT,
remise_promotion: r.REMISE_PROMOTION,
prixachat: safeDecimal(r.PRIXACHAT),
remise_promotion: safeDecimal(r.REMISE_PROMOTION),
}));
const count = await batchUpsert(pg, 'artfou2', rows, ['idartfou1'], ARTFOU2_COLS);
await logSync(pg, 'artfou2', count, 'ok');
+18 -18
View File
@@ -1,5 +1,5 @@
const { getMssql, getPg } = require('../db');
const { fullRefresh, batchUpsert, logSync } = require('../utils');
const { fullRefresh, batchUpsert, logSync, safeNum, safeInt, safeStr } = require('../utils');
const STOCK_COLS = [
'artnoid','site','qte','prmp','valstock','pv',
@@ -27,23 +27,23 @@ async function syncStock(force) {
`);
const rows = res.recordset.map(r => ({
artnoid: r.ArtNoId,
site: r.Site,
qte: r.QTE,
prmp: r.Prmp,
valstock: r.ValStock,
pv: r.PV,
stockdispo: r.StockDispo,
stockmort: r.StockMort,
stockcolis: r.StockColis,
dernierevente: r.DerniereVente,
dernierereception: r.DerniereReception,
premierevente: r.PremiereVente,
nbjoursdernierMouvement: r.NbJoursDernierMouvement,
nbjoursdernierevente: r.NbJoursDerniereVente,
nbjoursdernierrereception: r.NbJoursDerniereReception,
interditachat: r.InterditAchat,
codefou: r.CODEFOU,
artnoid: r.ArtNoId,
site: safeStr(r.Site),
qte: safeNum(r.QTE),
prmp: safeNum(r.Prmp),
valstock: safeNum(r.ValStock),
pv: safeNum(r.PV),
stockdispo: safeNum(r.StockDispo),
stockmort: safeNum(r.StockMort),
stockcolis: safeNum(r.StockColis),
dernierevente: r.DerniereVente,
dernierereception: r.DerniereReception,
premierevente: r.PremiereVente,
nbjoursdernierMouvement: safeInt(r.NbJoursDernierMouvement),
nbjoursdernierevente: safeInt(r.NbJoursDerniereVente),
nbjoursdernierrereception: safeInt(r.NbJoursDerniereReception),
interditachat: safeStr(r.InterditAchat),
codefou: safeStr(r.CODEFOU),
}));
const count = await fullRefresh(pg, 'cube_stock', rows, STOCK_COLS);
+47 -1
View File
@@ -88,4 +88,50 @@ async function logSync(pg, tableName, rowsSynced, status, errorMsg = null) {
`, [tableName, rowsSynced, status, errorMsg]);
}
module.exports = { batchUpsert, fullRefresh, getLastSync, logSync };
// ── Helpers de conversion SQL Server → PostgreSQL ───────────
/** Nettoie une chaîne : supprime null bytes (0x00), retourne null si vide */
function safeStr(v) {
if (v == null) return null;
return String(v).replace(/\0/g, '') || null;
}
/** Convertit en nombre, retourne null si NaN ou non numérique */
function safeNum(v) {
if (v == null) return null;
if (typeof v === 'number') return isNaN(v) ? null : v;
const n = parseFloat(String(v).replace(/[^0-9.\-]/g, ''));
return isNaN(n) ? null : n;
}
/** Convertit en entier, retourne null si NaN */
function safeInt(v) {
if (v == null) return null;
if (typeof v === 'number') return Math.round(v);
const n = parseInt(v, 10);
return isNaN(n) ? null : n;
}
/** Convertit un BIT SQL Server (true/false) en SMALLINT (1/0) */
function safeBit(v) {
if (v == null) return null;
return v ? 1 : 0;
}
/**
* Convertit une valeur DECIMAL/MONEY qui peut être retournée par mssql
* sous forme d'objet, tableau, ou string "[date,valeur]".
*/
function safeDecimal(v) {
if (v == null) return null;
if (typeof v === 'number') return isNaN(v) ? null : v;
if (Array.isArray(v)) {
const last = v[v.length - 1];
return typeof last === 'number' ? last : safeNum(last);
}
// ex: "[01/01/1901,163.000]" → 163
const m = String(v).match(/([\d]+\.?[\d]*)[\]]*$/);
return m ? parseFloat(m[1]) : null;
}
module.exports = { batchUpsert, fullRefresh, getLastSync, logSync, safeStr, safeNum, safeInt, safeBit, safeDecimal };