// /home/Autopartes/pos/static/js/accounting.js // Accounting module — wired to design-system HTML IDs // Tabs: panel-cxc, panel-cxp, panel-balance, panel-resultados, panel-flujo, panel-conciliacion, panel-cierre const Accounting = (() => { const API = '/pos/api/accounting'; function token() { return localStorage.getItem('pos_token') || ''; } function headers() { return { 'Authorization': `Bearer ${token()}`, 'Content-Type': 'application/json' }; } async function api(path, opts = {}) { const res = await fetch(`${API}${path}`, { headers: headers(), ...opts }); if (!res.ok) { const err = await res.json().catch(() => ({ error: res.statusText })); throw new Error(err.error || 'Request failed'); } return res.json(); } function fmt(n) { return parseFloat(n || 0).toLocaleString('es-MX', { minimumFractionDigits: 2, maximumFractionDigits: 2 }); } // ---- Auth check ---- function checkAuth() { if (!token()) { window.location.href = '/pos/login'; return false; } return true; } // ---- Tab switching (matches design system onclick="switchTab('xxx')") ---- function switchTab(name) { document.querySelectorAll('.tab-btn').forEach(btn => { btn.classList.remove('is-active'); btn.setAttribute('aria-selected', 'false'); }); document.querySelectorAll('.tab-panel').forEach(p => p.classList.remove('is-active')); // Activate button const tabBtn = document.querySelector(`.tab-btn[onclick*="'${name}'"]`); if (tabBtn) { tabBtn.classList.add('is-active'); tabBtn.setAttribute('aria-selected', 'true'); } // Activate panel const panel = document.getElementById(`panel-${name}`); if (panel) panel.classList.add('is-active'); // Load data if (name === 'cxc') loadAging(); if (name === 'cxp') loadAccountsPayable(); if (name === 'balance') loadBalanceSheet(); if (name === 'resultados') loadIncomeStatement(); if (name === 'flujo') loadCashFlow(); if (name === 'conciliacion') loadReconciliation(); if (name === 'cierre') loadPeriodClose(); } // ---- Badge helper ---- function statusBadge(status, label) { const map = { pending: 'badge--pending', vigente: 'badge--pending', overdue: 'badge--overdue', vencida: 'badge--overdue', partial: 'badge--partial', parcial: 'badge--partial', ok: 'badge--ok', pagada: 'badge--ok', open: 'badge--pending', closed: 'badge--ok', }; return `${label || status}`; } // ---- Tab 1: Cuentas por Cobrar (Aging) ---- async function loadAging() { const panel = document.getElementById('panel-cxc'); if (!panel) return; const tbody = panel.querySelector('.data-table tbody'); if (!tbody) return; try { const res = await api('/aging'); const rows = res.data || []; if (!rows.length) { tbody.innerHTML = '
Error: ${e.message}
`; } } // ---- Tab 4: Estado de Resultados ---- async function loadIncomeStatement() { const panel = document.getElementById('panel-resultados'); if (!panel) return; const grid = panel.querySelector('.finance-grid'); if (!grid) return; try { const now = new Date(); const res = await api(`/income-statement?year=${now.getFullYear()}&month=${now.getMonth() + 1}`); const card = grid.querySelector('.finance-card'); if (!card) return; let html = `Error: ${e.message}
`; } } // ---- Tab 5: Flujo de Efectivo ---- async function loadCashFlow() { // Flujo de Efectivo currently has no dedicated API endpoint, keep demo data // Future: wire to /pos/api/accounting/cash-flow } // ---- Tab 6: Conciliacion Bancaria ---- async function loadReconciliation() { // Bank reconciliation currently has no dedicated API endpoint, keep demo data // Future: wire to /pos/api/accounting/reconciliation } // ---- Tab 7: Cierre de Mes ---- async function loadPeriodClose() { const panel = document.getElementById('panel-cierre'); if (!panel) return; // Wire the "Ejecutar Cierre" button const closeBtn = panel.querySelector('.btn--primary'); if (closeBtn && !closeBtn.dataset.wired) { closeBtn.dataset.wired = 'true'; closeBtn.addEventListener('click', async () => { const now = new Date(); const year = now.getFullYear(); const month = now.getMonth() + 1; if (!confirm(`Cerrar periodo ${month}/${year}? Esta accion no se puede revertir.`)) return; try { await api('/periods/close', { method: 'POST', body: JSON.stringify({ year, month }), }); alert('Periodo cerrado exitosamente.'); } catch (e) { alert('Error: ' + e.message); } }); } } // ---- Summary cards update ---- async function loadSummaryCards() { const cards = document.querySelectorAll('.summary-card'); if (cards.length < 4) return; try { // Load trial balance for overall numbers const now = new Date(); const tb = await api(`/trial-balance?year=${now.getFullYear()}&month=${now.getMonth() + 1}`); // The summary cards will keep their structure, just update values if API returns data // This is best-effort; if API doesn't support summary data, demo values remain } catch (_) { // Non-critical, keep demo values } } // ---- Helper: update tab badge counts ---- function updateBadgeCount(tabName, count) { const btn = document.querySelector(`.tab-btn[onclick*="'${tabName}'"]`); if (!btn) return; const badge = btn.querySelector('.tab-btn__badge'); if (badge) badge.textContent = count; } // ---- Clock ---- function startClock() { const el = document.getElementById('live-clock'); if (!el) return; const tick = () => { const now = new Date(); el.textContent = now.toLocaleTimeString('es-MX', { hour: '2-digit', minute: '2-digit' }); }; tick(); setInterval(tick, 1000); } // ---- Init ---- function init() { if (!checkAuth()) return; startClock(); loadSummaryCards(); // Load initial tab data (cxc is active by default) loadAging(); } document.addEventListener('DOMContentLoaded', init); // Expose switchTab globally for onclick handlers in HTML window.switchTab = switchTab; return { switchTab, loadAging, loadAccountsPayable, loadBalanceSheet, loadIncomeStatement, loadCashFlow, loadReconciliation, loadPeriodClose, }; })();