erp-core/docs/05-user-stories/mgn-004/US-MGN-004-003-002-validar-y-postear-asiento.md

10 KiB

US-MGN-004-003-002: Validar y Postear Asiento Contable

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 validar y publicar (postear) asientos contables en estado draft, Para confirmar que son correctos e inmutables, y que impacten los balances de las cuentas contables.


Descripción Detallada

Esta US implementa la acción de "postear" un asiento contable, cambiando su estado de draft a posted. Una vez publicado:

  • El asiento NO puede modificarse ni eliminarse
  • Los balances de las cuentas contables se actualizan
  • Se registra fecha de publicación y usuario que publicó
  • El número de asiento queda definitivo

El sistema valida antes de postear:

  • Asiento está balanceado (débitos = créditos)
  • Todas las cuentas están activas
  • Journal está activo
  • Fecha contable está dentro del período fiscal abierto

Criterios de Aceptación

Escenario 1: Postear asiento draft exitosamente

Dado que tengo un asiento con id=10, state='draft', balanceado correctamente, Cuando ejecuto POST /api/v1/financial/journal-entries/10/post, Entonces el sistema:

  • Cambia state='posted'
  • Asigna posted_date=now()
  • Asigna posted_by=current_user_id
  • Actualiza balances de cuentas involucradas
  • Retorna asiento actualizado

Escenario 2: Error al postear asiento desbalanceado

Dado que tengo un asiento draft con total_debit=1000 y total_credit=900, Cuando intento postearlo, Entonces el sistema retorna error 400 "No se puede postear asiento desbalanceado".

Escenario 3: No se puede postear asiento ya posted

Dado que tengo un asiento con state='posted', Cuando intento postearlo nuevamente, Entonces el sistema retorna error 409 "El asiento ya está publicado".

Escenario 4: Validar cuentas activas antes de postear

Dado que un asiento draft tiene una línea con cuenta inactiva (active=false), Cuando intento postearlo, Entonces el sistema retorna error 400 "Cuenta [código] está inactiva. No se puede postear".

Escenario 5: Validar journal activo antes de postear

Dado que un asiento draft tiene journal inactivo, Cuando intento postearlo, Entonces el sistema retorna error 400 "Journal inactivo. No se puede postear".

Escenario 6: Actualizar balances de cuentas

Dado que una cuenta tiene balance_debit=5000 y balance_credit=3000, Cuando posteo un asiento que agrega debit=1000 a esa cuenta, Entonces el balance_debit se actualiza a 6000.


Reglas de Negocio

  • RN-1: Solo asientos con state='draft' pueden postearse.
  • RN-2: Al postear, se valida nuevamente que débitos = créditos (por si hubo cambios).
  • RN-3: Todas las cuentas en las líneas deben estar activas.
  • RN-4: Journal debe estar activo.
  • RN-5: Una vez posted, el asiento es inmutable (no se puede editar ni eliminar).
  • RN-6: Se actualizan los balances acumulados de las cuentas (balance_debit, balance_credit).
  • RN-7: Se registra auditoría: posted_date, posted_by.
  • RN-8: Fecha contable debe estar dentro de período fiscal abierto (validación futura).

Tareas Técnicas

Backend

  • Endpoint: POST /api/v1/financial/journal-entries/:id/post
  • Service: JournalEntryService.post(id, userId)
  • Service: JournalEntryService.validateForPosting(entry) - Validaciones previas
  • Service: AccountService.updateBalances(entryLines) - Actualizar balances
  • Validar state='draft' antes de postear
  • Validar balance débitos = créditos
  • Validar que cuentas están activas
  • Validar que journal está activo
  • Actualizar state='posted', posted_date=now(), posted_by=user_id
  • Actualizar balances de cuentas: balance_debit += line.debit, balance_credit += line.credit
  • Transacción atómica: postear + actualizar balances en misma transacción DB
  • Unit tests (10 test cases)
  • Integration tests (8 test cases)
  • Swagger docs

Frontend

  • Componente: PostJournalEntryButton.tsx (botón "Publicar")
  • Modal: ConfirmPostModal.tsx (confirmación antes de postear)
  • Estado: Deshabilitar edición si state='posted'
  • Badge: Mostrar "Borrador" / "Publicado" según state
  • API client: journalEntryApi.post(id)
  • Toast: "Asiento publicado exitosamente"
  • Component test: PostJournalEntryButton.test.tsx
  • E2E test: "should post draft entry successfully"
  • E2E test: "should prevent editing posted entry"

