// /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); // ---- Exportar placeholder ---- function exportarContabilidad() { // Find the first visible table in the active accounting tab and export as CSV var tables = document.querySelectorAll('table'); var table = null; for (var i = 0; i < tables.length; i++) { if (tables[i].offsetParent !== null && tables[i].querySelector('tbody tr')) { table = tables[i]; break; } } if (!table) { alert('No hay datos para exportar en la vista actual.'); return; } var rows = []; var ths = table.querySelectorAll('thead th'); if (ths.length) { rows.push(Array.from(ths).map(function(th) { return '"' + th.textContent.trim().replace(/"/g, '""') + '"'; }).join(',')); } table.querySelectorAll('tbody tr').forEach(function(tr) { var cells = tr.querySelectorAll('td'); rows.push(Array.from(cells).map(function(td) { return '"' + td.textContent.trim().replace(/"/g, '""') + '"'; }).join(',')); }); if (rows.length <= 1) { alert('Sin datos para exportar.'); return; } var csv = rows.join('\n'); var blob = new Blob(['\uFEFF' + csv], { type: 'text/csv;charset=utf-8;' }); var url = URL.createObjectURL(blob); var a = document.createElement('a'); a.href = url; a.download = 'contabilidad_nexus_' + new Date().toISOString().slice(0, 10) + '.csv'; a.click(); URL.revokeObjectURL(url); } // ---- Nueva Poliza modal ---- function showNewEntryModal() { const overlay = document.getElementById('newEntryModalOverlay'); if (!overlay) return; // Set default date to today const dateInput = overlay.querySelector('#entryDate'); if (dateInput && !dateInput.value) { dateInput.value = new Date().toISOString().slice(0, 10); } document.getElementById('entryResult').innerHTML = ''; overlay.style.display = 'flex'; } function closeNewEntryModal() { const overlay = document.getElementById('newEntryModalOverlay'); if (overlay) overlay.style.display = 'none'; } function addEntryLine() { const container = document.getElementById('entryLines'); if (!container) return; const line = document.createElement('div'); line.className = 'entry-line'; line.style.cssText = 'display:grid;grid-template-columns:2fr 1fr 1fr auto;gap:var(--space-2);margin-bottom:var(--space-2);align-items:center;'; line.innerHTML = '' + '' + '' + ''; container.appendChild(line); } async function submitNewEntry() { const date = document.getElementById('entryDate').value; const type = document.getElementById('entryType').value; const description = document.getElementById('entryDescription').value.trim(); const resultEl = document.getElementById('entryResult'); if (!date || !description) { resultEl.innerHTML = 'Fecha y descripcion son obligatorios.'; return; } const lines = []; document.querySelectorAll('#entryLines .entry-line').forEach(row => { const account = row.querySelector('.entry-account').value.trim(); const debit = parseFloat(row.querySelector('.entry-debit').value) || 0; const credit = parseFloat(row.querySelector('.entry-credit').value) || 0; if (account && (debit || credit)) { lines.push({ account, debit, credit }); } }); if (!lines.length) { resultEl.innerHTML = 'Agregue al menos una partida.'; return; } try { await api('/entries', { method: 'POST', body: JSON.stringify({ date, type, description, lines }), }); resultEl.innerHTML = 'Poliza creada exitosamente.'; setTimeout(() => closeNewEntryModal(), 1200); } catch (e) { resultEl.innerHTML = 'Error: ' + e.message + ''; } } // Expose switchTab globally for onclick handlers in HTML window.switchTab = switchTab; window.exportarContabilidad = exportarContabilidad; window.showNewEntryModal = showNewEntryModal; window.closeNewEntryModal = closeNewEntryModal; window.addEntryLine = addEntryLine; window.submitNewEntry = submitNewEntry; return { switchTab, loadAging, loadAccountsPayable, loadBalanceSheet, loadIncomeStatement, loadCashFlow, loadReconciliation, loadPeriodClose, exportarContabilidad, showNewEntryModal, closeNewEntryModal, addEntryLine, submitNewEntry, }; })();