erp-core/docs/02-fase-core-business/MGN-010-financial/requerimientos/RF-FIN-004.md

7.2 KiB

RF-FIN-004: Asientos Contables

Identificacion

Campo Valor
ID RF-FIN-004
Modulo MGN-010 Financial
Titulo Asientos Contables
Prioridad P0 - Critica
Estado Draft
Fecha 2025-12-05

Descripcion

El sistema debe permitir registrar asientos contables (journal entries) con multiples lineas de debe y haber, validando que el asiento este cuadrado y afectando los saldos de las cuentas.


Requisitos Funcionales

RF-FIN-004.1: Estructura del Asiento

interface JournalEntry {
  id: UUID;
  tenantId: UUID;
  periodId: UUID;           // Periodo contable
  entryNumber: string;      // "POL-2025-001234"
  entryDate: Date;          // Fecha contable
  description: string;      // Concepto/glosa
  reference?: string;       // Referencia externa
  sourceType?: string;      // "invoice", "payment", "manual"
  sourceId?: UUID;          // ID del documento origen
  status: EntryStatus;
  postedAt?: TIMESTAMPTZ;
  postedBy?: UUID;
  reversedEntryId?: UUID;   // Si es reverso
  createdBy: UUID;
  createdAt: TIMESTAMPTZ;
  updatedAt: TIMESTAMPTZ;
}

enum EntryStatus {
  DRAFT = 'draft',         // En edicion
  PENDING = 'pending',     // Pendiente aprobacion
  POSTED = 'posted',       // Contabilizado
  REVERSED = 'reversed'    // Reversado
}

RF-FIN-004.2: Estructura de Linea

interface JournalLine {
  id: UUID;
  entryId: UUID;
  lineNumber: number;
  accountId: UUID;          // Cuenta contable
  costCenterId?: UUID;      // Centro de costo
  description?: string;     // Detalle de linea
  debit: Decimal;           // Debe
  credit: Decimal;          // Haber
  currencyId: UUID;         // Moneda del movimiento
  exchangeRate: Decimal;    // Tipo de cambio
  debitBase: Decimal;       // Debe en moneda base
  creditBase: Decimal;      // Haber en moneda base
  partnerId?: UUID;         // Tercero (cliente/proveedor)
  tags?: string[];          // Etiquetas
  reconciled: boolean;      // Conciliado
  reconciledAt?: TIMESTAMPTZ;
}

RF-FIN-004.3: Validaciones del Asiento

interface EntryValidation {
  isValid: boolean;
  errors: ValidationError[];
  warnings: ValidationWarning[];
}

// Validaciones obligatorias
const VALIDATIONS = [
  'entry_balanced',        // Debe = Haber
  'date_in_open_period',   // Fecha en periodo abierto
  'accounts_are_detail',   // Cuentas de detalle
  'accounts_active',       // Cuentas activas
  'amounts_positive',      // Montos positivos
  'currency_valid',        // Moneda valida
  'exchange_rate_valid'    // Tipo de cambio valido
];

// Ejemplo de validacion
function validateEntry(entry: JournalEntry): EntryValidation {
  const totalDebit = entry.lines.reduce((sum, l) => sum + l.debitBase, 0);
  const totalCredit = entry.lines.reduce((sum, l) => sum + l.creditBase, 0);

  if (Math.abs(totalDebit - totalCredit) > 0.01) {
    return {
      isValid: false,
      errors: [{
        code: 'UNBALANCED',
        message: `Asiento descuadrado: Debe=${totalDebit}, Haber=${totalCredit}`
      }]
    };
  }
  // ... mas validaciones
}

RF-FIN-004.4: Numeracion de Asientos

interface EntryNumbering {
  tenantId: UUID;
  prefix: string;           // "POL", "ING", "EGR"
  yearFormat: string;       // "YYYY" o "YY"
  separator: string;        // "-"
  sequenceLength: number;   // 6 digitos
  resetYearly: boolean;     // Reiniciar cada año
  lastNumber: number;       // Ultimo numero usado
}

