# US-MGN-004-003-001: Crear Asiento Contable (Draft) **RF Asociado:** [RF-MGN-004-003](../../02-modelado/requerimientos-funcionales/mgn-004/RF-MGN-004-003-registro-de-asientos-contables.md) **Módulo:** MGN-004 - Financiero Básico **Epic:** Asientos Contables **Prioridad:** P0 (MVP) **Story Points:** 5 **Sprint:** Sprint 8 **Estado:** Ready for Development **Fecha:** 2025-11-24 --- ## User Story **Como** contador, **Quiero** crear asientos contables en estado borrador (draft) con múltiples líneas, **Para** registrar transacciones contables antes de confirmarlas y validarlas. --- ## Descripción Detallada Un asiento contable (journal entry) es el registro de una transacción en el sistema contable. Cada asiento tiene: - Número secuencial generado automáticamente por el journal - Fecha contable (accounting_date) - Journal asociado - Estado: draft (borrador) o posted (publicado) - Referencia (opcional, ej: "Factura #123") - Líneas contables (journal_entry_lines) con débitos y créditos Esta US cubre la creación de asientos en estado draft, permitiendo editar y corregir antes de publicar. --- ## Criterios de Aceptación ### Escenario 1: Crear asiento con líneas válidas **Dado que** soy contador con permisos accounting_user, **Cuando** creo un asiento con journal_id=1, date="2024-01-15", y líneas: - Cuenta 1.1.01.001 (Caja), debit=1000, credit=0 - Cuenta 4.1.01.001 (Ingresos), debit=0, credit=1000, **Entonces** el sistema crea el asiento en estado draft y retorna id, number (ej: "VEN/2024/0001"). ### Escenario 2: Validar suma débitos = créditos **Dado que** intento crear un asiento con total_debit=1000 y total_credit=900, **Cuando** envío el request, **Entonces** el sistema retorna error 400 "Asiento desbalanceado. Suma de débitos (1000) debe igualar suma de créditos (900)". ### Escenario 3: Validar que journal existe y está activo **Dado que** intento crear un asiento con journal_id=999 (no existe), **Cuando** envío el request, **Entonces** el sistema retorna error 404 "Journal no encontrado o inactivo". ### Escenario 4: Validar que cuentas existen **Dado que** una línea tiene account_id=888 (no existe), **Cuando** intento crear el asiento, **Entonces** el sistema retorna error 400 "Cuenta contable 888 no encontrada". ### Escenario 5: Generar número secuencial automático **Dado que** el journal "VEN" tiene secuencia "VEN/2024/{seq}", **Cuando** creo un asiento en el journal VEN, **Entonces** el sistema genera automáticamente number="VEN/2024/0001" (o el siguiente disponible). ### Escenario 6: Permitir múltiples líneas **Dado que** creo un asiento con 5 líneas contables, **Cuando** las sumas de débitos y créditos coinciden, **Entonces** el sistema crea el asiento exitosamente con las 5 líneas asociadas. --- ## Reglas de Negocio - **RN-1:** Suma de débitos debe igualar suma de créditos (asiento balanceado). - **RN-2:** Estado inicial es siempre "draft". No se puede crear directamente en "posted". - **RN-3:** Número de asiento se genera automáticamente usando secuencia del journal. - **RN-4:** Cada línea debe tener account_id válido (cuenta existente y activa). - **RN-5:** Cada línea debe tener debit O credit (no ambos, no ninguno). Al menos uno > 0. - **RN-6:** Fecha contable (accounting_date) no puede ser futura. Máximo fecha actual. - **RN-7:** Journal debe estar activo (active=true). - **RN-8:** Asientos draft pueden editarse y eliminarse libremente. --- ## Tareas Técnicas ### Backend - [ ] Endpoint: `POST /api/v1/financial/journal-entries` - [ ] Service: `JournalEntryService.create(createJournalEntryDto)` - [ ] Service: `JournalEntryService.validateBalance(lines)` - Suma débitos = créditos - [ ] Service: `JournalEntryService.generateEntryNumber(journalId, date)` - Secuencia - [ ] DTO: `CreateJournalEntryDto` (journal_id, date, reference, lines) - [ ] DTO: `JournalEntryLineDto` (account_id, debit, credit, label, partner_id?) - [ ] Validar journal existe y está activo - [ ] Validar cuentas existen y están activas - [ ] Validar que debit >= 0 y credit >= 0 - [ ] Validar que al menos una línea tiene debit > 0 o credit > 0 - [ ] Unit tests (10 test cases) - [ ] Integration tests (8 test cases) - [ ] Swagger docs con ejemplo de request/response ### Frontend - [ ] Componente: `CreateJournalEntryForm.tsx` - [ ] Componente: `JournalEntryLineItems.tsx` (tabla editable de líneas) - [ ] Componente: `AddLineButton.tsx` (agregar línea) - [ ] Página: `CreateJournalEntryPage.tsx` (/financial/journal-entries/create) - [ ] Lógica: Calcular totales de débito/crédito en tiempo real - [ ] Validación: Mostrar warning si débito ≠ crédito - [ ] API client: `journalEntryApi.create(data)` - [ ] Store: `useJournalEntryStore.ts` - [ ] Component tests (6 test cases) - [ ] E2E test: "should create journal entry successfully" ### Database - [ ] Tabla: `financial.journal_entries` (id, company_id, journal_id, number, date, state, reference) - [ ] Tabla: `financial.journal_entry_lines` (id, entry_id, account_id, debit, credit, label, partner_id) - [ ] Índices: idx_journal_entries_journal_id, idx_journal_entries_date, idx_journal_entries_state - [ ] Índices: idx_journal_entry_lines_entry_id, idx_journal_entry_lines_account_id - [ ] RLS policies: company_isolation --- ## Mockups / Wireframes **Formulario Crear Asiento:** ``` ┌─────────────────────────────────────────────────┐ │ Crear Asiento Contable │ ├─────────────────────────────────────────────────┤ │ Journal: [Dropdown: VEN - Ventas] ▼ │ │ Fecha: [Date Picker: 2024-01-15] │ │ Referencia: [Input: Factura #123] (opcional) │ ├─────────────────────────────────────────────────┤ │ Líneas Contables: │ │ ┌───────────────────────────────────────────┐ │ │ │Cuenta │ Partner │Debe │Haber │Etiqueta│ │ │ ├─────────┼─────────┼─────┼──────┼────────┤ │ │ │1.1.01..│ - │1000 │ 0 │Cobro │[×]│ │ │4.1.01..│Cliente A│ 0 │1000 │Venta │[×]│ │ └───────────────────────────────────────────┘ │ │ [+ Agregar Línea] │ ├─────────────────────────────────────────────────┤ │ Total Débito: 1000.00 Total Crédito: 1000.00 │ │ Diferencia: 0.00 ✓ │ ├─────────────────────────────────────────────────┤ │ [Guardar Borrador] [Cancelar] │ └─────────────────────────────────────────────────┘ ``` **Interacciones:** - Al agregar línea: Nueva fila en blanco - Al cambiar débito/crédito: Recalcular totales - Diferencia ≠ 0: Mostrar warning "Asiento desbalanceado" - Al guardar: Validar balance antes de enviar --- ## Casos de Prueba ### Funcionales 1. **TC-001:** Crear asiento balanceado exitosamente 2. **TC-002:** Error por asiento desbalanceado 3. **TC-003:** Error por journal inexistente 4. **TC-004:** Error por cuenta inexistente en línea 5. **TC-005:** Generar número secuencial correcto 6. **TC-006:** Crear asiento con 5 líneas 7. **TC-007:** Error por fecha futura 8. **TC-008:** RLS filtra por empresa ### No Funcionales 1. **Performance:** < 500ms para crear asiento con 10 líneas 2. **Seguridad:** JWT + permiso accounting_user --- ## Dependencias - **US bloqueantes:** - US-MGN-004-001-001 (CRUD Cuentas) - US-MGN-004-002-001 (CRUD Journals) - US-MGN-003-001-001 (CRUD Partners) - Opcional para partner_id - **Módulos:** MGN-003, MGN-004 --- ## Notas de Implementación - Secuencia de número: Usar formato "{journal_code}/{year}/{seq:05d}" ej: "VEN/2024/00001" - Validación de balance: Redondear a 2 decimales para evitar errores por precisión flotante - Líneas: Mínimo 2 líneas requeridas (al menos 1 débito y 1 crédito) - Frontend: Usar React Table o Tanstack Table para edición inline de líneas - Considerar agregar campo `description` al asiento para notas adicionales --- ## Estimación Detallada | Tarea | Estimación | |-------|------------| | Backend | 3 horas | | Frontend | 3 horas | | Testing | 2 horas | | Code Review | 1 hora | | **TOTAL** | **9 horas = 5 SP** | --- ## Definition of Done - [ ] Código backend implementado - [ ] Código frontend implementado - [ ] Unit tests pasando (>80%) - [ ] Integration tests pasando - [ ] E2E tests pasando - [ ] Code review aprobado - [ ] Swagger docs actualizado - [ ] RLS aplicado - [ ] Validación de balance funciona - [ ] Secuencia automática funciona - [ ] Merge a develop - [ ] QA validado - [ ] PO aprobado --- ## Referencias - [RF-MGN-004-003](../../02-modelado/requerimientos-funcionales/mgn-004/RF-MGN-004-003-registro-de-asientos-contables.md) - [ET-BACKEND-MGN-004-003](../../02-modelado/especificaciones-tecnicas/backend/mgn-004/ET-BACKEND-MGN-004-003-registro-de-asientos-contables.md) - [ET-FRONTEND-MGN-004-003](../../02-modelado/especificaciones-tecnicas/frontend/mgn-004/ET-FRONTEND-MGN-004-003-registro-de-asientos-contables.md) - [TRACEABILITY-MGN-004.yaml](../../02-modelado/trazabilidad/TRACEABILITY-MGN-004.yaml) - [Financial Schema DDL](../../02-modelado/database-design/schemas/financial-schema-ddl.sql)