# FASE 5: Refinamiento del Plan

**ID:** EPIC-VAL-005
**Fecha:** 2026-01-04
**Estado:** Completado
**Basado en:** FASE-4 (Validacion de Dependencias)

---

## 1. Ajustes al Plan Original

### 1.1 Correcciones Excluidas

| ID Original | Razon de Exclusion |
|-------------|-------------------|
| COR-034 | `core.attachments` ya existe en linea 271 de 02-core.sql |

### 1.2 Correcciones Ajustadas

| ID | Ajuste Realizado |
|----|------------------|
| COR-027 | Solo crear `hr.leave_allocations`. Tablas `leaves` y `leave_types` ya existen |
| COR-030 | Cambiar referencia de `lead_activities` a `activities` |

---

## 2. Plan de Ejecucion Refinado

### 2.1 LOTE 1: Fundamentos Core (Sin Dependencias)

**Archivo:** `02-core.sql`
**Orden:** 1

```sql
-- COR-021: Estados/Provincias
CREATE TABLE core.states (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    country_id UUID NOT NULL REFERENCES core.countries(id),
    name VARCHAR(100) NOT NULL,
    code VARCHAR(10) NOT NULL,
    is_active BOOLEAN DEFAULT TRUE,
    created_at TIMESTAMP DEFAULT NOW(),
    updated_at TIMESTAMP DEFAULT NOW(),
    UNIQUE(country_id, code)
);

CREATE INDEX idx_states_country ON core.states(country_id);
COMMENT ON TABLE core.states IS 'States/Provinces - Equivalent to res.country.state';
```

**Cambio en partners:**
```sql
ALTER TABLE core.partners ADD COLUMN state_id UUID REFERENCES core.states(id);
ALTER TABLE core.addresses ADD COLUMN state_id UUID REFERENCES core.states(id);
```

---

### 2.2 LOTE 2: Sistema Bancario

**Archivo:** `02-core.sql`
**Orden:** 2
**Depende de:** LOTE 1

```sql
-- COR-022a: Bancos
CREATE TABLE core.banks (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    name VARCHAR(255) NOT NULL,
    bic VARCHAR(11),
    country_id UUID REFERENCES core.countries(id),
    street VARCHAR(255),
    city VARCHAR(100),
    zip VARCHAR(20),
    phone VARCHAR(50),
    email VARCHAR(255),
    is_active BOOLEAN DEFAULT TRUE,
    created_at TIMESTAMP DEFAULT NOW(),
    updated_at TIMESTAMP DEFAULT NOW()
);

CREATE INDEX idx_banks_country ON core.banks(country_id);
CREATE UNIQUE INDEX idx_banks_bic ON core.banks(bic) WHERE bic IS NOT NULL;
COMMENT ON TABLE core.banks IS 'Banks catalog - Equivalent to res.bank';

-- COR-022b: Cuentas bancarias de partners
CREATE TABLE core.partner_banks (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
    partner_id UUID NOT NULL REFERENCES core.partners(id) ON DELETE CASCADE,
    bank_id UUID REFERENCES core.banks(id),
    acc_number VARCHAR(64) NOT NULL,
    acc_holder_name VARCHAR(255),
    sequence INTEGER DEFAULT 10,
    currency_id UUID REFERENCES core.currencies(id),
    is_active BOOLEAN DEFAULT TRUE,
    created_at TIMESTAMP DEFAULT NOW(),
    updated_at TIMESTAMP DEFAULT NOW()
);

CREATE INDEX idx_partner_banks_partner ON core.partner_banks(partner_id);
CREATE INDEX idx_partner_banks_bank ON core.partner_banks(bank_id);
COMMENT ON TABLE core.partner_banks IS 'Partner bank accounts - Equivalent to res.partner.bank';
```

---

### 2.3 LOTE 3: Financial - Tax Repartition

**Archivo:** `04-financial.sql`
**Orden:** 3
**Depende de:** Ninguno

