# Schema: parts_management ## Descripcion Schema para gestion de inventario, refacciones, movimientos de almacen y control de stock. **NOTA:** Este schema depende de `erp-core` para autenticacion y tenants. La columna `tenant_id` referencia a `core.tenants(id)`. ## Tablas ### part_categories Categorias de refacciones. ```sql CREATE TABLE parts_management.part_categories ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), tenant_id UUID NOT NULL, name VARCHAR(100) NOT NULL, description VARCHAR(300), parent_id UUID REFERENCES parts_management.part_categories(id), sort_order INTEGER DEFAULT 0, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() ); CREATE INDEX idx_part_categories_tenant ON parts_management.part_categories(tenant_id); CREATE INDEX idx_part_categories_parent ON parts_management.part_categories(parent_id); SELECT create_tenant_rls_policies('parts_management', 'part_categories'); ``` ### suppliers Proveedores. ```sql CREATE TABLE parts_management.suppliers ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), tenant_id UUID NOT NULL, name VARCHAR(200) NOT NULL, legal_name VARCHAR(300), rfc VARCHAR(13), -- Contacto contact_name VARCHAR(200), email VARCHAR(200), phone VARCHAR(20), -- Direccion address TEXT, -- Condiciones credit_days INTEGER DEFAULT 0 CHECK (credit_days >= 0), discount_pct DECIMAL(5,2) DEFAULT 0 CHECK (discount_pct >= 0 AND discount_pct <= 100), -- Calificacion rating DECIMAL(3,2) CHECK (rating >= 0 AND rating <= 5), notes TEXT, is_active BOOLEAN DEFAULT TRUE, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() ); CREATE INDEX idx_suppliers_tenant ON parts_management.suppliers(tenant_id); CREATE INDEX idx_suppliers_name ON parts_management.suppliers(name); SELECT create_tenant_rls_policies('parts_management', 'suppliers'); ``` ### warehouse_locations Ubicaciones de almacen. ```sql CREATE TABLE parts_management.warehouse_locations ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), tenant_id UUID NOT NULL, code VARCHAR(20) NOT NULL, name VARCHAR(100), description VARCHAR(200), -- Jerarquia zone VARCHAR(10), aisle VARCHAR(10), level VARCHAR(10), -- Capacidad max_weight DECIMAL(10,2) CHECK (max_weight > 0), max_volume DECIMAL(10,2) CHECK (max_volume > 0), is_active BOOLEAN DEFAULT TRUE, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), CONSTRAINT uq_location_code UNIQUE(tenant_id, code) ); CREATE INDEX idx_locations_tenant ON parts_management.warehouse_locations(tenant_id); CREATE INDEX idx_locations_zone ON parts_management.warehouse_locations(zone); SELECT create_tenant_rls_policies('parts_management', 'warehouse_locations'); ``` ### parts Catalogo de refacciones. ```sql CREATE TABLE parts_management.parts ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), tenant_id UUID NOT NULL, -- Identificacion sku VARCHAR(50) NOT NULL, name VARCHAR(300) NOT NULL, description TEXT, -- Categoria category_id UUID REFERENCES parts_management.part_categories(id), -- Marca/Fabricante brand VARCHAR(100), manufacturer VARCHAR(100), -- Compatibilidad compatible_engines TEXT[], -- Precios cost DECIMAL(12,2) CHECK (cost >= 0), price DECIMAL(12,2) NOT NULL CHECK (price >= 0), -- Inventario current_stock DECIMAL(10,3) DEFAULT 0 CHECK (current_stock >= 0), reserved_stock DECIMAL(10,3) DEFAULT 0 CHECK (reserved_stock >= 0), min_stock DECIMAL(10,3) DEFAULT 0 CHECK (min_stock >= 0), max_stock DECIMAL(10,3) CHECK (max_stock > 0), reorder_point DECIMAL(10,3) CHECK (reorder_point >= 0), -- Ubicacion principal location_id UUID REFERENCES parts_management.warehouse_locations(id), -- Unidad unit VARCHAR(20) DEFAULT 'pza', -- Codigo de barras barcode VARCHAR(50), -- Proveedor preferido preferred_supplier_id UUID REFERENCES parts_management.suppliers(id), -- Estado is_active BOOLEAN DEFAULT TRUE, -- Audit created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), CONSTRAINT uq_part_sku UNIQUE(tenant_id, sku), CONSTRAINT chk_min_max_stock CHECK (max_stock IS NULL OR max_stock >= min_stock) ); CREATE INDEX idx_parts_tenant ON parts_management.parts(tenant_id); CREATE INDEX idx_parts_sku ON parts_management.parts(sku); CREATE INDEX idx_parts_barcode ON parts_management.parts(barcode); CREATE INDEX idx_parts_category ON parts_management.parts(category_id); CREATE INDEX idx_parts_supplier ON parts_management.parts(preferred_supplier_id); CREATE INDEX idx_parts_location ON parts_management.parts(location_id); SELECT create_tenant_rls_policies('parts_management', 'parts'); ``` ### part_alternates Codigos alternos/equivalencias. ```sql CREATE TABLE parts_management.part_alternates ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), part_id UUID NOT NULL REFERENCES parts_management.parts(id) ON DELETE CASCADE, alternate_code VARCHAR(50) NOT NULL, manufacturer VARCHAR(100), is_preferred BOOLEAN DEFAULT FALSE, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() ); CREATE UNIQUE INDEX idx_alternates_code ON parts_management.part_alternates(alternate_code); CREATE INDEX idx_alternates_part ON parts_management.part_alternates(part_id); ``` ### part_locations Ubicaciones multiples por refaccion. ```sql CREATE TABLE parts_management.part_locations ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), part_id UUID NOT NULL REFERENCES parts_management.parts(id) ON DELETE CASCADE, location_id UUID NOT NULL REFERENCES parts_management.warehouse_locations(id), quantity DECIMAL(10,3) DEFAULT 0 CHECK (quantity >= 0), is_primary BOOLEAN DEFAULT FALSE, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), CONSTRAINT uq_part_location UNIQUE(part_id, location_id) ); CREATE INDEX idx_part_locations_part ON parts_management.part_locations(part_id); CREATE INDEX idx_part_locations_location ON parts_management.part_locations(location_id); ``` ### inventory_movements Movimientos de inventario (kardex). ```sql CREATE TABLE parts_management.inventory_movements ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), tenant_id UUID NOT NULL, part_id UUID NOT NULL REFERENCES parts_management.parts(id), -- Tipo de movimiento movement_type VARCHAR(30) NOT NULL CHECK (movement_type IN ('purchase', 'consumption', 'adjustment_in', 'adjustment_out', 'return', 'transfer')), -- Referencia reference_type VARCHAR(30) CHECK (reference_type IN ('service_order', 'purchase_order', 'adjustment', 'return')), reference_id UUID, reference_number VARCHAR(50), -- Cantidades quantity DECIMAL(10,3) NOT NULL, previous_stock DECIMAL(10,3) NOT NULL, new_stock DECIMAL(10,3) NOT NULL, -- Costo unit_cost DECIMAL(12,2) CHECK (unit_cost >= 0), total_cost DECIMAL(12,2) CHECK (total_cost >= 0), -- Ubicacion location_id UUID REFERENCES parts_management.warehouse_locations(id), notes TEXT, created_by UUID, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() ); CREATE INDEX idx_movements_tenant ON parts_management.inventory_movements(tenant_id); CREATE INDEX idx_movements_part ON parts_management.inventory_movements(part_id); CREATE INDEX idx_movements_date ON parts_management.inventory_movements(created_at DESC); CREATE INDEX idx_movements_reference ON parts_management.inventory_movements(reference_type, reference_id); SELECT create_tenant_rls_policies('parts_management', 'inventory_movements'); ``` ### inventory_adjustments Ajustes de inventario. ```sql CREATE TABLE parts_management.inventory_adjustments ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), tenant_id UUID NOT NULL, adjustment_number VARCHAR(20) NOT NULL, -- Tipo adjustment_type VARCHAR(30) NOT NULL CHECK (adjustment_type IN ('count', 'damage', 'expiry', 'correction', 'other')), -- Estado status VARCHAR(20) DEFAULT 'pending' CHECK (status IN ('pending', 'approved', 'applied', 'rejected')), -- Totales total_items INTEGER DEFAULT 0, total_value DECIMAL(12,2) DEFAULT 0, reason TEXT NOT NULL, notes TEXT, -- Aprobacion approved_by UUID, approved_at TIMESTAMP WITH TIME ZONE, created_by UUID, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), CONSTRAINT uq_adjustment_number UNIQUE(tenant_id, adjustment_number) ); CREATE INDEX idx_adjustments_tenant ON parts_management.inventory_adjustments(tenant_id); CREATE INDEX idx_adjustments_status ON parts_management.inventory_adjustments(status); SELECT create_tenant_rls_policies('parts_management', 'inventory_adjustments'); ``` ### adjustment_items Items del ajuste. ```sql CREATE TABLE parts_management.adjustment_items ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), adjustment_id UUID NOT NULL REFERENCES parts_management.inventory_adjustments(id) ON DELETE CASCADE, part_id UUID NOT NULL REFERENCES parts_management.parts(id), system_qty DECIMAL(10,3) NOT NULL, physical_qty DECIMAL(10,3) NOT NULL, difference DECIMAL(10,3) NOT NULL, unit_cost DECIMAL(12,2) CHECK (unit_cost >= 0), value_impact DECIMAL(12,2), notes TEXT, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() ); CREATE INDEX idx_adjustment_items_adjustment ON parts_management.adjustment_items(adjustment_id); CREATE INDEX idx_adjustment_items_part ON parts_management.adjustment_items(part_id); ``` ### stock_alerts Alertas de stock. ```sql CREATE TABLE parts_management.stock_alerts ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), tenant_id UUID NOT NULL, part_id UUID NOT NULL REFERENCES parts_management.parts(id), alert_type VARCHAR(30) NOT NULL CHECK (alert_type IN ('low_stock', 'out_of_stock', 'overstock')), current_stock DECIMAL(10,3), threshold DECIMAL(10,3), status VARCHAR(20) DEFAULT 'active' CHECK (status IN ('active', 'acknowledged', 'resolved')), acknowledged_by UUID, acknowledged_at TIMESTAMP WITH TIME ZONE, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() ); CREATE INDEX idx_alerts_tenant ON parts_management.stock_alerts(tenant_id); CREATE INDEX idx_alerts_status ON parts_management.stock_alerts(status); CREATE INDEX idx_alerts_part ON parts_management.stock_alerts(part_id); SELECT create_tenant_rls_policies('parts_management', 'stock_alerts'); ``` ### physical_inventory Sesiones de inventario fisico. ```sql CREATE TABLE parts_management.physical_inventory ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), tenant_id UUID NOT NULL, inventory_number VARCHAR(20) NOT NULL, -- Tipo inventory_type VARCHAR(30) NOT NULL CHECK (inventory_type IN ('full', 'zone', 'category', 'cyclic')), -- Estado status VARCHAR(20) DEFAULT 'in_progress' CHECK (status IN ('in_progress', 'completed', 'cancelled')), -- Filtros zones TEXT[], categories UUID[], -- Estadisticas total_items INTEGER DEFAULT 0, counted_items INTEGER DEFAULT 0, with_difference INTEGER DEFAULT 0, -- Fechas started_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), completed_at TIMESTAMP WITH TIME ZONE, -- Ajuste generado adjustment_id UUID REFERENCES parts_management.inventory_adjustments(id), created_by UUID, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), CONSTRAINT uq_inventory_number UNIQUE(tenant_id, inventory_number) ); CREATE INDEX idx_physical_inv_tenant ON parts_management.physical_inventory(tenant_id); CREATE INDEX idx_physical_inv_status ON parts_management.physical_inventory(status); SELECT create_tenant_rls_policies('parts_management', 'physical_inventory'); ``` ### inventory_counts Conteos del inventario fisico. ```sql CREATE TABLE parts_management.inventory_counts ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), inventory_id UUID NOT NULL REFERENCES parts_management.physical_inventory(id) ON DELETE CASCADE, part_id UUID NOT NULL REFERENCES parts_management.parts(id), location_id UUID REFERENCES parts_management.warehouse_locations(id), system_qty DECIMAL(10,3) NOT NULL, counted_qty DECIMAL(10,3), difference DECIMAL(10,3), counted_by UUID, counted_at TIMESTAMP WITH TIME ZONE, notes TEXT, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), CONSTRAINT uq_count_part_location UNIQUE(inventory_id, part_id, location_id) ); CREATE INDEX idx_inventory_counts_inventory ON parts_management.inventory_counts(inventory_id); CREATE INDEX idx_inventory_counts_part ON parts_management.inventory_counts(part_id); ``` ## Relacion con erp-core Este schema utiliza las siguientes referencias a erp-core: | Columna | Referencia erp-core | |---------|---------------------| | `tenant_id` | `core.tenants(id)` | | `created_by`, `approved_by`, `acknowledged_by`, `counted_by` | `auth.users(id)` | --- **Creado por:** Requirements-Analyst **Fecha:** 2025-12-06 **Actualizado:** 2025-12-06 (Correccion tenant_id, CHECK constraints, RLS completo)