# 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)