```sql
-- COR-024: Tax Repartition Lines
CREATE TYPE financial.repartition_type AS ENUM ('invoice', 'refund');

CREATE TABLE financial.tax_repartition_lines (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
    tax_id UUID NOT NULL REFERENCES financial.taxes(id) ON DELETE CASCADE,
    repartition_type financial.repartition_type NOT NULL,
    sequence INTEGER DEFAULT 1,
    factor_percent DECIMAL(10,4) DEFAULT 100,
    account_id UUID REFERENCES financial.accounts(id),
    tag_ids UUID[],
    use_in_tax_closing BOOLEAN DEFAULT TRUE,
    created_at TIMESTAMP DEFAULT NOW(),
    updated_at TIMESTAMP DEFAULT NOW()
);

CREATE INDEX idx_tax_repartition_tax ON financial.tax_repartition_lines(tax_id);
COMMENT ON TABLE financial.tax_repartition_lines IS 'Tax repartition lines - Equivalent to account.tax.repartition.line';
```

---

### 2.4 LOTE 4: Financial - Bank Statements

**Archivo:** `04-financial.sql`
**Orden:** 4
**Depende de:** LOTE 2

```sql
-- COR-023: Bank Statements
CREATE TYPE financial.statement_status AS ENUM ('draft', 'open', 'confirm', 'cancelled');

CREATE TABLE financial.bank_statements (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
    journal_id UUID NOT NULL REFERENCES financial.journals(id),
    name VARCHAR(100),
    reference VARCHAR(255),
    date DATE NOT NULL,
    date_done DATE,
    balance_start DECIMAL(20,6) DEFAULT 0,
    balance_end_real DECIMAL(20,6) DEFAULT 0,
    total_entry_encoding DECIMAL(20,6) DEFAULT 0,
    status financial.statement_status DEFAULT 'draft',
    currency_id UUID REFERENCES core.currencies(id),
    is_complete BOOLEAN DEFAULT FALSE,
    created_by UUID REFERENCES auth.users(id),
    created_at TIMESTAMP DEFAULT NOW(),
    updated_at TIMESTAMP DEFAULT NOW()
);

CREATE TABLE financial.bank_statement_lines (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
    statement_id UUID NOT NULL REFERENCES financial.bank_statements(id) ON DELETE CASCADE,
    sequence INTEGER DEFAULT 10,
    date DATE NOT NULL,
    payment_ref VARCHAR(255),
    ref VARCHAR(255),
    partner_id UUID REFERENCES core.partners(id),
    amount DECIMAL(20,6) NOT NULL,
    amount_currency DECIMAL(20,6),
    foreign_currency_id UUID REFERENCES core.currencies(id),
    transaction_type VARCHAR(50),
    narration TEXT,
    is_reconciled BOOLEAN DEFAULT FALSE,
    partner_bank_id UUID REFERENCES core.partner_banks(id),
    account_number VARCHAR(64),
    created_at TIMESTAMP DEFAULT NOW(),
    updated_at TIMESTAMP DEFAULT NOW()
);

CREATE INDEX idx_bank_statements_journal ON financial.bank_statements(journal_id);
CREATE INDEX idx_bank_statements_date ON financial.bank_statements(date);
CREATE INDEX idx_bank_statement_lines_statement ON financial.bank_statement_lines(statement_id);

COMMENT ON TABLE financial.bank_statements IS 'Bank statements - Equivalent to account.bank.statement';
COMMENT ON TABLE financial.bank_statement_lines IS 'Bank statement lines - Equivalent to account.bank.statement.line';
```

---

### 2.5 LOTE 5: Financial - Fiscal Positions

**Archivo:** `04-financial.sql`
**Orden:** 5
**Depende de:** LOTE 1

```sql
-- COR-028: Fiscal Positions
CREATE TABLE financial.fiscal_positions (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
    name VARCHAR(255) NOT NULL,
    sequence INTEGER DEFAULT 10,
    is_active BOOLEAN DEFAULT TRUE,
    company_id UUID REFERENCES core.companies(id),
    country_id UUID REFERENCES core.countries(id),
    state_ids UUID[],  -- Array of core.states IDs
    zip_from VARCHAR(20),
    zip_to VARCHAR(20),
    auto_apply BOOLEAN DEFAULT FALSE,
    vat_required BOOLEAN DEFAULT FALSE,
    note TEXT,
    created_at TIMESTAMP DEFAULT NOW(),
    updated_at TIMESTAMP DEFAULT NOW()
);

CREATE TABLE financial.fiscal_position_taxes (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    fiscal_position_id UUID NOT NULL REFERENCES financial.fiscal_positions(id) ON DELETE CASCADE,
    tax_src_id UUID NOT NULL REFERENCES financial.taxes(id),
    tax_dest_id UUID REFERENCES financial.taxes(id)
);

CREATE TABLE financial.fiscal_position_accounts (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    fiscal_position_id UUID NOT NULL REFERENCES financial.fiscal_positions(id) ON DELETE CASCADE,
    account_src_id UUID NOT NULL REFERENCES financial.accounts(id),
    account_dest_id UUID NOT NULL REFERENCES financial.accounts(id)
);

CREATE INDEX idx_fiscal_positions_country ON financial.fiscal_positions(country_id);
COMMENT ON TABLE financial.fiscal_positions IS 'Fiscal positions - Equivalent to account.fiscal.position';
```

