-- Never promote an unnamed placeholder lot automatically. 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 SELECT product_id INTO v_product_id FROM stock_lot WHERE id = NEW.lot_id; 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; PERFORM 1 FROM stock_lot WHERE product_id = v_product_id FOR UPDATE; IF NOT EXISTS ( SELECT 1 FROM stock_lot WHERE id = NEW.lot_id AND status = 'current' ) THEN RETURN NEW; END IF; SELECT id INTO v_open_lot_id FROM stock_lot WHERE product_id = v_product_id AND status = 'open' AND lot_number IS NOT NULL ORDER BY id LIMIT 1 FOR UPDATE; -- A real lot must be activated by a human through the lot-management flow. IF v_open_lot_id IS NULL THEN RETURN NEW; END IF; UPDATE stock_lot SET status = 'closed', updated_at = NOW() WHERE id = NEW.lot_id; UPDATE stock_lot SET status = 'current', 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; $$;