466 lines
14 KiB
Markdown
466 lines
14 KiB
Markdown
# 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)
|