---

### 2.6 LOTE 6: Inventory - Routes & Rules

**Archivo:** `05-inventory.sql`
**Orden:** 6
**Depende de:** Ninguno

```sql
-- COR-025: Routes & Rules
CREATE TYPE inventory.rule_action AS ENUM ('pull', 'push', 'pull_push', 'buy', 'manufacture');
CREATE TYPE inventory.procurement_type AS ENUM ('make_to_stock', 'make_to_order');

CREATE TABLE inventory.routes (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
    name VARCHAR(255) NOT NULL,
    sequence INTEGER DEFAULT 10,
    is_active BOOLEAN DEFAULT TRUE,
    product_selectable BOOLEAN DEFAULT TRUE,
    product_categ_selectable BOOLEAN DEFAULT TRUE,
    warehouse_selectable BOOLEAN DEFAULT TRUE,
    supplied_wh_id UUID REFERENCES inventory.warehouses(id),
    supplier_wh_id UUID REFERENCES inventory.warehouses(id),
    company_id UUID REFERENCES core.companies(id),
    created_at TIMESTAMP DEFAULT NOW(),
    updated_at TIMESTAMP DEFAULT NOW()
);

CREATE TABLE inventory.stock_rules (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
    name VARCHAR(255) NOT NULL,
    route_id UUID NOT NULL REFERENCES inventory.routes(id) ON DELETE CASCADE,
    sequence INTEGER DEFAULT 20,
    action inventory.rule_action NOT NULL,
    procure_method inventory.procurement_type DEFAULT 'make_to_stock',
    location_src_id UUID REFERENCES inventory.locations(id),
    location_dest_id UUID NOT NULL REFERENCES inventory.locations(id),
    picking_type_id UUID REFERENCES inventory.picking_types(id),
    delay INTEGER DEFAULT 0,
    partner_address_id UUID REFERENCES core.partners(id),
    propagate_cancel BOOLEAN DEFAULT FALSE,
    warehouse_id UUID REFERENCES inventory.warehouses(id),
    is_active BOOLEAN DEFAULT TRUE,
    created_at TIMESTAMP DEFAULT NOW(),
    updated_at TIMESTAMP DEFAULT NOW()
);

CREATE TABLE inventory.product_routes (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    product_id UUID NOT NULL REFERENCES inventory.products(id) ON DELETE CASCADE,
    route_id UUID NOT NULL REFERENCES inventory.routes(id) ON DELETE CASCADE,
    UNIQUE(product_id, route_id)
);

CREATE INDEX idx_routes_warehouse ON inventory.routes(supplied_wh_id);
CREATE INDEX idx_rules_route ON inventory.stock_rules(route_id);
COMMENT ON TABLE inventory.routes IS 'Stock routes - Equivalent to stock.route';
COMMENT ON TABLE inventory.stock_rules IS 'Stock rules - Equivalent to stock.rule';
```

---

### 2.7 LOTE 7: Inventory - Stock Scrap

**Archivo:** `05-inventory.sql`
**Orden:** 7
**Depende de:** Ninguno

