# FASE 5: Refinamiento del Plan de Correcciones

**Fecha:** 2026-01-04
**Objetivo:** Refinar plan con scripts de migracion, tests y rollback
**Estado:** Completado
**Basado en:** FASE-4 (Validacion de Dependencias)

---

## 1. Plan de Ejecucion Refinado

### 1.1 Batch 1: Foundation (Correcciones Independientes)

| Orden | Correccion | Archivo | Tipo Cambio | Riesgo |
|-------|------------|---------|-------------|--------|
| 1.1 | COR-001 | 06-purchase.sql | ALTER TYPE | MEDIO |
| 1.2 | COR-002 | 05-inventory.sql | ALTER TYPE | MEDIO |
| 1.3 | COR-004 | 04-financial.sql | ALTER TABLE + TYPE | MEDIO |
| 1.4 | COR-005 | 04-financial.sql | CREATE TABLE | BAJO |
| 1.5 | COR-006 | 07-sales.sql | ALTER TABLE | BAJO |
| 1.6 | COR-007 | 05-inventory.sql | CREATE TABLE + ALTER | BAJO |
| 1.7 | COR-008 | 05-inventory.sql | CREATE TABLEs | BAJO |
| 1.8 | COR-010 | 07-sales.sql, 06-purchase.sql | ALTER TABLE | BAJO |
| 1.9 | COR-011 | 07-sales.sql, 06-purchase.sql | ALTER TABLE | BAJO |

### 1.2 Batch 2: Dependencias Nivel 1

| Orden | Correccion | Depende De | Archivo | Tipo Cambio |
|-------|------------|------------|---------|-------------|
| 2.1 | COR-003 | COR-002 | 05-inventory.sql | CREATE TABLE |
| 2.2 | COR-009 | COR-001 | 06-purchase.sql | CREATE FUNCTION |
| 2.3 | COR-012 | COR-006 | 07-sales.sql | ALTER TABLE |

### 1.3 Batch 3: Dependencias Nivel 2

| Orden | Correccion | Depende De | Archivo | Tipo Cambio |
|-------|------------|------------|---------|-------------|
| 3.1 | COR-013 | COR-004 | 04-financial.sql | CREATE TABLEs |
| 3.2 | COR-018 | COR-002, COR-003 | 05-inventory.sql | CREATE FUNCTION |

### 1.4 Batch 4: Features Avanzados

| Orden | Correccion | Archivo | Tipo Cambio |
|-------|------------|---------|-------------|
| 4.1 | COR-014 | 11-crm.sql | CREATE TABLE + ALTER |
| 4.2 | COR-015 | 03-analytics.sql | ALTER + CREATE |
| 4.3 | COR-016 | 08-projects.sql | ALTER + CREATE |
| 4.4 | COR-017 | 08-projects.sql | ALTER TABLE |
| 4.5 | COR-019 | 11-crm.sql | CREATE TABLE |
| 4.6 | COR-020 | 02-core.sql | CREATE TABLE + FUNCTION |

---

## 2. Scripts de Migracion

### 2.1 Migration: COR-001 (PO States)

```sql
-- Migration: 20260104_001_po_to_approve_state.sql
-- Correccion: COR-001
-- Descripcion: Agregar estado 'to_approve' a purchase orders

BEGIN;

-- 1. Agregar nuevo valor al ENUM
ALTER TYPE purchase.order_status ADD VALUE IF NOT EXISTS 'to_approve' BEFORE 'confirmed';

-- 2. Renombrar 'confirmed' a 'purchase' (Odoo naming)
-- Nota: PostgreSQL no permite renombrar valores de ENUM directamente
-- Se debe crear nuevo tipo si se requiere renombrar

-- 3. Agregar campos de aprobacion
ALTER TABLE purchase.purchase_orders
ADD COLUMN IF NOT EXISTS approval_required BOOLEAN DEFAULT FALSE,
ADD COLUMN IF NOT EXISTS amount_approval_threshold DECIMAL(15, 2);

-- 4. Actualizar comentarios
COMMENT ON COLUMN purchase.purchase_orders.approval_required IS
'Indica si la PO requiere aprobacion segun threshold';

COMMIT;
```

