BEGIN; -- Fix lot auto-switch when current lot reaches zero: -- if the next open lot has no lot_number yet, assign one before switching to status=current. -- This avoids violating chk_stock_lot_number_required_for_non_open. CREATE OR REPLACE FUNCTION fn_auto_switch_lot_when_depleted() RETURNS TRIGGER LANGUAGE plpgsql AS $$ DECLARE v_product_id BIGINT; v_open_lot_id BIGINT; v_current_net NUMERIC; v_event_key TEXT; BEGIN -- only relevant when the updated lot is currently active SELECT product_id INTO v_product_id FROM stock_lot WHERE id = NEW.lot_id; -- if lot is not current anymore, nothing to do IF NOT EXISTS ( SELECT 1 FROM stock_lot WHERE id = NEW.lot_id AND status = 'current' ) THEN RETURN NEW; END IF; SELECT qty_net INTO v_current_net FROM v_stock_lot_balance WHERE stock_lot_id = NEW.lot_id; IF COALESCE(v_current_net, 0) > 0 THEN RETURN NEW; END IF; -- lock all lots for this product to avoid race conditions PERFORM 1 FROM stock_lot WHERE product_id = v_product_id FOR UPDATE; -- confirm current lot is still current after lock IF NOT EXISTS ( SELECT 1 FROM stock_lot WHERE id = NEW.lot_id AND status = 'current' ) THEN RETURN NEW; END IF; UPDATE stock_lot SET status = 'closed', updated_at = NOW() WHERE id = NEW.lot_id; SELECT id INTO v_open_lot_id FROM stock_lot WHERE product_id = v_product_id AND status = 'open' ORDER BY id LIMIT 1 FOR UPDATE; IF v_open_lot_id IS NULL THEN RAISE EXCEPTION 'No open lot available for product % during auto switch', v_product_id; END IF; -- current/closed lots must have a lot_number due to chk_stock_lot_number_required_for_non_open. UPDATE stock_lot SET status = 'current', lot_number = COALESCE(lot_number, format('AUTO-%s-%s', v_product_id, v_open_lot_id)), updated_at = NOW() WHERE id = v_open_lot_id; INSERT INTO stock_lot (product_id, lot_number, status) VALUES (v_product_id, NULL, 'open'); v_event_key := format('lot.auto_switched:%s:%s', v_product_id, txid_current()); PERFORM fn_enqueue_event( 'lot.auto_switched', v_event_key, 'stock_lot', NEW.lot_id::TEXT, jsonb_build_object( 'productId', v_product_id, 'closedLotId', NEW.lot_id, 'newCurrentLotId', v_open_lot_id, 'occurredAt', NOW() ) ); RETURN NEW; END; $$; COMMIT;