```sql
-- COR-031: Stock Scrap
CREATE TYPE inventory.scrap_status AS ENUM ('draft', 'done');

CREATE TABLE inventory.stock_scrap (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
    name VARCHAR(100),
    product_id UUID NOT NULL REFERENCES inventory.products(id),
    product_uom_id UUID REFERENCES core.uom(id),
    lot_id UUID REFERENCES inventory.lots(id),
    scrap_qty DECIMAL(20,6) NOT NULL,
    scrap_location_id UUID NOT NULL REFERENCES inventory.locations(id),
    location_id UUID NOT NULL REFERENCES inventory.locations(id),
    move_id UUID REFERENCES inventory.stock_moves(id),
    picking_id UUID REFERENCES inventory.pickings(id),
    origin VARCHAR(255),
    date_done TIMESTAMP,
    status inventory.scrap_status DEFAULT 'draft',
    created_by UUID REFERENCES auth.users(id),
    created_at TIMESTAMP DEFAULT NOW(),
    updated_at TIMESTAMP DEFAULT NOW()
);

CREATE INDEX idx_stock_scrap_product ON inventory.stock_scrap(product_id);
COMMENT ON TABLE inventory.stock_scrap IS 'Stock scrap - Equivalent to stock.scrap';

-- Funcion para validar scrap
CREATE OR REPLACE FUNCTION inventory.validate_scrap(p_scrap_id UUID)
RETURNS UUID AS $$
DECLARE
    v_scrap RECORD;
    v_move_id UUID;
BEGIN
    SELECT * INTO v_scrap FROM inventory.stock_scrap WHERE id = p_scrap_id;

    IF NOT FOUND THEN
        RAISE EXCEPTION 'Scrap record % not found', p_scrap_id;
    END IF;

    IF v_scrap.status = 'done' THEN
        RETURN v_scrap.move_id;
    END IF;

    INSERT INTO inventory.stock_moves (
        tenant_id, product_id, product_uom_id, quantity,
        location_id, location_dest_id, origin, status
    ) VALUES (
        v_scrap.tenant_id, v_scrap.product_id, v_scrap.product_uom_id,
        v_scrap.scrap_qty, v_scrap.location_id, v_scrap.scrap_location_id,
        v_scrap.name, 'done'
    ) RETURNING id INTO v_move_id;

    UPDATE inventory.stock_scrap
    SET status = 'done', move_id = v_move_id, date_done = NOW()
    WHERE id = p_scrap_id;

    RETURN v_move_id;
END;
$$ LANGUAGE plpgsql;
```

---

### 2.8 LOTE 8: Purchase Functions

**Archivo:** `06-purchase.sql`
**Orden:** 8
**Depende de:** Ninguno

```sql
-- COR-029: Purchase Functions
CREATE OR REPLACE FUNCTION purchase.button_cancel(p_order_id UUID)
RETURNS VOID AS $$
DECLARE
    v_order RECORD;
BEGIN
    SELECT * INTO v_order FROM purchase.purchase_orders WHERE id = p_order_id;

    IF NOT FOUND THEN
        RAISE EXCEPTION 'Purchase order % not found', p_order_id;
    END IF;

    IF v_order.locked THEN
        RAISE EXCEPTION 'Cannot cancel locked order';
    END IF;

    IF v_order.status = 'cancelled' THEN
        RETURN;
    END IF;

    UPDATE inventory.pickings
    SET status = 'cancelled'
    WHERE origin_document_type = 'purchase_order'
      AND origin_document_id = p_order_id
      AND status != 'done';

    UPDATE purchase.purchase_orders
    SET status = 'cancelled', updated_at = NOW()
    WHERE id = p_order_id;
END;
$$ LANGUAGE plpgsql;

CREATE OR REPLACE FUNCTION purchase.button_draft(p_order_id UUID)
RETURNS VOID AS $$
DECLARE
    v_order RECORD;
BEGIN
    SELECT * INTO v_order FROM purchase.purchase_orders WHERE id = p_order_id;

    IF NOT FOUND THEN
        RAISE EXCEPTION 'Purchase order % not found', p_order_id;
    END IF;

    IF v_order.status NOT IN ('cancelled', 'sent') THEN
        RAISE EXCEPTION 'Can only set to draft from cancelled or sent state';
    END IF;

    UPDATE purchase.purchase_orders
    SET status = 'draft', updated_at = NOW()
    WHERE id = p_order_id;
END;
$$ LANGUAGE plpgsql;

COMMENT ON FUNCTION purchase.button_cancel IS 'Cancel purchase order - COR-029';
COMMENT ON FUNCTION purchase.button_draft IS 'Set purchase order to draft - COR-029';
```

---

### 2.9 LOTE 9: Sales Templates

**Archivo:** `07-sales.sql`
**Orden:** 9
**Depende de:** Ninguno

