-- =============================================================
-- ARCHIVO: 21-inventory.sql
-- DESCRIPCION: Niveles de stock y movimientos de inventario
-- VERSION: 1.0.0
-- PROYECTO: ERP-Core V2
-- FECHA: 2026-01-13
-- DEPENDE DE: 17-products.sql, 18-warehouses.sql
-- =============================================================

-- =====================
-- SCHEMA: inventory (ya creado en 18-warehouses.sql)
-- =====================

-- =====================
-- TABLA: stock_levels
-- Niveles de inventario por producto/almacen/ubicacion
-- =====================
CREATE TABLE IF NOT EXISTS inventory.stock_levels (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
    product_id UUID NOT NULL REFERENCES products.products(id) ON DELETE CASCADE,
    warehouse_id UUID NOT NULL REFERENCES inventory.warehouses(id) ON DELETE CASCADE,
    location_id UUID REFERENCES inventory.warehouse_locations(id) ON DELETE SET NULL,

    -- Cantidades
    quantity_on_hand DECIMAL(15, 4) NOT NULL DEFAULT 0,    -- Cantidad fisica disponible
    quantity_reserved DECIMAL(15, 4) NOT NULL DEFAULT 0,   -- Reservada para ordenes
    quantity_available DECIMAL(15, 4) GENERATED ALWAYS AS (quantity_on_hand - quantity_reserved) STORED,
    quantity_incoming DECIMAL(15, 4) NOT NULL DEFAULT 0,   -- En transito/por recibir
    quantity_outgoing DECIMAL(15, 4) NOT NULL DEFAULT 0,   -- Por enviar

    -- Lote y serie
    lot_number VARCHAR(50),
    serial_number VARCHAR(50),
    expiry_date DATE,

    -- Costo
    unit_cost DECIMAL(15, 4),
    total_cost DECIMAL(15, 4),

    -- Ultima actividad
    last_movement_at TIMESTAMPTZ,
    last_count_at TIMESTAMPTZ,

    -- Metadata
    created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,

    UNIQUE(product_id, warehouse_id, COALESCE(location_id, '00000000-0000-0000-0000-000000000000'::UUID), COALESCE(lot_number, ''), COALESCE(serial_number, ''))
);

-- Indices para stock_levels
CREATE INDEX IF NOT EXISTS idx_stock_levels_tenant ON inventory.stock_levels(tenant_id);
CREATE INDEX IF NOT EXISTS idx_stock_levels_product ON inventory.stock_levels(product_id);
CREATE INDEX IF NOT EXISTS idx_stock_levels_warehouse ON inventory.stock_levels(warehouse_id);
CREATE INDEX IF NOT EXISTS idx_stock_levels_location ON inventory.stock_levels(location_id);
CREATE INDEX IF NOT EXISTS idx_stock_levels_lot ON inventory.stock_levels(lot_number);
CREATE INDEX IF NOT EXISTS idx_stock_levels_serial ON inventory.stock_levels(serial_number);
CREATE INDEX IF NOT EXISTS idx_stock_levels_expiry ON inventory.stock_levels(expiry_date) WHERE expiry_date IS NOT NULL;
CREATE INDEX IF NOT EXISTS idx_stock_levels_low_stock ON inventory.stock_levels(quantity_on_hand) WHERE quantity_on_hand <= 0;
CREATE INDEX IF NOT EXISTS idx_stock_levels_available ON inventory.stock_levels(quantity_available);

-- =====================
-- TABLA: stock_movements
-- Movimientos de inventario (entradas, salidas, transferencias, ajustes)
-- =====================
CREATE TABLE IF NOT EXISTS inventory.stock_movements (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,

    -- Tipo de movimiento
    movement_type VARCHAR(20) NOT NULL, -- receipt, shipment, transfer, adjustment, return, production, consumption
    movement_number VARCHAR(30) NOT NULL, -- Numero secuencial

    -- Producto
    product_id UUID NOT NULL REFERENCES products.products(id) ON DELETE RESTRICT,

    -- Origen y destino
    source_warehouse_id UUID REFERENCES inventory.warehouses(id),
    source_location_id UUID REFERENCES inventory.warehouse_locations(id),
    dest_warehouse_id UUID REFERENCES inventory.warehouses(id),
    dest_location_id UUID REFERENCES inventory.warehouse_locations(id),

    -- Cantidad
    quantity DECIMAL(15, 4) NOT NULL,
    uom VARCHAR(20) DEFAULT 'PZA',

    -- Lote y serie
    lot_number VARCHAR(50),
    serial_number VARCHAR(50),
    expiry_date DATE,

    -- Costo
    unit_cost DECIMAL(15, 4),
    total_cost DECIMAL(15, 4),

    -- Referencia
    reference_type VARCHAR(30),  -- sales_order, purchase_order, transfer_order, adjustment, return
    reference_id UUID,
    reference_number VARCHAR(50),

    -- Razon (para ajustes)
    reason VARCHAR(100),
    notes TEXT,

    -- Estado
    status VARCHAR(20) NOT NULL DEFAULT 'draft', -- draft, confirmed, cancelled
    confirmed_at TIMESTAMPTZ,
    confirmed_by UUID REFERENCES auth.users(id),

    -- Metadata
    created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
    created_by UUID REFERENCES auth.users(id),
    updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
    deleted_at TIMESTAMPTZ
);

