628 lines
11 KiB
Markdown
628 lines
11 KiB
Markdown
# 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)
|
|
```typescript
|
|
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)
|
|
```typescript
|
|
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)
|
|
```typescript
|
|
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)
|
|
```typescript
|
|
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)
|
|
```typescript
|
|
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)
|
|
```typescript
|
|
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)
|
|
```typescript
|
|
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)
|
|
```typescript
|
|
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)
|
|
```typescript
|
|
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)
|
|
```typescript
|
|
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
|
|
```typescript
|
|
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
|
|
```typescript
|
|
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
|
|
```typescript
|
|
interface FiscalPeriod {
|
|
year: number;
|
|
period: number; // 1-12 for monthly
|
|
isClosed: boolean;
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Domain Events
|
|
|
|
### Accounting Events
|
|
```typescript
|
|
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
|
|
```typescript
|
|
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
|
|
```typescript
|
|
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
|
|
|
|
- [DDL-SPEC-finance.md](../database-design/schemas/DDL-SPEC-finance.md)
|
|
- [EPIC-MAE-014](../../08-epicas/EPIC-MAE-014-finanzas.md)
|
|
|
|
---
|
|
|
|
*Ultima actualizacion: 2025-12-05*
|