feat(pos/facturapi): finalize Horux-to-Facturapi migration
- Normalize Facturapi key/org_id resolution (supports both cfdi_ prefixed tenant_config keys and short names used by invoicing_bp). - Add CSD upload end-to-end (backend + frontend). - Add helper scripts: setup_facturapi_orgs.py and check_facturapi_tenants.py. - Add 20 unit tests with mocks (pos/tests/test_facturapi_service.py). - Add CI workflow for lint + console tests on Python 3.11/3.13. - Add pyproject.toml and requirements-dev.txt with ruff/pytest config. - Update FASES_IMPLEMENTADAS.md with FASE 8 documentation. Tests: 81 passing (61 console + 20 Facturapi).
This commit is contained in:
@@ -329,6 +329,73 @@ const Invoicing = (() => {
|
||||
}
|
||||
}
|
||||
|
||||
function resetCsdForm() {
|
||||
document.getElementById('csd-form').reset();
|
||||
document.getElementById('csd-cer-label').textContent = 'Subir certificado .cer';
|
||||
document.getElementById('csd-key-label').textContent = 'Subir llave privada .key';
|
||||
}
|
||||
|
||||
function updateFileLabels() {
|
||||
const cer = document.getElementById('csd-cer');
|
||||
const key = document.getElementById('csd-key');
|
||||
if (cer && cer.files.length) {
|
||||
document.getElementById('csd-cer-label').textContent = cer.files[0].name;
|
||||
}
|
||||
if (key && key.files.length) {
|
||||
document.getElementById('csd-key-label').textContent = key.files[0].name;
|
||||
}
|
||||
}
|
||||
|
||||
async function uploadCsd(btn) {
|
||||
if (!btn) return;
|
||||
const cer = document.getElementById('csd-cer');
|
||||
const key = document.getElementById('csd-key');
|
||||
const password = document.getElementById('contrasena-csd').value.trim();
|
||||
|
||||
if (!cer || !cer.files.length || !key || !key.files.length) {
|
||||
alert('Selecciona el archivo .cer y .key');
|
||||
return;
|
||||
}
|
||||
if (!password) {
|
||||
alert('Escribe la contraseña del CSD');
|
||||
return;
|
||||
}
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append('certificate', cer.files[0]);
|
||||
formData.append('private_key', key.files[0]);
|
||||
formData.append('password', password);
|
||||
|
||||
btn.disabled = true;
|
||||
const originalText = btn.innerHTML;
|
||||
btn.textContent = 'Subiendo...';
|
||||
try {
|
||||
const res = await fetch(`${API}/facturapi/csd`, {
|
||||
method: 'POST',
|
||||
headers: { 'Authorization': `Bearer ${token()}` },
|
||||
body: formData,
|
||||
});
|
||||
const data = await res.json().catch(() => ({ error: res.statusText }));
|
||||
if (!res.ok) throw new Error(data.error || 'Upload failed');
|
||||
alert('CSD actualizado correctamente');
|
||||
resetCsdForm();
|
||||
loadFacturapiStatus();
|
||||
} catch (e) {
|
||||
alert('Error al subir CSD: ' + e.message);
|
||||
} finally {
|
||||
btn.disabled = false;
|
||||
btn.innerHTML = originalText;
|
||||
}
|
||||
}
|
||||
|
||||
// Wire file input change listeners
|
||||
setTimeout(() => {
|
||||
const cer = document.getElementById('csd-cer');
|
||||
const key = document.getElementById('csd-key');
|
||||
if (cer) cer.addEventListener('change', updateFileLabels);
|
||||
if (key) key.addEventListener('change', updateFileLabels);
|
||||
}, 0);
|
||||
|
||||
// ---- Detail modal (uses modalDetalleOverlay) ----
|
||||
async function showDetail(cfdiId) {
|
||||
const overlay = document.getElementById('modalDetalleOverlay');
|
||||
@@ -612,6 +679,7 @@ const Invoicing = (() => {
|
||||
showDetail, showCancelModal, confirmCancel, processQueue,
|
||||
showNewInvoiceModal, closeNewInvoiceModal, submitNewInvoice, notaCreditoPlaceholder,
|
||||
openGlobalInvoiceModal, previewGlobalInvoice, generateGlobalInvoice, setupFacturapi,
|
||||
uploadCsd, resetCsdForm,
|
||||
};
|
||||
// Register Cmd+K items
|
||||
if (typeof registerCmdKItem === "function") {
|
||||
|
||||
Reference in New Issue
Block a user