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

10 KiB

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

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

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

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

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


Importancia: CRÍTICO para ERPs de proyectos