-- Indices para stock_movements
CREATE INDEX IF NOT EXISTS idx_stock_movements_tenant ON inventory.stock_movements(tenant_id);
CREATE INDEX IF NOT EXISTS idx_stock_movements_type ON inventory.stock_movements(movement_type);
CREATE INDEX IF NOT EXISTS idx_stock_movements_number ON inventory.stock_movements(movement_number);
CREATE INDEX IF NOT EXISTS idx_stock_movements_product ON inventory.stock_movements(product_id);
CREATE INDEX IF NOT EXISTS idx_stock_movements_source ON inventory.stock_movements(source_warehouse_id);
CREATE INDEX IF NOT EXISTS idx_stock_movements_dest ON inventory.stock_movements(dest_warehouse_id);
CREATE INDEX IF NOT EXISTS idx_stock_movements_status ON inventory.stock_movements(status);
CREATE INDEX IF NOT EXISTS idx_stock_movements_reference ON inventory.stock_movements(reference_type, reference_id);
CREATE INDEX IF NOT EXISTS idx_stock_movements_date ON inventory.stock_movements(created_at);
CREATE INDEX IF NOT EXISTS idx_stock_movements_lot ON inventory.stock_movements(lot_number) WHERE lot_number IS NOT NULL;

-- =====================
-- TABLA: inventory_counts
-- Conteos fisicos de inventario
-- =====================
CREATE TABLE IF NOT EXISTS inventory.inventory_counts (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
    warehouse_id UUID NOT NULL REFERENCES inventory.warehouses(id) ON DELETE CASCADE,

    -- Identificacion
    count_number VARCHAR(30) NOT NULL,
    name VARCHAR(100),

    -- Tipo de conteo
    count_type VARCHAR(20) DEFAULT 'full', -- full, partial, cycle, spot

    -- Fecha programada
    scheduled_date DATE,
    started_at TIMESTAMPTZ,
    completed_at TIMESTAMPTZ,

    -- Estado
    status VARCHAR(20) NOT NULL DEFAULT 'draft', -- draft, in_progress, completed, cancelled

    -- Responsable
    assigned_to UUID REFERENCES auth.users(id),

    -- Notas
    notes TEXT,

    -- Metadata
    created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
    created_by UUID REFERENCES auth.users(id),
    updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
);

-- Indices para inventory_counts
CREATE INDEX IF NOT EXISTS idx_inventory_counts_tenant ON inventory.inventory_counts(tenant_id);
CREATE INDEX IF NOT EXISTS idx_inventory_counts_warehouse ON inventory.inventory_counts(warehouse_id);
CREATE INDEX IF NOT EXISTS idx_inventory_counts_status ON inventory.inventory_counts(status);
CREATE INDEX IF NOT EXISTS idx_inventory_counts_date ON inventory.inventory_counts(scheduled_date);

-- =====================
-- TABLA: inventory_count_lines
-- Lineas de conteo de inventario
-- =====================
CREATE TABLE IF NOT EXISTS inventory.inventory_count_lines (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    count_id UUID NOT NULL REFERENCES inventory.inventory_counts(id) ON DELETE CASCADE,
    product_id UUID NOT NULL REFERENCES products.products(id) ON DELETE RESTRICT,
    location_id UUID REFERENCES inventory.warehouse_locations(id),

    -- Cantidades
    system_quantity DECIMAL(15, 4),     -- Cantidad segun sistema
    counted_quantity DECIMAL(15, 4),    -- Cantidad contada
    difference DECIMAL(15, 4) GENERATED ALWAYS AS (COALESCE(counted_quantity, 0) - COALESCE(system_quantity, 0)) STORED,

    -- Lote y serie
    lot_number VARCHAR(50),
    serial_number VARCHAR(50),

    -- Estado
    is_counted BOOLEAN DEFAULT FALSE,
    counted_at TIMESTAMPTZ,
    counted_by UUID REFERENCES auth.users(id),

    -- Notas
    notes TEXT,

    -- Metadata
    created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
);

-- Indices para inventory_count_lines
CREATE INDEX IF NOT EXISTS idx_inventory_count_lines_count ON inventory.inventory_count_lines(count_id);
CREATE INDEX IF NOT EXISTS idx_inventory_count_lines_product ON inventory.inventory_count_lines(product_id);
CREATE INDEX IF NOT EXISTS idx_inventory_count_lines_location ON inventory.inventory_count_lines(location_id);
CREATE INDEX IF NOT EXISTS idx_inventory_count_lines_counted ON inventory.inventory_count_lines(is_counted);

