# US-MGN-004-005-003: Cancelar Factura de Cliente **RF Asociado:** [RF-MGN-004-005](../../02-modelado/requerimientos-funcionales/mgn-004/RF-MGN-004-005-gestión-de-facturas-de-cliente.md) **Módulo:** MGN-004 - Financiero Básico **Epic:** Facturas de Cliente **Prioridad:** P0 (MVP) **Story Points:** 3 **Sprint:** Sprint 10 **Estado:** Ready for Development **Fecha:** 2025-11-24 --- ## User Story **Como** contador, **Quiero** cancelar facturas de cliente validadas, **Para** anular facturas erróneas creando asiento de reversión automático. --- ## Descripción Detallada Cancelar factura genera asiento de reversión que anula el asiento contable original. Estado cambia a 'cancelled'. Factura queda cancelada permanentemente (no se puede reactivar). --- ## Criterios de Aceptación ### Escenario 1: Cancelar factura open **Dado que** factura tiene state='open', **Cuando** POST /customer-invoices/10/cancel con reason="Error en cliente", **Entonces** state='cancelled', asiento reversión creado y posteado, cancelled_date y cancelled_by asignados. ### Escenario 2: Generar asiento de reversión **Dado que** factura tiene asiento original con débito 1210 y créditos 1000+210, **Cuando** cancelo factura, **Entonces** asiento reversión invierte débitos ↔ créditos. ### Escenario 3: No cancelar factura ya cancelled **Dado que** state='cancelled', **Cuando** intento cancelar nuevamente, **Entonces** error 409 "Factura ya está cancelada". ### Escenario 4: No cancelar factura paid **Dado que** state='paid' (tiene pagos), **Cuando** intento cancelar, **Entonces** error 400 "No se puede cancelar factura pagada. Primero reversa los pagos". --- ## Reglas de Negocio - **RN-1:** Solo facturas open pueden cancelarse. - **RN-2:** Facturas paid requieren reversar pagos primero. - **RN-3:** Se genera asiento de reversión automático. - **RN-4:** Motivo de cancelación es obligatorio. - **RN-5:** Estado final 'cancelled' es irreversible. --- ## Tareas Técnicas ### Backend - [ ] POST /api/v1/financial/customer-invoices/:id/cancel - [ ] DTO: CancelInvoiceDto (reason required) - [ ] Service: CustomerInvoiceService.cancel() - [ ] Validar state='open', no paid - [ ] Crear asiento de reversión - [ ] Actualizar state='cancelled', cancelled_date, cancelled_by - [ ] Unit tests (6 test cases) - [ ] Integration tests (5 test cases) ### Frontend - [ ] Botón "Cancelar Factura" (si open y no paid) - [ ] Modal: CancelInvoiceModal.tsx (reason required) - [ ] Badge: Cancelada - [ ] E2E test: cancel invoice ### Database - [ ] Campo: invoices.cancelled_date, cancelled_by, cancel_reason --- ## Estimación: 6 horas = 3 SP --- ## References - [RF-MGN-004-005](../../02-modelado/requerimientos-funcionales/mgn-004/RF-MGN-004-005-gestión-de-facturas-de-cliente.md)