feat(pos): cross-references en detalle de producto de inventario

Al abrir el detalle de un producto, se cargan automaticamente:
- Disponibilidad en bodegas (stock + precio)
- Partes equivalentes aftermarket (cross-references del catalogo TecDoc)
Usa catalog_part_id o busqueda por part_number contra el catalogo central.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-02 07:41:58 +00:00
parent b5d62c2812
commit 77e45bdc1e

View File

@@ -470,6 +470,77 @@
html += '<div><span style="font-size:var(--text-caption);color:var(--color-text-muted);text-transform:uppercase;display:block;">Precio 3</span><span class="td--amount">$' + fmt(data.price_3) + '</span></div>';
html += '</div>';
// Cross-references section
html += '<div style="font-size:var(--text-caption);color:var(--color-text-muted);text-transform:uppercase;letter-spacing:var(--tracking-widest);margin-bottom:8px;">Cross-References / Equivalencias</div>';
html += '<div id="crossRefContent" style="margin-bottom:16px;padding-bottom:16px;border-bottom:1px solid var(--color-border);">';
html += '<p style="color:var(--color-text-muted);font-size:var(--text-caption);">Cargando equivalencias...</p>';
html += '</div>';
// Load cross-references from catalog API
var partNumber = data.part_number;
var catalogPartId = data.catalog_part_id;
(function loadCrossRefs() {
// Try catalog part detail if we have catalog_part_id
var url = catalogPartId
? '/pos/api/catalog/part/' + catalogPartId
: '/pos/api/catalog/search?q=' + encodeURIComponent(partNumber);
fetch(url, { headers: { 'Authorization': 'Bearer ' + token } })
.then(function(r) { return r.json(); })
.then(function(d) {
var el = document.getElementById('crossRefContent');
if (!el) return;
var alternatives = d.alternatives || [];
var bodegas = d.bodegas || [];
// If it was a search, get alternatives from first result
if (!catalogPartId && d.data && d.data.length > 0) {
// Fetch detail for first match
fetch('/pos/api/catalog/part/' + d.data[0].id_part, { headers: { 'Authorization': 'Bearer ' + token } })
.then(function(r2) { return r2.json(); })
.then(function(d2) {
renderCrossRefs(el, d2.alternatives || [], d2.bodegas || []);
})
.catch(function() { el.innerHTML = '<p style="color:var(--color-text-muted);font-size:var(--text-caption);">Sin conexion al catalogo.</p>'; });
return;
}
renderCrossRefs(el, alternatives, bodegas);
})
.catch(function() {
var el = document.getElementById('crossRefContent');
if (el) el.innerHTML = '<p style="color:var(--color-text-muted);font-size:var(--text-caption);">Sin conexion al catalogo central.</p>';
});
})();
function renderCrossRefs(el, alternatives, bodegas) {
var html2 = '';
if (bodegas && bodegas.length > 0) {
html2 += '<div style="margin-bottom:12px;"><strong style="font-size:var(--text-body-sm);color:var(--color-text-primary);">Disponible en Bodegas:</strong></div>';
html2 += '<table class="data-table"><thead><tr><th>Bodega</th><th>Stock</th><th>Precio</th><th>Ubicacion</th></tr></thead><tbody>';
bodegas.forEach(function(b) {
html2 += '<tr><td>' + esc(b.business_name || b.bodega || '') + '</td><td>' + (b.stock || b.stock_quantity || 0) + '</td><td class="td--amount">$' + fmt(b.price || 0) + '</td><td>' + esc(b.location || b.warehouse_location || '') + '</td></tr>';
});
html2 += '</tbody></table>';
}
if (alternatives && alternatives.length > 0) {
html2 += '<div style="margin-top:12px;margin-bottom:8px;"><strong style="font-size:var(--text-body-sm);color:var(--color-text-primary);">Partes Equivalentes (Aftermarket):</strong></div>';
html2 += '<table class="data-table"><thead><tr><th>No. Parte</th><th>Fabricante</th><th>Nombre</th></tr></thead><tbody>';
alternatives.forEach(function(a) {
html2 += '<tr><td class="td--mono">' + esc(a.part_number || a.cross_reference_number || '') + '</td><td>' + esc(a.manufacturer || a.source_ref || '') + '</td><td>' + esc(a.name || a.name_aftermarket_parts || '') + '</td></tr>';
});
html2 += '</tbody></table>';
}
if (!html2) {
html2 = '<p style="color:var(--color-text-muted);font-size:var(--text-caption);">No se encontraron equivalencias para esta parte.</p>';
}
el.innerHTML = html2;
}
// Movement history
html += '<div style="font-size:var(--text-caption);color:var(--color-text-muted);text-transform:uppercase;letter-spacing:var(--tracking-widest);margin-bottom:8px;">Historial de Movimientos</div>';
if (!history.length) {