erp-core/docs/05-user-stories/mgn-004/US-MGN-004-003-001-crear-asiento-contable-draft.md

9.9 KiB
Raw Blame History

US-MGN-004-003-001: Crear Asiento Contable (Draft)

RF Asociado: RF-MGN-004-003 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