# 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