-- =====================
-- TABLA: transfer_orders
-- Ordenes de transferencia entre almacenes
-- =====================
CREATE TABLE IF NOT EXISTS inventory.transfer_orders (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,

    -- Identificacion
    transfer_number VARCHAR(30) NOT NULL,

    -- Origen y destino
    source_warehouse_id UUID NOT NULL REFERENCES inventory.warehouses(id),
    dest_warehouse_id UUID NOT NULL REFERENCES inventory.warehouses(id),

    -- Fechas
    scheduled_date DATE,
    shipped_at TIMESTAMPTZ,
    received_at TIMESTAMPTZ,

    -- Estado
    status VARCHAR(20) NOT NULL DEFAULT 'draft', -- draft, confirmed, shipped, in_transit, received, cancelled

    -- Notas
    notes TEXT,

    -- Metadata
    created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
    created_by UUID REFERENCES auth.users(id),
    updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
    deleted_at TIMESTAMPTZ,

    UNIQUE(tenant_id, transfer_number)
);

-- Indices para transfer_orders
CREATE INDEX IF NOT EXISTS idx_transfer_orders_tenant ON inventory.transfer_orders(tenant_id);
CREATE INDEX IF NOT EXISTS idx_transfer_orders_source ON inventory.transfer_orders(source_warehouse_id);
CREATE INDEX IF NOT EXISTS idx_transfer_orders_dest ON inventory.transfer_orders(dest_warehouse_id);
CREATE INDEX IF NOT EXISTS idx_transfer_orders_status ON inventory.transfer_orders(status);
CREATE INDEX IF NOT EXISTS idx_transfer_orders_date ON inventory.transfer_orders(scheduled_date);

-- =====================
-- TABLA: transfer_order_lines
-- Lineas de orden de transferencia
-- =====================
CREATE TABLE IF NOT EXISTS inventory.transfer_order_lines (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    transfer_id UUID NOT NULL REFERENCES inventory.transfer_orders(id) ON DELETE CASCADE,
    product_id UUID NOT NULL REFERENCES products.products(id) ON DELETE RESTRICT,

    -- Ubicaciones especificas
    source_location_id UUID REFERENCES inventory.warehouse_locations(id),
    dest_location_id UUID REFERENCES inventory.warehouse_locations(id),

    -- Cantidades
    quantity_requested DECIMAL(15, 4) NOT NULL,
    quantity_shipped DECIMAL(15, 4) DEFAULT 0,
    quantity_received DECIMAL(15, 4) DEFAULT 0,

    -- Lote y serie
    lot_number VARCHAR(50),
    serial_number VARCHAR(50),

    -- Notas
    notes TEXT,

    -- Metadata
    created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
);

-- Indices para transfer_order_lines
CREATE INDEX IF NOT EXISTS idx_transfer_order_lines_transfer ON inventory.transfer_order_lines(transfer_id);
CREATE INDEX IF NOT EXISTS idx_transfer_order_lines_product ON inventory.transfer_order_lines(product_id);

-- =====================
-- COMENTARIOS
-- =====================
COMMENT ON TABLE inventory.stock_levels IS 'Niveles actuales de inventario por producto/almacen/ubicacion';
COMMENT ON COLUMN inventory.stock_levels.quantity_on_hand IS 'Cantidad fisica disponible en el almacen';
COMMENT ON COLUMN inventory.stock_levels.quantity_reserved IS 'Cantidad reservada para ordenes pendientes';
COMMENT ON COLUMN inventory.stock_levels.quantity_available IS 'Cantidad disponible para venta (on_hand - reserved)';
COMMENT ON COLUMN inventory.stock_levels.quantity_incoming IS 'Cantidad en transito o por recibir';

COMMENT ON TABLE inventory.stock_movements IS 'Historial de movimientos de inventario';
COMMENT ON COLUMN inventory.stock_movements.movement_type IS 'Tipo: receipt (entrada), shipment (salida), transfer, adjustment, return, production, consumption';
COMMENT ON COLUMN inventory.stock_movements.status IS 'Estado: draft, confirmed, cancelled';

COMMENT ON TABLE inventory.inventory_counts IS 'Conteos fisicos de inventario para reconciliacion';
COMMENT ON COLUMN inventory.inventory_counts.count_type IS 'Tipo: full (completo), partial, cycle (ciclico), spot (aleatorio)';

COMMENT ON TABLE inventory.transfer_orders IS 'Ordenes de transferencia entre almacenes';
COMMENT ON COLUMN inventory.transfer_orders.status IS 'Estado: draft, confirmed, shipped, in_transit, received, cancelled';
