Fix sync: null bytes, BIT→SMALLINT, DECIMAL complex types, NbJours safe int, error_msg column
This commit is contained in:
1 parent
20a1396810
commit
984ed4da5f
5 files changed
+83
-37
No files matched your search
+2
-2
@@ -61,7 +61,7 @@ app.get('/api/status', async (req, res) => {
|
|||||||
try {
|
try {
|
||||||
const pg = getPg();
|
const pg = getPg();
|
||||||
const { rows } = await pg.query(
|
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`
|
FROM sync_log ORDER BY table_name`
|
||||||
);
|
);
|
||||||
res.json({
|
res.json({
|
||||||
@@ -238,7 +238,7 @@ function renderStatus(data){
|
|||||||
<td><strong>\${t.table_name}</strong></td>
|
<td><strong>\${t.table_name}</strong></td>
|
||||||
<td>\${fmtDate(t.last_sync)}</td>
|
<td>\${fmtDate(t.last_sync)}</td>
|
||||||
<td>\${t.rows_synced ?? '—'}</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>
|
</tr>
|
||||||
\`).join('');
|
\`).join('');
|
||||||
}
|
}
|
||||||
|
|||||||
+13
-13
@@ -1,5 +1,5 @@
|
|||||||
const { getMssql, getPg } = require('../db');
|
const { getMssql, getPg } = require('../db');
|
||||||
const { batchUpsert, getLastSync, logSync } = require('../utils');
|
const { batchUpsert, getLastSync, logSync, safeStr, safeBit } = require('../utils');
|
||||||
|
|
||||||
const ARTICLES_COLS = [
|
const ARTICLES_COLS = [
|
||||||
'no_id','codein','libelle1','libelle2','lib_ticket',
|
'no_id','codein','libelle1','libelle2','lib_ticket',
|
||||||
@@ -32,15 +32,15 @@ async function syncArticles(force) {
|
|||||||
|
|
||||||
const rows = res.recordset.map(r => ({
|
const rows = res.recordset.map(r => ({
|
||||||
no_id: r.NO_ID,
|
no_id: r.NO_ID,
|
||||||
codein: r.CODEIN,
|
codein: safeStr(r.CODEIN),
|
||||||
libelle1: r.LIBELLE1,
|
libelle1: safeStr(r.LIBELLE1),
|
||||||
libelle2: r.LIBELLE2,
|
libelle2: safeStr(r.LIBELLE2),
|
||||||
lib_ticket: r.LIB_TICKET,
|
lib_ticket: safeStr(r.LIB_TICKET),
|
||||||
tax_code: r.TAX_CODE,
|
tax_code: safeStr(r.TAX_CODE),
|
||||||
ach_code: r.ACH_CODE,
|
ach_code: safeStr(r.ACH_CODE),
|
||||||
utilisable: r.UTILISABLE,
|
utilisable: safeStr(r.UTILISABLE),
|
||||||
actif: r.ACTIF,
|
actif: safeStr(r.ACTIF),
|
||||||
suspendu: r.SUSPENDU,
|
suspendu: safeStr(r.SUSPENDU),
|
||||||
suividatecreation: r.SUIVIDATECREATION,
|
suividatecreation: r.SUIVIDATECREATION,
|
||||||
suividatemodif: r.SUIVIDATEMODIF,
|
suividatemodif: r.SUIVIDATEMODIF,
|
||||||
nom_no_id: r.NOM_NO_ID,
|
nom_no_id: r.NOM_NO_ID,
|
||||||
@@ -102,9 +102,9 @@ async function syncArticles(force) {
|
|||||||
`SELECT IDARTICLE, GTIN, PREFERENTIEL FROM ART_GTIN`
|
`SELECT IDARTICLE, GTIN, PREFERENTIEL FROM ART_GTIN`
|
||||||
);
|
);
|
||||||
const rows = res.recordset.map(r => ({
|
const rows = res.recordset.map(r => ({
|
||||||
idarticle: r.IDARTICLE,
|
idarticle: r.IDARTICLE,
|
||||||
gtin: r.GTIN,
|
gtin: safeStr(r.GTIN),
|
||||||
preferentiel: r.PREFERENTIEL,
|
preferentiel: safeBit(r.PREFERENTIEL),
|
||||||
}));
|
}));
|
||||||
const count = await batchUpsert(pg, 'art_gtin', rows, ['idarticle','gtin'], GTIN_COLS);
|
const count = await batchUpsert(pg, 'art_gtin', rows, ['idarticle','gtin'], GTIN_COLS);
|
||||||
await logSync(pg, 'art_gtin', count, 'ok');
|
await logSync(pg, 'art_gtin', count, 'ok');
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
const { getMssql, getPg } = require('../db');
|
const { getMssql, getPg } = require('../db');
|
||||||
const { batchUpsert, fullRefresh, getLastSync, logSync } = require('../utils');
|
const { batchUpsert, fullRefresh, getLastSync, logSync, safeDecimal } = require('../utils');
|
||||||
|
|
||||||
const ARTFOU1_COLS = [
|
const ARTFOU1_COLS = [
|
||||||
'no_id','art_no_id','code','reference','ean13','itf',
|
'no_id','art_no_id','code','reference','ean13','itf',
|
||||||
@@ -58,8 +58,8 @@ async function syncFournisseurs(force) {
|
|||||||
);
|
);
|
||||||
const rows = res.recordset.map(r => ({
|
const rows = res.recordset.map(r => ({
|
||||||
idartfou1: r.IDARTFOU1,
|
idartfou1: r.IDARTFOU1,
|
||||||
prixachat: r.PRIXACHAT,
|
prixachat: safeDecimal(r.PRIXACHAT),
|
||||||
remise_promotion: r.REMISE_PROMOTION,
|
remise_promotion: safeDecimal(r.REMISE_PROMOTION),
|
||||||
}));
|
}));
|
||||||
const count = await batchUpsert(pg, 'artfou2', rows, ['idartfou1'], ARTFOU2_COLS);
|
const count = await batchUpsert(pg, 'artfou2', rows, ['idartfou1'], ARTFOU2_COLS);
|
||||||
await logSync(pg, 'artfou2', count, 'ok');
|
await logSync(pg, 'artfou2', count, 'ok');
|
||||||
|
|||||||
+18
-18
@@ -1,5 +1,5 @@
|
|||||||
const { getMssql, getPg } = require('../db');
|
const { getMssql, getPg } = require('../db');
|
||||||
const { fullRefresh, batchUpsert, logSync } = require('../utils');
|
const { fullRefresh, batchUpsert, logSync, safeNum, safeInt, safeStr } = require('../utils');
|
||||||
|
|
||||||
const STOCK_COLS = [
|
const STOCK_COLS = [
|
||||||
'artnoid','site','qte','prmp','valstock','pv',
|
'artnoid','site','qte','prmp','valstock','pv',
|
||||||
@@ -27,23 +27,23 @@ async function syncStock(force) {
|
|||||||
`);
|
`);
|
||||||
|
|
||||||
const rows = res.recordset.map(r => ({
|
const rows = res.recordset.map(r => ({
|
||||||
artnoid: r.ArtNoId,
|
artnoid: r.ArtNoId,
|
||||||
site: r.Site,
|
site: safeStr(r.Site),
|
||||||
qte: r.QTE,
|
qte: safeNum(r.QTE),
|
||||||
prmp: r.Prmp,
|
prmp: safeNum(r.Prmp),
|
||||||
valstock: r.ValStock,
|
valstock: safeNum(r.ValStock),
|
||||||
pv: r.PV,
|
pv: safeNum(r.PV),
|
||||||
stockdispo: r.StockDispo,
|
stockdispo: safeNum(r.StockDispo),
|
||||||
stockmort: r.StockMort,
|
stockmort: safeNum(r.StockMort),
|
||||||
stockcolis: r.StockColis,
|
stockcolis: safeNum(r.StockColis),
|
||||||
dernierevente: r.DerniereVente,
|
dernierevente: r.DerniereVente,
|
||||||
dernierereception: r.DerniereReception,
|
dernierereception: r.DerniereReception,
|
||||||
premierevente: r.PremiereVente,
|
premierevente: r.PremiereVente,
|
||||||
nbjoursdernierMouvement: r.NbJoursDernierMouvement,
|
nbjoursdernierMouvement: safeInt(r.NbJoursDernierMouvement),
|
||||||
nbjoursdernierevente: r.NbJoursDerniereVente,
|
nbjoursdernierevente: safeInt(r.NbJoursDerniereVente),
|
||||||
nbjoursdernierrereception: r.NbJoursDerniereReception,
|
nbjoursdernierrereception: safeInt(r.NbJoursDerniereReception),
|
||||||
interditachat: r.InterditAchat,
|
interditachat: safeStr(r.InterditAchat),
|
||||||
codefou: r.CODEFOU,
|
codefou: safeStr(r.CODEFOU),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const count = await fullRefresh(pg, 'cube_stock', rows, STOCK_COLS);
|
const count = await fullRefresh(pg, 'cube_stock', rows, STOCK_COLS);
|
||||||
|
|||||||
+47
-1
@@ -88,4 +88,50 @@ async function logSync(pg, tableName, rowsSynced, status, errorMsg = null) {
|
|||||||
`, [tableName, rowsSynced, status, errorMsg]);
|
`, [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 };
|
||||||
Reference in new issue
Block a user