erp-construccion/docs/04-modelado/domain-models/FINANCE-CONTEXT.md

11 KiB

DOMAIN MODEL: Finance Context

Version: 1.0.0 Fecha: 2025-12-05 Modulos: MAE-014


Descripcion

El contexto de Finance gestiona toda la contabilidad y control financiero integrado con proyectos de construccion. Incluye libro mayor, cuentas por pagar/cobrar, flujo de efectivo y conciliacion bancaria.


Agregados

1. Accounting Aggregate

AccountingEntry (Aggregate Root)
|
+-- EntryLine (Entity)
|   +-- Account (Reference)
|   +-- CostCenter (Reference)
|
+-- EntryStatus (Value Object)

AccountingEntry (Root)

interface AccountingEntry {
  id: UUID;
  tenantId: UUID;

  entryNumber: string;
  entryType: EntryType;
  entryDate: Date;

  description: string;
  reference?: string;

  // Source
  sourceModule?: string;  // purchases, estimations, payroll
  sourceId?: UUID;
  sourceReference?: string;

  // Project link
  projectId?: UUID;
  costCenterId?: UUID;

  // Totals
  totalDebit: Money;
  totalCredit: Money;
  isBalanced: boolean;

  // Status
  status: EntryStatus;
  postedAt?: Timestamp;
  postedBy?: UUID;

  // Period
  fiscalYear: number;
  fiscalPeriod: number;

  lines: EntryLine[];
}

enum EntryType {
  INCOME = 'income',
  EXPENSE = 'expense',
  JOURNAL = 'journal',
  TRANSFER = 'transfer',
  ADJUSTMENT = 'adjustment',
  OPENING = 'opening',
  CLOSING = 'closing'
}

enum EntryStatus {
  DRAFT = 'draft',
  PENDING = 'pending',
  POSTED = 'posted',
  CANCELLED = 'cancelled'
}

EntryLine (Entity)

interface EntryLine {
  id: UUID;
  entryId: UUID;
  lineNumber: number;

  // Account
  accountId: UUID;
  accountCode: string;

  description?: string;

  // Amounts
  debitAmount: Money;
  creditAmount: Money;

  // Attribution
  costCenterId?: UUID;
  projectId?: UUID;

  // Partner
  partnerId?: UUID;
  partnerName?: string;
}

2. AccountsPayable Aggregate

AccountPayable (Aggregate Root)
|
+-- APPayment (Entity)
|
+-- PaymentSchedule (Value Object)

AccountPayable (Root)

interface AccountPayable {
  id: UUID;
  tenantId: UUID;

  documentNumber: string;
  documentType: DocumentType;

  // Supplier
  supplierId: UUID;
  supplierName: string;
  supplierRfc?: string;

  // Source
  purchaseOrderId?: UUID;
  contractId?: UUID;

  // Amounts
  subtotal: Money;
  taxAmount: Money;
  retentionAmount: Money;
  totalAmount: Money;
  paidAmount: Money;
  balance: Money;  // totalAmount - paidAmount

  // Dates
  documentDate: Date;
  dueDate: Date;
  receivedDate?: Date;

  // Project
  projectId?: UUID;
  costCenterId?: UUID;

  // Status
  status: PaymentStatus;

  // Linked entry
  accountingEntryId?: UUID;

  payments: APPayment[];
}

enum PaymentStatus {
  PENDING = 'pending',
  PARTIAL = 'partial',
  PAID = 'paid',
  OVERDUE = 'overdue',
  CANCELLED = 'cancelled'
}

APPayment (Entity)

interface APPayment {
  id: UUID;
  payableId: UUID;

  paymentNumber: string;
  paymentDate: Date;
  amount: Money;

  paymentMethod: PaymentMethod;
  bankAccountId?: UUID;
  reference?: string;

  status: string;
  accountingEntryId?: UUID;
}

enum PaymentMethod {
  CASH = 'cash',
  CHECK = 'check',
  TRANSFER = 'transfer',
  CARD = 'card',
  DEPOSIT = 'deposit',
  OTHER = 'other'
}

3. AccountsReceivable Aggregate

