feat: cashier/counter reports, service-order & remission flows, Rached migration utils
- Add "Mis cortes de caja" report for cashiers/counters with sales detail. - Cash register history scoped to own cuts for non-admin roles; new /register/<id>/sales endpoint. - Remove dashboard from cashier menu; add Reports to cashier/counter. - Service orders: assign mechanic, budget field, invoice flag, counter/cashier can add items/remissions, convert to remission. - Remission notes module (UI, CSS, courier, counter remissions). - Customer hard-delete and vehicle/customer linkage in workshop. - POS: always show search results, compact payment grid, credit validation, tier pricing (5%/10%), ticket with customer/folio. - Inventory: CSV template with sku_secondary, alias import. - Rached migration scripts and DB migrations. - Version-bump cached JS/CSS query strings. Excludes local Rached session tokens/captures (rached_*.json / rached_*.txt).
This commit is contained in:
@@ -492,11 +492,11 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="/pos/static/js/i18n.js?v=36" defer></script>
|
||||
<script src="/pos/static/js/app-init.js?v=2" defer></script>
|
||||
<script src="/pos/static/js/i18n.js?v=39" defer></script>
|
||||
<script src="/pos/static/js/app-init.js?v=5" defer></script>
|
||||
<script src="/pos/static/js/splash-loader.js?v=33" defer></script>
|
||||
<script src="/pos/static/js/pos-utils.js?v=33" defer></script>
|
||||
<script src="/pos/static/js/sidebar.js?v=36" defer></script>
|
||||
<script src="/pos/static/js/sidebar.js?v=44" defer></script>
|
||||
<script src="/pos/static/js/accounting.v9.js?v=33" 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>
|
||||
|
||||
@@ -314,12 +314,12 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="/pos/static/js/i18n.js?v=36" defer></script>
|
||||
<script src="/pos/static/js/i18n.js?v=39" defer></script>
|
||||
<script src="/pos/static/js/kiosk.js" defer></script>
|
||||
<script src="/pos/static/js/app-init.js?v=2" defer></script>
|
||||
<script src="/pos/static/js/app-init.js?v=5" defer></script>
|
||||
<script src="/pos/static/js/splash-loader.js?v=33" defer></script>
|
||||
<script src="/pos/static/js/pos-utils.js?v=33" defer></script>
|
||||
<script src="/pos/static/js/sidebar.js?v=36" defer></script>
|
||||
<script src="/pos/static/js/sidebar.js?v=44" defer></script>
|
||||
<script src="/pos/static/js/catalog.js?v=33" defer></script>
|
||||
<script src="/pos/static/js/offline-banner.js" defer></script>
|
||||
<script src="/pos/static/js/chat.js" defer></script>
|
||||
|
||||
@@ -15,7 +15,39 @@
|
||||
<meta name="theme-color" content="#F5A623" />
|
||||
<link rel="shortcut icon" type="image/png" href="/pos/static/pwa/icon-192.png" />
|
||||
|
||||
<link rel="stylesheet" href="/pos/static/css/config.css?v=33"></head>
|
||||
<link rel="stylesheet" href="/pos/static/css/config.css?v=34">
|
||||
<style>
|
||||
.cfg-tabs {
|
||||
display: flex;
|
||||
gap: var(--space-2);
|
||||
padding: var(--space-3) var(--space-4);
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
background: var(--color-surface-1);
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 10;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.cfg-tab-btn {
|
||||
padding: var(--space-2) var(--space-4);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--radius-sm);
|
||||
background: var(--color-surface-2);
|
||||
color: var(--color-text-secondary);
|
||||
cursor: pointer;
|
||||
font-size: var(--text-body-sm);
|
||||
font-weight: var(--font-weight-medium);
|
||||
}
|
||||
.cfg-tab-btn:hover { background: var(--color-surface-3); }
|
||||
.cfg-tab-btn.active {
|
||||
background: var(--color-accent);
|
||||
color: #fff;
|
||||
border-color: var(--color-accent);
|
||||
}
|
||||
.settings-section[data-tab] { display: none !important; }
|
||||
.settings-section[data-tab].active { display: block !important; }
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
@@ -138,11 +170,20 @@
|
||||
|
||||
<!-- Scrollable Content -->
|
||||
<div class="content-scroll">
|
||||
<!-- Config tabs -->
|
||||
<div class="cfg-tabs" id="configTabs">
|
||||
<button class="cfg-tab-btn active" data-tab="general" onclick="Config.switchTab('general')">General</button>
|
||||
<button class="cfg-tab-btn" data-tab="fiscal" onclick="Config.switchTab('fiscal')">Fiscal</button>
|
||||
<button class="cfg-tab-btn" data-tab="catalog" onclick="Config.switchTab('catalog')">Catálogo</button>
|
||||
<button class="cfg-tab-btn" data-tab="employees" onclick="Config.switchTab('employees')">Empleados</button>
|
||||
<button class="cfg-tab-btn cfg-tab-btn--permissions" data-tab="permissions" onclick="Config.switchTab('permissions')" style="display:none;">Permisos</button>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- ===============================================================
|
||||
SECTION 1: APARIENCIA / TEMA
|
||||
=============================================================== -->
|
||||
<div class="settings-section">
|
||||
<div class="settings-section" data-tab="general">
|
||||
<div class="settings-section__header">
|
||||
<div class="settings-section__icon">
|
||||
<svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="5"/><line x1="12" y1="1" x2="12" y2="3"/><line x1="12" y1="21" x2="12" y2="23"/><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/><line x1="1" y1="12" x2="3" y2="12"/><line x1="21" y1="12" x2="23" y2="12"/><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/></svg>
|
||||
@@ -205,7 +246,7 @@
|
||||
<!-- ===============================================================
|
||||
SECTION 2: DATOS DE LA EMPRESA
|
||||
=============================================================== -->
|
||||
<div class="settings-section">
|
||||
<div class="settings-section" data-tab="general">
|
||||
<div class="settings-section__header">
|
||||
<div class="settings-section__icon">
|
||||
<svg viewBox="0 0 24 24"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><polyline points="9 22 9 12 15 12 15 22"/></svg>
|
||||
@@ -256,7 +297,7 @@
|
||||
<!-- ===============================================================
|
||||
SECTION 3: PERSONALIZACIÓN DE TICKET
|
||||
=============================================================== -->
|
||||
<div class="settings-section">
|
||||
<div class="settings-section" data-tab="general">
|
||||
<div class="settings-section__header">
|
||||
<div class="settings-section__icon">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="16" y1="13" x2="8" y2="13"/><line x1="16" y1="17" x2="8" y2="17"/><polyline points="10 9 9 9 8 9"/></svg>
|
||||
@@ -328,7 +369,7 @@
|
||||
<!-- ===============================================================
|
||||
SECTION 4: MÓDULOS E INTEGRACIONES
|
||||
=============================================================== -->
|
||||
<div class="settings-section">
|
||||
<div class="settings-section" data-tab="general">
|
||||
<div class="settings-section__header">
|
||||
<div class="settings-section__icon">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="3" width="20" height="14" rx="2"/><path d="M8 21h8M12 17v4"/></svg>
|
||||
@@ -380,6 +421,16 @@
|
||||
<span class="toggle__slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="toggle-row">
|
||||
<div class="toggle-row__info">
|
||||
<span class="toggle-row__label">Notas de remisión en mostrador</span>
|
||||
<span class="toggle-row__desc">Permite generar notas de remisión desde el POS para cobrar posteriormente en caja</span>
|
||||
</div>
|
||||
<label class="toggle">
|
||||
<input type="checkbox" id="cfg-module-counter-remission" />
|
||||
<span class="toggle__slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
<div style="margin-top:var(--space-4);text-align:right;">
|
||||
<button class="btn btn--primary" onclick="Config.saveModules()">Guardar módulos</button>
|
||||
</div>
|
||||
@@ -389,13 +440,13 @@
|
||||
<!-- ===============================================================
|
||||
SECTION 4: USUARIOS Y PERMISOS
|
||||
=============================================================== -->
|
||||
<div class="settings-section">
|
||||
<div class="settings-section" data-tab="employees">
|
||||
<div class="settings-section__header">
|
||||
<div class="settings-section__icon">
|
||||
<svg viewBox="0 0 24 24"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></svg>
|
||||
</div>
|
||||
<div>
|
||||
<div class="settings-section__title">Usuarios y Permisos</div>
|
||||
<div class="settings-section__title">Empleados</div>
|
||||
<div class="settings-section__desc">Gestiona quién accede al sistema y qué puede hacer</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -432,7 +483,7 @@
|
||||
<!-- ===============================================================
|
||||
SECTION 4: IMPRESORAS
|
||||
=============================================================== -->
|
||||
<div class="settings-section">
|
||||
<div class="settings-section" data-tab="general">
|
||||
<div class="settings-section__header">
|
||||
<div class="settings-section__icon">
|
||||
<svg viewBox="0 0 24 24"><polyline points="6 9 6 2 18 2 18 9"/><path d="M6 18H4a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2"/><rect x="6" y="14" width="12" height="8"/></svg>
|
||||
@@ -458,7 +509,7 @@
|
||||
<!-- ===============================================================
|
||||
SECTION 5: SUCURSALES
|
||||
=============================================================== -->
|
||||
<div class="settings-section">
|
||||
<div class="settings-section" data-tab="fiscal">
|
||||
<div class="settings-section__header">
|
||||
<div class="settings-section__icon">
|
||||
<svg viewBox="0 0 24 24"><path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"/><circle cx="12" cy="10" r="3"/></svg>
|
||||
@@ -477,7 +528,7 @@
|
||||
<!-- ===============================================================
|
||||
SECTION 6: PARÁMETROS FISCALES
|
||||
=============================================================== -->
|
||||
<div class="settings-section">
|
||||
<div class="settings-section" data-tab="fiscal">
|
||||
<div class="settings-section__header">
|
||||
<div class="settings-section__icon">
|
||||
<svg viewBox="0 0 24 24"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="16" y1="13" x2="8" y2="13"/><line x1="16" y1="17" x2="8" y2="17"/></svg>
|
||||
@@ -613,7 +664,7 @@
|
||||
<!-- ===============================================================
|
||||
SECTION 7: PREFERENCIAS DEL SISTEMA
|
||||
=============================================================== -->
|
||||
<div class="settings-section">
|
||||
<div class="settings-section" data-tab="general">
|
||||
<div class="settings-section__header">
|
||||
<div class="settings-section__icon">
|
||||
<svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>
|
||||
@@ -691,7 +742,7 @@
|
||||
<!-- ===============================================================
|
||||
SECTION 8: VEHICLE COMPATIBILITY SOURCE
|
||||
=============================================================== -->
|
||||
<div class="settings-section">
|
||||
<div class="settings-section" data-tab="catalog">
|
||||
<div class="settings-section__header">
|
||||
<div class="settings-section__icon">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="16" y1="13" x2="8" y2="13"/><line x1="16" y1="17" x2="8" y2="17"/></svg>
|
||||
@@ -724,7 +775,7 @@
|
||||
<!-- ===============================================================
|
||||
SECTION 9: MARCAS DE PARTES PERMITIDAS
|
||||
=============================================================== -->
|
||||
<div class="settings-section">
|
||||
<div class="settings-section" data-tab="catalog">
|
||||
<div class="settings-section__header">
|
||||
<div class="settings-section__icon">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2L2 7l10 5 10-5-10-5z"/><polyline points="2 17 12 22 22 17"/></svg>
|
||||
@@ -785,6 +836,47 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- ===============================================================
|
||||
SECTION: PERMISOS POR ROL
|
||||
=============================================================== -->
|
||||
<div class="settings-section" data-tab="permissions">
|
||||
<div class="settings-section__header">
|
||||
<div class="settings-section__icon">
|
||||
<svg viewBox="0 0 24 24"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></svg>
|
||||
</div>
|
||||
<div>
|
||||
<div class="settings-section__title">Permisos por Rol</div>
|
||||
<div class="settings-section__desc">Define los permisos predeterminados para cada rol del sistema</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="settings-card">
|
||||
<div class="form-group" style="max-width:320px;">
|
||||
<label class="form-label">Rol</label>
|
||||
<select class="form-input" id="cfg-perm-role" onchange="Config.renderRolePermissions()">
|
||||
<option value="">Selecciona un rol</option>
|
||||
<option value="admin">Administrador</option>
|
||||
<option value="cashier">Cajero</option>
|
||||
<option value="counter">Mostrador</option>
|
||||
<option value="warehouse">Almacén</option>
|
||||
<option value="accountant">Contador</option>
|
||||
<option value="workshop">Taller</option>
|
||||
<option value="mechanic">Mecánico</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div id="role-permissions-container" style="margin-top:var(--space-4);">
|
||||
<p style="color:var(--color-text-muted);">Selecciona un rol para ver y editar sus permisos.</p>
|
||||
</div>
|
||||
|
||||
<div style="margin-top:var(--space-4);">
|
||||
<button class="btn btn--primary" id="btn-save-role-permissions" onclick="Config.saveRolePermissions()">Guardar permisos del rol</button>
|
||||
<span id="role-permissions-status" style="font-size:var(--text-caption);color:var(--color-text-muted);margin-left:var(--space-3);"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div><!-- /content-scroll -->
|
||||
</main>
|
||||
</div><!-- /app-shell -->
|
||||
@@ -893,6 +985,7 @@
|
||||
<option value="">-- Seleccionar --</option>
|
||||
<option value="admin">Administrador</option>
|
||||
<option value="cashier">Cajero</option>
|
||||
<option value="counter">Mostrador</option>
|
||||
<option value="warehouse">Almacenista</option>
|
||||
<option value="accountant">Contador</option>
|
||||
<option value="workshop">Taller</option>
|
||||
@@ -922,13 +1015,13 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="/pos/static/js/i18n.js?v=36" defer></script>
|
||||
<script src="/pos/static/js/app-init.js?v=2" defer></script>
|
||||
<script src="/pos/static/js/i18n.js?v=39" defer></script>
|
||||
<script src="/pos/static/js/app-init.js?v=5" defer></script>
|
||||
<script src="/pos/static/js/splash-loader.js?v=33" defer></script>
|
||||
<script src="/pos/static/js/pos-utils.js?v=33" defer></script>
|
||||
<script src="/pos/static/js/sidebar.js?v=36" defer></script>
|
||||
<script src="/pos/static/js/sidebar.js?v=44" defer></script>
|
||||
<script src="/pos/static/js/kiosk.js" defer></script>
|
||||
<script src="/pos/static/js/config.js?v=35" defer></script>
|
||||
<script src="/pos/static/js/config.js?v=39" 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>
|
||||
|
||||
@@ -650,11 +650,11 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="/pos/static/js/i18n.js?v=36" defer></script>
|
||||
<script src="/pos/static/js/app-init.js?v=2" defer></script>
|
||||
<script src="/pos/static/js/i18n.js?v=39" defer></script>
|
||||
<script src="/pos/static/js/app-init.js?v=5" defer></script>
|
||||
<script src="/pos/static/js/splash-loader.js?v=33" defer></script>
|
||||
<script src="/pos/static/js/pos-utils.js?v=33" defer></script>
|
||||
<script src="/pos/static/js/sidebar.js?v=36" defer></script>
|
||||
<script src="/pos/static/js/sidebar.js?v=44" defer></script>
|
||||
<script src="/pos/static/js/virtual-scroll.js" defer></script>
|
||||
<script src="/pos/static/js/customers.js?v=34" defer></script>
|
||||
<script src="/pos/static/js/offline-banner.js" defer></script>
|
||||
|
||||
@@ -158,6 +158,16 @@
|
||||
Ventas Históricas
|
||||
</a>
|
||||
|
||||
<a href="/pos/remission-notes" class="nav-link">
|
||||
<span class="nav-link__icon">
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5">
|
||||
<path d="M2 3h12v10H2z"/>
|
||||
<path d="M5 7h6M5 10h4" stroke-width="1.2"/>
|
||||
</svg>
|
||||
</span>
|
||||
Notas de Remisión
|
||||
</a>
|
||||
|
||||
<div class="sidebar__section-label">Gestión</div>
|
||||
|
||||
<a href="/pos/marketplace" class="nav-link">
|
||||
@@ -595,15 +605,15 @@
|
||||
|
||||
|
||||
<script src="/pos/static/js/chart.umd.min.js" defer></script>
|
||||
<script src="/pos/static/js/i18n.js?v=36" defer></script>
|
||||
<script src="/pos/static/js/app-init.js?v=2" defer></script>
|
||||
<script src="/pos/static/js/i18n.js?v=39" defer></script>
|
||||
<script src="/pos/static/js/app-init.js?v=5" defer></script>
|
||||
<script src="/pos/static/js/splash-loader.js?v=33" defer></script>
|
||||
<script src="/pos/static/js/pos-utils.js?v=33" defer></script>
|
||||
<script src="/pos/static/js/sidebar.js?v=36" defer></script>
|
||||
<script src="/pos/static/js/sidebar.js?v=44" defer></script>
|
||||
<script src="/pos/static/js/dashboard-stats.js?v=33" defer></script>
|
||||
<script src="/pos/static/js/dashboard.js?v=33" 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>if('serviceWorker' in navigator){navigator.serviceWorker.register('/pos/sw.js?v=34',{scope:'/pos/'});}</script>
|
||||
<script src="/pos/static/js/pwa-install.js" defer></script>
|
||||
|
||||
<script src="/pos/static/js/chat.js" defer></script>
|
||||
|
||||
@@ -148,12 +148,12 @@
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<script src="/pos/static/js/i18n.js?v=36" defer></script>
|
||||
<script src="/pos/static/js/i18n.js?v=39" defer></script>
|
||||
<script src="/pos/static/js/kiosk.js" defer></script>
|
||||
<script src="/pos/static/js/app-init.js?v=2" defer></script>
|
||||
<script src="/pos/static/js/app-init.js?v=5" defer></script>
|
||||
<script src="/pos/static/js/splash-loader.js?v=33" defer></script>
|
||||
<script src="/pos/static/js/pos-utils.js?v=33" defer></script>
|
||||
<script src="/pos/static/js/sidebar.js?v=36" defer></script>
|
||||
<script src="/pos/static/js/sidebar.js?v=44" defer></script>
|
||||
<script src="/pos/static/js/diagrams.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>
|
||||
|
||||
@@ -302,11 +302,11 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="/pos/static/js/i18n.js?v=36" defer></script>
|
||||
<script src="/pos/static/js/app-init.js?v=2" defer></script>
|
||||
<script src="/pos/static/js/i18n.js?v=39" defer></script>
|
||||
<script src="/pos/static/js/app-init.js?v=5" defer></script>
|
||||
<script src="/pos/static/js/splash-loader.js?v=33" defer></script>
|
||||
<script src="/pos/static/js/pos-utils.js?v=33" defer></script>
|
||||
<script src="/pos/static/js/sidebar.js?v=36" defer></script>
|
||||
<script src="/pos/static/js/sidebar.js?v=44" defer></script>
|
||||
<script src="/pos/static/js/fleet.js" defer></script>
|
||||
<script src="/pos/static/js/offline-banner.js" defer></script>
|
||||
<script>if('serviceWorker' in navigator){navigator.serviceWorker.register('/pos/sw.js',{scope:'/pos/'});}</script>
|
||||
|
||||
@@ -192,7 +192,7 @@
|
||||
<h1 class="page-header__title">Inventario</h1>
|
||||
</div>
|
||||
<div class="page-header__actions">
|
||||
<button class="btn btn--ghost" onclick="document.getElementById('bulkImportModal').classList.add('is-open')">
|
||||
<button class="btn btn--ghost" id="btnHeaderImport" onclick="document.getElementById('bulkImportModal').classList.add('is-open')">
|
||||
<svg viewBox="0 0 24 24"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" y1="3" x2="12" y2="15"/></svg>
|
||||
Importar CSV
|
||||
</button>
|
||||
@@ -204,7 +204,7 @@
|
||||
<svg viewBox="0 0 24 24"><polyline points="23 4 23 10 17 10"/><path d="M20.49 15a9 9 0 1 1-.38-4.93"/></svg>
|
||||
Sincronizar
|
||||
</button>
|
||||
<button class="btn btn--primary" onclick="showCreateModal()">
|
||||
<button class="btn btn--primary" id="btnHeaderNewProduct" onclick="showCreateModal()">
|
||||
<svg viewBox="0 0 24 24"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>
|
||||
Nuevo Producto
|
||||
</button>
|
||||
@@ -336,7 +336,7 @@
|
||||
<svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="10"/><line x1="12" y1="6" x2="12" y2="12"/><line x1="16.24" y1="16.24" x2="12" y2="12"/></svg>
|
||||
<span id="tierDiscountBadge">Taller -15% · Mayoreo -25%</span>
|
||||
</button>
|
||||
<button class="btn btn--primary btn--sm" onclick="showCreateModal()">
|
||||
<button class="btn btn--primary btn--sm" id="btnStockNewProduct" onclick="showCreateModal()">
|
||||
<svg viewBox="0 0 24 24"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>
|
||||
Nuevo Producto
|
||||
</button>
|
||||
@@ -1018,6 +1018,9 @@
|
||||
<button class="inv-modal__close" onclick="document.getElementById('bulkImportModal').classList.remove('is-open')">×</button>
|
||||
</div>
|
||||
<div class="inv-modal__body">
|
||||
<div style="margin-bottom:12px;">
|
||||
<button class="btn btn--ghost btn--sm" onclick="downloadBulkImportTemplate()" type="button">📥 Descargar plantilla CSV</button>
|
||||
</div>
|
||||
<div style="margin-bottom:12px;">
|
||||
<label style="display:block;margin-bottom:4px;font-size:var(--text-caption);color:var(--color-text-muted);">Archivo CSV o Excel</label>
|
||||
<input type="file" id="bulkImportFile" accept=".csv,.xlsx,.xls" style="width:100%;padding:8px;border:1px dashed var(--color-border);border-radius:6px;background:var(--color-surface);color:var(--color-text);" />
|
||||
@@ -1039,8 +1042,8 @@
|
||||
</div>
|
||||
<div style="font-size:var(--text-caption);color:var(--color-text-muted);background:var(--color-surface);padding:10px;border-radius:6px;">
|
||||
<strong>Columnas esperadas:</strong>
|
||||
<code style="display:block;margin-top:4px;word-break:break-all;">sku, name, brand, price, stock, cost, location, description, category, make, model, year, engine, engine_code</code>
|
||||
<span style="display:block;margin-top:4px;">También se aceptan sinónimos en español: <em>numero_de_parte, nombre, marca, precio, cantidad, costo, ubicacion, categoria, fabricante, modelo, anio, motor, codigo_motor</em></span>
|
||||
<code style="display:block;margin-top:4px;word-break:break-all;">sku, name, brand, price, stock, cost, sku_secondary, description, category, make, model, year, engine, engine_code</code>
|
||||
<span style="display:block;margin-top:4px;">También se aceptan sinónimos en español: <em>numero_de_parte, nombre, marca, precio, cantidad, costo, sku_secundario, categoria, fabricante, modelo, anio, motor, codigo_motor</em></span>
|
||||
</div>
|
||||
<div id="bulkImportResult" style="margin-top:12px;display:none;"></div>
|
||||
</div>
|
||||
@@ -1058,13 +1061,13 @@
|
||||
<button class="banner__dismiss" onclick="document.getElementById('offlineBanner').style.display='none'" aria-label="Cerrar">×</button>
|
||||
</div>
|
||||
|
||||
<script src="/pos/static/js/i18n.js?v=36" defer></script>
|
||||
<script src="/pos/static/js/app-init.js?v=2" defer></script>
|
||||
<script src="/pos/static/js/i18n.js?v=39" defer></script>
|
||||
<script src="/pos/static/js/app-init.js?v=5" defer></script>
|
||||
<script src="/pos/static/js/splash-loader.js?v=33" defer></script>
|
||||
<script src="/pos/static/js/pos-utils.js?v=33" defer></script>
|
||||
<script src="/pos/static/js/sidebar.js?v=36" defer></script>
|
||||
<script src="/pos/static/js/sidebar.js?v=44" defer></script>
|
||||
<script src="/pos/static/js/virtual-scroll.js?v=33" defer></script>
|
||||
<script src="/pos/static/js/inventory.js?v=34" defer></script>
|
||||
<script src="/pos/static/js/inventory.js?v=37" defer></script>
|
||||
<script src="/pos/static/js/offline-banner.js" 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>
|
||||
|
||||
@@ -1063,11 +1063,11 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="/pos/static/js/i18n.js?v=36" defer></script>
|
||||
<script src="/pos/static/js/app-init.js?v=2" defer></script>
|
||||
<script src="/pos/static/js/i18n.js?v=39" defer></script>
|
||||
<script src="/pos/static/js/app-init.js?v=5" defer></script>
|
||||
<script src="/pos/static/js/splash-loader.js?v=33" defer></script>
|
||||
<script src="/pos/static/js/pos-utils.js?v=33" defer></script>
|
||||
<script src="/pos/static/js/sidebar.js?v=36" defer></script>
|
||||
<script src="/pos/static/js/sidebar.js?v=44" defer></script>
|
||||
<script src="/pos/static/js/invoicing.js?v=33" 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>
|
||||
|
||||
@@ -387,7 +387,7 @@
|
||||
|
||||
setTimeout(function() {
|
||||
var role = (result.data.employee.role || '').toLowerCase();
|
||||
if (role === 'workshop' || role === 'mechanic') {
|
||||
if (role === 'workshop' || role === 'mechanic' || role === 'counter') {
|
||||
window.location.href = '/pos/workshop';
|
||||
} else {
|
||||
window.location.href = '/pos/catalog';
|
||||
|
||||
@@ -342,11 +342,11 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="/pos/static/js/i18n.js?v=36" defer></script>
|
||||
<script src="/pos/static/js/app-init.js?v=2" defer></script>
|
||||
<script src="/pos/static/js/i18n.js?v=39" defer></script>
|
||||
<script src="/pos/static/js/app-init.js?v=5" defer></script>
|
||||
<script src="/pos/static/js/splash-loader.js?v=33" defer></script>
|
||||
<script src="/pos/static/js/pos-utils.js?v=33" defer></script>
|
||||
<script src="/pos/static/js/sidebar.js?v=36" defer></script>
|
||||
<script src="/pos/static/js/sidebar.js?v=44" defer></script>
|
||||
<script src="/pos/static/js/marketplace_external.js?v=33" defer></script>
|
||||
<script>if('serviceWorker' in navigator){navigator.serviceWorker.register('/pos/sw.js',{scope:'/pos/'});}</script>
|
||||
</body>
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
<link rel="shortcut icon" type="image/png" href="/pos/static/pwa/icon-192.png" />
|
||||
<script src="/pos/static/js/native-bridge.js" defer></script>
|
||||
|
||||
<link rel="stylesheet" href="/pos/static/css/pos.css?v=33"></head>
|
||||
<link rel="stylesheet" href="/pos/static/css/pos.css?v=34"></head>
|
||||
|
||||
<body class="pos-shell" id="appBody">
|
||||
|
||||
@@ -123,7 +123,6 @@
|
||||
</div>
|
||||
</div>
|
||||
<button class="btn-change-customer" onclick="POS.clearCustomer()" aria-label="Cambiar cliente">Cambiar</button>
|
||||
<button class="btn-change-customer" onclick="POS.creditSale()" aria-label="Venta a credito" style="margin-left:4px;background:var(--color-primary);color:#fff;border-color:var(--color-primary);">Crédito</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -199,15 +198,29 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Courier selector for counter remission notes -->
|
||||
<div class="form-field" id="courierSelectField" style="display:none; margin-bottom: 12px;">
|
||||
<label class="form-label" for="remissionCourier">Repartidor</label>
|
||||
<select class="form-input" id="remissionCourier">
|
||||
<option value="">-- Sin repartidor --</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- COBRAR Button -->
|
||||
<button class="btn-cobrar" id="btnCobrar" onclick="POS.checkout()" aria-label="Procesar cobro">
|
||||
<span>COBRAR</span>
|
||||
</button>
|
||||
|
||||
<!-- Counter remission button (shown for counter role when feature enabled) -->
|
||||
<button class="btn-cobrar" id="btnRemission" onclick="POS.createRemissionNote()" aria-label="Generar nota de remision" style="display:none;background:var(--color-secondary);">
|
||||
<span>NOTA DE REMISIÓN</span>
|
||||
</button>
|
||||
|
||||
<!-- 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" id="btnPayRemission" onclick="POS.openPayRemissionModal()" title="Cobrar nota de remision" style="display:none;">Cobrar Nota</button>
|
||||
<button class="btn-secondary-action" onclick="POS.createLayaway()" title="Apartado (requiere cliente)">Apartado</button>
|
||||
<button class="btn-secondary-action" onclick="POS.createServiceOrder()" title="Orden de servicio (F7)">Orden Taller</button>
|
||||
<button class="btn-secondary-action" onclick="POS.showLastSale()" title="Ultima venta (F5)">Ult.Venta</button>
|
||||
@@ -295,6 +308,15 @@
|
||||
<button class="pago-tab" data-method="mixto" onclick="POS.selectPaymentMethod('mixto', this)">
|
||||
Mixto
|
||||
</button>
|
||||
<button class="pago-tab" data-method="credito" onclick="POS.selectPaymentMethod('credito', this)">
|
||||
Crédito
|
||||
</button>
|
||||
<button class="pago-tab" data-method="cheque" onclick="POS.selectPaymentMethod('cheque', this)">
|
||||
Cheque
|
||||
</button>
|
||||
<button class="pago-tab" data-method="pendiente" onclick="POS.selectPaymentMethod('pendiente', this)">
|
||||
Pendiente
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- TAB: Efectivo -->
|
||||
@@ -358,6 +380,35 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- TAB: Crédito -->
|
||||
<div class="tab-content" id="creditPayment">
|
||||
<div class="form-group">
|
||||
<label class="form-label">Venta a crédito</label>
|
||||
<p style="color:var(--color-text-muted);font-size:var(--text-body-sm);">Se registrará como venta a crédito para el cliente seleccionado y se agregará a su cuenta.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- TAB: Cheque -->
|
||||
<div class="tab-content" id="chequePayment">
|
||||
<div class="form-group">
|
||||
<label class="form-label">Monto</label>
|
||||
<input type="text" class="form-input form-input-lg" id="chequeAmount" readonly />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">No. de cheque / referencia</label>
|
||||
<input type="text" class="form-input" id="chequeRef" placeholder="No. de cheque" />
|
||||
</div>
|
||||
<div class="form-hint">Verificar que el cheque sea válido antes de confirmar</div>
|
||||
</div>
|
||||
|
||||
<!-- TAB: Pendiente -->
|
||||
<div class="tab-content" id="pendingPayment">
|
||||
<div class="form-group">
|
||||
<label class="form-label">Pago pendiente</label>
|
||||
<p style="color:var(--color-text-muted);font-size:var(--text-body-sm);">La venta quedará pendiente de pago. Podrá cobrarse más tarde desde el listado de ventas o notas de remisión.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- CFDI Checkbox -->
|
||||
<div class="cfdi-check">
|
||||
<input type="checkbox" id="cfdiCheck" />
|
||||
@@ -373,6 +424,68 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ================================================================
|
||||
PAY REMISSION NOTE MODAL
|
||||
================================================================ -->
|
||||
<div class="modal-overlay" id="payRemissionModal">
|
||||
<div class="modal-pago">
|
||||
<div class="modal-header">
|
||||
<h3>Cobrar Nota de Remisión</h3>
|
||||
<button class="modal-close" onclick="POS.closePayRemissionModal()">✕</button>
|
||||
</div>
|
||||
|
||||
<div class="form-row" style="padding: 16px; gap: 8px;">
|
||||
<input class="form-input" type="number" id="payRemissionFolio" placeholder="Folio de la nota (NR-XXXX)" style="flex:1;" />
|
||||
<button class="btn btn-primary" onclick="POS.searchRemissionToPay()">Buscar</button>
|
||||
</div>
|
||||
|
||||
<div id="payRemissionDetail" style="padding: 0 16px 16px; max-height: 220px; overflow-y: auto;"></div>
|
||||
|
||||
<div id="payRemissionActions" style="display:none; padding: 0 16px 16px;">
|
||||
<div class="form-row" style="gap: 8px; margin-bottom: 8px;">
|
||||
<select class="form-input" id="payRemissionMethod" onchange="POS.updatePayRemissionMethod()">
|
||||
<option value="efectivo">Efectivo</option>
|
||||
<option value="transferencia">Transferencia</option>
|
||||
<option value="tarjeta">Tarjeta</option>
|
||||
<option value="mixto">Mixto</option>
|
||||
</select>
|
||||
</div>
|
||||
<div id="payRemissionCash" class="form-row" style="gap: 8px; margin-bottom: 8px;">
|
||||
<input class="form-input" type="number" id="payRemissionReceived" placeholder="Recibido" step="0.01" />
|
||||
</div>
|
||||
<div id="payRemissionRef" class="form-row" style="gap: 8px; margin-bottom: 8px; display:none;">
|
||||
<input class="form-input" type="text" id="payRemissionReference" placeholder="Referencia" />
|
||||
</div>
|
||||
<div id="payRemissionMixed" class="form-row" style="gap: 8px; margin-bottom: 8px; display:none; flex-direction: column;">
|
||||
<div class="mixed-row" style="display:flex; gap:8px; width:100%;">
|
||||
<select class="form-input" style="flex:1;">
|
||||
<option value="efectivo">Efectivo</option>
|
||||
<option value="transferencia">Transferencia</option>
|
||||
<option value="tarjeta">Tarjeta</option>
|
||||
</select>
|
||||
<input class="form-input mixed-amount" type="number" placeholder="Monto" step="0.01" style="flex:1;" />
|
||||
<input class="form-input" type="text" placeholder="Referencia" style="flex:1;" />
|
||||
</div>
|
||||
<div class="mixed-row" style="display:flex; gap:8px; width:100%;">
|
||||
<select class="form-input" style="flex:1;">
|
||||
<option value="efectivo">Efectivo</option>
|
||||
<option value="transferencia">Transferencia</option>
|
||||
<option value="tarjeta">Tarjeta</option>
|
||||
</select>
|
||||
<input class="form-input mixed-amount" type="number" placeholder="Monto" step="0.01" style="flex:1;" />
|
||||
<input class="form-input" type="text" placeholder="Referencia" style="flex:1;" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer" style="padding:0;">
|
||||
<button class="btn btn-ghost" onclick="POS.closePayRemissionModal()">Cancelar</button>
|
||||
<button class="btn btn-primary" id="btnConfirmPayRemission" onclick="POS.confirmPayRemission()">Confirmar Pago</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="payRemissionResult" style="padding: 0 16px 16px;"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ================================================================
|
||||
CANCEL SALE CONFIRMATION MODAL
|
||||
================================================================ -->
|
||||
@@ -613,14 +726,14 @@
|
||||
<!-- ================================================================
|
||||
JAVASCRIPT
|
||||
================================================================ -->
|
||||
<script src="/pos/static/js/i18n.js?v=36" defer></script>
|
||||
<script src="/pos/static/js/i18n.js?v=39" defer></script>
|
||||
<script src="/pos/static/js/kiosk.js" defer></script>
|
||||
<script src="/pos/static/js/pos-utils.js?v=33" defer></script>
|
||||
<script src="/pos/static/js/app-init.js?v=2" defer></script>
|
||||
<script src="/pos/static/js/app-init.js?v=5" defer></script>
|
||||
<script src="/pos/static/js/splash-loader.js?v=33" 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=33" defer></script>
|
||||
<script src="/pos/static/js/pos.js?v=41" defer></script>
|
||||
|
||||
<script>
|
||||
// Cancel sale button wiring
|
||||
|
||||
@@ -14,10 +14,10 @@
|
||||
<link rel="stylesheet" href="/pos/static/css/quotations.css"></head>
|
||||
<body>
|
||||
<script src="/pos/static/js/pos-utils.js?v=33" defer></script>
|
||||
<script src="/pos/static/js/i18n.js?v=36" defer></script>
|
||||
<script src="/pos/static/js/app-init.js?v=2" defer></script>
|
||||
<script src="/pos/static/js/i18n.js?v=39" defer></script>
|
||||
<script src="/pos/static/js/app-init.js?v=5" defer></script>
|
||||
<script src="/pos/static/js/splash-loader.js?v=33" defer></script>
|
||||
<script src="/pos/static/js/sidebar.js?v=36" defer></script>
|
||||
<script src="/pos/static/js/sidebar.js?v=44" defer></script>
|
||||
|
||||
<div class="page">
|
||||
<h1 class="page-title">Cotizaciones</h1>
|
||||
|
||||
436
pos/templates/remission_notes.html
Normal file
436
pos/templates/remission_notes.html
Normal file
@@ -0,0 +1,436 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="es">
|
||||
<head>
|
||||
<script>/*pos_theme_early*/(function(){var t=localStorage.getItem("pos_theme")||"industrial";document.documentElement.setAttribute("data-theme",t);})()</script>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Notas de Remisión — Nexus Autoparts POS</title>
|
||||
<link rel="stylesheet" href="/pos/static/css/chat.css" />
|
||||
<link rel="stylesheet" href="/pos/static/css/tokens.css" />
|
||||
<link rel="stylesheet" href="/pos/static/css/common.css" />
|
||||
<link rel="stylesheet" href="/pos/static/css/pos-ui.css?v=33" />
|
||||
<link rel="stylesheet" href="/pos/static/css/sidebar.css" />
|
||||
<link rel="stylesheet" href="/pos/static/css/pos-glass.css" />
|
||||
<link rel="stylesheet" href="/pos/static/css/inventory.css?v=34" />
|
||||
<link rel="stylesheet" href="/pos/static/css/remission_notes.css?v=1" />
|
||||
<link rel="manifest" href="/pos/static/pwa/manifest.json" />
|
||||
<meta name="theme-color" content="#F5A623" />
|
||||
<link rel="shortcut icon" type="image/png" href="/pos/static/pwa/icon-192.png" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<!-- THEME BAR -->
|
||||
<header class="theme-bar" role="banner">
|
||||
<div class="theme-bar__left">
|
||||
<div class="theme-bar__store">
|
||||
<span class="theme-bar__dot"></span>
|
||||
Nexus Autoparts
|
||||
</div>
|
||||
<div class="theme-bar__sep"></div>
|
||||
<span class="theme-bar__label">Sucursal Centro — Usuario: H. García</span>
|
||||
</div>
|
||||
<div class="theme-bar__right">
|
||||
<span class="theme-bar__label">Tema:</span>
|
||||
<button class="theme-btn theme-btn--industrial is-active" data-theme-target="industrial" onclick="setTheme('industrial')">
|
||||
<span class="theme-btn__swatch"></span>
|
||||
Industrial
|
||||
</button>
|
||||
<button class="theme-btn theme-btn--modern" data-theme-target="modern" onclick="setTheme('modern')">
|
||||
<span class="theme-btn__swatch"></span>
|
||||
Moderno
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- APP SHELL -->
|
||||
<div class="app-shell">
|
||||
|
||||
<!-- SIDEBAR -->
|
||||
<aside class="sidebar" role="navigation" aria-label="Navegación principal">
|
||||
<div class="sidebar__brand">
|
||||
<div class="brand-logo">NA</div>
|
||||
<div class="brand-name">
|
||||
<span class="brand-name__primary">Nexus</span>
|
||||
<span class="brand-name__sub">Autoparts POS</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="sidebar__nav">
|
||||
<div class="nav-section-label">Principal</div>
|
||||
<a class="nav-item" href="/pos/dashboard">
|
||||
<svg class="nav-item__icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round">
|
||||
<rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/>
|
||||
<rect x="14" y="14" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/>
|
||||
</svg>
|
||||
<span>Dashboard</span>
|
||||
</a>
|
||||
<a class="nav-item" href="/pos/sale">
|
||||
<svg class="nav-item__icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round">
|
||||
<rect x="2" y="3" width="20" height="14" rx="2"/><path d="M8 21h8M12 17v4"/>
|
||||
</svg>
|
||||
<span>POS</span>
|
||||
</a>
|
||||
<a class="nav-item" href="/pos/catalog">
|
||||
<svg class="nav-item__icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M4 6h16M4 10h16M4 14h16M4 18h16"/>
|
||||
</svg>
|
||||
<span>Catálogo</span>
|
||||
</a>
|
||||
<a class="nav-item" href="/pos/inventory">
|
||||
<svg class="nav-item__icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/>
|
||||
<polyline points="3.27 6.96 12 12.01 20.73 6.96"/><line x1="12" y1="22.08" x2="12" y2="12"/>
|
||||
</svg>
|
||||
<span>Inventario</span>
|
||||
</a>
|
||||
|
||||
<div class="nav-section-label">Gestión</div>
|
||||
<a class="nav-item" href="/pos/customers">
|
||||
<svg class="nav-item__icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/>
|
||||
<circle cx="9" cy="7" r="4"/>
|
||||
<path d="M23 21v-2a4 4 0 0 0-3-3.87M16 3.13a4 4 0 0 1 0 7.75"/>
|
||||
</svg>
|
||||
<span>Clientes</span>
|
||||
</a>
|
||||
<a class="nav-item is-active" href="/pos/remission-notes" aria-current="page">
|
||||
<svg class="nav-item__icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/>
|
||||
<polyline points="14 2 14 8 20 8"/>
|
||||
<line x1="16" y1="13" x2="8" y2="13"/>
|
||||
<line x1="16" y1="17" x2="8" y2="17"/>
|
||||
<polyline points="10 9 9 9 8 9"/>
|
||||
</svg>
|
||||
<span>Notas de Remisión</span>
|
||||
</a>
|
||||
<a class="nav-item" href="/pos/invoicing">
|
||||
<svg class="nav-item__icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/>
|
||||
<polyline points="14 2 14 8 20 8"/>
|
||||
<line x1="16" y1="13" x2="8" y2="13"/>
|
||||
<line x1="16" y1="17" x2="8" y2="17"/>
|
||||
</svg>
|
||||
<span>Facturación</span>
|
||||
</a>
|
||||
<a class="nav-item" href="/pos/reports">
|
||||
<svg class="nav-item__icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round">
|
||||
<line x1="18" y1="20" x2="18" y2="10"/><line x1="12" y1="20" x2="12" y2="4"/>
|
||||
<line x1="6" y1="20" x2="6" y2="14"/>
|
||||
</svg>
|
||||
<span>Reportes</span>
|
||||
</a>
|
||||
|
||||
<div class="nav-section-label">Sistema</div>
|
||||
<a class="nav-item" href="/pos/config">
|
||||
<svg class="nav-item__icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="12" cy="12" r="3"/>
|
||||
<path d="M19.07 4.93a10 10 0 0 1 0 14.14M4.93 4.93a10 10 0 0 0 0 14.14"/>
|
||||
</svg>
|
||||
<span>Configuración</span>
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
<div class="sidebar__footer">
|
||||
<div class="sidebar__user-avatar">HG</div>
|
||||
<div class="sidebar__user-info">
|
||||
<div class="sidebar__user-name">Hugo García</div>
|
||||
<div class="sidebar__user-role">Administrador</div>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<!-- MAIN CONTENT -->
|
||||
<main class="main" role="main">
|
||||
<div class="page-header">
|
||||
<div class="page-header__title-group">
|
||||
<span class="page-header__eyebrow">Ventas</span>
|
||||
<h1 class="page-header__title">Notas de Remisión</h1>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="page-content">
|
||||
<!-- Filters -->
|
||||
<div class="filters-card">
|
||||
<div class="toolbar">
|
||||
<div class="search-box">
|
||||
<svg viewBox="0 0 24 24" stroke-linecap="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
|
||||
<input type="text" id="filterFolio" placeholder="Buscar folio NR-XXXX..." />
|
||||
</div>
|
||||
<div class="search-box">
|
||||
<svg viewBox="0 0 24 24" stroke-linecap="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
|
||||
<input type="text" id="filterCustomer" placeholder="Cliente..." />
|
||||
</div>
|
||||
<select class="select-filter" id="filterStatus">
|
||||
<option value="">Todos los estados</option>
|
||||
<option value="pending_payment" selected>Pendientes</option>
|
||||
<option value="completed">Pagadas</option>
|
||||
<option value="cancelled">Canceladas</option>
|
||||
</select>
|
||||
<select class="select-filter" id="filterCourier">
|
||||
<option value="">Todos los repartidores</option>
|
||||
</select>
|
||||
<input type="date" class="select-filter" id="dateFrom" />
|
||||
<input type="date" class="select-filter" id="dateTo" />
|
||||
<div class="toolbar__spacer"></div>
|
||||
<button class="btn btn--primary" onclick="loadData(1)">
|
||||
<svg viewBox="0 0 24 24"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
|
||||
Buscar
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Table -->
|
||||
<div class="table-wrapper">
|
||||
<table class="data-table" id="remissionTable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Folio</th>
|
||||
<th>Fecha</th>
|
||||
<th>Cliente</th>
|
||||
<th>Vendedor</th>
|
||||
<th>Repartidor</th>
|
||||
<th style="text-align:right">Total</th>
|
||||
<th>Estado</th>
|
||||
<th style="text-align:right">Acciones</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="remissionTableBody">
|
||||
<tr><td colspan="8" style="text-align:center;padding:var(--space-8);">Cargando...</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="table-footer">
|
||||
<div class="pagination" id="pagination"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<!-- Ticket Modal -->
|
||||
<div class="modal-overlay" id="ticketModal" onclick="if(event.target===this) closeTicketModal()">
|
||||
<div class="modal">
|
||||
<div class="modal__header">
|
||||
<h3 class="modal__title">Vista previa</h3>
|
||||
<button class="modal__close" onclick="closeTicketModal()">×</button>
|
||||
</div>
|
||||
<div class="modal__body">
|
||||
<div id="ticketContent" class="ticket-preview"></div>
|
||||
</div>
|
||||
<div class="modal__footer">
|
||||
<button class="btn btn--ghost" onclick="closeTicketModal()">Cerrar</button>
|
||||
<button class="btn btn--primary" onclick="printTicket()">
|
||||
<svg viewBox="0 0 24 24"><polyline points="6 9 6 2 18 2 18 9"/><path d="M6 18H4a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2"/><rect x="6" y="14" width="12" height="8"/></svg>
|
||||
Imprimir
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="/pos/static/js/i18n.js?v=39" defer></script>
|
||||
<script src="/pos/static/js/app-init.js?v=5" defer></script>
|
||||
<script src="/pos/static/js/sidebar.js?v=44" defer></script>
|
||||
|
||||
<script>
|
||||
const token = localStorage.getItem('pos_token') || '';
|
||||
let currentPage = 1;
|
||||
let couriers = [];
|
||||
|
||||
function headers() {
|
||||
return { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + token };
|
||||
}
|
||||
|
||||
async function api(url, options = {}) {
|
||||
options.headers = headers();
|
||||
const res = await fetch(url, options);
|
||||
const data = await res.json();
|
||||
if (!res.ok) throw new Error(data.error || `HTTP ${res.status}`);
|
||||
return data;
|
||||
}
|
||||
|
||||
const fmt = (n) => '$' + parseFloat(n || 0).toLocaleString('es-MX', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
|
||||
|
||||
function statusLabel(status) {
|
||||
return { pending_payment: 'Pendiente', completed: 'Pagada', cancelled: 'Cancelada' }[status] || status;
|
||||
}
|
||||
function statusClass(status) {
|
||||
return { pending_payment: 'badge--pending_payment', completed: 'badge--completed', cancelled: 'badge--cancelled' }[status] || '';
|
||||
}
|
||||
|
||||
async function loadCouriers() {
|
||||
try {
|
||||
const data = await api('/pos/api/logistics/couriers');
|
||||
couriers = data.couriers || [];
|
||||
const sel = document.getElementById('filterCourier');
|
||||
const current = sel.value;
|
||||
sel.innerHTML = '<option value="">Todos los repartidores</option>' + couriers.map(c => `<option value="${c.id}">${c.name}</option>`).join('');
|
||||
sel.value = current;
|
||||
} catch (e) {
|
||||
console.warn('No se pudieron cargar repartidores', e);
|
||||
}
|
||||
}
|
||||
|
||||
async function loadData(page) {
|
||||
currentPage = page;
|
||||
const tbody = document.getElementById('remissionTableBody');
|
||||
tbody.innerHTML = '<tr><td colspan="8" style="text-align:center;padding:var(--space-8);color:var(--color-text-muted);">Cargando...</td></tr>';
|
||||
|
||||
const params = new URLSearchParams();
|
||||
params.set('sale_type', 'counter_remission');
|
||||
params.set('per_page', '50');
|
||||
params.set('page', String(page));
|
||||
|
||||
const status = document.getElementById('filterStatus').value;
|
||||
if (status) params.set('status', status);
|
||||
const folio = document.getElementById('filterFolio').value.trim();
|
||||
if (folio) params.set('q', folio.replace(/^NR-/i, ''));
|
||||
const customer = document.getElementById('filterCustomer').value.trim();
|
||||
if (customer) params.set('customer', customer);
|
||||
const courier = document.getElementById('filterCourier').value;
|
||||
if (courier) params.set('courier_id', courier);
|
||||
const from = document.getElementById('dateFrom').value;
|
||||
const to = document.getElementById('dateTo').value;
|
||||
if (from) params.set('date_from', from);
|
||||
if (to) params.set('date_to', to);
|
||||
|
||||
try {
|
||||
const res = await api('/pos/api/sales?' + params.toString());
|
||||
render(res.data || [], res.pagination || {});
|
||||
} catch (e) {
|
||||
tbody.innerHTML = `<tr><td colspan="8" style="text-align:center;padding:var(--space-8);color:var(--color-error);">Error: ${e.message}</td></tr>`;
|
||||
}
|
||||
}
|
||||
|
||||
function render(rows, pagination) {
|
||||
const tbody = document.getElementById('remissionTableBody');
|
||||
const pag = document.getElementById('pagination');
|
||||
|
||||
if (!rows.length) {
|
||||
tbody.innerHTML = `<tr><td colspan="8">
|
||||
<div class="empty-state">
|
||||
<div class="empty-state__title">No hay notas de remisión</div>
|
||||
<div class="empty-state__subtitle">Ajusta los filtros o genera una nueva nota desde el POS.</div>
|
||||
</div>
|
||||
</td></tr>`;
|
||||
pag.innerHTML = '';
|
||||
return;
|
||||
}
|
||||
|
||||
tbody.innerHTML = rows.map(r => {
|
||||
const canPay = r.status === 'pending_payment';
|
||||
return `
|
||||
<tr>
|
||||
<td class="td--mono">NR-${r.id}</td>
|
||||
<td>${new Date(r.created_at).toLocaleString('es-MX')}</td>
|
||||
<td class="td--primary">${r.customer_name || 'Público General'}</td>
|
||||
<td>${r.employee_name || '-'}</td>
|
||||
<td>${r.courier_name || '-'}</td>
|
||||
<td class="td--amount" style="text-align:right">${fmt(r.total)}</td>
|
||||
<td><span class="badge ${statusClass(r.status)}">${statusLabel(r.status)}</span></td>
|
||||
<td style="text-align:right">
|
||||
<div style="display:flex;justify-content:flex-end;gap:var(--space-2);">
|
||||
<button class="action-btn action-btn--ghost" onclick="viewTicket(${r.id})" title="Imprimir">
|
||||
<svg viewBox="0 0 24 24"><polyline points="6 9 6 2 18 2 18 9"/><path d="M6 18H4a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2"/><rect x="6" y="14" width="12" height="8"/></svg>
|
||||
</button>
|
||||
${canPay ? `<button class="action-btn action-btn--primary" onclick="payNote(${r.id}, ${r.total})" title="Cobrar">
|
||||
<svg viewBox="0 0 24 24"><rect x="2" y="6" width="20" height="12" rx="2"/><path d="M6 10h.01M6 14h.01"/></svg>
|
||||
</button>` : ''}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
`;
|
||||
}).join('');
|
||||
|
||||
if (pagination.total_pages > 1) {
|
||||
pag.innerHTML = `
|
||||
<button class="page-btn" onclick="loadData(${pagination.page - 1})" ${pagination.page <= 1 ? 'disabled' : ''}>←</button>
|
||||
<span style="color:var(--color-text-muted);font-size:var(--text-caption);">Página ${pagination.page} de ${pagination.total_pages}</span>
|
||||
<button class="page-btn" onclick="loadData(${pagination.page + 1})" ${pagination.page >= pagination.total_pages ? 'disabled' : ''}>→</button>
|
||||
`;
|
||||
} else {
|
||||
pag.innerHTML = '';
|
||||
}
|
||||
}
|
||||
|
||||
async function viewTicket(saleId) {
|
||||
try {
|
||||
const data = await api('/pos/api/sales/' + saleId + '/print-remission', { method: 'POST', body: '{}' });
|
||||
const dateStr = new Date(data.date).toLocaleString('es-MX');
|
||||
const itemsHtml = (data.items || []).map(it => `
|
||||
<div class="ticket-preview__item"><span>${it.quantity} x ${it.name}</span><span>${fmt(it.subtotal)}</span></div>
|
||||
`).join('');
|
||||
|
||||
document.getElementById('ticketContent').innerHTML = `
|
||||
<div class="ticket-preview__center ticket-preview__bold">${data.business_name || 'NEXUS AUTOPARTS'}</div>
|
||||
<div class="ticket-preview__center">${data.business_rfc || ''}</div>
|
||||
<div class="ticket-preview__center">${data.business_address || ''}</div>
|
||||
<div class="ticket-preview__divider"></div>
|
||||
<div class="ticket-preview__center ticket-preview__bold">NOTA DE REMISIÓN</div>
|
||||
<div class="ticket-preview__center">${data.folio}</div>
|
||||
<div class="ticket-preview__center">${dateStr}</div>
|
||||
<div class="ticket-preview__divider"></div>
|
||||
<div class="ticket-preview__line"><span>Cliente:</span><span>${data.customer || 'Público General'}</span></div>
|
||||
<div class="ticket-preview__line"><span>Vendedor:</span><span>${data.employee || '-'}</span></div>
|
||||
${data.courier ? `<div class="ticket-preview__line"><span>Repartidor:</span><span>${data.courier}</span></div>` : ''}
|
||||
<div class="ticket-preview__items">${itemsHtml}</div>
|
||||
<div class="ticket-preview__divider"></div>
|
||||
<div class="ticket-preview__line"><span>Subtotal:</span><span>${fmt(data.subtotal)}</span></div>
|
||||
${data.discount_total ? `<div class="ticket-preview__line"><span>Descuento:</span><span>-${fmt(data.discount_total)}</span></div>` : ''}
|
||||
<div class="ticket-preview__line"><span>IVA:</span><span>${fmt(data.tax_total)}</span></div>
|
||||
<div class="ticket-preview__line ticket-preview__bold"><span>TOTAL:</span><span>${fmt(data.total)}</span></div>
|
||||
<div class="ticket-preview__divider"></div>
|
||||
<div class="ticket-preview__footer ticket-preview__bold">PENDIENTE DE PAGO</div>
|
||||
<div class="ticket-preview__footer" style="font-size:0.75rem;opacity:0.8;">Presente esta nota en caja para pagar</div>
|
||||
`;
|
||||
document.getElementById('ticketModal').classList.add('is-open');
|
||||
} catch (e) {
|
||||
alert('Error: ' + e.message);
|
||||
}
|
||||
}
|
||||
|
||||
function closeTicketModal() {
|
||||
document.getElementById('ticketModal').classList.remove('is-open');
|
||||
}
|
||||
|
||||
function printTicket() {
|
||||
const w = window.open('', '_blank');
|
||||
w.document.write('<html><head><title>Nota de Remisión</title></head><body>' + document.getElementById('ticketContent').innerHTML + '</body></html>');
|
||||
w.document.close();
|
||||
w.print();
|
||||
}
|
||||
|
||||
async function payNote(saleId, total) {
|
||||
const method = prompt(`Cobrar nota NR-${saleId}\nTotal: ${fmt(total)}\n\nForma de pago: efectivo / transferencia / tarjeta`, 'efectivo');
|
||||
if (!method) return;
|
||||
const reference = ['transferencia', 'tarjeta'].includes(method) ? prompt('Referencia:') : '';
|
||||
try {
|
||||
await api('/pos/api/sales/' + saleId + '/pay', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
payment_method: method,
|
||||
amount_paid: total,
|
||||
reference: reference || ''
|
||||
})
|
||||
});
|
||||
alert('Nota cobrada correctamente');
|
||||
loadData(currentPage);
|
||||
} catch (e) {
|
||||
alert('Error al cobrar: ' + e.message);
|
||||
}
|
||||
}
|
||||
|
||||
(async function init() {
|
||||
if (!token) {
|
||||
document.getElementById('remissionTableBody').innerHTML = `<tr><td colspan="8">
|
||||
<div class="empty-state">
|
||||
<div class="empty-state__title">Inicia sesión</div>
|
||||
<div class="empty-state__subtitle">Se requiere autenticación para ver las notas de remisión.</div>
|
||||
</div>
|
||||
</td></tr>`;
|
||||
return;
|
||||
}
|
||||
await loadCouriers();
|
||||
loadData(1);
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -229,6 +229,14 @@
|
||||
</svg>
|
||||
Histórico
|
||||
</button>
|
||||
<button class="tab-btn" onclick="switchTab('cortes', this)">
|
||||
<svg viewBox="0 0 15 15" fill="none" stroke="currentColor" stroke-width="1.4">
|
||||
<rect x="1" y="3" width="13" height="10" rx="1"/>
|
||||
<path d="M4 7h7M4 10h5"/>
|
||||
<circle cx="11" cy="10" r="1.5" fill="currentColor"/>
|
||||
</svg>
|
||||
Cortes de caja
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- ==================================================================
|
||||
@@ -355,6 +363,38 @@
|
||||
<!-- Sales detail table -->
|
||||
<div class="table-card mb-5" id="historico-detalle"></div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- ==================================================================
|
||||
TAB 6: CORTES DE CAJA
|
||||
================================================================== -->
|
||||
<div class="tab-panel" id="panel-cortes">
|
||||
|
||||
<!-- Filter Bar -->
|
||||
<div class="filter-bar">
|
||||
<span class="filter-bar__label">Desde</span>
|
||||
<input type="date" class="filter-input" id="cortes-date-from" />
|
||||
<span class="filter-bar__label">Hasta</span>
|
||||
<input type="date" class="filter-input" id="cortes-date-to" />
|
||||
<span id="cortes-employee-filter">
|
||||
<span class="filter-bar__label">Cajero</span>
|
||||
<select class="filter-select" id="cortes-employee">
|
||||
<option value="">Todos</option>
|
||||
</select>
|
||||
</span>
|
||||
<div class="filter-bar__spacer"></div>
|
||||
<button class="btn btn-primary btn-sm" onclick="Reports.loadCortes()">Generar</button>
|
||||
</div>
|
||||
|
||||
<!-- KPI Cards (dynamic) -->
|
||||
<div class="kpi-grid" id="cortes-kpis"></div>
|
||||
|
||||
<!-- Cortes detail table -->
|
||||
<div class="table-card mb-5" id="cortes-detalle"></div>
|
||||
|
||||
<!-- Detail of sales for the selected cash cut -->
|
||||
<div class="table-card mb-5" id="corte-ventas-detalle" style="display:none;"></div>
|
||||
|
||||
</div>
|
||||
<!-- End panels -->
|
||||
|
||||
@@ -365,12 +405,12 @@
|
||||
</div>
|
||||
<!-- End app-shell -->
|
||||
|
||||
<script src="/pos/static/js/i18n.js?v=36" defer></script>
|
||||
<script src="/pos/static/js/app-init.js?v=2" defer></script>
|
||||
<script src="/pos/static/js/i18n.js?v=39" defer></script>
|
||||
<script src="/pos/static/js/app-init.js?v=5" defer></script>
|
||||
<script src="/pos/static/js/splash-loader.js?v=33" defer></script>
|
||||
<script src="/pos/static/js/pos-utils.js?v=33" defer></script>
|
||||
<script src="/pos/static/js/sidebar.js?v=36" defer></script>
|
||||
<script src="/pos/static/js/reports.js?v=33" defer></script>
|
||||
<script src="/pos/static/js/sidebar.js?v=44" defer></script>
|
||||
<script src="/pos/static/js/reports.js?v=35" 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>
|
||||
|
||||
@@ -126,8 +126,8 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="/pos/static/js/app-init.js?v=2" defer></script>
|
||||
<script src="/pos/static/js/sidebar.js?v=36" defer></script>
|
||||
<script src="/pos/static/js/app-init.js?v=5" defer></script>
|
||||
<script src="/pos/static/js/sidebar.js?v=44" defer></script>
|
||||
<script src="/pos/static/js/supplier_catalog.js?v=33" defer></script>
|
||||
<script>if('serviceWorker' in navigator){navigator.serviceWorker.register('/pos/sw.js',{scope:'/pos/'});}</script>
|
||||
</body>
|
||||
|
||||
@@ -131,12 +131,12 @@ function posLogout(){localStorage.removeItem('pos_token');window.location.href='
|
||||
</script>
|
||||
|
||||
<!-- Sidebar -->
|
||||
<script src="/pos/static/js/i18n.js?v=36" defer></script>
|
||||
<script src="/pos/static/js/app-init.js?v=2" defer></script>
|
||||
<script src="/pos/static/js/i18n.js?v=39" defer></script>
|
||||
<script src="/pos/static/js/app-init.js?v=5" defer></script>
|
||||
<script src="/pos/static/js/splash-loader.js?v=33" defer></script>
|
||||
<script src="/pos/static/js/whatsapp2.js?v=33" defer></script>
|
||||
<script src="/pos/static/js/pos-utils.js?v=33" defer></script>
|
||||
<script src="/pos/static/js/sidebar.js?v=36" defer></script>
|
||||
<script src="/pos/static/js/sidebar.js?v=44" defer></script>
|
||||
|
||||
<script src="/pos/static/js/chat.js" defer></script>
|
||||
</body>
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<meta name="theme-color" content="#F5A623" />
|
||||
<link rel="shortcut icon" type="image/png" href="/pos/static/pwa/icon-192.png" />
|
||||
|
||||
<link rel="stylesheet" href="/pos/static/css/workshop.css?v=42">
|
||||
<link rel="stylesheet" href="/pos/static/css/workshop.css?v=44">
|
||||
<style>
|
||||
.so-notes-grid { display: grid; grid-template-columns: 120px 1fr; gap: var(--space-2); align-items: start; }
|
||||
.so-notes-grid .form-label { margin: 0; padding-top: var(--space-2); }
|
||||
@@ -55,7 +55,7 @@
|
||||
<svg viewBox="0 0 24 24"><path d="M21 15a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2v10z"/><polyline points="3 8 12 13 21 8"/></svg>
|
||||
</div>
|
||||
<div class="summary-card__body">
|
||||
<div class="summary-card__label">Recibidos</div>
|
||||
<div class="summary-card__label">Por revisar</div>
|
||||
<div class="summary-card__value" id="statReceived">--</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -73,7 +73,7 @@
|
||||
<svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="10"/><polyline points="9 12 12 15 16 10"/></svg>
|
||||
</div>
|
||||
<div class="summary-card__body">
|
||||
<div class="summary-card__label">Listos</div>
|
||||
<div class="summary-card__label">Por entregar</div>
|
||||
<div class="summary-card__value" id="statReady">--</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -96,20 +96,28 @@
|
||||
</select>
|
||||
<select class="form-input" id="filterStatus">
|
||||
<option value="">Todos los estatus</option>
|
||||
<option value="received">Recibido</option>
|
||||
<option value="diagnosis">Diagnóstico</option>
|
||||
<option value="waiting_parts">Espera refacciones</option>
|
||||
<option value="repair">En reparación</option>
|
||||
<option value="quality_check">Control calidad</option>
|
||||
<option value="ready">Listo</option>
|
||||
<option value="delivered">Entregado</option>
|
||||
<option value="cancelled">Cancelado</option>
|
||||
<option value="por_revisar">Por revisar</option>
|
||||
<option value="en_revision">En revisión</option>
|
||||
<option value="revisada">Revisada</option>
|
||||
<option value="cotizada">Cotizada</option>
|
||||
<option value="por_autorizar">Por autorizar</option>
|
||||
<option value="autorizada">Autorizada</option>
|
||||
<option value="autorizacion_parcial">Autorización parcial</option>
|
||||
<option value="en_reparacion">En reparación</option>
|
||||
<option value="reparada">Reparada</option>
|
||||
<option value="por_entregar">Por entregar</option>
|
||||
<option value="entregado">Entregado</option>
|
||||
<option value="por_enviar">Por enviar</option>
|
||||
<option value="enviado">Enviado</option>
|
||||
<option value="por_facturar">Por facturar</option>
|
||||
<option value="facturada">Facturada</option>
|
||||
<option value="por_recolectar">Por recolectar</option>
|
||||
<option value="cancelada">Cancelada</option>
|
||||
</select>
|
||||
<select class="form-input" id="filterDelivery">
|
||||
<option value="">Todas las vías de entrega</option>
|
||||
<option value="pickup">Pasa cliente</option>
|
||||
<option value="pickup">Mostrador</option>
|
||||
<option value="delivery">Envío a domicilio</option>
|
||||
<option value="courier">Motociclista</option>
|
||||
</select>
|
||||
<label class="toolbar-toggle">
|
||||
<input type="checkbox" id="filterDirect" />
|
||||
@@ -137,15 +145,16 @@
|
||||
<tr>
|
||||
<th>Sucursal</th>
|
||||
<th>Orden</th>
|
||||
<th>Cliente</th>
|
||||
<th>Vehículo</th>
|
||||
<th class="restricted-hide">Cliente</th>
|
||||
<th class="restricted-hide">Taller</th>
|
||||
<th class="restricted-hide">Vehículo</th>
|
||||
<th>Estatus</th>
|
||||
<th class="price-col">Total</th>
|
||||
<th class="price-col restricted-hide">Total</th>
|
||||
<th>Acciones</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="listBody">
|
||||
<tr><td colspan="7" style="text-align:center;padding:var(--space-4);">Cargando...</td></tr>
|
||||
<tr><td colspan="8" style="text-align:center;padding:var(--space-4);">Cargando...</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@@ -184,6 +193,10 @@
|
||||
</div>
|
||||
<div class="modal__body">
|
||||
<form id="newOrderForm" class="form-grid">
|
||||
<div class="form-field">
|
||||
<label class="form-label" for="noBranch">Sucursal</label>
|
||||
<select class="form-input" id="noBranch" required></select>
|
||||
</div>
|
||||
<div class="form-field">
|
||||
<label class="form-label" for="noCustomer">Cliente</label>
|
||||
<div style="display:flex;gap:var(--space-2);">
|
||||
@@ -192,29 +205,26 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-field">
|
||||
<label class="form-label" for="noVehicle">Vehículo</label>
|
||||
<div style="display:flex;gap:var(--space-2);">
|
||||
<select class="form-input" id="noVehicle" style="flex:1;"></select>
|
||||
<button class="btn btn--secondary" type="button" onclick="Workshop.openNewVehicleModalFromNewOrder()">+ Nuevo</button>
|
||||
</div>
|
||||
<label class="form-label" for="noWorkshopName">Taller</label>
|
||||
<input class="form-input" id="noWorkshopName" placeholder="Nombre del taller" />
|
||||
</div>
|
||||
<div class="form-field">
|
||||
<label class="form-label" for="noMechanic">Mecánico asignado</label>
|
||||
<select class="form-input" id="noMechanic"></select>
|
||||
<label class="form-label" for="noCustomerPhone">Teléfono</label>
|
||||
<input class="form-input" id="noCustomerPhone" placeholder="3312345678" />
|
||||
</div>
|
||||
<div class="form-field form-field--span2">
|
||||
<label class="form-label" for="noCustomerAddress">Dirección</label>
|
||||
<input class="form-input" id="noCustomerAddress" placeholder="Calle, número, colonia" />
|
||||
</div>
|
||||
<div class="form-field">
|
||||
<label class="form-label" for="noPriority">Prioridad</label>
|
||||
<select class="form-input" id="noPriority">
|
||||
<option value="normal">Normal</option>
|
||||
<option value="high">Alta</option>
|
||||
<option value="urgent">Urgente</option>
|
||||
</select>
|
||||
<label class="form-label" for="noVehicleDescription">Vehículo</label>
|
||||
<input class="form-input" id="noVehicleDescription" placeholder="Ej. Versa 2020" />
|
||||
</div>
|
||||
<div class="form-field">
|
||||
<label class="form-label" for="noDelivery">Vía de entrega</label>
|
||||
<select class="form-input" id="noDelivery">
|
||||
<option value="">—</option>
|
||||
<option value="pickup">Pasa cliente</option>
|
||||
<option value="pickup">Mostrador</option>
|
||||
<option value="delivery">Envío a domicilio</option>
|
||||
<option value="courier">Motociclista</option>
|
||||
</select>
|
||||
@@ -224,21 +234,31 @@
|
||||
<select class="form-input" id="noCourier"></select>
|
||||
</div>
|
||||
<div class="form-field">
|
||||
<label class="form-label" for="noEstimatedCompletion">Entrega estimada</label>
|
||||
<input class="form-input" type="datetime-local" id="noEstimatedCompletion" />
|
||||
<label class="form-label" for="noMechanic">Mecánico asignado (usuario)</label>
|
||||
<select class="form-input" id="noMechanic"></select>
|
||||
</div>
|
||||
<div class="form-field">
|
||||
<label class="form-label" for="noMileage">Kilometraje</label>
|
||||
<input class="form-input" type="number" id="noMileage" placeholder="Ej. 45200" />
|
||||
<label class="form-label" for="noMechanicName">Mecánico asignado (nombre libre)</label>
|
||||
<input class="form-input" type="text" id="noMechanicName" placeholder="Ej. Juan Pérez" />
|
||||
</div>
|
||||
<div class="form-field form-field--span2">
|
||||
<div class="form-field">
|
||||
<label class="form-label" for="noEstimatedCost">Presupuesto</label>
|
||||
<input class="form-input" type="number" id="noEstimatedCost" step="0.01" placeholder="0.00" />
|
||||
</div>
|
||||
<div class="form-field">
|
||||
<label class="toolbar-toggle">
|
||||
<input type="checkbox" id="noRequiresInvoice" />
|
||||
<span>Requiere factura</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-field">
|
||||
<label class="toolbar-toggle">
|
||||
<input type="checkbox" id="noDirect" />
|
||||
<span>Orden directa (sin inventario)</span>
|
||||
<span>Orden directa</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-field form-field--span2">
|
||||
<label class="form-label" for="noNotes">Notas de recepción</label>
|
||||
<label class="form-label" for="noNotes">Observaciones</label>
|
||||
<textarea class="form-input" id="noNotes" rows="3" placeholder="Falla reportada, observaciones..."></textarea>
|
||||
</div>
|
||||
</form>
|
||||
@@ -270,63 +290,59 @@
|
||||
</div>
|
||||
<input type="hidden" id="eoCustomerId" />
|
||||
</div>
|
||||
<div class="form-field form-field--span2" style="position:relative;">
|
||||
<label class="form-label" for="eoVehicleSearch">Vehículo</label>
|
||||
<div style="display:flex;gap:var(--space-2);">
|
||||
<div style="position:relative;flex:1;">
|
||||
<input class="form-input" id="eoVehicleSearch" autocomplete="off" placeholder="Buscar por placa..." oninput="Workshop.searchVehiclesForSO()" />
|
||||
<div id="eoVehicleResults" style="display:none;position:absolute;z-index:10;top:100%;left:0;right:0;max-height:180px;overflow-y:auto;background:#fff;border:1px solid var(--color-border);border-radius:var(--radius-md);box-shadow:0 4px 12px rgba(0,0,0,.15);"></div>
|
||||
</div>
|
||||
<button class="btn btn--secondary" type="button" onclick="Workshop.openNewVehicleModal('edit')">+ Nuevo</button>
|
||||
</div>
|
||||
<input type="hidden" id="eoVehicleId" />
|
||||
</div>
|
||||
<div class="form-field">
|
||||
<label class="form-label" for="eoBranch">Sucursal</label>
|
||||
<select class="form-input" id="eoBranch"></select>
|
||||
</div>
|
||||
<div class="form-field">
|
||||
<label class="form-label" for="eoMechanic">Mecánico asignado</label>
|
||||
<select class="form-input" id="eoMechanic"><option value="">— Ninguno —</option></select>
|
||||
<label class="form-label" for="eoWorkshopName">Taller</label>
|
||||
<input class="form-input" id="eoWorkshopName" placeholder="Nombre del taller" />
|
||||
</div>
|
||||
<div class="form-field">
|
||||
<label class="form-label" for="eoPriority">Prioridad</label>
|
||||
<select class="form-input" id="eoPriority">
|
||||
<option value="low">Baja</option>
|
||||
<option value="normal">Normal</option>
|
||||
<option value="high">Alta</option>
|
||||
<option value="urgent">Urgente</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-field">
|
||||
<label class="form-label" for="eoFuelLevel">Nivel de combustible</label>
|
||||
<select class="form-input" id="eoFuelLevel">
|
||||
<option value="">—</option>
|
||||
<option value="empty">Vacío</option>
|
||||
<option value="quarter">1/4</option>
|
||||
<option value="half">1/2</option>
|
||||
<option value="three_quarters">3/4</option>
|
||||
<option value="full">Lleno</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-field">
|
||||
<label class="form-label" for="eoMileageIn">Kilometraje entrada</label>
|
||||
<input class="form-input" type="number" id="eoMileageIn" />
|
||||
</div>
|
||||
<div class="form-field">
|
||||
<label class="form-label" for="eoMileageOut">Kilometraje salida</label>
|
||||
<input class="form-input" type="number" id="eoMileageOut" />
|
||||
</div>
|
||||
<div class="form-field">
|
||||
<label class="form-label" for="eoEstimatedCompletion">Entrega estimada</label>
|
||||
<input class="form-input" type="datetime-local" id="eoEstimatedCompletion" />
|
||||
</div>
|
||||
<div class="form-field">
|
||||
<label class="form-label" for="eoEstimatedCost">Costo estimado</label>
|
||||
<input class="form-input" type="number" id="eoEstimatedCost" step="0.01" />
|
||||
<label class="form-label" for="eoCustomerPhone">Teléfono</label>
|
||||
<input class="form-input" id="eoCustomerPhone" placeholder="3312345678" />
|
||||
</div>
|
||||
<div class="form-field form-field--span2">
|
||||
<label class="form-label" for="eoNotes">Notas de recepción</label>
|
||||
<label class="form-label" for="eoCustomerAddress">Dirección</label>
|
||||
<input class="form-input" id="eoCustomerAddress" placeholder="Calle, número, colonia" />
|
||||
</div>
|
||||
<div class="form-field">
|
||||
<label class="form-label" for="eoVehicleDescription">Vehículo</label>
|
||||
<input class="form-input" id="eoVehicleDescription" placeholder="Ej. Versa 2020" />
|
||||
</div>
|
||||
<div class="form-field">
|
||||
<label class="form-label" for="eoDelivery">Vía de entrega</label>
|
||||
<select class="form-input" id="eoDelivery">
|
||||
<option value="">—</option>
|
||||
<option value="pickup">Mostrador</option>
|
||||
<option value="delivery">Envío a domicilio</option>
|
||||
<option value="courier">Motociclista</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-field" id="eoCourierField" style="display:none;">
|
||||
<label class="form-label" for="eoCourier">Motociclista</label>
|
||||
<select class="form-input" id="eoCourier"></select>
|
||||
</div>
|
||||
<div class="form-field">
|
||||
<label class="form-label" for="eoEstimatedCost">Presupuesto</label>
|
||||
<input class="form-input" type="number" id="eoEstimatedCost" step="0.01" />
|
||||
</div>
|
||||
<div class="form-field">
|
||||
<label class="form-label" for="eoMechanic">Mecánico asignado (usuario)</label>
|
||||
<select class="form-input" id="eoMechanic"></select>
|
||||
</div>
|
||||
<div class="form-field">
|
||||
<label class="form-label" for="eoMechanicName">Mecánico asignado (nombre libre)</label>
|
||||
<input class="form-input" type="text" id="eoMechanicName" placeholder="Ej. Juan Pérez" />
|
||||
</div>
|
||||
<div class="form-field">
|
||||
<label class="toolbar-toggle">
|
||||
<input type="checkbox" id="eoRequiresInvoice" />
|
||||
<span>Requiere factura</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-field form-field--span2">
|
||||
<label class="form-label" for="eoNotes">Observaciones</label>
|
||||
<textarea class="form-input" id="eoNotes" rows="3"></textarea>
|
||||
</div>
|
||||
</form>
|
||||
@@ -477,13 +493,13 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="/pos/static/js/i18n.js?v=36" defer></script>
|
||||
<script src="/pos/static/js/app-init.js?v=2" defer></script>
|
||||
<script src="/pos/static/js/i18n.js?v=39" defer></script>
|
||||
<script src="/pos/static/js/app-init.js?v=5" defer></script>
|
||||
<script src="/pos/static/js/splash-loader.js?v=33" defer></script>
|
||||
<script src="/pos/static/js/pos-utils.js?v=33" defer></script>
|
||||
<script src="/pos/static/js/sidebar.js?v=36" defer></script>
|
||||
<script src="/pos/static/js/sidebar.js?v=44" defer></script>
|
||||
<script src="/pos/static/js/offline-banner.js" defer></script>
|
||||
<script src="/pos/static/js/workshop.js?v=42" defer></script>
|
||||
<script src="/pos/static/js/workshop.js?v=55" 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>
|
||||
<script src="/pos/static/js/chat.js" defer></script>
|
||||
|
||||
Reference in New Issue
Block a user