```sql
-- COR-033: Sales Order Templates
CREATE TABLE sales.order_templates (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
    name VARCHAR(255) NOT NULL,
    note TEXT,
    number_of_days INTEGER DEFAULT 0,
    require_signature BOOLEAN DEFAULT FALSE,
    require_payment BOOLEAN DEFAULT FALSE,
    prepayment_percent DECIMAL(5,2) DEFAULT 0,
    is_active BOOLEAN DEFAULT TRUE,
    created_at TIMESTAMP DEFAULT NOW(),
    updated_at TIMESTAMP DEFAULT NOW()
);

CREATE TABLE sales.order_template_lines (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    template_id UUID NOT NULL REFERENCES sales.order_templates(id) ON DELETE CASCADE,
    sequence INTEGER DEFAULT 10,
    product_id UUID REFERENCES inventory.products(id),
    name TEXT,
    quantity DECIMAL(20,6) DEFAULT 1,
    product_uom_id UUID REFERENCES core.uom(id),
    display_type VARCHAR(20)
);

CREATE INDEX idx_order_templates_tenant ON sales.order_templates(tenant_id);
COMMENT ON TABLE sales.order_templates IS 'Sale order templates - Equivalent to sale.order.template';
```

---

### 2.10 LOTE 10: Projects Updates

**Archivo:** `08-projects.sql`
**Orden:** 10
**Depende de:** Ninguno

```sql
-- COR-032: Project Updates
CREATE TYPE projects.update_status AS ENUM ('on_track', 'at_risk', 'off_track', 'done');

CREATE TABLE projects.project_updates (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
    project_id UUID NOT NULL REFERENCES projects.projects(id) ON DELETE CASCADE,
    name VARCHAR(255) NOT NULL,
    status projects.update_status DEFAULT 'on_track',
    progress INTEGER CHECK (progress >= 0 AND progress <= 100),
    date DATE NOT NULL DEFAULT CURRENT_DATE,
    description TEXT,
    user_id UUID NOT NULL REFERENCES auth.users(id),
    created_at TIMESTAMP DEFAULT NOW(),
    updated_at TIMESTAMP DEFAULT NOW()
);

CREATE INDEX idx_project_updates_project ON projects.project_updates(project_id);
CREATE INDEX idx_project_updates_date ON projects.project_updates(date DESC);
COMMENT ON TABLE projects.project_updates IS 'Project updates - Equivalent to project.update';
```

---

### 2.11 LOTE 11: CRM Merge Leads

**Archivo:** `11-crm.sql`
**Orden:** 11
**Depende de:** Ninguno

```sql
-- COR-030: Merge Leads (Ajustado)
ALTER TABLE crm.leads ADD COLUMN IF NOT EXISTS merged_into_id UUID REFERENCES crm.leads(id);

CREATE OR REPLACE FUNCTION crm.merge_leads(
    p_lead_ids UUID[],
    p_target_lead_id UUID
)
RETURNS UUID AS $$
DECLARE
    v_lead_id UUID;
    v_target RECORD;
BEGIN
    SELECT * INTO v_target FROM crm.leads WHERE id = p_target_lead_id;
    IF NOT FOUND THEN
        RAISE EXCEPTION 'Target lead % not found', p_target_lead_id;
    END IF;

    FOREACH v_lead_id IN ARRAY p_lead_ids LOOP
        IF v_lead_id != p_target_lead_id THEN
            -- Move activities
            UPDATE crm.activities
            SET lead_id = p_target_lead_id
            WHERE lead_id = v_lead_id;

            -- Accumulate expected revenue
            UPDATE crm.leads t
            SET expected_revenue = t.expected_revenue + COALESCE(
                (SELECT expected_revenue FROM crm.leads WHERE id = v_lead_id), 0
            )
            WHERE t.id = p_target_lead_id;

            -- Mark as merged
            UPDATE crm.leads
            SET is_deleted = TRUE,
                merged_into_id = p_target_lead_id,
                updated_at = NOW()
            WHERE id = v_lead_id;
        END IF;
    END LOOP;

    RETURN p_target_lead_id;
END;
$$ LANGUAGE plpgsql;

COMMENT ON FUNCTION crm.merge_leads IS 'Merge multiple leads into one - COR-030';
```

---

### 2.12 LOTE 12: HR Attendances & Leave Allocations

**Archivo:** `12-hr.sql`
**Orden:** 12
**Depende de:** Ninguno