### 2.2 Migration: COR-002 (Move States)

```sql
-- Migration: 20260104_002_move_states.sql
-- Correccion: COR-002
-- Descripcion: Agregar estados 'waiting' y 'partially_available' a stock moves

BEGIN;

-- 1. Agregar nuevos valores al ENUM
ALTER TYPE inventory.move_status ADD VALUE IF NOT EXISTS 'waiting' AFTER 'draft';
ALTER TYPE inventory.move_status ADD VALUE IF NOT EXISTS 'partially_available' AFTER 'confirmed';

-- 2. Actualizar comentarios
COMMENT ON TYPE inventory.move_status IS
'Estados de movimiento: draft -> waiting -> confirmed -> partially_available -> assigned -> done/cancelled';

COMMIT;
```

### 2.3 Migration: COR-003 (Move Lines)

```sql
-- Migration: 20260104_003_stock_move_lines.sql
-- Correccion: COR-003
-- Descripcion: Crear tabla stock_move_lines para granularidad lote/serie

BEGIN;

-- 1. Crear tabla stock_move_lines
CREATE TABLE IF NOT EXISTS inventory.stock_move_lines (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,

  -- Relacion con move
  move_id UUID NOT NULL REFERENCES inventory.stock_moves(id) ON DELETE CASCADE,

  -- Producto
  product_id UUID NOT NULL REFERENCES inventory.products(id),
  product_uom_id UUID NOT NULL REFERENCES core.uom(id),

  -- Lote/Serie/Paquete
  lot_id UUID REFERENCES inventory.lots(id),
  package_id UUID, -- Futuro: packages table
  result_package_id UUID, -- Futuro: packages table
  owner_id UUID REFERENCES core.partners(id),

  -- Ubicaciones
  location_id UUID NOT NULL REFERENCES inventory.locations(id),
  location_dest_id UUID NOT NULL REFERENCES inventory.locations(id),

  -- Cantidades
  quantity DECIMAL(12, 4) NOT NULL,
  quantity_done DECIMAL(12, 4) DEFAULT 0,

  -- Estado
  state VARCHAR(20),

  -- Fechas
  date TIMESTAMP,

  -- Referencia
  reference VARCHAR(255),

  -- Auditoria
  created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  created_by UUID REFERENCES auth.users(id),
  updated_at TIMESTAMP,

  CONSTRAINT chk_move_lines_qty CHECK (quantity > 0),
  CONSTRAINT chk_move_lines_qty_done CHECK (quantity_done >= 0 AND quantity_done <= quantity)
);

-- 2. Indices
CREATE INDEX idx_stock_move_lines_tenant_id ON inventory.stock_move_lines(tenant_id);
CREATE INDEX idx_stock_move_lines_move_id ON inventory.stock_move_lines(move_id);
CREATE INDEX idx_stock_move_lines_product_id ON inventory.stock_move_lines(product_id);
CREATE INDEX idx_stock_move_lines_lot_id ON inventory.stock_move_lines(lot_id);
CREATE INDEX idx_stock_move_lines_location ON inventory.stock_move_lines(location_id, location_dest_id);

-- 3. RLS
ALTER TABLE inventory.stock_move_lines ENABLE ROW LEVEL SECURITY;
CREATE POLICY tenant_isolation_stock_move_lines ON inventory.stock_move_lines
  USING (tenant_id = get_current_tenant_id());

-- 4. Comentarios
COMMENT ON TABLE inventory.stock_move_lines IS
'Lineas de movimiento de stock para granularidad a nivel lote/serie (equivalente a stock.move.line Odoo)';

COMMIT;
```

### 2.4 Migration: COR-004 (Payment State)

