feat: Fase 1-3 completas - precios proveedor, multi-sucursal, factura global

Fase 1: Lista de precios de proveedor
- Tabla supplier_catalog_prices en master DB
- Endpoints GET/POST/PUT/DELETE /supplier-catalog/prices
- Upload CSV/Excel de precios de proveedor
- Visualizacion de supplier_price en catalogo y POS

Fase 2: Multi-sucursal completo
- Migracion v4.0: inventory.branch_id=NULL, tabla inventory_stock
- Campos fiscales en branches (RFC, regimen, CP, serie CFDI, certificados)
- Trigger trg_update_inventory_stock para sincronizar stock por sucursal
- Backend config_bp.py con CRUD de sucursales fiscales
- Backend inventory_bp.py y pos_bp.py refactorizados para inventario compartido
- Backend invoicing_bp.py usa datos fiscales de la sucursal de la venta
- Frontend config.html/js con modal de sucursales expandido

Fase 3: Factura global mensual
- Migracion v4.1: tablas global_invoice_sales, sales.global_invoiced_at
- build_global_invoice_xml() con InformacionGlobal SAT-compliant
- Servicio global_invoice.py para agrupar ventas PUE <=000
- Endpoints POST/GET /global-invoice y /global-invoice/eligible-sales
- Frontend invoicing.html/js con boton y modal de factura global
This commit is contained in:
2026-06-11 08:59:56 +00:00
parent ea29cc31c0
commit 2b73c2c6db
23 changed files with 1665 additions and 230 deletions

View File

@@ -46,6 +46,11 @@
var checkoutBtn = document.getElementById('checkoutBtn');
var cartFab = document.getElementById('cartFab');
var cartCloseBtn = document.getElementById('cartCloseBtn');
// Supplier prices upload
var uploadPricesBtn = document.getElementById('uploadPricesBtn');
var uploadPricesModal= document.getElementById('uploadPricesModal');
var uploadPricesFile = document.getElementById('uploadPricesFile');
var uploadPricesStatus=document.getElementById('uploadPricesStatus');
// ─── Navigation State ───
var nav = {
@@ -1053,6 +1058,7 @@
'</div>' +
'<div class="part-card__footer">' +
(p.local_price ? '<span class="part-card__price">$' + fmt(p.local_price) + '</span>' : '<span class="part-card__price" style="color:var(--color-text-muted);">Sin precio</span>') +
(p.supplier_price ? '<span class="part-card__price" style="color:#2d7d46;font-size:0.85em;">Prov: $' + fmt(p.supplier_price) + '</span>' : '') +
stockBadge +
'</div>' +
'</article>';
@@ -2105,6 +2111,53 @@
});
}
// ─── Supplier prices upload ─────────────────────────────────────────────
function openUploadPricesModal() {
if (uploadPricesModal) uploadPricesModal.style.display = 'flex';
if (uploadPricesStatus) uploadPricesStatus.innerHTML = '';
if (uploadPricesFile) uploadPricesFile.value = '';
}
function closeUploadPricesModal() {
if (uploadPricesModal) uploadPricesModal.style.display = 'none';
}
async function submitUploadPrices() {
if (!uploadPricesFile || !uploadPricesFile.files || !uploadPricesFile.files[0]) {
if (uploadPricesStatus) uploadPricesStatus.innerHTML = '<span style="color:var(--color-error);">Selecciona un archivo primero.</span>';
return;
}
var form = new FormData();
form.append('file', uploadPricesFile.files[0]);
if (uploadPricesStatus) uploadPricesStatus.innerHTML = 'Subiendo...';
try {
var res = await fetch('/pos/api/supplier-catalog/prices/upload', {
method: 'POST',
headers: { 'Authorization': 'Bearer ' + token },
body: form
});
var data = await res.json();
if (res.ok && data.success) {
if (uploadPricesStatus) uploadPricesStatus.innerHTML = '<span style="color:var(--color-success);">✓ Precios actualizados: ' + data.processed + ' (insertados: ' + data.inserted + ', actualizados: ' + data.updated + ')</span>';
uploadPricesFile.value = '';
} else {
var msg = data.error || 'Error al subir precios';
var details = (data.details || []).join('<br>');
if (uploadPricesStatus) uploadPricesStatus.innerHTML = '<span style="color:var(--color-error);">' + esc(msg) + '</span>' + (details ? '<div style="margin-top:4px;font-size:0.9em;">' + details + '</div>' : '');
}
} catch (e) {
if (uploadPricesStatus) uploadPricesStatus.innerHTML = '<span style="color:var(--color-error);">Error de red: ' + esc(e.message) + '</span>';
}
}
function shouldShowUploadPricesButton() {
try {
var user = JSON.parse(localStorage.getItem('pos_employee') || '{}');
return user.role === 'owner' || user.role === 'admin';
} catch (e) { return false; }
}
if (uploadPricesBtn && shouldShowUploadPricesButton()) {
uploadPricesBtn.style.display = 'inline-flex';
}
window.CatalogApp = {
toggleCart: toggleCart,
goToCheckout: goToCheckout,
@@ -2124,6 +2177,9 @@
togglePlate: togglePlate,
lookupPlate: lookupPlate,
setMode: setCatalogMode,
openUploadPricesModal: openUploadPricesModal,
closeUploadPricesModal: closeUploadPricesModal,
submitUploadPrices: submitUploadPrices,
};
// ─── INIT ───