```sql
-- COR-026: Attendances
CREATE TABLE hr.attendances (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
    employee_id UUID NOT NULL REFERENCES hr.employees(id) ON DELETE CASCADE,
    check_in TIMESTAMP NOT NULL,
    check_out TIMESTAMP,
    worked_hours DECIMAL(10,4),
    overtime_hours DECIMAL(10,4) DEFAULT 0,
    is_overtime BOOLEAN DEFAULT FALSE,
    notes TEXT,
    created_at TIMESTAMP DEFAULT NOW(),
    updated_at TIMESTAMP DEFAULT NOW(),
    CONSTRAINT valid_checkout CHECK (check_out IS NULL OR check_out > check_in)
);

CREATE INDEX idx_attendances_employee ON hr.attendances(employee_id);
CREATE INDEX idx_attendances_checkin ON hr.attendances(check_in);
COMMENT ON TABLE hr.attendances IS 'Employee attendances - Equivalent to hr.attendance';

-- COR-027: Leave Allocations (Solo tabla faltante)
CREATE TABLE hr.leave_allocations (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
    employee_id UUID NOT NULL REFERENCES hr.employees(id) ON DELETE CASCADE,
    leave_type_id UUID NOT NULL REFERENCES hr.leave_types(id),
    name VARCHAR(255),
    number_of_days DECIMAL(10,2) NOT NULL,
    date_from DATE,
    date_to DATE,
    status hr.leave_status DEFAULT 'draft',
    allocation_type VARCHAR(20) DEFAULT 'regular',
    notes TEXT,
    approved_by UUID REFERENCES auth.users(id),
    approved_at TIMESTAMP,
    created_at TIMESTAMP DEFAULT NOW(),
    updated_at TIMESTAMP DEFAULT NOW()
);

CREATE INDEX idx_leave_allocations_employee ON hr.leave_allocations(employee_id);
COMMENT ON TABLE hr.leave_allocations IS 'Leave allocations - Equivalent to hr.leave.allocation';
```

---

## 3. Resumen de Ejecucion

### 3.1 Orden de Ejecucion

| Lote | Archivo | Correcciones | Tablas | Funciones |
|------|---------|--------------|--------|-----------|
| 1 | 02-core.sql | COR-021 | 1 | 0 |
| 2 | 02-core.sql | COR-022 | 2 | 0 |
| 3 | 04-financial.sql | COR-024 | 1 | 0 |
| 4 | 04-financial.sql | COR-023 | 2 | 0 |
| 5 | 04-financial.sql | COR-028 | 3 | 0 |
| 6 | 05-inventory.sql | COR-025 | 3 | 0 |
| 7 | 05-inventory.sql | COR-031 | 1 | 1 |
| 8 | 06-purchase.sql | COR-029 | 0 | 2 |
| 9 | 07-sales.sql | COR-033 | 2 | 0 |
| 10 | 08-projects.sql | COR-032 | 1 | 0 |
| 11 | 11-crm.sql | COR-030 | 0 | 1 |
| 12 | 12-hr.sql | COR-026, COR-027 | 2 | 0 |
| **TOTAL** | **8 archivos** | **13 correcciones** | **18 tablas** | **4 funciones** |

### 3.2 Dependencias Criticas

```
LOTE 1 (states) ──→ LOTE 2 (banks) ──→ LOTE 4 (bank_statements)
                 └──→ LOTE 5 (fiscal_positions)
```

### 3.3 Lotes Independientes (Pueden Ejecutarse en Paralelo)

- LOTE 3, 6, 7, 8, 9, 10, 11, 12

---

## 4. Criterios de Validacion por Lote

| Lote | Criterio | Validacion |
|------|----------|------------|
| 1 | core.states referenciable | SELECT FROM core.states |
| 2 | partner_banks con FK validas | INSERT test |
| 3 | tax_repartition con FK a taxes | INSERT test |
| 4 | bank_statements con statement_lines | FK CASCADE test |
| 5 | fiscal_position_taxes funcional | INSERT mapping test |
| 6 | routes con rules | FK cascade test |
| 7 | scrap crea move | CALL validate_scrap test |
| 8 | button_cancel cancela pickings | Test con PO |
| 9 | templates con lines | INSERT test |
| 10 | updates con status | INSERT test |
| 11 | merge actualiza activities | CALL merge_leads test |
| 12 | attendances + allocations | INSERT test |

---

## 5. Proximos Pasos

1. **FASE 6:** Ejecutar correcciones por lotes
2. **FASE 7:** Validar cada lote despues de ejecucion
3. Actualizar documentacion downstream

---

**Generado:** 2026-01-04
**Herramienta:** Claude Code
**Metodologia:** SCRUM/SIMCO
