erp-core/docs/04-modelado/domain-models/analytics-domain.md

338 lines
10 KiB
Markdown

# MODELO DE DOMINIO: Contabilidad Analítica
**Módulos:** MGN-008 (Contabilidad Analítica)
**Fecha:** 2025-11-24
**Referencia Odoo:** analytic
**Referencia Gamilit:** No implementado
---
## Diagrama de Entidades (Texto UML)
```
[AnalyticAccount]
- id: UUID (PK)
- tenant_id: UUID (FK)
- company_id: UUID (FK)
- name: String
- code: String
- account_type: ENUM (project, department, cost_center, customer)
- parent_id: UUID (FK self)
- partner_id: UUID (FK)
- budget: Decimal
- status: ENUM (active, inactive, closed)
1 <----> * [AnalyticLine]
1 <----> * [AnalyticDistribution]
[AnalyticLine]
- id: UUID (PK)
- tenant_id: UUID (FK)
- company_id: UUID (FK)
- analytic_account_id: UUID (FK)
- date: Date
- amount: Decimal
- unit_amount: Decimal (horas, cantidad)
- product_id: UUID (FK)
- employee_id: UUID (FK)
- description: String
- line_type: ENUM (expense, income, timesheet)
- source_document: String (ej: "invoice/123")
- source_model: String (ej: "Invoice")
- source_id: UUID
* <----> * [AnalyticTag]
[AnalyticDistribution]
- id: UUID (PK)
- source_line_id: UUID (FK)
- source_model: String (ej: "PurchaseOrderLine")
- analytic_account_id: UUID (FK)
- percentage: Decimal
- amount: Decimal
[AnalyticTag]
- id: UUID (PK)
- tenant_id: UUID (FK)
- name: String
- color: String
- tag_type: ENUM (phase, location, category)
* <----> * [AnalyticLine]
[AnalyticPlan]
- id: UUID (PK)
- tenant_id: UUID (FK)
- company_id: UUID (FK)
- name: String
- description: Text
1 <----> * [AnalyticAccount]
```
## Entidades Principales
### 1. AnalyticAccount (Cuenta Analítica)
**Descripción:** Cuenta para tracking de costos/ingresos por proyecto, departamento, centro de costo.
**Atributos:**
- `id`: UUID
- `name`: Nombre (ej: "Proyecto Torre A")
- `code`: Código (ej: "PRJ-001")
- `account_type`: project, department, cost_center, customer
- `parent_id`: Cuenta padre (jerarquía)
- `partner_id`: Cliente/Partner asociado
- `budget`: Presupuesto asignado
- `status`: active, inactive, closed
**Relaciones:**
- 1 AnalyticAccount → N AnalyticLines
- 1 AnalyticAccount → N Subdepartments
- 1 AnalyticAccount → 1 Project (MGN-011)
**Patrón Odoo:** account.analytic.account
**Tipos:**
- project: Proyectos (vinculado a MGN-011)
- department: Departamentos
- cost_center: Centros de costo
- customer: Por cliente
### 2. AnalyticLine (Línea Analítica)
**Descripción:** Registro individual de costo o ingreso en cuenta analítica.
**Atributos:**
- `id`: UUID
- `analytic_account_id`: Cuenta analítica
- `date`: Fecha del registro
- `amount`: Monto (negativo=costo, positivo=ingreso)
- `unit_amount`: Cantidad (horas para timesheet, unidades para productos)
- `product_id`: Producto asociado (opcional)
- `employee_id`: Empleado asociado (para timesheet)
- `description`: Descripción
- `line_type`: expense, income, timesheet
- `source_document`: Documento origen (ej: "invoice/123")
- `source_model`: Modelo origen (ej: "Invoice")
- `source_id`: ID del registro origen
**Relaciones:**
- N AnalyticLines → 1 AnalyticAccount
- N AnalyticLines ←→ N AnalyticTags
**Patrón Odoo:** account.analytic.line
**Creación:**
- Manual: Usuario crea línea directamente
- Automática: Generada desde transacciones (facturas, PO, SO, timesheet)
### 3. AnalyticDistribution (Distribución Analítica)
**Descripción:** Distribución de un monto a múltiples cuentas analíticas.
**Atributos:**
- `id`: UUID
- `source_line_id`: Línea origen (ej: PurchaseOrderLine)
- `source_model`: Modelo origen
- `analytic_account_id`: Cuenta analítica destino
- `percentage`: Porcentaje (ej: 60%)
- `amount`: Monto calculado
**Relaciones:**
- N AnalyticDistributions → 1 AnalyticAccount
**Patrón Odoo:** account.analytic.distribution (Odoo 16+)
**Validación:** SUM(percentage) = 100% por source_line_id
**Ejemplo:**
```
Compra de $10,000
- 60% → Proyecto A ($6,000)
- 40% → Proyecto B ($4,000)
```
### 4. AnalyticTag (Etiqueta Analítica)
**Descripción:** Etiqueta adicional para clasificación analítica.
**Atributos:**
- `id`: UUID
- `name`: Nombre (ej: "Fase 1", "Torre Norte")
- `color`: Color para UI
- `tag_type`: phase, location, category
**Relaciones:**
- N AnalyticTags ←→ N AnalyticLines
**Patrón Odoo:** account.analytic.tag
**Uso:** Clasificación adicional cross-cutting (no jerárquica)
**Ejemplos:**
- Tags de fase: "Planeación", "Ejecución", "Cierre"
- Tags de ubicación: "Torre A", "Torre B", "Área Común"
- Tags de categoría: "CAPEX", "OPEX"
### 5. AnalyticPlan (Plan Analítico)
**Descripción:** Agrupación de cuentas analíticas (opcional, para multi-dimensión).
**Atributos:**
- `id`: UUID
- `name`: Nombre del plan
- `description`: Descripción
**Relaciones:**
- 1 AnalyticPlan → N AnalyticAccounts
**Patrón Odoo:** account.analytic.plan (Odoo 16+)
**Uso:** Análisis multi-dimensional (por proyecto, por departamento, etc.)
## Reglas de Negocio
### RN-ANA-001: Generación Automática de Líneas
- Al validar factura: generar líneas analíticas desde invoice_lines con analytic_account_id
- Al validar PO: generar líneas desde purchase_order_lines
- Al validar SO: generar líneas desde sale_order_lines
- Al validar timesheet: generar líneas desde timesheet
### RN-ANA-002: Tipo de Línea
- expense: Monto negativo (costos)
- Compras, gastos, timesheet
- income: Monto positivo (ingresos)
- Ventas, facturación
- timesheet: Tipo especial (puede ser expense si se valora)
### RN-ANA-003: Distribución 100%
- Si línea tiene distribución analítica, SUM(percentage) = 100%
- Validación antes de guardar
### RN-ANA-004: Balance Analítico
- Balance = SUM(amount WHERE analytic_account_id = X)
- Balance positivo: más ingresos que costos
- Balance negativo: más costos que ingresos
### RN-ANA-005: Jerarquía de Cuentas
- Cuentas pueden tener parent_id (jerarquía)
- Balance se puede calcular recursivamente (cuenta + subcuentas)
### RN-ANA-006: Presupuesto vs Real
- budget: Presupuesto asignado
- real: SUM(amount WHERE line_type='expense')
- variance: budget - real
- Alertas si variance < 0 (sobre presupuesto)
### RN-ANA-007: Tags Multi-Dimensionales
- Una línea puede tener múltiples tags
- Filtros combinados: Proyecto X + Fase 1 + CAPEX
### RN-ANA-008: Inmutabilidad
- Líneas analíticas generadas automáticamente no se editan manualmente
- Si se corrige documento origen, se regeneran líneas
## Casos de Uso Principales
1. **UC-ANA-001:** Administrador crea cuenta analítica para proyecto
2. **UC-ANA-002:** Usuario asigna cuenta analítica a línea de PO
3. **UC-ANA-003:** Usuario distribuye costo a múltiples cuentas (60%-40%)
4. **UC-ANA-004:** Sistema genera líneas analíticas al validar factura
5. **UC-ANA-005:** Empleado registra timesheet genera línea analítica
6. **UC-ANA-006:** Usuario consulta balance por cuenta analítica (P&L por proyecto)
7. **UC-ANA-007:** Usuario compara presupuesto vs real
8. **UC-ANA-008:** Usuario filtra líneas analíticas por tags
9. **UC-ANA-009:** Gerente consulta reporte de rentabilidad por proyecto
## Validaciones y Constraints
```sql
-- Code único por company
UNIQUE (tenant_id, company_id, code)
-- Cuenta no puede ser su propia padre
CHECK (parent_id != id)
-- Presupuesto >= 0
CHECK (budget >= 0)
-- Distribución: percentage entre 0 y 100
CHECK (percentage >= 0 AND percentage <= 100)
-- SUM(percentage) = 100 por source_line_id
-- (trigger custom)
-- unit_amount >= 0 (para timesheet)
CHECK (unit_amount >= 0)
```
## Índices Requeridos
```sql
CREATE INDEX idx_analytic_accounts_company_id ON analytics.analytic_accounts(company_id);
CREATE INDEX idx_analytic_accounts_parent_id ON analytics.analytic_accounts(parent_id);
CREATE INDEX idx_analytic_accounts_status ON analytics.analytic_accounts(status);
CREATE INDEX idx_analytic_lines_analytic_account_id ON analytics.analytic_lines(analytic_account_id);
CREATE INDEX idx_analytic_lines_date ON analytics.analytic_lines(date);
CREATE INDEX idx_analytic_lines_employee_id ON analytics.analytic_lines(employee_id);
CREATE INDEX idx_analytic_lines_line_type ON analytics.analytic_lines(line_type);
CREATE INDEX idx_analytic_distributions_source_line_id ON analytics.analytic_distributions(source_line_id);
CREATE INDEX idx_analytic_distributions_analytic_account_id ON analytics.analytic_distributions(analytic_account_id);
```
## Integración con Otros Módulos
### Con MGN-004 (Financiero)
- Invoice lines tienen analytic_account_id
- Al validar invoice, genera analytic lines
### Con MGN-006 (Compras)
- Purchase order lines tienen analytic_account_id
- Al validar PO, genera analytic lines (expense)
### Con MGN-007 (Ventas)
- Sale order lines tienen analytic_account_id
- Al validar SO, genera analytic lines (income)
### Con MGN-010 (RRHH)
- Timesheet tiene analytic_account_id
- Al validar timesheet, genera analytic lines (expense o neutral)
### Con MGN-011 (Proyectos)
- Project tiene analytic_account_id (1-1)
- Todos los costos/ingresos del proyecto se registran en su cuenta analítica
### Con MGN-012 (Reportes)
- Reportes analíticos:
- P&L por cuenta analítica
- Balance por proyecto
- Presupuesto vs Real
- Análisis por tags
## Vistas y Reportes Estándar
### 1. Balance Analítico
```sql
SELECT
analytic_account_id,
SUM(CASE WHEN line_type = 'expense' THEN amount ELSE 0 END) as total_costs,
SUM(CASE WHEN line_type = 'income' THEN amount ELSE 0 END) as total_income,
SUM(amount) as balance
FROM analytics.analytic_lines
WHERE tenant_id = :tenant_id
GROUP BY analytic_account_id
```
### 2. Presupuesto vs Real
```sql
SELECT
aa.name,
aa.budget,
SUM(al.amount) as real_cost,
aa.budget - SUM(al.amount) as variance
FROM analytics.analytic_accounts aa
LEFT JOIN analytics.analytic_lines al ON aa.id = al.analytic_account_id
WHERE al.line_type = 'expense'
GROUP BY aa.id
```
## Referencias
- [ALCANCE-POR-MODULO.md - MGN-008](../../01-definicion-modulos/ALCANCE-POR-MODULO.md#mgn-008)
- [ADR-007: Database Design](../../adr/ADR-007-database-design.md)
- [odoo-analytic-analysis.md](../../00-analisis-referencias/odoo/odoo-analytic-analysis.md)
---
**Importancia:** ⭐⭐⭐⭐⭐ CRÍTICO para ERPs de proyectos