```sql
-- Migration: 20260104_004_invoice_payment_state.sql
-- Correccion: COR-004
-- Descripcion: Agregar payment_state a facturas

BEGIN;

-- 1. Crear ENUM para payment_state
CREATE TYPE financial.payment_state AS ENUM (
  'not_paid',
  'in_payment',
  'paid',
  'partial',
  'reversed'
);

-- 2. Agregar columna
ALTER TABLE financial.invoices
ADD COLUMN IF NOT EXISTS payment_state financial.payment_state DEFAULT 'not_paid';

-- 3. Migrar datos existentes
UPDATE financial.invoices
SET payment_state = CASE
  WHEN status = 'paid' THEN 'paid'::financial.payment_state
  WHEN amount_paid > 0 AND amount_paid < amount_total THEN 'partial'::financial.payment_state
  ELSE 'not_paid'::financial.payment_state
END
WHERE payment_state IS NULL;

-- 4. Comentarios
COMMENT ON COLUMN financial.invoices.payment_state IS
'Estado de pago: not_paid, in_payment, paid, partial, reversed (independiente del estado contable)';

COMMIT;
```

### 2.5 Migration: COR-005 (Tax Groups)

```sql
-- Migration: 20260104_005_tax_groups.sql
-- Correccion: COR-005
-- Descripcion: Implementar sistema de tax groups

BEGIN;

-- 1. Crear tabla tax_groups
CREATE TABLE IF NOT EXISTS financial.tax_groups (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,

  name VARCHAR(100) NOT NULL,
  sequence INTEGER DEFAULT 10,
  country_id UUID, -- Futuro: countries table

  -- Auditoria
  created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  created_by UUID REFERENCES auth.users(id),

  CONSTRAINT uq_tax_groups_name_tenant UNIQUE (tenant_id, name)
);

-- 2. Agregar campos a taxes
ALTER TABLE financial.taxes
ADD COLUMN IF NOT EXISTS tax_group_id UUID REFERENCES financial.tax_groups(id),
ADD COLUMN IF NOT EXISTS amount_type VARCHAR(20) DEFAULT 'percent', -- percent, fixed, group, division
ADD COLUMN IF NOT EXISTS include_base_amount BOOLEAN DEFAULT FALSE,
ADD COLUMN IF NOT EXISTS price_include BOOLEAN DEFAULT FALSE,
ADD COLUMN IF NOT EXISTS children_tax_ids UUID[] DEFAULT '{}';

-- 3. Indices y RLS
CREATE INDEX idx_tax_groups_tenant_id ON financial.tax_groups(tenant_id);

ALTER TABLE financial.tax_groups ENABLE ROW LEVEL SECURITY;
CREATE POLICY tenant_isolation_tax_groups ON financial.tax_groups
  USING (tenant_id = get_current_tenant_id());

-- 4. Constraint para amount_type
ALTER TABLE financial.taxes
ADD CONSTRAINT chk_taxes_amount_type
CHECK (amount_type IN ('percent', 'fixed', 'group', 'division'));

-- 5. Comentarios
COMMENT ON TABLE financial.tax_groups IS
'Grupos de impuestos para clasificacion y reporte (equivalente a account.tax.group Odoo)';
COMMENT ON COLUMN financial.taxes.amount_type IS
'Tipo de calculo: percent (%), fixed (monto fijo), group (suma de hijos), division (100*price/100+rate)';

COMMIT;
```

### 2.6 Migration: COR-006 (SO-Invoice Link)

```sql
-- Migration: 20260104_006_sales_invoice_link.sql
-- Correccion: COR-006
-- Descripcion: Vincular sales orders con invoices

BEGIN;

-- 1. Agregar campos a sales_orders
ALTER TABLE sales.sales_orders
ADD COLUMN IF NOT EXISTS invoice_ids UUID[] DEFAULT '{}';

-- 2. Agregar campo computed (simulado con trigger)
ALTER TABLE sales.sales_orders
ADD COLUMN IF NOT EXISTS invoice_count INTEGER DEFAULT 0;

-- 3. Funcion para actualizar invoice_count
CREATE OR REPLACE FUNCTION sales.update_invoice_count()
RETURNS TRIGGER AS $$
BEGIN
  NEW.invoice_count := COALESCE(array_length(NEW.invoice_ids, 1), 0);
  RETURN NEW;
END;
$$ LANGUAGE plpgsql;

-- 4. Trigger
CREATE TRIGGER trg_sales_orders_invoice_count
  BEFORE INSERT OR UPDATE OF invoice_ids ON sales.sales_orders
  FOR EACH ROW
  EXECUTE FUNCTION sales.update_invoice_count();

-- 5. Comentarios
COMMENT ON COLUMN sales.sales_orders.invoice_ids IS
'Array de UUIDs de facturas vinculadas a esta orden de venta';
COMMENT ON COLUMN sales.sales_orders.invoice_count IS
'Cantidad de facturas vinculadas (computed)';

COMMIT;
```

