-- ============================================================================ -- TABLAS RETAIL/POS - ERP Retail -- ============================================================================ -- Módulos: RT-001 (POS), RT-002 (Inventario), RT-003 (Productos), RT-004 (Clientes) -- Versión: 1.0.0 -- Fecha: 2025-12-09 -- ============================================================================ -- PREREQUISITOS: -- 1. ERP-Core instalado (auth, core, inventory, sales, financial) -- 2. Schema retail creado -- ============================================================================ -- ============================================================================ -- TYPES (ENUMs) -- ============================================================================ DO $$ BEGIN CREATE TYPE retail.pos_session_status AS ENUM ( 'opening', 'open', 'closing', 'closed' ); EXCEPTION WHEN duplicate_object THEN NULL; END $$; DO $$ BEGIN CREATE TYPE retail.pos_order_status AS ENUM ( 'draft', 'paid', 'done', 'cancelled', 'refunded' ); EXCEPTION WHEN duplicate_object THEN NULL; END $$; DO $$ BEGIN CREATE TYPE retail.payment_method AS ENUM ( 'cash', 'card', 'transfer', 'credit', 'mixed' ); EXCEPTION WHEN duplicate_object THEN NULL; END $$; DO $$ BEGIN CREATE TYPE retail.cash_movement_type AS ENUM ( 'in', 'out' ); EXCEPTION WHEN duplicate_object THEN NULL; END $$; DO $$ BEGIN CREATE TYPE retail.transfer_status AS ENUM ( 'draft', 'pending', 'in_transit', 'received', 'cancelled' ); EXCEPTION WHEN duplicate_object THEN NULL; END $$; DO $$ BEGIN CREATE TYPE retail.promotion_type AS ENUM ( 'percentage', 'fixed_amount', 'buy_x_get_y', 'bundle' ); EXCEPTION WHEN duplicate_object THEN NULL; END $$; -- ============================================================================ -- SUCURSALES Y CONFIGURACIÓN -- ============================================================================ -- Tabla: branches (Sucursales) CREATE TABLE IF NOT EXISTS retail.branches ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE, company_id UUID REFERENCES auth.companies(id), -- Identificación code VARCHAR(20) NOT NULL, name VARCHAR(100) NOT NULL, -- Ubicación address VARCHAR(255), city VARCHAR(100), state VARCHAR(100), zip_code VARCHAR(10), country VARCHAR(100) DEFAULT 'México', latitude DECIMAL(10,8), longitude DECIMAL(11,8), -- Contacto phone VARCHAR(20), email VARCHAR(255), manager_id UUID REFERENCES auth.users(id), -- Configuración warehouse_id UUID, -- FK a inventory.warehouses (ERP Core) default_pricelist_id UUID, timezone VARCHAR(50) DEFAULT 'America/Mexico_City', -- Control is_active BOOLEAN NOT NULL DEFAULT TRUE, opening_date DATE, -- Auditoría created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), created_by UUID REFERENCES auth.users(id), updated_at TIMESTAMPTZ, updated_by UUID REFERENCES auth.users(id), deleted_at TIMESTAMPTZ, deleted_by UUID REFERENCES auth.users(id), CONSTRAINT uq_branches_code UNIQUE (tenant_id, code) ); -- Tabla: cash_registers (Cajas registradoras) CREATE TABLE IF NOT EXISTS retail.cash_registers ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE, branch_id UUID NOT NULL REFERENCES retail.branches(id), -- Identificación code VARCHAR(20) NOT NULL, name VARCHAR(100) NOT NULL, -- Configuración is_active BOOLEAN NOT NULL DEFAULT TRUE, default_payment_method retail.payment_method DEFAULT 'cash', -- Auditoría created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), created_by UUID REFERENCES auth.users(id), updated_at TIMESTAMPTZ, updated_by UUID REFERENCES auth.users(id), CONSTRAINT uq_cash_registers_code UNIQUE (tenant_id, branch_id, code) ); -- ============================================================================ -- PUNTO DE VENTA (RT-001) -- ============================================================================ -- Tabla: pos_sessions (Sesiones de POS) CREATE TABLE IF NOT EXISTS retail.pos_sessions ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE, branch_id UUID NOT NULL REFERENCES retail.branches(id), cash_register_id UUID NOT NULL REFERENCES retail.cash_registers(id), -- Usuario user_id UUID NOT NULL REFERENCES auth.users(id), -- Estado status retail.pos_session_status NOT NULL DEFAULT 'opening', -- Apertura opening_date TIMESTAMPTZ NOT NULL DEFAULT NOW(), opening_balance DECIMAL(14,2) NOT NULL DEFAULT 0, -- Cierre closing_date TIMESTAMPTZ, closing_balance DECIMAL(14,2), closing_notes TEXT, -- Totales calculados total_sales DECIMAL(14,2) DEFAULT 0, total_refunds DECIMAL(14,2) DEFAULT 0, total_cash_in DECIMAL(14,2) DEFAULT 0, total_cash_out DECIMAL(14,2) DEFAULT 0, total_card DECIMAL(14,2) DEFAULT 0, total_transfer DECIMAL(14,2) DEFAULT 0, -- Diferencia expected_balance DECIMAL(14,2), difference DECIMAL(14,2), -- Auditoría created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), created_by UUID REFERENCES auth.users(id), updated_at TIMESTAMPTZ, updated_by UUID REFERENCES auth.users(id) ); -- Tabla: pos_orders (Órdenes/Ventas de POS) CREATE TABLE IF NOT EXISTS retail.pos_orders ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE, session_id UUID NOT NULL REFERENCES retail.pos_sessions(id), branch_id UUID NOT NULL REFERENCES retail.branches(id), -- Número de ticket order_number VARCHAR(30) NOT NULL, order_date TIMESTAMPTZ NOT NULL DEFAULT NOW(), -- Cliente (opcional) customer_id UUID, -- FK a core.partners (ERP Core) customer_name VARCHAR(200), -- Estado status retail.pos_order_status NOT NULL DEFAULT 'draft', -- Totales subtotal DECIMAL(14,2) NOT NULL DEFAULT 0, discount_amount DECIMAL(14,2) DEFAULT 0, tax_amount DECIMAL(14,2) DEFAULT 0, total DECIMAL(14,2) NOT NULL DEFAULT 0, -- Pago payment_method retail.payment_method, amount_paid DECIMAL(14,2) DEFAULT 0, change_amount DECIMAL(14,2) DEFAULT 0, -- Facturación requires_invoice BOOLEAN DEFAULT FALSE, invoice_id UUID, -- FK a financial.invoices (ERP Core) -- Notas notes TEXT, -- Auditoría created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), created_by UUID REFERENCES auth.users(id), updated_at TIMESTAMPTZ, updated_by UUID REFERENCES auth.users(id), CONSTRAINT uq_pos_orders_number UNIQUE (tenant_id, order_number) ); -- Tabla: pos_order_lines (Líneas de venta) CREATE TABLE IF NOT EXISTS retail.pos_order_lines ( 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 retail.pos_orders(id) ON DELETE CASCADE, -- Producto product_id UUID NOT NULL, -- FK a inventory.products (ERP Core) product_name VARCHAR(255) NOT NULL, barcode VARCHAR(50), -- Cantidades quantity DECIMAL(12,4) NOT NULL, unit_price DECIMAL(12,4) NOT NULL, -- Descuentos discount_percent DECIMAL(5,2) DEFAULT 0, discount_amount DECIMAL(12,2) DEFAULT 0, -- Totales subtotal DECIMAL(14,2) GENERATED ALWAYS AS (quantity * unit_price) STORED, tax_amount DECIMAL(12,2) DEFAULT 0, total DECIMAL(14,2) NOT NULL, -- Orden sequence INTEGER DEFAULT 1, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), created_by UUID REFERENCES auth.users(id) ); -- Tabla: pos_payments (Pagos de orden - para pagos mixtos) CREATE TABLE IF NOT EXISTS retail.pos_payments ( 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 retail.pos_orders(id) ON DELETE CASCADE, payment_method retail.payment_method NOT NULL, amount DECIMAL(14,2) NOT NULL, -- Referencia (para tarjeta/transferencia) reference VARCHAR(100), card_last_four VARCHAR(4), created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), created_by UUID REFERENCES auth.users(id) ); -- Tabla: cash_movements (Movimientos de efectivo) CREATE TABLE IF NOT EXISTS retail.cash_movements ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE, session_id UUID NOT NULL REFERENCES retail.pos_sessions(id), -- Tipo y monto movement_type retail.cash_movement_type NOT NULL, amount DECIMAL(14,2) NOT NULL, -- Razón reason VARCHAR(255) NOT NULL, notes TEXT, -- Autorización authorized_by UUID REFERENCES auth.users(id), created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), created_by UUID REFERENCES auth.users(id) ); -- ============================================================================ -- INVENTARIO MULTI-SUCURSAL (RT-002) -- ============================================================================ -- Tabla: branch_stock (Stock por sucursal) CREATE TABLE IF NOT EXISTS retail.branch_stock ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE, branch_id UUID NOT NULL REFERENCES retail.branches(id), product_id UUID NOT NULL, -- FK a inventory.products (ERP Core) -- Cantidades quantity_on_hand DECIMAL(12,4) NOT NULL DEFAULT 0, quantity_reserved DECIMAL(12,4) DEFAULT 0, quantity_available DECIMAL(12,4) GENERATED ALWAYS AS (quantity_on_hand - COALESCE(quantity_reserved, 0)) STORED, -- Límites reorder_point DECIMAL(12,4), max_stock DECIMAL(12,4), -- Control last_count_date DATE, last_count_qty DECIMAL(12,4), created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ, CONSTRAINT uq_branch_stock UNIQUE (branch_id, product_id) ); -- Tabla: stock_transfers (Transferencias entre sucursales) CREATE TABLE IF NOT EXISTS retail.stock_transfers ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE, -- Número transfer_number VARCHAR(30) NOT NULL, -- Origen y destino source_branch_id UUID NOT NULL REFERENCES retail.branches(id), destination_branch_id UUID NOT NULL REFERENCES retail.branches(id), -- Estado status retail.transfer_status NOT NULL DEFAULT 'draft', -- Fechas request_date TIMESTAMPTZ NOT NULL DEFAULT NOW(), ship_date TIMESTAMPTZ, receive_date TIMESTAMPTZ, -- Responsables requested_by UUID NOT NULL REFERENCES auth.users(id), shipped_by UUID REFERENCES auth.users(id), received_by UUID REFERENCES auth.users(id), -- Notas notes TEXT, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), created_by UUID REFERENCES auth.users(id), updated_at TIMESTAMPTZ, updated_by UUID REFERENCES auth.users(id), CONSTRAINT uq_stock_transfers_number UNIQUE (tenant_id, transfer_number), CONSTRAINT chk_different_branches CHECK (source_branch_id != destination_branch_id) ); -- Tabla: stock_transfer_lines (Líneas de transferencia) CREATE TABLE IF NOT EXISTS retail.stock_transfer_lines ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE, transfer_id UUID NOT NULL REFERENCES retail.stock_transfers(id) ON DELETE CASCADE, product_id UUID NOT NULL, -- FK a inventory.products (ERP Core) quantity_requested DECIMAL(12,4) NOT NULL, quantity_shipped DECIMAL(12,4), quantity_received DECIMAL(12,4), notes TEXT, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), created_by UUID REFERENCES auth.users(id) ); -- ============================================================================ -- PRODUCTOS RETAIL (RT-003) -- ============================================================================ -- Tabla: product_barcodes (Códigos de barras múltiples) CREATE TABLE IF NOT EXISTS retail.product_barcodes ( 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, -- FK a inventory.products (ERP Core) barcode VARCHAR(50) NOT NULL, barcode_type VARCHAR(20) DEFAULT 'EAN13', -- EAN13, EAN8, UPC, CODE128, etc. is_primary BOOLEAN DEFAULT FALSE, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), created_by UUID REFERENCES auth.users(id), CONSTRAINT uq_product_barcodes UNIQUE (tenant_id, barcode) ); -- Tabla: promotions (Promociones) CREATE TABLE IF NOT EXISTS retail.promotions ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE, code VARCHAR(30) NOT NULL, name VARCHAR(100) NOT NULL, description TEXT, -- Tipo de promoción promotion_type retail.promotion_type NOT NULL, discount_value DECIMAL(10,2), -- Porcentaje o monto fijo -- Vigencia start_date TIMESTAMPTZ NOT NULL, end_date TIMESTAMPTZ NOT NULL, -- Aplicación applies_to_all BOOLEAN DEFAULT FALSE, min_quantity DECIMAL(12,4), min_amount DECIMAL(14,2), -- Sucursales (NULL = todas) branch_ids UUID[], -- Control is_active BOOLEAN NOT NULL DEFAULT TRUE, max_uses INTEGER, current_uses INTEGER DEFAULT 0, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), created_by UUID REFERENCES auth.users(id), updated_at TIMESTAMPTZ, updated_by UUID REFERENCES auth.users(id), CONSTRAINT uq_promotions_code UNIQUE (tenant_id, code), CONSTRAINT chk_promotion_dates CHECK (end_date > start_date) ); -- Tabla: promotion_products (Productos en promoción) CREATE TABLE IF NOT EXISTS retail.promotion_products ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE, promotion_id UUID NOT NULL REFERENCES retail.promotions(id) ON DELETE CASCADE, product_id UUID NOT NULL, -- FK a inventory.products (ERP Core) created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); -- ============================================================================ -- CLIENTES Y FIDELIZACIÓN (RT-004) -- ============================================================================ -- Tabla: loyalty_programs (Programas de fidelización) CREATE TABLE IF NOT EXISTS retail.loyalty_programs ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE, code VARCHAR(20) NOT NULL, name VARCHAR(100) NOT NULL, description TEXT, -- Configuración de puntos points_per_currency DECIMAL(10,4) DEFAULT 1, -- Puntos por peso gastado currency_per_point DECIMAL(10,4) DEFAULT 0.01, -- Valor del punto en pesos min_points_redeem INTEGER DEFAULT 100, -- Vigencia points_expiry_days INTEGER, -- NULL = no expiran is_active BOOLEAN NOT NULL DEFAULT TRUE, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), created_by UUID REFERENCES auth.users(id), updated_at TIMESTAMPTZ, updated_by UUID REFERENCES auth.users(id), CONSTRAINT uq_loyalty_programs_code UNIQUE (tenant_id, code) ); -- Tabla: loyalty_cards (Tarjetas de fidelización) CREATE TABLE IF NOT EXISTS retail.loyalty_cards ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE, program_id UUID NOT NULL REFERENCES retail.loyalty_programs(id), customer_id UUID NOT NULL, -- FK a core.partners (ERP Core) card_number VARCHAR(30) NOT NULL, issue_date DATE NOT NULL DEFAULT CURRENT_DATE, -- Balance points_balance INTEGER NOT NULL DEFAULT 0, points_earned INTEGER NOT NULL DEFAULT 0, points_redeemed INTEGER NOT NULL DEFAULT 0, points_expired INTEGER NOT NULL DEFAULT 0, is_active BOOLEAN NOT NULL DEFAULT TRUE, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), created_by UUID REFERENCES auth.users(id), updated_at TIMESTAMPTZ, updated_by UUID REFERENCES auth.users(id), CONSTRAINT uq_loyalty_cards_number UNIQUE (tenant_id, card_number) ); -- Tabla: loyalty_transactions (Transacciones de puntos) CREATE TABLE IF NOT EXISTS retail.loyalty_transactions ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE, card_id UUID NOT NULL REFERENCES retail.loyalty_cards(id), -- Tipo transaction_type VARCHAR(20) NOT NULL, -- earn, redeem, expire, adjust points INTEGER NOT NULL, -- Referencia order_id UUID REFERENCES retail.pos_orders(id), description TEXT, -- Balance después de la transacción balance_after INTEGER NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), created_by UUID REFERENCES auth.users(id) ); -- ============================================================================ -- ÍNDICES -- ============================================================================ -- Branches CREATE INDEX IF NOT EXISTS idx_branches_tenant ON retail.branches(tenant_id); CREATE INDEX IF NOT EXISTS idx_branches_company ON retail.branches(company_id); -- Cash registers CREATE INDEX IF NOT EXISTS idx_cash_registers_tenant ON retail.cash_registers(tenant_id); CREATE INDEX IF NOT EXISTS idx_cash_registers_branch ON retail.cash_registers(branch_id); -- POS sessions CREATE INDEX IF NOT EXISTS idx_pos_sessions_tenant ON retail.pos_sessions(tenant_id); CREATE INDEX IF NOT EXISTS idx_pos_sessions_branch ON retail.pos_sessions(branch_id); CREATE INDEX IF NOT EXISTS idx_pos_sessions_user ON retail.pos_sessions(user_id); CREATE INDEX IF NOT EXISTS idx_pos_sessions_status ON retail.pos_sessions(status); CREATE INDEX IF NOT EXISTS idx_pos_sessions_date ON retail.pos_sessions(opening_date); -- POS orders CREATE INDEX IF NOT EXISTS idx_pos_orders_tenant ON retail.pos_orders(tenant_id); CREATE INDEX IF NOT EXISTS idx_pos_orders_session ON retail.pos_orders(session_id); CREATE INDEX IF NOT EXISTS idx_pos_orders_branch ON retail.pos_orders(branch_id); CREATE INDEX IF NOT EXISTS idx_pos_orders_customer ON retail.pos_orders(customer_id); CREATE INDEX IF NOT EXISTS idx_pos_orders_date ON retail.pos_orders(order_date); CREATE INDEX IF NOT EXISTS idx_pos_orders_status ON retail.pos_orders(status); -- POS order lines CREATE INDEX IF NOT EXISTS idx_pos_order_lines_tenant ON retail.pos_order_lines(tenant_id); CREATE INDEX IF NOT EXISTS idx_pos_order_lines_order ON retail.pos_order_lines(order_id); CREATE INDEX IF NOT EXISTS idx_pos_order_lines_product ON retail.pos_order_lines(product_id); -- POS payments CREATE INDEX IF NOT EXISTS idx_pos_payments_tenant ON retail.pos_payments(tenant_id); CREATE INDEX IF NOT EXISTS idx_pos_payments_order ON retail.pos_payments(order_id); -- Cash movements CREATE INDEX IF NOT EXISTS idx_cash_movements_tenant ON retail.cash_movements(tenant_id); CREATE INDEX IF NOT EXISTS idx_cash_movements_session ON retail.cash_movements(session_id); -- Branch stock CREATE INDEX IF NOT EXISTS idx_branch_stock_tenant ON retail.branch_stock(tenant_id); CREATE INDEX IF NOT EXISTS idx_branch_stock_branch ON retail.branch_stock(branch_id); CREATE INDEX IF NOT EXISTS idx_branch_stock_product ON retail.branch_stock(product_id); -- Stock transfers CREATE INDEX IF NOT EXISTS idx_stock_transfers_tenant ON retail.stock_transfers(tenant_id); CREATE INDEX IF NOT EXISTS idx_stock_transfers_source ON retail.stock_transfers(source_branch_id); CREATE INDEX IF NOT EXISTS idx_stock_transfers_dest ON retail.stock_transfers(destination_branch_id); CREATE INDEX IF NOT EXISTS idx_stock_transfers_status ON retail.stock_transfers(status); -- Product barcodes CREATE INDEX IF NOT EXISTS idx_product_barcodes_tenant ON retail.product_barcodes(tenant_id); CREATE INDEX IF NOT EXISTS idx_product_barcodes_barcode ON retail.product_barcodes(barcode); CREATE INDEX IF NOT EXISTS idx_product_barcodes_product ON retail.product_barcodes(product_id); -- Promotions CREATE INDEX IF NOT EXISTS idx_promotions_tenant ON retail.promotions(tenant_id); CREATE INDEX IF NOT EXISTS idx_promotions_dates ON retail.promotions(start_date, end_date); CREATE INDEX IF NOT EXISTS idx_promotions_active ON retail.promotions(is_active); -- Loyalty CREATE INDEX IF NOT EXISTS idx_loyalty_cards_tenant ON retail.loyalty_cards(tenant_id); CREATE INDEX IF NOT EXISTS idx_loyalty_cards_customer ON retail.loyalty_cards(customer_id); CREATE INDEX IF NOT EXISTS idx_loyalty_transactions_tenant ON retail.loyalty_transactions(tenant_id); CREATE INDEX IF NOT EXISTS idx_loyalty_transactions_card ON retail.loyalty_transactions(card_id); -- ============================================================================ -- ROW LEVEL SECURITY -- ============================================================================ ALTER TABLE retail.branches ENABLE ROW LEVEL SECURITY; ALTER TABLE retail.cash_registers ENABLE ROW LEVEL SECURITY; ALTER TABLE retail.pos_sessions ENABLE ROW LEVEL SECURITY; ALTER TABLE retail.pos_orders ENABLE ROW LEVEL SECURITY; ALTER TABLE retail.pos_order_lines ENABLE ROW LEVEL SECURITY; ALTER TABLE retail.pos_payments ENABLE ROW LEVEL SECURITY; ALTER TABLE retail.cash_movements ENABLE ROW LEVEL SECURITY; ALTER TABLE retail.branch_stock ENABLE ROW LEVEL SECURITY; ALTER TABLE retail.stock_transfers ENABLE ROW LEVEL SECURITY; ALTER TABLE retail.stock_transfer_lines ENABLE ROW LEVEL SECURITY; ALTER TABLE retail.product_barcodes ENABLE ROW LEVEL SECURITY; ALTER TABLE retail.promotions ENABLE ROW LEVEL SECURITY; ALTER TABLE retail.promotion_products ENABLE ROW LEVEL SECURITY; ALTER TABLE retail.loyalty_programs ENABLE ROW LEVEL SECURITY; ALTER TABLE retail.loyalty_cards ENABLE ROW LEVEL SECURITY; ALTER TABLE retail.loyalty_transactions ENABLE ROW LEVEL SECURITY; -- Políticas de aislamiento por tenant DO $$ BEGIN DROP POLICY IF EXISTS tenant_isolation_branches ON retail.branches; CREATE POLICY tenant_isolation_branches ON retail.branches FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID); EXCEPTION WHEN undefined_object THEN NULL; END $$; DO $$ BEGIN DROP POLICY IF EXISTS tenant_isolation_cash_registers ON retail.cash_registers; CREATE POLICY tenant_isolation_cash_registers ON retail.cash_registers FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID); EXCEPTION WHEN undefined_object THEN NULL; END $$; DO $$ BEGIN DROP POLICY IF EXISTS tenant_isolation_pos_sessions ON retail.pos_sessions; CREATE POLICY tenant_isolation_pos_sessions ON retail.pos_sessions FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID); EXCEPTION WHEN undefined_object THEN NULL; END $$; DO $$ BEGIN DROP POLICY IF EXISTS tenant_isolation_pos_orders ON retail.pos_orders; CREATE POLICY tenant_isolation_pos_orders ON retail.pos_orders FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID); EXCEPTION WHEN undefined_object THEN NULL; END $$; DO $$ BEGIN DROP POLICY IF EXISTS tenant_isolation_pos_order_lines ON retail.pos_order_lines; CREATE POLICY tenant_isolation_pos_order_lines ON retail.pos_order_lines FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID); EXCEPTION WHEN undefined_object THEN NULL; END $$; DO $$ BEGIN DROP POLICY IF EXISTS tenant_isolation_pos_payments ON retail.pos_payments; CREATE POLICY tenant_isolation_pos_payments ON retail.pos_payments FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID); EXCEPTION WHEN undefined_object THEN NULL; END $$; DO $$ BEGIN DROP POLICY IF EXISTS tenant_isolation_cash_movements ON retail.cash_movements; CREATE POLICY tenant_isolation_cash_movements ON retail.cash_movements FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID); EXCEPTION WHEN undefined_object THEN NULL; END $$; DO $$ BEGIN DROP POLICY IF EXISTS tenant_isolation_branch_stock ON retail.branch_stock; CREATE POLICY tenant_isolation_branch_stock ON retail.branch_stock FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID); EXCEPTION WHEN undefined_object THEN NULL; END $$; DO $$ BEGIN DROP POLICY IF EXISTS tenant_isolation_stock_transfers ON retail.stock_transfers; CREATE POLICY tenant_isolation_stock_transfers ON retail.stock_transfers FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID); EXCEPTION WHEN undefined_object THEN NULL; END $$; DO $$ BEGIN DROP POLICY IF EXISTS tenant_isolation_stock_transfer_lines ON retail.stock_transfer_lines; CREATE POLICY tenant_isolation_stock_transfer_lines ON retail.stock_transfer_lines FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID); EXCEPTION WHEN undefined_object THEN NULL; END $$; DO $$ BEGIN DROP POLICY IF EXISTS tenant_isolation_product_barcodes ON retail.product_barcodes; CREATE POLICY tenant_isolation_product_barcodes ON retail.product_barcodes FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID); EXCEPTION WHEN undefined_object THEN NULL; END $$; DO $$ BEGIN DROP POLICY IF EXISTS tenant_isolation_promotions ON retail.promotions; CREATE POLICY tenant_isolation_promotions ON retail.promotions FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID); EXCEPTION WHEN undefined_object THEN NULL; END $$; DO $$ BEGIN DROP POLICY IF EXISTS tenant_isolation_promotion_products ON retail.promotion_products; CREATE POLICY tenant_isolation_promotion_products ON retail.promotion_products FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID); EXCEPTION WHEN undefined_object THEN NULL; END $$; DO $$ BEGIN DROP POLICY IF EXISTS tenant_isolation_loyalty_programs ON retail.loyalty_programs; CREATE POLICY tenant_isolation_loyalty_programs ON retail.loyalty_programs FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID); EXCEPTION WHEN undefined_object THEN NULL; END $$; DO $$ BEGIN DROP POLICY IF EXISTS tenant_isolation_loyalty_cards ON retail.loyalty_cards; CREATE POLICY tenant_isolation_loyalty_cards ON retail.loyalty_cards FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID); EXCEPTION WHEN undefined_object THEN NULL; END $$; DO $$ BEGIN DROP POLICY IF EXISTS tenant_isolation_loyalty_transactions ON retail.loyalty_transactions; CREATE POLICY tenant_isolation_loyalty_transactions ON retail.loyalty_transactions FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID); EXCEPTION WHEN undefined_object THEN NULL; END $$; -- ============================================================================ -- COMENTARIOS -- ============================================================================ COMMENT ON TABLE retail.branches IS 'Sucursales de la empresa'; COMMENT ON TABLE retail.cash_registers IS 'Cajas registradoras por sucursal'; COMMENT ON TABLE retail.pos_sessions IS 'Sesiones de punto de venta'; COMMENT ON TABLE retail.pos_orders IS 'Órdenes/Ventas de punto de venta'; COMMENT ON TABLE retail.pos_order_lines IS 'Líneas de venta'; COMMENT ON TABLE retail.pos_payments IS 'Pagos de orden (para pagos mixtos)'; COMMENT ON TABLE retail.cash_movements IS 'Entradas/salidas de efectivo'; COMMENT ON TABLE retail.branch_stock IS 'Stock por sucursal'; COMMENT ON TABLE retail.stock_transfers IS 'Transferencias entre sucursales'; COMMENT ON TABLE retail.stock_transfer_lines IS 'Líneas de transferencia'; COMMENT ON TABLE retail.product_barcodes IS 'Códigos de barras múltiples por producto'; COMMENT ON TABLE retail.promotions IS 'Promociones y descuentos'; COMMENT ON TABLE retail.promotion_products IS 'Productos en promoción'; COMMENT ON TABLE retail.loyalty_programs IS 'Programas de fidelización'; COMMENT ON TABLE retail.loyalty_cards IS 'Tarjetas de fidelización'; COMMENT ON TABLE retail.loyalty_transactions IS 'Transacciones de puntos'; -- ============================================================================ -- FIN TABLAS RETAIL -- Total: 16 tablas, 6 ENUMs -- ============================================================================