Mejoras de sesion: estabilizacion, IA, Nexus Yonke y marketplace B2B
Some checks failed
CI / lint-and-test (3.11) (push) Has been cancelled
CI / lint-and-test (3.13) (push) Has been cancelled

- F0 blindaje: Redis/Celery, pg_hba sin trust LAN, secretos a env 600, backups, webhook auth, jobs muertos
- F1 caja: bugs POS, reembolsos al corte, apartados, CFDI, topes de descuento en cotizacion/apartado
- F2/F7 IA de catalogo: autocategorizacion, dedup de SKUs con embeddings, OCR de facturas, descripciones, claves SAT, limpieza de fitment
- F3 IA en el flujo: vision Yonke, preguntas MeLi, venta cruzada, anomalias, sinonimos, busqueda semantica (rerank/embeddings)
- F4/F8 escala y cartera: /metrics+Prometheus+Grafana, pool, score de credito, churn, metricas multi-tenant
- Nexus Yonke (M1-M6): modelo de datos 1-de-1, alta por donador, vitrina publica, MeLi usado, consignacion, garantia por dias, reportes, etiquetas
- Marketplace B2B entre yonkes (catalogo + pedidos cross-tenant)
- Seguridad: auth en /api/admin/* (35 endpoints), alertas por tenant
- Migraciones v4.27/v4.28; timers expire-demos y rollup-metrics
This commit is contained in:
2026-10-12 00:57:31 +00:00
parent f2d4558b80
commit a386ca5b4a
165 changed files with 31313 additions and 2203 deletions

120
restore_inventory.js Normal file
View File

@@ -0,0 +1,120 @@
#!/usr/bin/env node
/* Restaura funciones borradas de inventory.js desde git HEAD. */
const fs = require('fs');
const { execSync } = require('child_process');
const old = execSync('git show HEAD:pos/static/js/inventory.js', { maxBuffer: 20 * 1024 * 1024 }).toString();
let cur = fs.readFileSync('pos/static/js/inventory.js', 'utf8');
const MISSING = ['viewHistory', 'closeHistoryModal', 'recordTransfer', 'approvePhysicalCount',
'cancelDraft', 'loadAlerts', 'printBarcode', 'deleteItem', 'publishToMeli'];
// extraer bloque de función con llaves balanceadas (indent 4 espacios)
function extractFn(name) {
const marker = '\n function ' + name + '(';
const i = old.indexOf(marker);
if (i === -1) return null;
let depth = 0, j = i + 1, started = false;
for (; j < old.length; j++) {
const ch = old[j];
if (ch === '{') { depth++; started = true; }
else if (ch === '}') { depth--; if (started && depth === 0) { j++; break; } }
}
// incluir hasta el final de línea
while (old[j] === '\n') j++;
return old.slice(i + 1, j);
}
const blocks = [];
for (const name of MISSING) {
const b = extractFn(name);
if (b) blocks.push(b.trimEnd());
else console.log('⚠ no encontrado en HEAD:', name);
}
const anchor = '\n // =====================================================================\n // EXPOSE GLOBALS (for onclick handlers in HTML)';
if (!cur.includes(anchor)) { console.log('anchor EXPOSE no encontrado'); process.exit(1); }
const restore = '\n // =====================================================================\n' +
' // RESTAURADAS (borradas por error en el patch de transferencias)\n' +
' // =====================================================================\n\n' +
blocks.join('\n\n') + '\n' + anchor;
cur = cur.replace(anchor, restore);
// printBarcode: reemplazar por la versión Code128 (la de HEAD imprime texto plano)
if (blocks.some(b => b.includes('function printBarcode'))) {
const iStart = cur.indexOf(' function printBarcode(');
if (iStart !== -1) {
let depth = 0, j = iStart, started = false;
for (; j < cur.length; j++) {
const ch = cur[j];
if (ch === '{') { depth++; started = true; }
else if (ch === '}') { depth--; if (started && depth === 0) { j++; break; } }
}
while (cur[j] === '\n') j++;
const code128 = ` // ── Code128B encoder → SVG (escaneable en lector estándar) ──
var CODE128_BARS = [
'212222','222122','222221','121223','121322','131222','122213','122312','132212','221213',
'221312','231212','112232','122132','122231','113222','123122','123221','223211','221132',
'221231','213212','223112','312131','311222','321122','321221','312212','322112','322211',
'212123','212321','232121','111323','131123','131321','112313','132113','132311','211313',
'231113','231311','112133','112331','132131','113123','113321','133121','313121','211331',
'231131','213113','213311','213131','311123','311321','331121','312113','312311','332111',
'314111','221411','431111','111224','111422','121124','121421','141122','141221','112214',
'112412','122114','122411','142112','142211','241211','221114','413111','241112','134111',
'111242','121142','121241','114212','124112','124211','411212','421112','421211','212141',
'214121','412121','111143','111341','131141','114113','114311','411113','411311','113141',
'114131','311141','411131','211412','211214','211232','2331112'
];
function code128Svg(data, width, height) {
data = String(data || '');
if (!data.length) return '';
var codes = [104];
var sum = 104;
for (var i = 0; i < data.length; i++) {
var c = data.charCodeAt(i);
if (c < 32 || c > 126) c = 32;
var v = c - 32;
codes.push(v);
sum += v * (i + 1);
}
codes.push(sum % 103);
codes.push(106);
var quiet = 10;
var modules = quiet * 2;
codes.forEach(function (cd) {
CODE128_BARS[cd].split('').forEach(function (w) { modules += parseInt(w, 10); });
});
var mw = width / modules;
var bars = '';
var x = quiet * mw;
codes.forEach(function (cd) {
var pat = CODE128_BARS[cd];
for (var k = 0; k < pat.length; k++) {
var w = parseInt(pat[k], 10) * mw;
if (k % 2 === 0) bars += '<rect x="' + x.toFixed(2) + '" y="0" width="' + w.toFixed(2) + '" height="' + height + '"/>';
x += w;
}
});
return '<svg xmlns="http://www.w3.org/2000/svg" width="' + width + '" height="' + height + '" viewBox="0 0 ' + width + ' ' + height + '" fill="#000">' + bars + '</svg>';
}
function printBarcode(barcode, partNumber, name) {
var w = window.open('', '_blank', 'width=420,height=300');
w.document.write('<html><head><title>Etiqueta</title><style>body{font-family:system-ui,sans-serif;text-align:center;padding:16px;margin:0;}h1{font-size:1rem;margin:4px 0;}p{margin:2px 0;font-size:0.85rem;}</style></head><body>');
w.document.write(code128Svg(barcode || partNumber, 320, 70));
w.document.write('<h1>' + (barcode || partNumber) + '</h1>');
w.document.write('<p style="font-weight:600;">' + partNumber + '</p>');
w.document.write('<p style="font-size:0.85rem;">' + name + '</p>');
w.document.write('</body></html>');
w.document.close();
w.print();
}
`;
cur = cur.slice(0, iStart) + code128 + cur.slice(j);
console.log('printBarcode reemplazado por versión Code128');
}
}
fs.writeFileSync('pos/static/js/inventory.js', cur);
console.log('restauración completa');