AccountReceivable (Aggregate Root)
|
+-- ARCollection (Entity)
|
+-- CollectionReminder (Value Object)

AccountReceivable (Root)

interface AccountReceivable {
  id: UUID;
  tenantId: UUID;

  documentNumber: string;
  documentType: string;

  // Customer
  customerId: UUID;
  customerName: string;
  customerRfc?: string;

  // Source
  estimationId?: UUID;
  saleOrderId?: UUID;
  contractId?: UUID;

  // Amounts
  subtotal: Money;
  taxAmount: Money;
  retentionAmount: Money;
  totalAmount: Money;
  collectedAmount: Money;
  balance: Money;  // totalAmount - collectedAmount

  // Dates
  documentDate: Date;
  dueDate: Date;

  // Project
  projectId?: UUID;

  // Status
  status: PaymentStatus;

  accountingEntryId?: UUID;

  collections: ARCollection[];
}

ARCollection (Entity)

interface ARCollection {
  id: UUID;
  receivableId: UUID;

  collectionNumber: string;
  collectionDate: Date;
  amount: Money;

  paymentMethod: PaymentMethod;
  bankAccountId?: UUID;
  reference?: string;

  status: string;
  accountingEntryId?: UUID;
}

4. CashFlow Aggregate

CashFlowProjection (Aggregate Root)
|
+-- CashFlowItem (Entity)
|
+-- Variance (Value Object)

CashFlowProjection (Root)

interface CashFlowProjection {
  id: UUID;
  tenantId: UUID;
  projectId?: UUID;

  periodDate: Date;
  periodType: PeriodType;

  // Projected Income
  projectedIncome: Money;
  projectedCollections: Money;

  // Projected Expenses
  projectedExpenses: Money;
  projectedPayments: Money;

  // Actual
  actualIncome: Money;
  actualExpenses: Money;

  // Balances
  openingBalance: Money;
  projectedClosing: Money;
  actualClosing?: Money;

  cashFlowType: CashFlowType;

  items: CashFlowItem[];
}

enum PeriodType {
  DAILY = 'daily',
  WEEKLY = 'weekly',
  MONTHLY = 'monthly'
}

enum CashFlowType {
  OPERATING = 'operating',
  INVESTING = 'investing',
  FINANCING = 'financing'
}

CashFlowItem (Entity)

interface CashFlowItem {
  id: UUID;
  projectionId: UUID;

  itemType: 'income' | 'expense';
  category?: string;

  description: string;

  sourceType?: string;  // estimation, purchase_order, contract, payroll
  sourceId?: UUID;

  projectedAmount: Money;
  actualAmount?: Money;

  expectedDate?: Date;
  actualDate?: Date;
}

5. BankReconciliation Aggregate

BankAccount (Aggregate Root)
|
+-- BankMovement (Entity)
|
+-- BankReconciliation (Entity)

BankAccount (Root)

interface BankAccount {
  id: UUID;
  tenantId: UUID;

  accountNumber: string;
  clabe?: string;
  name: string;

  bankName: string;
  bankCode?: string;

  accountType?: string;  // checking, savings, credit
  currency: string;

  currentBalance: Money;
  availableBalance: Money;
  lastReconciledBalance?: Money;
  lastReconciledDate?: Date;

  ledgerAccountId?: UUID;
  projectId?: UUID;

  isActive: boolean;

  movements: BankMovement[];
}

BankMovement (Entity)

interface BankMovement {
  id: UUID;
  bankAccountId: UUID;

  movementDate: Date;
  valueDate?: Date;
  reference?: string;
  description?: string;

  debitAmount: Money;
  creditAmount: Money;
  balanceAfter?: Money;

  // Bank source
  bankReference?: string;
  importBatchId?: UUID;

  // Reconciliation
  reconciliationStatus: ReconciliationStatus;
  reconciledWithId?: UUID;
  reconciledWithType?: string;  // ap_payment, ar_collection, entry_line
  reconciledAt?: Timestamp;
  reconciledBy?: UUID;
}

enum ReconciliationStatus {
  PENDING = 'pending',
  MATCHED = 'matched',
  PARTIAL = 'partial',
  UNMATCHED = 'unmatched'
}

