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

@@ -113,6 +113,9 @@
<span class="breadcrumb__current">Catalogo</span>
</nav>
<div class="header-actions" style="position:relative;">
<button class="btn btn--sm" id="uploadPricesBtn" onclick="CatalogApp.openUploadPricesModal()" title="Subir precios de proveedor" style="margin-right:var(--space-2);display:none;">
💰 Precios proveedor
</button>
<div class="mode-toggle" id="modeToggle" title="Cambiar entre catalogo OEM (TecDoc), marcas locales, por marca de vehiculo y consumibles">
<button data-mode="oem" onclick="CatalogApp.setMode('oem')" disabled style="opacity:0.5;cursor:not-allowed;" title="Próximamente">OEM 🔒</button>
<button data-mode="local" onclick="CatalogApp.setMode('local')">Local</button>
@@ -275,6 +278,29 @@
<button class="banner__dismiss" onclick="document.getElementById('offlineBanner').style.display='none'" aria-label="Cerrar">&times;</button>
</div>
<!-- Upload Supplier Prices Modal -->
<div id="uploadPricesModal" style="display:none;position:fixed;top:0;left:0;right:0;bottom:0;z-index:9500;background:rgba(0,0,0,0.6);align-items:center;justify-content:center;padding:var(--space-4);">
<div style="background:var(--color-surface-1);border:1px solid var(--color-border);border-radius:var(--radius-md);max-width:520px;width:100%;max-height:90vh;overflow:auto;padding:var(--space-5);">
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:var(--space-4);">
<h2 style="margin:0;font-family:var(--font-heading);font-size:var(--text-h4);">Subir precios de proveedor</h2>
<button onclick="CatalogApp.closeUploadPricesModal()" style="background:none;border:none;cursor:pointer;font-size:1.4rem;color:var(--color-text-secondary);">&#10005;</button>
</div>
<p style="color:var(--color-text-secondary);font-size:var(--text-body-sm);margin-bottom:var(--space-3);">
Sube un CSV o Excel con las columnas: <code>supplier_name, sku, price, currency, effective_from</code>.<br>
El precio se mostrará en el catálogo junto a cada parte del proveedor.
</p>
<div style="margin-bottom:var(--space-3);">
<label style="display:block;font-size:var(--text-caption);color:var(--color-text-muted);margin-bottom:4px;">Archivo CSV / Excel</label>
<input type="file" id="uploadPricesFile" accept=".csv,.xlsx,.xls" style="width:100%;" />
</div>
<div style="display:flex;gap:var(--space-2);justify-content:flex-end;">
<a href="/pos/api/supplier-catalog/prices/template" class="btn btn--ghost" style="text-decoration:none;">Descargar plantilla</a>
<button class="btn btn-primary" onclick="CatalogApp.submitUploadPrices()">Subir precios</button>
</div>
<div id="uploadPricesStatus" style="margin-top:var(--space-3);font-size:var(--text-body-sm);"></div>
</div>
</div>
<!-- Brand Catalog Overlay (full-screen overlay for brand-first browsing) -->
<div id="brandCatalogOverlay" style="display:none;position:fixed;top:0;left:0;right:0;bottom:0;z-index:9000;background:var(--color-bg-base);overflow:auto;padding:var(--space-4);">
<div style="max-width:1200px;margin:0 auto;">
@@ -295,7 +321,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/catalog.js?v=5" defer></script>
<script src="/pos/static/js/catalog.js?v=6" defer></script>
<script src="/pos/static/js/offline-banner.js" defer></script>
<script src="/pos/static/js/chat.js" defer></script>
<script src="/pos/static/js/sync-engine.js" defer></script>