---

## 3. Scripts de Rollback

### 3.1 Rollback: COR-001

```sql
-- Rollback: 20260104_001_po_to_approve_state_rollback.sql
BEGIN;

-- Nota: PostgreSQL no permite eliminar valores de ENUM
-- Se deben migrar datos y recrear tipo si es necesario

ALTER TABLE purchase.purchase_orders
DROP COLUMN IF EXISTS approval_required,
DROP COLUMN IF EXISTS amount_approval_threshold;

COMMIT;
```

### 3.2 Rollback: COR-003

```sql
-- Rollback: 20260104_003_stock_move_lines_rollback.sql
BEGIN;

DROP TABLE IF EXISTS inventory.stock_move_lines CASCADE;

COMMIT;
```

### 3.3 Rollback: COR-004

```sql
-- Rollback: 20260104_004_invoice_payment_state_rollback.sql
BEGIN;

ALTER TABLE financial.invoices
DROP COLUMN IF EXISTS payment_state;

DROP TYPE IF EXISTS financial.payment_state;

COMMIT;
```

### 3.4 Rollback: COR-005

```sql
-- Rollback: 20260104_005_tax_groups_rollback.sql
BEGIN;

ALTER TABLE financial.taxes
DROP COLUMN IF EXISTS tax_group_id,
DROP COLUMN IF EXISTS amount_type,
DROP COLUMN IF EXISTS include_base_amount,
DROP COLUMN IF EXISTS price_include,
DROP COLUMN IF EXISTS children_tax_ids;

DROP TABLE IF EXISTS financial.tax_groups;

COMMIT;
```

### 3.5 Rollback: COR-006

```sql
-- Rollback: 20260104_006_sales_invoice_link_rollback.sql
BEGIN;

DROP TRIGGER IF EXISTS trg_sales_orders_invoice_count ON sales.sales_orders;
DROP FUNCTION IF EXISTS sales.update_invoice_count();

ALTER TABLE sales.sales_orders
DROP COLUMN IF EXISTS invoice_ids,
DROP COLUMN IF EXISTS invoice_count;

COMMIT;
```

---

## 4. Tests de Regresion

### 4.1 Test Suite: COR-001 (PO States)

```sql
-- Test: test_cor001_po_states.sql

-- Test 1: Verificar que nuevo estado existe
DO $$
BEGIN
  ASSERT EXISTS (
    SELECT 1 FROM pg_enum
    WHERE enumlabel = 'to_approve'
    AND enumtypid = 'purchase.order_status'::regtype
  ), 'Estado to_approve debe existir';
END $$;

-- Test 2: Verificar transicion de estados
DO $$
DECLARE
  v_po_id UUID;
BEGIN
  -- Crear PO de prueba
  INSERT INTO purchase.purchase_orders (tenant_id, company_id, name, partner_id, order_date, currency_id, status)
  VALUES (get_current_tenant_id(), '...', 'TEST-001', '...', CURRENT_DATE, '...', 'draft')
  RETURNING id INTO v_po_id;

  -- Verificar transicion draft -> to_approve
  UPDATE purchase.purchase_orders SET status = 'to_approve' WHERE id = v_po_id;
  ASSERT (SELECT status FROM purchase.purchase_orders WHERE id = v_po_id) = 'to_approve';

  -- Cleanup
  DELETE FROM purchase.purchase_orders WHERE id = v_po_id;
END $$;
```

### 4.2 Test Suite: COR-002 (Move States)

