-- ============================================================= -- ARCHIVO: 23-purchases.sql -- DESCRIPCION: Ordenes de compra -- VERSION: 1.0.0 -- PROYECTO: ERP-Core V2 -- FECHA: 2026-01-13 -- DEPENDE DE: 16-partners.sql, 17-products.sql, 18-warehouses.sql -- ============================================================= -- ===================== -- SCHEMA: purchases -- ===================== CREATE SCHEMA IF NOT EXISTS purchases; -- ===================== -- TABLA: purchase_orders -- Ordenes de compra a proveedores -- ===================== CREATE TABLE IF NOT EXISTS purchases.purchase_orders ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE, -- Identificacion order_number VARCHAR(30) NOT NULL, -- Proveedor supplier_id UUID NOT NULL REFERENCES partners.partners(id) ON DELETE RESTRICT, supplier_name VARCHAR(200), supplier_email VARCHAR(255), -- Direcciones shipping_address JSONB, -- Direccion de recepcion -- Fechas order_date DATE NOT NULL DEFAULT CURRENT_DATE, expected_date DATE, -- Fecha esperada de recepcion received_date DATE, -- Comprador buyer_id UUID REFERENCES auth.users(id), -- Almacen destino warehouse_id UUID REFERENCES inventory.warehouses(id), -- Totales currency VARCHAR(3) DEFAULT 'MXN', subtotal DECIMAL(15, 2) NOT NULL DEFAULT 0, tax_amount DECIMAL(15, 2) NOT NULL DEFAULT 0, discount_amount DECIMAL(15, 2) NOT NULL DEFAULT 0, shipping_amount DECIMAL(15, 2) DEFAULT 0, total DECIMAL(15, 2) NOT NULL DEFAULT 0, -- Terminos payment_term_days INTEGER DEFAULT 0, payment_method VARCHAR(50), incoterm VARCHAR(10), -- FOB, CIF, EXW, etc. -- Estado status VARCHAR(20) NOT NULL DEFAULT 'draft', -- draft, sent, confirmed, partial, received, cancelled -- Referencia del proveedor supplier_reference VARCHAR(100), -- Notas notes TEXT, internal_notes TEXT, -- Metadata created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP, created_by UUID REFERENCES auth.users(id), updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP, updated_by UUID REFERENCES auth.users(id), deleted_at TIMESTAMPTZ, UNIQUE(tenant_id, order_number) ); -- Indices para purchase_orders CREATE INDEX IF NOT EXISTS idx_purchase_orders_tenant ON purchases.purchase_orders(tenant_id); CREATE INDEX IF NOT EXISTS idx_purchase_orders_number ON purchases.purchase_orders(order_number); CREATE INDEX IF NOT EXISTS idx_purchase_orders_supplier ON purchases.purchase_orders(supplier_id); CREATE INDEX IF NOT EXISTS idx_purchase_orders_status ON purchases.purchase_orders(status); CREATE INDEX IF NOT EXISTS idx_purchase_orders_date ON purchases.purchase_orders(order_date); CREATE INDEX IF NOT EXISTS idx_purchase_orders_expected ON purchases.purchase_orders(expected_date); CREATE INDEX IF NOT EXISTS idx_purchase_orders_warehouse ON purchases.purchase_orders(warehouse_id); CREATE INDEX IF NOT EXISTS idx_purchase_orders_buyer ON purchases.purchase_orders(buyer_id); -- ===================== -- TABLA: purchase_order_items -- Lineas de orden de compra -- ===================== CREATE TABLE IF NOT EXISTS purchases.purchase_order_items ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), order_id UUID NOT NULL REFERENCES purchases.purchase_orders(id) ON DELETE CASCADE, product_id UUID REFERENCES products.products(id) ON DELETE SET NULL, -- Linea line_number INTEGER NOT NULL DEFAULT 1, -- Producto product_sku VARCHAR(50), product_name VARCHAR(200) NOT NULL, supplier_sku VARCHAR(50), -- SKU del proveedor description TEXT, -- Cantidad quantity DECIMAL(15, 4) NOT NULL DEFAULT 1, quantity_received DECIMAL(15, 4) DEFAULT 0, quantity_returned DECIMAL(15, 4) DEFAULT 0, uom VARCHAR(20) DEFAULT 'PZA', -- Precio unit_price DECIMAL(15, 4) NOT NULL DEFAULT 0, -- Descuentos discount_percent DECIMAL(5, 2) DEFAULT 0, discount_amount DECIMAL(15, 2) DEFAULT 0, -- Impuestos tax_rate DECIMAL(5, 2) DEFAULT 16.00, tax_amount DECIMAL(15, 2) DEFAULT 0, -- Totales subtotal DECIMAL(15, 2) NOT NULL DEFAULT 0, total DECIMAL(15, 2) NOT NULL DEFAULT 0, -- Lote/Serie lot_number VARCHAR(50), expiry_date DATE, -- Estado status VARCHAR(20) DEFAULT 'pending', -- pending, partial, received, cancelled -- Metadata created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP ); -- Indices para purchase_order_items CREATE INDEX IF NOT EXISTS idx_purchase_order_items_order ON purchases.purchase_order_items(order_id); CREATE INDEX IF NOT EXISTS idx_purchase_order_items_product ON purchases.purchase_order_items(product_id); CREATE INDEX IF NOT EXISTS idx_purchase_order_items_line ON purchases.purchase_order_items(order_id, line_number); CREATE INDEX IF NOT EXISTS idx_purchase_order_items_status ON purchases.purchase_order_items(status); -- ===================== -- TABLA: purchase_receipts -- Recepciones de mercancia -- ===================== CREATE TABLE IF NOT EXISTS purchases.purchase_receipts ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE, order_id UUID NOT NULL REFERENCES purchases.purchase_orders(id) ON DELETE RESTRICT, -- Identificacion receipt_number VARCHAR(30) NOT NULL, -- Recepcion receipt_date DATE NOT NULL DEFAULT CURRENT_DATE, received_by UUID REFERENCES auth.users(id), -- Almacen warehouse_id UUID REFERENCES inventory.warehouses(id), location_id UUID REFERENCES inventory.warehouse_locations(id), -- Documentos del proveedor supplier_delivery_note VARCHAR(100), supplier_invoice_number VARCHAR(100), -- Estado status VARCHAR(20) NOT NULL DEFAULT 'draft', -- draft, confirmed, cancelled -- Notas notes TEXT, -- Metadata created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP, created_by UUID REFERENCES auth.users(id), updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP, UNIQUE(tenant_id, receipt_number) ); -- Indices para purchase_receipts CREATE INDEX IF NOT EXISTS idx_purchase_receipts_tenant ON purchases.purchase_receipts(tenant_id); CREATE INDEX IF NOT EXISTS idx_purchase_receipts_order ON purchases.purchase_receipts(order_id); CREATE INDEX IF NOT EXISTS idx_purchase_receipts_number ON purchases.purchase_receipts(receipt_number); CREATE INDEX IF NOT EXISTS idx_purchase_receipts_date ON purchases.purchase_receipts(receipt_date); CREATE INDEX IF NOT EXISTS idx_purchase_receipts_status ON purchases.purchase_receipts(status); -- ===================== -- TABLA: purchase_receipt_items -- Lineas de recepcion -- ===================== CREATE TABLE IF NOT EXISTS purchases.purchase_receipt_items ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), receipt_id UUID NOT NULL REFERENCES purchases.purchase_receipts(id) ON DELETE CASCADE, order_item_id UUID REFERENCES purchases.purchase_order_items(id), product_id UUID REFERENCES products.products(id) ON DELETE SET NULL, -- Cantidad quantity_expected DECIMAL(15, 4), quantity_received DECIMAL(15, 4) NOT NULL, quantity_rejected DECIMAL(15, 4) DEFAULT 0, -- Lote/Serie lot_number VARCHAR(50), serial_number VARCHAR(50), expiry_date DATE, -- Ubicacion de almacenamiento location_id UUID REFERENCES inventory.warehouse_locations(id), -- Control de calidad quality_status VARCHAR(20) DEFAULT 'pending', -- pending, approved, rejected, quarantine quality_notes TEXT, -- Metadata created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP ); -- Indices para purchase_receipt_items CREATE INDEX IF NOT EXISTS idx_purchase_receipt_items_receipt ON purchases.purchase_receipt_items(receipt_id); CREATE INDEX IF NOT EXISTS idx_purchase_receipt_items_order_item ON purchases.purchase_receipt_items(order_item_id); CREATE INDEX IF NOT EXISTS idx_purchase_receipt_items_product ON purchases.purchase_receipt_items(product_id); CREATE INDEX IF NOT EXISTS idx_purchase_receipt_items_lot ON purchases.purchase_receipt_items(lot_number); -- ===================== -- COMENTARIOS -- ===================== COMMENT ON TABLE purchases.purchase_orders IS 'Ordenes de compra a proveedores'; COMMENT ON COLUMN purchases.purchase_orders.status IS 'Estado: draft, sent, confirmed, partial (parcialmente recibido), received, cancelled'; COMMENT ON COLUMN purchases.purchase_orders.incoterm IS 'Termino de comercio internacional: FOB, CIF, EXW, etc.'; COMMENT ON TABLE purchases.purchase_order_items IS 'Lineas de detalle de ordenes de compra'; COMMENT ON COLUMN purchases.purchase_order_items.supplier_sku IS 'Codigo del producto segun el proveedor'; COMMENT ON COLUMN purchases.purchase_order_items.quantity_received IS 'Cantidad ya recibida de esta linea'; COMMENT ON TABLE purchases.purchase_receipts IS 'Documentos de recepcion de mercancia'; COMMENT ON COLUMN purchases.purchase_receipts.status IS 'Estado: draft, confirmed, cancelled'; COMMENT ON TABLE purchases.purchase_receipt_items IS 'Lineas de detalle de recepciones'; COMMENT ON COLUMN purchases.purchase_receipt_items.quality_status IS 'Estado QC: pending, approved, rejected, quarantine';