// Genera: POL-2025-000001
function generateEntryNumber(config: EntryNumbering, date: Date): string {
  const year = format(date, config.yearFormat);
  const sequence = (config.lastNumber + 1).toString().padStart(config.sequenceLength, '0');
  return `${config.prefix}${config.separator}${year}${config.separator}${sequence}`;
}

RF-FIN-004.5: Mayorizar Asiento

Al contabilizar (post) un asiento:

  1. Validar que este cuadrado
  2. Validar fecha en periodo abierto
  3. Actualizar saldos de cuentas
  4. Marcar como posted
  5. Registrar en audit log
interface PostResult {
  entryId: UUID;
  entryNumber: string;
  postedAt: TIMESTAMPTZ;
  affectedAccounts: {
    accountId: UUID;
    previousBalance: Decimal;
    newBalance: Decimal;
  }[];
}

RF-FIN-004.6: Reversar Asiento

interface ReversalEntry {
  originalEntryId: UUID;
  reversalEntryId: UUID;
  reversalDate: Date;
  reason: string;
}

// El asiento de reverso:
// - Tiene las mismas lineas pero Debe/Haber invertidos
// - Referencia al asiento original
// - Anula efecto en saldos

RF-FIN-004.7: Asientos desde Documentos

Los modulos de negocio generan asientos:

Documento Asiento
Factura de Venta Debe: CxC, Haber: Ingresos + IVA
Factura de Compra Debe: Gastos + IVA, Haber: CxP
Pago Recibido Debe: Banco, Haber: CxC
Pago Emitido Debe: CxP, Haber: Banco
Ajuste Inventario Debe/Haber: Inventario vs Ajustes

Operaciones

Listar Asientos

GET /api/v1/financial/journal?periodId=uuid&status=posted

Response:
{
  "data": [
    {
      "id": "uuid",
      "entryNumber": "POL-2025-001234",
      "entryDate": "2025-12-05",
      "description": "Pago a proveedor XYZ",
      "status": "posted",
      "totalDebit": 10000,
      "linesCount": 3
    }
  ]
}

Crear Asiento

POST /api/v1/financial/journal
{
  "entryDate": "2025-12-05",
  "description": "Registro de venta",
  "lines": [
    {
      "accountId": "uuid-cxc",
      "debit": 11600,
      "credit": 0,
      "description": "Cliente ABC"
    },
    {
      "accountId": "uuid-ingresos",
      "debit": 0,
      "credit": 10000,
      "description": "Venta de servicios"
    },
    {
      "accountId": "uuid-iva",
      "debit": 0,
      "credit": 1600,
      "description": "IVA 16%"
    }
  ]
}

Response:
{
  "id": "uuid",
  "entryNumber": "POL-2025-001235",
  "status": "draft",
  "totalDebit": 11600,
  "totalCredit": 11600,
  "isBalanced": true
}

Contabilizar Asiento

POST /api/v1/financial/journal/:id/post

Response:
{
  "id": "uuid",
  "status": "posted",
  "postedAt": "2025-12-05T10:00:00Z",
  "affectedAccounts": 3
}

Reversar Asiento

POST /api/v1/financial/journal/:id/reverse
{
  "reversalDate": "2025-12-06",
  "reason": "Error en monto"
}

Response:
{
  "originalEntryId": "uuid-original",
  "reversalEntryId": "uuid-reverso",
  "reversalNumber": "POL-2025-001236"
}

Reglas de Negocio

ID Regla Severidad
BR-001 Asiento debe estar cuadrado para contabilizar Error
BR-002 Fecha debe estar en periodo abierto Error
BR-003 Solo cuentas de detalle en asientos Error
BR-004 Asiento contabilizado no es editable Error
BR-005 Reverso de asiento ya reversado no permitido Error

Criterios de Aceptacion

  • CRUD de asientos contables
  • Validacion automatica de cuadre
  • Numeracion automatica configurable
  • Mayorizar asiento (post)
  • Reversar asiento
  • Soporte multi-moneda
  • Centros de costo opcionales
  • Integracion con documentos

Historial

Version Fecha Autor Cambios
1.0 2025-12-05 Requirements-Analyst Creacion inicial