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:
@@ -478,6 +478,51 @@ const Invoicing = (() => {
|
||||
alert('Nota de credito: proximamente');
|
||||
}
|
||||
|
||||
// ---- Global Invoice ----
|
||||
function openGlobalInvoiceModal() {
|
||||
const now = new Date();
|
||||
document.getElementById('global-year').value = now.getFullYear();
|
||||
document.getElementById('global-month').value = now.getMonth() + 1;
|
||||
document.getElementById('global-preview').innerHTML = 'Presiona "Vista previa" para ver ventas elegibles.';
|
||||
document.getElementById('modalGlobalInvoice').style.display = 'flex';
|
||||
}
|
||||
|
||||
async function previewGlobalInvoice() {
|
||||
const year = document.getElementById('global-year').value;
|
||||
const month = document.getElementById('global-month').value;
|
||||
const preview = document.getElementById('global-preview');
|
||||
preview.innerHTML = 'Cargando...';
|
||||
try {
|
||||
const res = await api(`/global-invoice/eligible-sales?year=${year}&month=${month}`);
|
||||
preview.innerHTML = `<strong>${res.count} ventas elegibles</strong> — Total: $${fmt(res.total)}<br><small>${res.sales.map(s => '#' + s.id).join(', ')}</small>`;
|
||||
} catch (e) {
|
||||
preview.innerHTML = '<span style="color:var(--color-error);">Error: ' + e.message + '</span>';
|
||||
}
|
||||
}
|
||||
|
||||
async function generateGlobalInvoice() {
|
||||
const year = parseInt(document.getElementById('global-year').value, 10);
|
||||
const month = parseInt(document.getElementById('global-month').value, 10);
|
||||
const btn = document.querySelector('#modalGlobalInvoice .btn--primary');
|
||||
const originalText = btn.textContent;
|
||||
btn.disabled = true;
|
||||
btn.textContent = 'Generando...';
|
||||
try {
|
||||
const res = await api('/global-invoice', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ year, month })
|
||||
});
|
||||
alert(`Factura global generada: ${res.provisional_folio} (${res.sales_count} ventas, $${fmt(res.total)})`);
|
||||
document.getElementById('modalGlobalInvoice').style.display = 'none';
|
||||
loadFacturas();
|
||||
} catch (e) {
|
||||
alert('Error: ' + e.message);
|
||||
} finally {
|
||||
btn.disabled = false;
|
||||
btn.textContent = originalText;
|
||||
}
|
||||
}
|
||||
|
||||
// Expose switchTab globally for onclick handlers in HTML
|
||||
window.switchTab = switchTab;
|
||||
window.showNewInvoiceModal = showNewInvoiceModal;
|
||||
@@ -489,6 +534,7 @@ const Invoicing = (() => {
|
||||
switchTab, loadFacturas, loadNotas, loadComplementos, loadCancelaciones,
|
||||
showDetail, showCancelModal, confirmCancel, processQueue,
|
||||
showNewInvoiceModal, closeNewInvoiceModal, submitNewInvoice, notaCreditoPlaceholder,
|
||||
openGlobalInvoiceModal, previewGlobalInvoice, generateGlobalInvoice,
|
||||
};
|
||||
// Register Cmd+K items
|
||||
if (typeof registerCmdKItem === "function") {
|
||||
|
||||
Reference in New Issue
Block a user