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

@@ -356,6 +356,16 @@
<div class="toolbar__spacer"></div>
<button class="btn btn--primary" onclick="Invoicing.openGlobalInvoiceModal()">
<svg viewBox="0 0 24 24" style="width:16px;height:16px;stroke:currentColor;fill:none;stroke-width:2;">
<rect x="3" y="4" width="18" height="18" rx="2" ry="2"/>
<line x1="16" y1="2" x2="16" y2="6"/>
<line x1="8" y1="2" x2="8" y2="6"/>
<line x1="3" y1="10" x2="21" y2="10"/>
</svg>
Factura Global
</button>
<button class="btn btn--ghost">
<svg viewBox="0 0 24 24">
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/>
@@ -1057,7 +1067,7 @@
<script src="/pos/static/js/splash-loader.js?v=1" defer></script>
<script src="/pos/static/js/pos-utils.js?v=2" defer></script>
<script src="/pos/static/js/sidebar.js" defer></script>
<script src="/pos/static/js/invoicing.js" defer></script>
<script src="/pos/static/js/invoicing.js?v=2" defer></script>
<script src="/pos/static/js/sync-engine.js" defer></script>
<script>if('serviceWorker' in navigator){navigator.serviceWorker.register('/pos/sw.js',{scope:'/pos/'});}</script>
<script src="/pos/static/js/pwa-install.js" defer></script>
@@ -1091,5 +1101,40 @@
window.loadInvoicingStats = loadInvoicingStats;
loadInvoicingStats();
</script>
<!-- =====================================================================
MODAL: FACTURA GLOBAL
===================================================================== -->
<div class="modal-overlay" id="modalGlobalInvoice" style="display:none; position:fixed; inset:0; z-index:var(--z-modal); background:var(--overlay-backdrop); align-items:center; justify-content:center;">
<div class="modal-card" style="background:var(--color-bg-elevated); border:1px solid var(--color-border); border-radius:var(--radius-lg); width:480px; max-width:95vw; box-shadow:var(--shadow-xl);">
<div style="display:flex; align-items:center; justify-content:space-between; padding:var(--space-5) var(--space-6); border-bottom:1px solid var(--color-border);">
<div>
<div style="font-weight:var(--font-weight-semibold); font-size:var(--text-body-lg);">Generar Factura Global</div>
<div style="font-size:var(--text-caption); color:var(--color-text-muted); margin-top:2px;">Agrupa ventas de contado no facturadas</div>
</div>
<button onclick="document.getElementById('modalGlobalInvoice').style.display='none'" style="background:none; border:none; color:var(--color-text-muted); font-size:1.4rem; cursor:pointer; padding:var(--space-2);"></button>
</div>
<div style="padding:var(--space-5) var(--space-6);">
<div style="display:flex; gap:var(--space-3); margin-bottom:var(--space-4);">
<div style="flex:1;">
<label style="font-size:var(--text-caption); color:var(--color-text-muted); display:block; margin-bottom:var(--space-1);">Año</label>
<input type="number" id="global-year" class="form-input" style="width:100%;" />
</div>
<div style="flex:1;">
<label style="font-size:var(--text-caption); color:var(--color-text-muted); display:block; margin-bottom:var(--space-1);">Mes</label>
<input type="number" id="global-month" class="form-input" style="width:100%;" min="1" max="12" />
</div>
</div>
<div id="global-preview" style="background:var(--color-surface); border-radius:var(--radius-md); padding:var(--space-3); font-size:var(--text-caption); color:var(--color-text-muted);">
Presiona "Vista previa" para ver ventas elegibles.
</div>
</div>
<div style="display:flex; gap:var(--space-3); justify-content:flex-end; padding:var(--space-4) var(--space-6); border-top:1px solid var(--color-border);">
<button onclick="document.getElementById('modalGlobalInvoice').style.display='none'" class="btn btn--ghost">Cancelar</button>
<button onclick="Invoicing.previewGlobalInvoice()" class="btn btn--ghost">Vista previa</button>
<button onclick="Invoicing.generateGlobalInvoice()" class="btn btn--primary">Generar</button>
</div>
</div>
</div>
</body>
</html>