- 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
65 lines
2.8 KiB
JavaScript
65 lines
2.8 KiB
JavaScript
#!/usr/bin/env node
|
|
/* Restauración v4: extrae funciones de HEAD en ambos formatos (function X / window.X = function). */
|
|
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');
|
|
|
|
// Nombres realmente faltantes (referenciados por templates o expuestos), según auditoría:
|
|
const CANDIDATES = ['searchMeliCategories', 'handleMeliCatKeydown', 'onMeliShippingChange',
|
|
'validateMeliPublish', 'executeMeliPublish', 'openMeliPublishModal', 'closeMeliPublishModal',
|
|
'refreshMeliPublishPreview', 'selectMeliCategory', 'selectMeliCategoryIdx', 'highlightMeliCat',
|
|
'loadCategoryAttributes', 'updateMeliTitleCount', 'handleMeliImageUpload', 'showTierDiscountModal',
|
|
'closeTierDiscountModal', 'saveTierDiscounts', 'openInventoryFilters'];
|
|
|
|
function hasDef(src, name) {
|
|
return new RegExp('\\n function ' + name + '\\s*\\(').test(src) ||
|
|
new RegExp('\\n window\\.' + name + '\\s*=').test(src) ||
|
|
new RegExp('\\n (?:var|let|const) ' + name + '\\s*=').test(src);
|
|
}
|
|
|
|
// extrae desde el inicio de la definición hasta el próximo inicio de nivel 4
|
|
function extract(src, name) {
|
|
const patterns = [
|
|
'\n function ' + name + '(',
|
|
'\n function ' + name + ' (',
|
|
'\n window.' + name + ' = function',
|
|
'\n window.' + name + ' = async function',
|
|
'\n var ' + name + ' = function',
|
|
];
|
|
let start = -1;
|
|
for (const p of patterns) { start = src.indexOf(p); if (start !== -1) break; }
|
|
if (start === -1) return null;
|
|
let end = src.length;
|
|
const stops = [
|
|
src.indexOf('\n function ', start + 30),
|
|
src.indexOf('\n window.', start + 30),
|
|
src.indexOf('\n var ', start + 30),
|
|
src.indexOf('\n // ====', start + 30),
|
|
].filter(i => i !== -1);
|
|
if (stops.length) end = Math.min(...stops);
|
|
return src.slice(start + 1, end);
|
|
}
|
|
|
|
const blocks = [];
|
|
const missingNoHead = [];
|
|
for (const name of CANDIDATES) {
|
|
if (hasDef(cur, name)) continue; // ya existe
|
|
const b = extract(old, name);
|
|
if (b) blocks.push(b.trimEnd());
|
|
else missingNoHead.push(name);
|
|
}
|
|
|
|
console.log('restaurables desde HEAD:', blocks.length);
|
|
console.log('no halladas en HEAD:', missingNoHead);
|
|
|
|
if (blocks.length) {
|
|
const anchor = '\n // =====================================================================\n // EXPOSE GLOBALS (for onclick handlers in HTML)';
|
|
const idx = cur.indexOf(anchor);
|
|
if (idx === -1) { console.log('ERROR anchor'); process.exit(1); }
|
|
cur = cur.slice(0, idx) + '\n // ==== RESTAURADAS v4 (MELI + descuentos) ====\n\n' + blocks.join('\n\n') + '\n' + cur.slice(idx);
|
|
}
|
|
fs.writeFileSync('pos/static/js/inventory.js', cur);
|
|
console.log('escrito');
|