```sql
-- Test: test_cor002_move_states.sql

-- Test 1: Verificar nuevos estados
DO $$
BEGIN
  ASSERT EXISTS (
    SELECT 1 FROM pg_enum
    WHERE enumlabel = 'waiting'
    AND enumtypid = 'inventory.move_status'::regtype
  ), 'Estado waiting debe existir';

  ASSERT EXISTS (
    SELECT 1 FROM pg_enum
    WHERE enumlabel = 'partially_available'
    AND enumtypid = 'inventory.move_status'::regtype
  ), 'Estado partially_available debe existir';
END $$;
```

### 4.3 Test Suite: COR-003 (Move Lines)

```sql
-- Test: test_cor003_move_lines.sql

-- Test 1: Verificar tabla existe
DO $$
BEGIN
  ASSERT EXISTS (
    SELECT 1 FROM information_schema.tables
    WHERE table_schema = 'inventory'
    AND table_name = 'stock_move_lines'
  ), 'Tabla stock_move_lines debe existir';
END $$;

-- Test 2: Verificar FK a stock_moves
DO $$
BEGIN
  ASSERT EXISTS (
    SELECT 1 FROM information_schema.table_constraints
    WHERE table_schema = 'inventory'
    AND table_name = 'stock_move_lines'
    AND constraint_type = 'FOREIGN KEY'
  ), 'FK a stock_moves debe existir';
END $$;
```

---

## 5. Documentacion a Actualizar Post-Ejecucion

### 5.1 Por Batch

| Batch | Documentos a Actualizar |
|-------|------------------------|
| Batch 1 | inventory-domain.md, financial-domain.md, sales-domain.md, INVENTARIO-OBJETOS-BD.yml |
| Batch 2 | inventory-domain.md, purchase workflows, GRAFO-DEPENDENCIAS-SCHEMAS.md |
| Batch 3 | financial-domain.md, MATRIZ-TRAZABILIDAD-RF-ET-BD.md |
| Batch 4 | crm-domain.md, analytics-domain.md, projects-domain.md |

### 5.2 Checklist Post-Ejecucion

- [ ] Actualizar domain models con nuevas entidades/campos
- [ ] Sincronizar schema docs (docs/04-modelado/database-design/schemas/)
- [ ] Actualizar INVENTARIO-OBJETOS-BD.yml
- [ ] Actualizar GRAFO-DEPENDENCIAS-SCHEMAS.md
- [ ] Crear nuevos workflows (WORKFLOW-PURCHASE-APPROVAL.md, etc.)
- [ ] Actualizar VALIDACION-COBERTURA-ODOO.md
- [ ] Regenerar documentacion de API si aplica

---

## 6. Plan de Rollback General

### 6.1 Criterios de Rollback

| Criterio | Accion |
|----------|--------|
| Test de regresion falla | Rollback inmediato |
| Error en produccion < 1 hora | Rollback script |
| Error en produccion > 1 hora | Evaluar fix forward |
| Datos corruptos | Restore de backup |

### 6.2 Orden de Rollback

```
Rollback debe ser en orden inverso:
1. Batch 4 -> Batch 3 -> Batch 2 -> Batch 1

Dentro de cada batch, rollback en orden inverso de ejecucion.
```

---

## 7. Aprobacion del Plan Refinado

### 7.1 Checklist de Aprobacion

- [x] Plan de ejecucion por batches definido
- [x] Scripts de migracion creados (P0)
- [x] Scripts de rollback creados
- [x] Tests de regresion definidos
- [x] Documentacion a actualizar identificada
- [x] Plan de rollback general establecido

### 7.2 Resultado

**PLAN REFINADO APROBADO** para proceder a FASE 6 (Ejecucion)

---

## 8. Proximos Pasos (FASE 6)

1. Crear branch `feature/odoo-alignment-batch1`
2. Ejecutar Batch 1 migrations
3. Ejecutar tests de regresion
4. Actualizar documentacion
5. Merge y continuar con Batch 2

---

**Generado:** 2026-01-04
**Herramienta:** Claude Code
