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:
@@ -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">×</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);">✕</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>
|
||||
|
||||
@@ -718,19 +718,48 @@
|
||||
MODALS
|
||||
===================================================================== -->
|
||||
|
||||
<!-- Modal: Nueva Sucursal -->
|
||||
<!-- Modal: Nueva / Editar Sucursal -->
|
||||
<div class="cfg-modal-overlay" id="modal-branch" style="display:none;">
|
||||
<div class="cfg-modal">
|
||||
<div class="cfg-modal" style="max-width:640px;">
|
||||
<div class="cfg-modal__header">
|
||||
<h3 class="cfg-modal__title">Nueva Sucursal</h3>
|
||||
<h3 class="cfg-modal__title" id="branch-modal-title">Nueva Sucursal</h3>
|
||||
<button class="cfg-modal__close" onclick="Config.closeModal('modal-branch')">×</button>
|
||||
</div>
|
||||
<div class="cfg-modal__body">
|
||||
<input type="hidden" id="branch-id" value="" />
|
||||
<div class="form-grid">
|
||||
<div class="form-group form-group--full">
|
||||
<label class="form-label">Nombre</label>
|
||||
<label class="form-label">Nombre *</label>
|
||||
<input class="form-input" id="branch-name" type="text" placeholder="Ej. Sucursal Norte" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">RFC</label>
|
||||
<input class="form-input" id="branch-rfc" type="text" placeholder="ABC010101ABC" maxlength="13" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">Razon Social</label>
|
||||
<input class="form-input" id="branch-razon" type="text" placeholder="Razon social fiscal" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">Regimen Fiscal</label>
|
||||
<input class="form-input" id="branch-regimen" type="text" placeholder="601" maxlength="10" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">Codigo Postal</label>
|
||||
<input class="form-input" id="branch-cp" type="text" placeholder="00000" maxlength="5" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">Serie CFDI</label>
|
||||
<input class="form-input" id="branch-serie" type="text" placeholder="A" maxlength="10" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">Folio Inicial</label>
|
||||
<input class="form-input" id="branch-folio" type="number" placeholder="1" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">Licencia Fiscal</label>
|
||||
<input class="form-input" id="branch-licencia" type="text" placeholder="Opcional" />
|
||||
</div>
|
||||
<div class="form-group form-group--full">
|
||||
<label class="form-label">Direccion</label>
|
||||
<input class="form-input" id="branch-address" type="text" placeholder="Calle, Colonia, Ciudad" />
|
||||
@@ -739,6 +768,20 @@
|
||||
<label class="form-label">Telefono</label>
|
||||
<input class="form-input" id="branch-phone" type="tel" placeholder="(55) 1234-5678" />
|
||||
</div>
|
||||
<div class="form-group form-group--full">
|
||||
<label class="form-check" style="display:flex;align-items:center;gap:8px;cursor:pointer;">
|
||||
<input type="checkbox" id="branch-main" style="width:auto;" />
|
||||
<span>Sucursal principal (datos fiscales por defecto)</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group form-group--full">
|
||||
<label class="form-label">Certificado PEM (opcional)</label>
|
||||
<textarea class="form-input" id="branch-cert" rows="3" placeholder="-----BEGIN CERTIFICATE-----"></textarea>
|
||||
</div>
|
||||
<div class="form-group form-group--full">
|
||||
<label class="form-label">Llave PEM (opcional)</label>
|
||||
<textarea class="form-input" id="branch-key" rows="3" placeholder="-----BEGIN PRIVATE KEY-----"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="cfg-modal__footer">
|
||||
@@ -808,7 +851,7 @@
|
||||
<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/kiosk.js" defer></script>
|
||||
<script src="/pos/static/js/config.js?v=2" defer></script>
|
||||
<script src="/pos/static/js/config.js?v=3" 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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -206,6 +206,7 @@
|
||||
|
||||
<!-- Secondary Actions -->
|
||||
<div class="secondary-actions" role="toolbar" aria-label="Acciones secundarias">
|
||||
<button class="btn-secondary-action" onclick="POS.modifyPrice()" title="Modificar precio">Mod.Precio</button>
|
||||
<button class="btn-secondary-action" onclick="POS.saveQuotation()" title="Cotizacion (F4)">Cotizar</button>
|
||||
<button class="btn-secondary-action" onclick="POS.showLastSale()" title="Ultima venta (F5)">Ult.Venta</button>
|
||||
<button class="btn-secondary-action" onclick="POS.showCutZModal()" title="Corte Z - Cerrar caja">Corte Z</button>
|
||||
@@ -570,7 +571,7 @@
|
||||
<script src="/pos/static/js/splash-loader.js?v=1" defer></script>
|
||||
<script src="/pos/static/js/push.js" defer></script>
|
||||
<script src="/pos/static/js/printer.js" defer></script>
|
||||
<script src="/pos/static/js/pos.js?v=6" defer></script>
|
||||
<script src="/pos/static/js/pos.js?v=7" defer></script>
|
||||
|
||||
<script>
|
||||
// Cancel sale button wiring
|
||||
|
||||
Reference in New Issue
Block a user