-- v1.8 Performance indexes and FK fixes -- Applied to each tenant database -- ═══════════════════════════════════════════════════════════════════════════ -- PERFORMANCE INDEXES -- ═══════════════════════════════════════════════════════════════════════════ -- Stock queries (used thousands of times per day) CREATE INDEX IF NOT EXISTS idx_inv_ops_inventory_branch ON inventory_operations(inventory_id, branch_id); CREATE INDEX IF NOT EXISTS idx_inv_ops_inventory_type_created ON inventory_operations(inventory_id, operation_type, created_at); CREATE INDEX IF NOT EXISTS idx_inv_ops_inventory_created_desc ON inventory_operations(inventory_id, created_at DESC); -- Cash register lookups CREATE INDEX IF NOT EXISTS idx_cash_movements_register ON cash_movements(register_id); CREATE INDEX IF NOT EXISTS idx_sales_register_status ON sales(register_id, status); -- Inventory filtering CREATE INDEX IF NOT EXISTS idx_inventory_branch_active ON inventory(branch_id, is_active); -- Transaction tables CREATE INDEX IF NOT EXISTS idx_sale_items_inventory ON sale_items(inventory_id); CREATE INDEX IF NOT EXISTS idx_sale_payments_sale ON sale_payments(sale_id); CREATE INDEX IF NOT EXISTS idx_sale_payments_register ON sale_payments(register_id); CREATE INDEX IF NOT EXISTS idx_layaway_items_inventory ON layaway_items(inventory_id); CREATE INDEX IF NOT EXISTS idx_layaway_items_layaway ON layaway_items(layaway_id); CREATE INDEX IF NOT EXISTS idx_return_items_inventory ON return_items(inventory_id); CREATE INDEX IF NOT EXISTS idx_return_items_return ON return_items(return_id); -- Employees and permissions CREATE INDEX IF NOT EXISTS idx_employees_email ON employees(email); CREATE INDEX IF NOT EXISTS idx_employees_branch ON employees(branch_id); CREATE INDEX IF NOT EXISTS idx_employee_permissions_employee ON employee_permissions(employee_id); -- Customers and fleet CREATE INDEX IF NOT EXISTS idx_customers_phone ON customers(phone); CREATE INDEX IF NOT EXISTS idx_fleet_vehicles_branch_vin ON fleet_vehicles(branch_id, vin); CREATE INDEX IF NOT EXISTS idx_fleet_maintenance_schedules_next_due ON fleet_maintenance_schedules(next_due_at); -- WhatsApp and quotations CREATE INDEX IF NOT EXISTS idx_whatsapp_messages_status ON whatsapp_messages(status); CREATE INDEX IF NOT EXISTS idx_whatsapp_messages_related ON whatsapp_messages(related_type, related_id); CREATE INDEX IF NOT EXISTS idx_quotations_branch ON quotations(branch_id); -- Accounting CREATE INDEX IF NOT EXISTS idx_accounts_parent ON accounts(parent_id); CREATE INDEX IF NOT EXISTS idx_journal_entries_date ON journal_entries(date); CREATE INDEX IF NOT EXISTS idx_fiscal_periods_year_month ON fiscal_periods(year, month); -- Physical counts CREATE INDEX IF NOT EXISTS idx_physical_counts_branch_status ON physical_counts(branch_id, status); CREATE INDEX IF NOT EXISTS idx_physical_count_lines_inventory ON physical_count_lines(inventory_id); -- ═══════════════════════════════════════════════════════════════════════════ -- FOREIGN KEY FIXES -- ═══════════════════════════════════════════════════════════════════════════ -- Sale dependencies ALTER TABLE sale_items ADD CONSTRAINT fk_sale_items_sale FOREIGN KEY (sale_id) REFERENCES sales(id) ON DELETE CASCADE; ALTER TABLE sale_payments ADD CONSTRAINT fk_sale_payments_sale FOREIGN KEY (sale_id) REFERENCES sales(id) ON DELETE CASCADE; -- Quotation dependencies ALTER TABLE quotation_items ADD CONSTRAINT fk_quotation_items_quotation FOREIGN KEY (quotation_id) REFERENCES quotations(id) ON DELETE CASCADE; -- Layaway dependencies ALTER TABLE layaway_items ADD CONSTRAINT fk_layaway_items_layaway FOREIGN KEY (layaway_id) REFERENCES layaways(id) ON DELETE CASCADE; -- Return dependencies ALTER TABLE return_items ADD CONSTRAINT fk_return_items_return FOREIGN KEY (return_id) REFERENCES returns(id) ON DELETE CASCADE; ALTER TABLE return_items ADD CONSTRAINT fk_return_items_sale_item FOREIGN KEY (sale_item_id) REFERENCES sale_items(id) ON DELETE SET NULL; -- Cash movements ALTER TABLE cash_movements ADD CONSTRAINT fk_cash_movements_register FOREIGN KEY (register_id) REFERENCES cash_registers(id) ON DELETE CASCADE; -- ═══════════════════════════════════════════════════════════════════════════ -- DATA TYPE NORMALIZATION -- ═══════════════════════════════════════════════════════════════════════════ ALTER TABLE physical_counts ALTER COLUMN created_at TYPE TIMESTAMPTZ; -- ═══════════════════════════════════════════════════════════════════════════ -- CONSTRAINT IMPROVEMENTS -- ═══════════════════════════════════════════════════════════════════════════ -- Ensure unique entry numbers per date (fiscal period approximation) -- Note: journal_entries uses 'date' column, not period_year/month CREATE UNIQUE INDEX IF NOT EXISTS idx_journal_entries_unique_number ON journal_entries(date, entry_number); -- Add CHECK constraint for sales.status (idempotent for VARCHAR) -- Note: PostgreSQL does not support adding CHECK constraints via IF NOT EXISTS -- This is left as documentation; apply manually if needed: -- ALTER TABLE sales ADD CONSTRAINT chk_sales_status -- CHECK (status IN ('completed', 'cancelled', 'returned', 'partially_returned'));