- 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
111 lines
4.9 KiB
JavaScript
111 lines
4.9 KiB
JavaScript
#!/usr/bin/env node
|
|
/* Reintento: extracción por marcador de siguiente función (no por llaves). */
|
|
const fs = require('fs');
|
|
const { execSync } = require('child_process');
|
|
|
|
let cur = fs.readFileSync('pos/static/js/inventory.js', 'utf8');
|
|
|
|
// 1. Quitar el bloque restaurado mal insertado
|
|
const startMark = '\n // =====================================================================\n // RESTAURADAS (borradas por error en el patch de transferencias)';
|
|
const endMark = '\n // =====================================================================\n // EXPOSE GLOBALS (for onclick handlers in HTML)';
|
|
const i1 = cur.indexOf(startMark);
|
|
const i2 = cur.indexOf(endMark);
|
|
if (i1 !== -1 && i2 !== -1 && i2 > i1) {
|
|
cur = cur.slice(0, i1) + cur.slice(i2);
|
|
console.log('bloque malo removido');
|
|
} else {
|
|
console.log('bloque restaurado no encontrado (¿ya limpio?)', i1, i2);
|
|
}
|
|
|
|
// 2. Extraer funciones de HEAD por marcadores
|
|
const old = execSync('git show HEAD:pos/static/js/inventory.js', { maxBuffer: 20 * 1024 * 1024 }).toString();
|
|
const MISSING = ['viewHistory', 'closeHistoryModal', 'recordTransfer', 'approvePhysicalCount',
|
|
'cancelDraft', 'loadAlerts', 'printBarcode', 'deleteItem', 'publishToMeli'];
|
|
|
|
function extractFn(name) {
|
|
const start = '\n function ' + name + '(';
|
|
const i = old.indexOf(start);
|
|
if (i === -1) return null;
|
|
// fin: el próximo " function " o " // ====" a nivel 4 después del inicio
|
|
let end = old.length;
|
|
const nextFn = old.indexOf('\n function ', i + start.length);
|
|
const nextMark = old.indexOf('\n // =====================================================================', i + start.length);
|
|
if (nextFn !== -1) end = Math.min(end, nextFn);
|
|
if (nextMark !== -1) end = Math.min(end, nextMark);
|
|
return old.slice(i + 1, end);
|
|
}
|
|
|
|
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 = endMark;
|
|
const restore = '\n // =====================================================================\n' +
|
|
' // RESTAURADAS (recuperadas de git HEAD)\n' +
|
|
' // =====================================================================\n\n' +
|
|
blocks.join('\n\n') + '\n' + anchor;
|
|
cur = cur.replace(anchor, restore);
|
|
|
|
// printBarcode versión Code128
|
|
const iStart = cur.indexOf(' function printBarcode(');
|
|
if (iStart !== -1) {
|
|
let end = cur.length;
|
|
const nextFn = cur.indexOf('\n function ', iStart + 30);
|
|
const nextMark = cur.indexOf('\n // =====================================================================', iStart + 30);
|
|
if (nextFn !== -1) end = Math.min(end, nextFn);
|
|
if (nextMark !== -1) end = Math.min(end, nextMark);
|
|
const code128 = ` 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(end);
|
|
console.log('printBarcode Code128 OK');
|
|
}
|
|
|
|
fs.writeFileSync('pos/static/js/inventory.js', cur);
|
|
console.log('restauración v2 completa');
|