diff --git a/db/README.md b/db/README.md index ccc0068..59d5e01 100644 --- a/db/README.md +++ b/db/README.md @@ -11,6 +11,7 @@ 7. `db/migrations/0007_phase1_excel_webhook.sql` 8. `db/migrations/0008_shopify_sync_technical.sql` 9. `db/migrations/0013_virtual_shortage_lot_allocations.sql` +10. `db/migrations/0014_require_human_lot_activation.sql` ## Beispielausfuehrung diff --git a/db/migrations/0014_require_human_lot_activation.sql b/db/migrations/0014_require_human_lot_activation.sql new file mode 100644 index 0000000..84f5ce4 --- /dev/null +++ b/db/migrations/0014_require_human_lot_activation.sql @@ -0,0 +1,87 @@ +-- 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; +$$; diff --git a/modules/erp/lager/service.php b/modules/erp/lager/service.php index 2e100ca..7900848 100644 --- a/modules/erp/lager/service.php +++ b/modules/erp/lager/service.php @@ -193,6 +193,7 @@ function switch_current_lot(PDO $pdo, int $productId, int $oldCurrentLotId, int FROM stock_lot WHERE product_id = :product_id AND status = 'open' + AND lot_number IS NOT NULL ORDER BY id LIMIT 1 FOR UPDATE"