Database

  • Campo: journal_entries.state (enum: 'draft', 'posted')
  • Campo: journal_entries.posted_date (timestamp nullable)
  • Campo: journal_entries.posted_by (uuid nullable, FK a auth.users)
  • Campo: accounts.balance_debit (numeric, default 0)
  • Campo: accounts.balance_credit (numeric, default 0)
  • Índice: idx_journal_entries_state (para filtrar drafts vs posted)
  • Trigger o función: Actualizar balances de cuentas después de postear

Mockups / Wireframes

Vista Detalle de Asiento (Draft):

┌──────────────────────────────────────────┐
│ Asiento VEN/2024/0001  [Borrador]       │
│ [Editar] [Publicar] [Eliminar]          │
├──────────────────────────────────────────┤
│ Journal: VEN - Ventas                    │
│ Fecha: 2024-01-15                        │
│ Referencia: Factura #123                 │
│                                          │
│ Líneas:                                  │
│ 1.1.01.001 | Debe: 1000 | Haber: 0      │
│ 4.1.01.001 | Debe: 0    | Haber: 1000   │
└──────────────────────────────────────────┘

Modal Confirmación Publicar:

┌──────────────────────────────────────────┐
│ ⚠️  Confirmar Publicación                │
├──────────────────────────────────────────┤
│ ¿Está seguro de publicar este asiento?  │
│                                          │
│ Una vez publicado, NO podrá modificarlo  │
│ ni eliminarlo.                           │
│                                          │
│ [Cancelar]  [Sí, Publicar]              │
└──────────────────────────────────────────┘

Vista Detalle de Asiento (Posted):

┌──────────────────────────────────────────┐
│ Asiento VEN/2024/0001  [Publicado ✓]    │
│ [Ver Asiento de Reversión]              │
├──────────────────────────────────────────┤
│ Journal: VEN - Ventas                    │
│ Fecha: 2024-01-15                        │
│ Publicado: 2024-01-15 10:30 (Juan P.)   │
│ Referencia: Factura #123                 │
└──────────────────────────────────────────┘

Casos de Prueba

Funcionales

  1. TC-001: Postear draft exitosamente
  2. TC-002: Error por asiento desbalanceado
  3. TC-003: Error por asiento ya posted
  4. TC-004: Error por cuenta inactiva
  5. TC-005: Error por journal inactivo
  6. TC-006: Actualizar balance_debit correctamente
  7. TC-007: Actualizar balance_credit correctamente
  8. TC-008: No se puede editar asiento posted
  9. TC-009: No se puede eliminar asiento posted

No Funcionales

  1. Performance: < 300ms para postear (incluye actualización balances)
  2. Atomicidad: Rollback completo si falla actualización de balances
  3. Seguridad: Solo usuarios con permiso accounting_manager pueden postear

Dependencias

  • US bloqueantes:
    • US-MGN-004-003-001 (Crear Asiento Draft)
  • Módulos: MGN-004

Notas de Implementación

  • Transacción atómica: Usar @Transactional o BEGIN/COMMIT para garantizar que postear + actualizar balances ocurre todo o nada
  • Actualización de balances: Considerar usar triggers de PostgreSQL para actualizar automáticamente accounts.balance_* al insertar/actualizar journal_entry_lines
  • Performance: Si hay muchas líneas, batch update los balances en una sola query
  • Auditoría: Registrar evento de "post" en tabla audit.journal_entry_events para trazabilidad
  • Validación de período fiscal: En Fase 2, agregar validación de que fecha contable está dentro de período fiscal abierto
  • Reversión: Esta US NO cubre reversión (cancelación). Eso es US siguiente

Estimación Detallada

Tarea Estimación
Backend 3 horas
Frontend 2 horas
Testing 2 horas
Code Review 1 hora
TOTAL 8 horas = 5 SP

Definition of Done

  • Código backend implementado
  • Código frontend implementado
  • Unit tests pasando (>80%)
  • Integration tests pasando
  • E2E tests pasando
  • Validaciones de negocio funcionan
  • Balances se actualizan correctamente
  • Transacción atómica funciona (rollback en caso de error)
  • Code review aprobado
  • Swagger docs actualizado
  • Merge a develop
  • QA validado (happy path + errores)
  • PO aprobado

Referencias