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)
-- 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)
-- 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)
-- 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)
-- 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)
-- 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)
-- 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
-- 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
-- Rollback: 20260104_003_stock_move_lines_rollback.sql
BEGIN;
DROP TABLE IF EXISTS inventory.stock_move_lines CASCADE;
COMMIT;
3.3 Rollback: COR-004
-- 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
-- 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
-- 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)
-- 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)
-- 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)
-- 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
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
7.2 Resultado
PLAN REFINADO APROBADO para proceder a FASE 6 (Ejecucion)
8. Proximos Pasos (FASE 6)
- Crear branch
feature/odoo-alignment-batch1
- Ejecutar Batch 1 migrations
- Ejecutar tests de regresion
- Actualizar documentacion
- Merge y continuar con Batch 2
Generado: 2026-01-04
Herramienta: Claude Code