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 {
|
||||
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
@@ -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');
|
||||
|
||||
@@ -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
@@ -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
@@ -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 };
|
||||
Reference in new issue
Block a user