2026-02-03 15:26:05 +00:00
|
|
|
|
import { getInvoiceById, updateInvoice, validateInvoice, addInvoiceLine } from '../services/invoices.js';
|
|
|
|
|
|
|
|
|
|
|
|
export function InvoiceModal(invoiceId, onClose, onUpdate) {
|
|
|
|
|
|
const modal = document.createElement('div');
|
|
|
|
|
|
modal.className = 'modal-overlay';
|
|
|
|
|
|
|
|
|
|
|
|
let invoice = null;
|
|
|
|
|
|
let isLoading = true;
|
|
|
|
|
|
|
|
|
|
|
|
// Formatear fecha para input type="date"
|
|
|
|
|
|
const formatDateForInput = (dateString) => {
|
|
|
|
|
|
if (!dateString) return '';
|
|
|
|
|
|
const date = new Date(dateString);
|
|
|
|
|
|
return date.toISOString().split('T')[0];
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// Formatear fecha para mostrar
|
|
|
|
|
|
const formatDate = (dateString) => {
|
|
|
|
|
|
if (!dateString) return '-';
|
|
|
|
|
|
const date = new Date(dateString);
|
|
|
|
|
|
return date.toLocaleDateString('es-ES');
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// Formatear moneda
|
|
|
|
|
|
const formatCurrency = (amount) => {
|
|
|
|
|
|
return new Intl.NumberFormat('es-ES', {
|
|
|
|
|
|
style: 'currency',
|
|
|
|
|
|
currency: 'EUR'
|
|
|
|
|
|
}).format(amount || 0);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// Obtener texto de estado
|
|
|
|
|
|
const getStatusText = (status) => {
|
|
|
|
|
|
const statusTextMap = {
|
|
|
|
|
|
'draft': 'Borrador',
|
|
|
|
|
|
'validated': 'Validada',
|
|
|
|
|
|
'paid': 'Pagada',
|
|
|
|
|
|
'unpaid': 'Impagada',
|
|
|
|
|
|
'canceled': 'Cancelada'
|
|
|
|
|
|
};
|
|
|
|
|
|
return statusTextMap[status] || status;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// Renderizar contenido del modal
|
|
|
|
|
|
const renderContent = () => {
|
|
|
|
|
|
const modalContent = modal.querySelector('.modal-content');
|
|
|
|
|
|
|
|
|
|
|
|
if (isLoading) {
|
|
|
|
|
|
modalContent.innerHTML = `
|
|
|
|
|
|
<div class="modal-header">
|
|
|
|
|
|
<h2>Cargando...</h2>
|
|
|
|
|
|
<button class="btn-close">×</button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="modal-body">
|
|
|
|
|
|
<div class="loading">Cargando detalles de la factura...</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
`;
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!invoice) {
|
|
|
|
|
|
modalContent.innerHTML = `
|
|
|
|
|
|
<div class="modal-header">
|
|
|
|
|
|
<h2>Error</h2>
|
|
|
|
|
|
<button class="btn-close">×</button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="modal-body">
|
|
|
|
|
|
<div class="error">No se pudo cargar la factura</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
`;
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const isDraft = invoice.status === 'draft';
|
|
|
|
|
|
const canEdit = isDraft || invoice.status === 'validated' || invoice.status === 'unpaid';
|
|
|
|
|
|
|
|
|
|
|
|
modalContent.innerHTML = `
|
|
|
|
|
|
<div class="modal-header">
|
|
|
|
|
|
<div class="modal-title">
|
|
|
|
|
|
<h2>${invoice.number || 'Nueva Factura'}</h2>
|
|
|
|
|
|
<span class="status-badge status-${invoice.status}">
|
|
|
|
|
|
${getStatusText(invoice.status)}
|
|
|
|
|
|
</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<button class="btn-close">×</button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div class="modal-body">
|
|
|
|
|
|
<form id="invoice-form">
|
|
|
|
|
|
<!-- Información básica -->
|
|
|
|
|
|
<div class="form-section">
|
|
|
|
|
|
<h3>Información de la Factura</h3>
|
|
|
|
|
|
|
|
|
|
|
|
<div class="form-row">
|
|
|
|
|
|
<div class="form-group">
|
|
|
|
|
|
<label>Número de Factura</label>
|
|
|
|
|
|
<input
|
|
|
|
|
|
type="text"
|
|
|
|
|
|
name="number"
|
|
|
|
|
|
value="${invoice.number || ''}"
|
|
|
|
|
|
disabled
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div class="form-group">
|
|
|
|
|
|
<label>Fecha</label>
|
|
|
|
|
|
<input
|
|
|
|
|
|
type="date"
|
|
|
|
|
|
name="date"
|
|
|
|
|
|
value="${formatDateForInput(invoice.date)}"
|
|
|
|
|
|
disabled
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div class="form-row">
|
|
|
|
|
|
<div class="form-group">
|
|
|
|
|
|
<label>Fecha de Vencimiento</label>
|
|
|
|
|
|
<input
|
|
|
|
|
|
type="date"
|
|
|
|
|
|
name="expireDate"
|
|
|
|
|
|
value="${formatDateForInput(invoice.expireDate)}"
|
|
|
|
|
|
${!canEdit ? 'disabled' : ''}
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div class="form-group">
|
|
|
|
|
|
<label>Cliente ID</label>
|
|
|
|
|
|
<input
|
|
|
|
|
|
type="text"
|
|
|
|
|
|
name="clientId"
|
|
|
|
|
|
value="${invoice.clientId || ''}"
|
|
|
|
|
|
disabled
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div class="form-row">
|
|
|
|
|
|
<div class="form-group full-width">
|
|
|
|
|
|
<label>Nota Pública</label>
|
2026-02-03 15:55:40 +00:00
|
|
|
|
<textarea name="note_public" rows="3">${invoice.note_public || ''}</textarea>
|
2026-02-03 15:26:05 +00:00
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div class="form-row">
|
|
|
|
|
|
<div class="form-group full-width">
|
|
|
|
|
|
<label>Nota Privada</label>
|
2026-02-03 15:55:40 +00:00
|
|
|
|
<textarea name="note_private" rows="3">${invoice.note_private || ''}</textarea>
|
2026-02-03 15:26:05 +00:00
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- Líneas de factura -->
|
|
|
|
|
|
<div class="form-section">
|
|
|
|
|
|
<div class="section-header">
|
|
|
|
|
|
<h3>Líneas de Factura</h3>
|
|
|
|
|
|
${isDraft ? '<button type="button" class="btn-add-line btn-small">+ Añadir Línea</button>' : ''}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
${isDraft ? `
|
|
|
|
|
|
<div class="add-line-form" style="display: none;">
|
|
|
|
|
|
<div class="form-row">
|
|
|
|
|
|
<div class="form-group full-width">
|
|
|
|
|
|
<label>Descripción</label>
|
|
|
|
|
|
<input type="text" id="line-description" placeholder="Descripción del producto/servicio" />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="form-row">
|
|
|
|
|
|
<div class="form-group">
|
|
|
|
|
|
<label>Cantidad</label>
|
|
|
|
|
|
<input type="number" id="line-quantity" value="1" min="0.01" step="0.01" />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="form-group">
|
|
|
|
|
|
<label>Precio Unitario (€)</label>
|
|
|
|
|
|
<input type="number" id="line-price" value="0" min="0" step="0.01" />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="form-row">
|
|
|
|
|
|
<div class="form-group">
|
|
|
|
|
|
<label>IVA (%)</label>
|
|
|
|
|
|
<input type="number" id="line-tax" value="21" min="0" max="100" step="0.01" />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="form-group" style="display: flex; align-items: flex-end; gap: 0.5rem;">
|
|
|
|
|
|
<button type="button" class="btn-save-line btn-small btn-success">Guardar Línea</button>
|
|
|
|
|
|
<button type="button" class="btn-cancel-line btn-small btn-cancel">Cancelar</button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
` : ''}
|
|
|
|
|
|
|
|
|
|
|
|
<div class="invoice-lines">
|
|
|
|
|
|
<table class="lines-table">
|
|
|
|
|
|
<thead>
|
|
|
|
|
|
<tr>
|
|
|
|
|
|
<th>Descripción</th>
|
|
|
|
|
|
<th>Cantidad</th>
|
|
|
|
|
|
<th>Precio Unitario</th>
|
|
|
|
|
|
<th>IVA %</th>
|
|
|
|
|
|
<th>Total</th>
|
|
|
|
|
|
</tr>
|
|
|
|
|
|
</thead>
|
|
|
|
|
|
<tbody>
|
|
|
|
|
|
${invoice.lines && invoice.lines.length > 0
|
|
|
|
|
|
? invoice.lines.map(line => `
|
|
|
|
|
|
<tr>
|
|
|
|
|
|
<td>${line.description || ''}</td>
|
|
|
|
|
|
<td>${line.quantity || 0}</td>
|
|
|
|
|
|
<td>${formatCurrency(line.unitPrice)}</td>
|
|
|
|
|
|
<td>${line.taxRate || 0}%</td>
|
|
|
|
|
|
<td>${formatCurrency(line.total)}</td>
|
|
|
|
|
|
</tr>
|
|
|
|
|
|
`).join('')
|
|
|
|
|
|
: '<tr><td colspan="5" class="no-lines">No hay líneas en esta factura</td></tr>'
|
|
|
|
|
|
}
|
|
|
|
|
|
</tbody>
|
|
|
|
|
|
</table>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- Totales -->
|
|
|
|
|
|
<div class="invoice-totals">
|
|
|
|
|
|
<div class="total-row">
|
|
|
|
|
|
<span>Total:</span>
|
|
|
|
|
|
<strong>${formatCurrency(invoice.total)}</strong>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="total-row pending">
|
|
|
|
|
|
<span>Pendiente:</span>
|
|
|
|
|
|
<strong>${formatCurrency(invoice.remainToPay)}</strong>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</form>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div class="modal-footer">
|
|
|
|
|
|
<div class="footer-actions-left">
|
2026-02-03 15:55:40 +00:00
|
|
|
|
${isDraft ? '<button type="button" class="btn-validate btn-success"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="vertical-align: middle; margin-right: 4px;"><polyline points="20 6 9 17 4 12"/></svg>Validar Factura</button>' : ''}
|
2026-02-03 15:26:05 +00:00
|
|
|
|
</div>
|
|
|
|
|
|
<div class="footer-actions-right">
|
|
|
|
|
|
<button type="button" class="btn-cancel-modal">Cancelar</button>
|
|
|
|
|
|
${canEdit ? '<button type="button" class="btn-save btn-primary">Guardar Cambios</button>' : ''}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
`;
|
|
|
|
|
|
|
|
|
|
|
|
attachEventListeners();
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// Adjuntar event listeners
|
|
|
|
|
|
const attachEventListeners = () => {
|
|
|
|
|
|
// Botón cerrar
|
|
|
|
|
|
const closeBtn = modal.querySelector('.btn-close');
|
|
|
|
|
|
closeBtn?.addEventListener('click', handleClose);
|
|
|
|
|
|
|
|
|
|
|
|
// Botón cancelar modal
|
|
|
|
|
|
const cancelBtn = modal.querySelector('.btn-cancel-modal');
|
|
|
|
|
|
cancelBtn?.addEventListener('click', handleClose);
|
|
|
|
|
|
|
|
|
|
|
|
// Botón guardar
|
|
|
|
|
|
const saveBtn = modal.querySelector('.btn-save');
|
|
|
|
|
|
saveBtn?.addEventListener('click', handleSave);
|
|
|
|
|
|
|
|
|
|
|
|
// Botón validar
|
|
|
|
|
|
const validateBtn = modal.querySelector('.btn-validate');
|
|
|
|
|
|
validateBtn?.addEventListener('click', handleValidate);
|
|
|
|
|
|
|
|
|
|
|
|
// Botón añadir línea
|
|
|
|
|
|
const addLineBtn = modal.querySelector('.btn-add-line');
|
|
|
|
|
|
addLineBtn?.addEventListener('click', toggleAddLineForm);
|
|
|
|
|
|
|
|
|
|
|
|
// Botón guardar línea
|
|
|
|
|
|
const saveLineBtn = modal.querySelector('.btn-save-line');
|
|
|
|
|
|
saveLineBtn?.addEventListener('click', handleSaveLine);
|
|
|
|
|
|
|
|
|
|
|
|
// Botón cancelar línea
|
|
|
|
|
|
const cancelLineBtn = modal.querySelector('.btn-cancel-line');
|
|
|
|
|
|
cancelLineBtn?.addEventListener('click', () => {
|
|
|
|
|
|
const form = modal.querySelector('.add-line-form');
|
|
|
|
|
|
if (form) form.style.display = 'none';
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// Click fuera del modal
|
|
|
|
|
|
modal.addEventListener('click', (e) => {
|
|
|
|
|
|
if (e.target === modal) {
|
|
|
|
|
|
handleClose();
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// Manejar cierre
|
|
|
|
|
|
const handleClose = () => {
|
|
|
|
|
|
modal.remove();
|
|
|
|
|
|
if (onClose) onClose();
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// Manejar guardado
|
|
|
|
|
|
const handleSave = async () => {
|
|
|
|
|
|
const form = modal.querySelector('#invoice-form');
|
|
|
|
|
|
const formData = new FormData(form);
|
|
|
|
|
|
|
2026-02-03 15:55:40 +00:00
|
|
|
|
// La API espera camelCase en el PUT (notePublic/notePrivate)
|
2026-02-03 15:26:05 +00:00
|
|
|
|
const data = {
|
|
|
|
|
|
number: formData.get('number') || undefined,
|
|
|
|
|
|
expireDate: formData.get('expireDate') || undefined,
|
2026-02-03 15:55:40 +00:00
|
|
|
|
notePublic: formData.get('note_public') || undefined,
|
|
|
|
|
|
notePrivate: formData.get('note_private') || undefined
|
2026-02-03 15:26:05 +00:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// Filtrar valores undefined
|
|
|
|
|
|
Object.keys(data).forEach(key => {
|
|
|
|
|
|
if (data[key] === undefined || data[key] === '') {
|
|
|
|
|
|
delete data[key];
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
const saveBtn = modal.querySelector('.btn-save');
|
|
|
|
|
|
saveBtn.disabled = true;
|
|
|
|
|
|
saveBtn.textContent = 'Guardando...';
|
|
|
|
|
|
|
|
|
|
|
|
await updateInvoice(invoiceId, data);
|
|
|
|
|
|
|
|
|
|
|
|
alert('Factura actualizada correctamente');
|
|
|
|
|
|
if (onUpdate) onUpdate();
|
|
|
|
|
|
handleClose();
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('Error al guardar:', error);
|
|
|
|
|
|
alert('Error al guardar la factura: ' + error.message);
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
const saveBtn = modal.querySelector('.btn-save');
|
|
|
|
|
|
if (saveBtn) {
|
|
|
|
|
|
saveBtn.disabled = false;
|
|
|
|
|
|
saveBtn.textContent = 'Guardar Cambios';
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// Manejar validación
|
|
|
|
|
|
const handleValidate = async () => {
|
|
|
|
|
|
if (!confirm('¿Estás seguro de que quieres validar esta factura? No podrás editarla completamente después.')) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
const validateBtn = modal.querySelector('.btn-validate');
|
|
|
|
|
|
validateBtn.disabled = true;
|
|
|
|
|
|
validateBtn.textContent = 'Validando...';
|
|
|
|
|
|
|
|
|
|
|
|
await validateInvoice(invoiceId);
|
|
|
|
|
|
|
|
|
|
|
|
alert('Factura validada correctamente');
|
|
|
|
|
|
if (onUpdate) onUpdate();
|
|
|
|
|
|
|
|
|
|
|
|
// Recargar la factura para mostrar el nuevo estado
|
|
|
|
|
|
await loadInvoice();
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('Error al validar:', error);
|
|
|
|
|
|
alert('Error al validar la factura: ' + error.message);
|
|
|
|
|
|
|
|
|
|
|
|
const validateBtn = modal.querySelector('.btn-validate');
|
|
|
|
|
|
if (validateBtn) {
|
|
|
|
|
|
validateBtn.disabled = false;
|
2026-02-03 15:55:40 +00:00
|
|
|
|
validateBtn.innerHTML = '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="vertical-align: middle; margin-right: 4px;"><polyline points="20 6 9 17 4 12"/></svg>Validar Factura';
|
2026-02-03 15:26:05 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// Mostrar/ocultar formulario de añadir línea
|
|
|
|
|
|
const toggleAddLineForm = () => {
|
|
|
|
|
|
const form = modal.querySelector('.add-line-form');
|
|
|
|
|
|
if (form) {
|
|
|
|
|
|
const isVisible = form.style.display !== 'none';
|
|
|
|
|
|
form.style.display = isVisible ? 'none' : 'block';
|
|
|
|
|
|
|
|
|
|
|
|
if (!isVisible) {
|
|
|
|
|
|
// Limpiar campos
|
|
|
|
|
|
modal.querySelector('#line-description').value = '';
|
|
|
|
|
|
modal.querySelector('#line-quantity').value = '1';
|
|
|
|
|
|
modal.querySelector('#line-price').value = '0';
|
|
|
|
|
|
modal.querySelector('#line-tax').value = '21';
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// Guardar nueva línea
|
|
|
|
|
|
const handleSaveLine = async () => {
|
|
|
|
|
|
const description = modal.querySelector('#line-description').value.trim();
|
|
|
|
|
|
const quantity = parseFloat(modal.querySelector('#line-quantity').value);
|
|
|
|
|
|
const unitPrice = parseFloat(modal.querySelector('#line-price').value);
|
|
|
|
|
|
const taxRate = parseFloat(modal.querySelector('#line-tax').value);
|
|
|
|
|
|
|
|
|
|
|
|
if (!description) {
|
|
|
|
|
|
alert('La descripción es obligatoria');
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!quantity || quantity <= 0) {
|
|
|
|
|
|
alert('La cantidad debe ser mayor que 0');
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (unitPrice < 0) {
|
|
|
|
|
|
alert('El precio no puede ser negativo');
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (taxRate < 0 || taxRate > 100) {
|
|
|
|
|
|
alert('El IVA debe estar entre 0 y 100');
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
const saveBtn = modal.querySelector('.btn-save-line');
|
|
|
|
|
|
saveBtn.disabled = true;
|
|
|
|
|
|
saveBtn.textContent = 'Guardando...';
|
|
|
|
|
|
|
|
|
|
|
|
await addInvoiceLine(invoiceId, {
|
|
|
|
|
|
description,
|
|
|
|
|
|
quantity,
|
|
|
|
|
|
unitPrice,
|
|
|
|
|
|
taxRate
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
alert('Línea añadida correctamente');
|
|
|
|
|
|
toggleAddLineForm();
|
|
|
|
|
|
await loadInvoice();
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('Error al añadir línea:', error);
|
|
|
|
|
|
alert('Error al añadir línea: ' + error.message);
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
const saveBtn = modal.querySelector('.btn-save-line');
|
|
|
|
|
|
if (saveBtn) {
|
|
|
|
|
|
saveBtn.disabled = false;
|
|
|
|
|
|
saveBtn.textContent = 'Guardar Línea';
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// Cargar factura
|
|
|
|
|
|
const loadInvoice = async () => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
isLoading = true;
|
|
|
|
|
|
renderContent();
|
|
|
|
|
|
|
|
|
|
|
|
invoice = await getInvoiceById(invoiceId);
|
|
|
|
|
|
isLoading = false;
|
|
|
|
|
|
renderContent();
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('Error al cargar factura:', error);
|
|
|
|
|
|
isLoading = false;
|
|
|
|
|
|
invoice = null;
|
|
|
|
|
|
renderContent();
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// Inicializar modal
|
|
|
|
|
|
modal.innerHTML = `
|
|
|
|
|
|
<div class="modal-content invoice-modal">
|
|
|
|
|
|
</div>
|
|
|
|
|
|
`;
|
|
|
|
|
|
|
|
|
|
|
|
// Cargar factura
|
|
|
|
|
|
loadInvoice();
|
|
|
|
|
|
|
|
|
|
|
|
return modal;
|
|
|
|
|
|
}
|