100 lines
2.3 KiB
PL/PgSQL
100 lines
2.3 KiB
PL/PgSQL
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;
|