// /home/Autopartes/pos/static/js/invoicing.js // Invoicing module: CFDI queue management, cancel, PDF const Invoicing = (() => { const API = '/pos/api/invoicing'; 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 }); } function badgeClass(status) { return { pending: 'badge-pending', sending: 'badge-sending', stamped: 'badge-stamped', failed: 'badge-failed', cancelled: 'badge-cancelled', }[status] || ''; } function badgeLabel(status) { return { pending: 'Pendiente', sending: 'Enviando', stamped: 'Timbrado', failed: 'Fallido', cancelled: 'Cancelado', }[status] || status; } // ─── Queue List ──────────────────────────────── async function loadQueue() { try { const status = document.getElementById('filter-status').value; const type = document.getElementById('filter-type').value; let qs = '?per_page=50'; if (status) qs += `&status=${status}`; if (type) qs += `&type=${type}`; const res = await api(`/queue${qs}`); renderQueue(res.data || []); updateStats(res.data || []); } catch (e) { document.getElementById('queue-list').innerHTML = `

Error: ${e.message}

`; } } function updateStats(items) { const counts = { pending: 0, sending: 0, stamped: 0, failed: 0, cancelled: 0 }; items.forEach(i => { if (counts[i.status] !== undefined) counts[i.status]++; }); document.getElementById('queue-stats').innerHTML = `
${counts.pending}
Pendientes
${counts.sending}
Enviando
${counts.stamped}
Timbrados
${counts.failed}
Fallidos
${counts.cancelled}
Cancelados
`; } function renderQueue(items) { const container = document.getElementById('queue-list'); if (!items.length) { container.innerHTML = '

No hay CFDIs en la cola.

'; return; } let html = ``; for (const item of items) { const uuid = item.uuid_fiscal ? `${item.uuid_fiscal.substring(0, 8)}...` : '-'; html += ``; } html += '
#VentaTipoFolio UUIDEstadoReintentosFechaAcciones
${item.id} #${item.sale_id} ${item.type} ${item.provisional_folio || '-'} ${uuid} ${badgeLabel(item.status)} ${item.retry_count || 0} ${item.created_at ? new Date(item.created_at).toLocaleDateString('es-MX') : ''} ${item.status === 'stamped' ? `` : ''} ${item.sale_id ? `PDF` : ''}
'; container.innerHTML = html; } // ─── Detail ──────────────────────────────────── async function showDetail(cfdiId) { try { const item = await api(`/queue/${cfdiId}`); let html = `

CFDI #${item.id}

#${item.sale_id}
${item.type}
${badgeLabel(item.status)}
${item.provisional_folio || '-'}
${item.uuid_fiscal || '-'}
${item.retry_count}
${item.created_at || '-'}
${item.stamped_at || '-'}
`; if (item.error_message) { html += `

Error: ${item.error_message}

`; } if (item.cancel_motive) { html += `

Motivo cancelacion: ${item.cancel_motive}

`; } // XML preview const xml = item.xml_signed || item.xml_unsigned; if (xml) { html += `

XML

${escapeHtml(xml)}
`; } document.getElementById('detail-content').innerHTML = html; document.getElementById('detail-modal').classList.add('active'); } catch (e) { alert('Error: ' + e.message); } } function escapeHtml(text) { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } // ─── Process Queue ───────────────────────────── async function processQueue() { if (!confirm('Procesar todos los CFDIs pendientes?')) return; try { const result = await api('/queue/process', { method: 'POST' }); alert(`Procesados: ${result.processed}, Timbrados: ${result.stamped}, Fallidos: ${result.failed}`); loadQueue(); } catch (e) { alert('Error: ' + e.message); } } // ─── Cancel ──────────────────────────────────── function showCancelModal(cfdiId) { document.getElementById('cancel-cfdi-id').value = cfdiId; document.getElementById('cancel-motive').value = ''; document.getElementById('cancel-replacement-uuid').value = ''; document.getElementById('replacement-uuid-group').style.display = 'none'; document.getElementById('cancel-modal').classList.add('active'); } function onMotiveChange() { const motive = document.getElementById('cancel-motive').value; document.getElementById('replacement-uuid-group').style.display = motive === '01' ? 'block' : 'none'; } async function confirmCancel() { const cfdiId = document.getElementById('cancel-cfdi-id').value; const motive = document.getElementById('cancel-motive').value; const replacementUuid = document.getElementById('cancel-replacement-uuid').value; if (!motive) { alert('Selecciona un motivo de cancelacion.'); return; } if (motive === '01' && !replacementUuid) { alert('UUID sustituto requerido para motivo 01.'); return; } if (!confirm('Confirmar cancelacion ante el SAT?')) return; try { const body = { motive }; if (replacementUuid) body.replacement_uuid = replacementUuid; await api(`/cancel/${cfdiId}`, { method: 'POST', body: JSON.stringify(body) }); closeModal('cancel-modal'); loadQueue(); alert('CFDI cancelado exitosamente.'); } catch (e) { alert('Error: ' + e.message); } } // ─── Modal helpers ───────────────────────────── function closeModal(id) { document.getElementById(id).classList.remove('active'); } // ─── Init ────────────────────────────────────── document.addEventListener('DOMContentLoaded', () => { loadQueue(); }); return { loadQueue, processQueue, showDetail, showCancelModal, onMotiveChange, confirmCancel, closeModal, }; })();