Value Objects

Money

interface Money {
  amount: number;
  currency: string;
}

class Money {
  static add(a: Money, b: Money): Money;
  static subtract(a: Money, b: Money): Money;
  static multiply(a: Money, factor: number): Money;
  static isPositive(m: Money): boolean;
  static isNegative(m: Money): boolean;
  static equals(a: Money, b: Money): boolean;
}

AccountCode

interface AccountCode {
  code: string;
  type: AccountType;
  nature: AccountNature;
  level: number;
}

enum AccountType {
  ASSET = 'asset',
  LIABILITY = 'liability',
  EQUITY = 'equity',
  INCOME = 'income',
  EXPENSE = 'expense'
}

enum AccountNature {
  DEBIT = 'debit',
  CREDIT = 'credit'
}

FiscalPeriod

interface FiscalPeriod {
  year: number;
  period: number;  // 1-12 for monthly
  isClosed: boolean;
}

Domain Events

Accounting Events

interface EntryPosted {
  entryId: UUID;
  entryNumber: string;
  entryType: EntryType;
  totalAmount: Money;
  postedBy: UUID;
  timestamp: Timestamp;
}

interface PeriodClosed {
  fiscalYear: number;
  fiscalPeriod: number;
  closedBy: UUID;
  timestamp: Timestamp;
}

AP/AR Events

interface PayableCreated {
  payableId: UUID;
  supplierId: UUID;
  totalAmount: Money;
  dueDate: Date;
  timestamp: Timestamp;
}

interface PaymentMade {
  paymentId: UUID;
  payableId: UUID;
  amount: Money;
  remainingBalance: Money;
  timestamp: Timestamp;
}

interface CollectionReceived {
  collectionId: UUID;
  receivableId: UUID;
  amount: Money;
  remainingBalance: Money;
  timestamp: Timestamp;
}

Bank Events

interface MovementsImported {
  bankAccountId: UUID;
  batchId: UUID;
  movementsCount: number;
  totalDebits: Money;
  totalCredits: Money;
  timestamp: Timestamp;
}

interface ReconciliationCompleted {
  reconciliationId: UUID;
  bankAccountId: UUID;
  periodEnd: Date;
  isBalanced: boolean;
  difference: Money;
  timestamp: Timestamp;
}

Business Rules

Accounting Rules

  1. Toda poliza debe estar balanceada (debitos = creditos)
  2. No se puede modificar una poliza en status 'posted'
  3. Los periodos cerrados no permiten nuevas polizas
  4. Las cuentas de nivel 1 no permiten movimientos directos

AP/AR Rules

  1. El saldo no puede ser negativo
  2. Un pago no puede exceder el saldo pendiente
  3. Las CxP vencidas deben generar alertas automaticas
  4. Los pagos parciales cambian status a 'partial'

Cash Flow Rules

  1. La proyeccion debe coincidir con CxP/CxC pendientes
  2. Varianza > 10% requiere justificacion
  3. Proyecciones semanales son obligatorias

Bank Rules

  1. Solo movimientos conciliados afectan el libro
  2. La conciliacion requiere diferencia = 0
  3. Importacion de estados de cuenta es idempotente

Invariantes

  1. Entry.totalDebit = Entry.totalCredit (siempre)
  2. AccountPayable.balance = totalAmount - paidAmount
  3. AccountReceivable.balance = totalAmount - collectedAmount
  4. BankAccount.currentBalance = SUM(movements.credits) - SUM(movements.debits)
  5. CashFlow.projectedClosing = openingBalance + income - expenses

Integraciones

Con Project Context

  • Estimaciones aprobadas -> CxC
  • Ordenes de compra -> CxP
  • Costos de proyecto -> Polizas

Con Assets Context

  • Depreciacion -> Polizas automaticas
  • Mantenimiento -> CxP

Con Documents Context

  • Facturas digitales almacenadas
  • Polizas exportadas a PDF

Con ERP Externos

  • SAP: Sincronizacion de polizas via RFC
  • CONTPAQi: Export XML de polizas
  • ASPEL: Export archivo plano

Referencias


Ultima actualizacion: 2025-12-05