Opción C: Vinculación híbrida de inventario local con vehículos

- Nueva tabla inventory_vehicle_compat (v3.1)
- Motor inventory_vehicle_compat.py: auto-match + gestión manual
- catalog_service.get_parts_local() ahora incluye piezas locales vinculadas
- inventory_bp: auto-match en create/update + endpoints REST /vehicles
- Frontend catalog.js: badge 'Stock Local' para piezas nativas del tenant
- Frontend inventory.js: panel de vehículos compatibles con auto-match
- Tests: test_compatibility.py (9/9 pasan)

Piezas locales aparecen en navegación por vehículo aunque no estén en TecDoc.
Auto-match busca part_number en parts/aftermarket_parts y copia MYEs compatibles.
This commit is contained in:
2026-04-27 06:52:30 +00:00
parent 142abbc217
commit efbd763e43
8 changed files with 690 additions and 14 deletions

View File

@@ -653,6 +653,40 @@
el.innerHTML = html2;
}
// Vehicle compatibility 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;">Vehiculos Compatibles</div>';
html += '<div id="compatContent" 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 compatibilidades...</p>';
html += '</div>';
// Load vehicle compatibilities
(function loadCompat() {
fetch('/pos/api/inventory/items/' + itemId + '/vehicles', { headers: { 'Authorization': 'Bearer ' + token } })
.then(function(r) { return r.json(); })
.then(function(d) {
var el = document.getElementById('compatContent');
if (!el) return;
var list = d.vehicles || [];
var html2 = '';
if (list.length > 0) {
html2 += '<table class="data-table"><thead><tr><th>Marca</th><th>Modelo</th><th>Ano</th><th>Motor</th><th>Origen</th><th></th></tr></thead><tbody>';
list.forEach(function(c) {
html2 += '<tr><td>' + esc(c.brand || '') + '</td><td>' + esc(c.model || '') + '</td><td>' + esc(c.year || '') + '</td><td>' + esc(c.engine || '') + '</td><td>' + esc(c.source || '') + '</td>';
html2 += '<td><button class="btn btn--ghost btn--sm" style="color:var(--color-error);" onclick="removeCompat(' + itemId + ',' + c.model_year_engine_id + ')">Quitar</button></td></tr>';
});
html2 += '</tbody></table>';
} else {
html2 += '<p style="color:var(--color-text-muted);font-size:var(--text-caption);">Sin vehiculos vinculados.</p>';
}
html2 += '<div style="margin-top:8px;"><button class="btn btn--primary btn--sm" onclick="autoMatchCompat(' + itemId + ')">Auto-Match por TecDoc</button> <span style="font-size:var(--text-caption);color:var(--color-text-muted);">Busca en catalogo central y vincula automaticamente</span></div>';
el.innerHTML = html2;
})
.catch(function() {
var el = document.getElementById('compatContent');
if (el) el.innerHTML = '<p style="color:var(--color-text-muted);font-size:var(--text-caption);">Error al cargar compatibilidades.</p>';
});
})();
// 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) {
@@ -677,6 +711,29 @@
});
}
// Vehicle compatibility actions
function autoMatchCompat(itemId) {
fetch('/pos/api/inventory/items/' + itemId + '/vehicles/auto-match', {
method: 'POST',
headers: { 'Authorization': 'Bearer ' + token }
}).then(function(r) { return r.json(); })
.then(function(d) {
alert('Auto-match completado. Vehiculos vinculados: ' + (d.matched || 0));
viewProductDetail(itemId);
}).catch(function() { alert('Error en auto-match'); });
}
function removeCompat(itemId, myeId) {
if (!confirm('Quitar compatibilidad con este vehiculo?')) return;
fetch('/pos/api/inventory/items/' + itemId + '/compatibility/' + myeId, {
method: 'DELETE',
headers: { 'Authorization': 'Bearer ' + token }
}).then(function(r) { return r.json(); })
.then(function() {
viewProductDetail(itemId);
}).catch(function() { alert('Error al quitar compatibilidad'); });
}
// =====================================================================
// EXPOSE GLOBALS (for onclick handlers in HTML)
// =====================================================================