@flowselections/logistiek-engine 1.0.193 → 1.0.195
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/db/schema/logistiek-engine.sql +1740 -0
- package/dist-lib/integrations/supabase/types.d.ts +340 -0
- package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
- package/dist-lib/lib/handlers/logistics-tick.server.d.ts.map +1 -1
- package/dist-lib/lib/handlers/logistics-tick.server.js +58 -26
- package/dist-lib/lib/moduleVersion.d.ts +1 -1
- package/dist-lib/lib/moduleVersion.js +1 -1
- package/dist-lib/lib/stickerQueue.server.d.ts +6 -0
- package/dist-lib/lib/stickerQueue.server.d.ts.map +1 -1
- package/dist-lib/lib/stickerQueue.server.js +23 -18
- package/package.json +1 -1
|
@@ -31832,3 +31832,1743 @@ BEGIN
|
|
|
31832
31832
|
END $$;
|
|
31833
31833
|
REVOKE ALL ON FUNCTION public.logistics_schedule_tasks_batch(jsonb) FROM PUBLIC, anon;
|
|
31834
31834
|
GRANT EXECUTE ON FUNCTION public.logistics_schedule_tasks_batch(jsonb) TO authenticated, service_role;
|
|
31835
|
+
|
|
31836
|
+
-- ===== 1.0.194: stickers, direct indelen, lichte ronde, printstatus (db/migrations/20261026110000) =====
|
|
31837
|
+
-- Logistiek Engine 1.0.194: stickers werken weer, direct indelen, lichte minuutronde, printstatus volgt print_jobs.
|
|
31838
|
+
-- Additief: CREATE OR REPLACE / IF NOT EXISTS; triggers via DO-blok met bestaan-check; geen REVOKE/DROP/cron.schedule.
|
|
31839
|
+
SET check_function_bodies = off;
|
|
31840
|
+
|
|
31841
|
+
-- A1: leverdatum (tekst) veilig als datum lezen. NULL als het geen geldige YYYY-MM-DD is.
|
|
31842
|
+
CREATE OR REPLACE FUNCTION public.logistics_order_date(p text)
|
|
31843
|
+
RETURNS date LANGUAGE plpgsql IMMUTABLE PARALLEL SAFE SET search_path TO 'public' AS $$
|
|
31844
|
+
BEGIN
|
|
31845
|
+
IF p IS NULL OR p !~ '^\d{4}-\d{2}-\d{2}' THEN RETURN NULL; END IF;
|
|
31846
|
+
RETURN left(p, 10)::date;
|
|
31847
|
+
EXCEPTION WHEN OTHERS THEN
|
|
31848
|
+
RETURN NULL;
|
|
31849
|
+
END $$;
|
|
31850
|
+
CREATE OR REPLACE FUNCTION public.logistics_order_date(p date)
|
|
31851
|
+
RETURNS date LANGUAGE sql IMMUTABLE PARALLEL SAFE SET search_path TO 'public' AS $$ SELECT p $$;
|
|
31852
|
+
|
|
31853
|
+
-- B7: is de order geannuleerd? Werkt ook als cancelled_at/status in deze shell ontbreken.
|
|
31854
|
+
CREATE OR REPLACE FUNCTION public.logistics_order_geannuleerd(p_order_id uuid)
|
|
31855
|
+
RETURNS boolean LANGUAGE plpgsql STABLE SECURITY DEFINER SET search_path TO 'public' AS $$
|
|
31856
|
+
DECLARE j jsonb;
|
|
31857
|
+
BEGIN
|
|
31858
|
+
PERFORM public.logistics_acc_guard('ingelogd', 'logistics_order_geannuleerd');
|
|
31859
|
+
SELECT to_jsonb(o) INTO j FROM public.orders o WHERE o.id = p_order_id;
|
|
31860
|
+
IF j IS NULL THEN RETURN false; END IF;
|
|
31861
|
+
RETURN (j->>'cancelled_at') IS NOT NULL
|
|
31862
|
+
OR lower(coalesce(j->>'status', '')) IN ('cancelled', 'canceled', 'geannuleerd');
|
|
31863
|
+
END $$;
|
|
31864
|
+
|
|
31865
|
+
-- Toestand per minuutronde (watermerken) en vastlegging per wek-aanroep.
|
|
31866
|
+
CREATE TABLE IF NOT EXISTS public.logistics_tick_state (
|
|
31867
|
+
key text PRIMARY KEY,
|
|
31868
|
+
value jsonb NOT NULL DEFAULT '{}'::jsonb,
|
|
31869
|
+
updated_at timestamptz NOT NULL DEFAULT now()
|
|
31870
|
+
);
|
|
31871
|
+
GRANT SELECT ON public.logistics_tick_state TO authenticated;
|
|
31872
|
+
GRANT ALL ON public.logistics_tick_state TO service_role;
|
|
31873
|
+
ALTER TABLE public.logistics_tick_state ENABLE ROW LEVEL SECURITY;
|
|
31874
|
+
DO $p$ BEGIN
|
|
31875
|
+
IF NOT EXISTS (SELECT 1 FROM pg_policies WHERE schemaname='public' AND tablename='logistics_tick_state' AND policyname='Beheer leest rondetoestand') THEN
|
|
31876
|
+
CREATE POLICY "Beheer leest rondetoestand" ON public.logistics_tick_state FOR SELECT TO authenticated USING (public.logistics_is_beheer());
|
|
31877
|
+
END IF;
|
|
31878
|
+
END $p$;
|
|
31879
|
+
|
|
31880
|
+
CREATE TABLE IF NOT EXISTS public.logistics_kick_log (
|
|
31881
|
+
id bigserial PRIMARY KEY,
|
|
31882
|
+
kicked_at timestamptz NOT NULL DEFAULT clock_timestamp(),
|
|
31883
|
+
request_id bigint,
|
|
31884
|
+
skipped_reason text,
|
|
31885
|
+
round_started_at timestamptz
|
|
31886
|
+
);
|
|
31887
|
+
CREATE INDEX IF NOT EXISTS logistics_kick_log_kicked_at_idx ON public.logistics_kick_log (kicked_at DESC);
|
|
31888
|
+
GRANT SELECT ON public.logistics_kick_log TO authenticated;
|
|
31889
|
+
GRANT ALL ON public.logistics_kick_log TO service_role;
|
|
31890
|
+
GRANT USAGE, SELECT ON SEQUENCE public.logistics_kick_log_id_seq TO service_role;
|
|
31891
|
+
ALTER TABLE public.logistics_kick_log ENABLE ROW LEVEL SECURITY;
|
|
31892
|
+
DO $p$ BEGIN
|
|
31893
|
+
IF NOT EXISTS (SELECT 1 FROM pg_policies WHERE schemaname='public' AND tablename='logistics_kick_log' AND policyname='Beheer leest wek-aanroepen') THEN
|
|
31894
|
+
CREATE POLICY "Beheer leest wek-aanroepen" ON public.logistics_kick_log FOR SELECT TO authenticated USING (public.logistics_is_beheer());
|
|
31895
|
+
END IF;
|
|
31896
|
+
END $p$;
|
|
31897
|
+
|
|
31898
|
+
-- B2: time-out van de aanroep naar de Shell. Standaard 30000 ms; ongeldige waarde = luide fout.
|
|
31899
|
+
CREATE OR REPLACE FUNCTION public.logistics_tick_timeout_ms()
|
|
31900
|
+
RETURNS integer LANGUAGE plpgsql STABLE SECURITY DEFINER SET search_path TO 'public' AS $$
|
|
31901
|
+
DECLARE v text; n int;
|
|
31902
|
+
BEGIN
|
|
31903
|
+
PERFORM public.logistics_acc_guard('server', 'logistics_tick_timeout_ms');
|
|
31904
|
+
SELECT NULLIF(btrim(value), '') INTO v FROM public.module_runtime_config WHERE key = 'logistics_tick_timeout_ms';
|
|
31905
|
+
IF v IS NULL THEN RETURN 30000; END IF;
|
|
31906
|
+
IF v !~ '^\d{3,6}$' THEN
|
|
31907
|
+
RAISE EXCEPTION 'logistics_tick_timeout_ms in module_runtime_config is ongeldig (%); verwacht een getal in milliseconden', v;
|
|
31908
|
+
END IF;
|
|
31909
|
+
n := v::int;
|
|
31910
|
+
IF n < 1000 OR n > 120000 THEN
|
|
31911
|
+
RAISE EXCEPTION 'logistics_tick_timeout_ms (%) valt buiten 1000–120000 ms', n;
|
|
31912
|
+
END IF;
|
|
31913
|
+
RETURN n;
|
|
31914
|
+
END $$;
|
|
31915
|
+
|
|
31916
|
+
-- Instellingen aanvullen (idempotent). Bestaande waarden blijven, behalve de oude claim-timeout 10.
|
|
31917
|
+
CREATE OR REPLACE FUNCTION public.logistics_ensure_tick_settings()
|
|
31918
|
+
RETURNS jsonb LANGUAGE plpgsql SECURITY DEFINER SET search_path TO 'public' AS $$
|
|
31919
|
+
DECLARE v_t int := 0; v_c int := 0;
|
|
31920
|
+
BEGIN
|
|
31921
|
+
PERFORM public.logistics_acc_guard('server', 'logistics_ensure_tick_settings');
|
|
31922
|
+
INSERT INTO public.module_runtime_config (key, value, updated_at)
|
|
31923
|
+
VALUES ('logistics_tick_timeout_ms', '30000', now())
|
|
31924
|
+
ON CONFLICT (key) DO NOTHING;
|
|
31925
|
+
GET DIAGNOSTICS v_t = ROW_COUNT;
|
|
31926
|
+
INSERT INTO public.logistics_plan_settings (key, value, updated_at)
|
|
31927
|
+
VALUES ('claim_timeout_minutes', '2', now())
|
|
31928
|
+
ON CONFLICT (key) DO UPDATE SET value = '2', updated_at = now()
|
|
31929
|
+
WHERE NULLIF(btrim(public.logistics_plan_settings.value), '') IS NULL
|
|
31930
|
+
OR btrim(public.logistics_plan_settings.value) = '10';
|
|
31931
|
+
GET DIAGNOSTICS v_c = ROW_COUNT;
|
|
31932
|
+
RETURN jsonb_build_object('timeoutIngesteld', v_t > 0, 'claimTimeoutBijgewerkt', v_c > 0);
|
|
31933
|
+
END $$;
|
|
31934
|
+
|
|
31935
|
+
-- B5: periodieke ensure-stappen hooguit 1× per uur.
|
|
31936
|
+
CREATE OR REPLACE FUNCTION public.logistics_ensure_periodiek()
|
|
31937
|
+
RETURNS jsonb LANGUAGE plpgsql SECURITY DEFINER SET search_path TO 'public' AS $$
|
|
31938
|
+
DECLARE v_last timestamptz; v_r jsonb := '{}'::jsonb;
|
|
31939
|
+
BEGIN
|
|
31940
|
+
PERFORM public.logistics_acc_guard('server', 'logistics_ensure_periodiek');
|
|
31941
|
+
SELECT (value->>'at')::timestamptz INTO v_last FROM public.logistics_tick_state WHERE key = 'ensure_periodiek';
|
|
31942
|
+
IF v_last IS NOT NULL AND v_last > now() - interval '1 hour' THEN
|
|
31943
|
+
RETURN jsonb_build_object('overgeslagen', 'binnen het uur', 'laatst', v_last);
|
|
31944
|
+
END IF;
|
|
31945
|
+
v_r := v_r || jsonb_build_object('rebuffer', public.logistics_ensure_rebuffer_setting());
|
|
31946
|
+
v_r := v_r || jsonb_build_object('tick', public.logistics_ensure_tick_settings());
|
|
31947
|
+
INSERT INTO public.logistics_tick_state(key, value, updated_at)
|
|
31948
|
+
VALUES ('ensure_periodiek', jsonb_build_object('at', now()) || v_r, now())
|
|
31949
|
+
ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value, updated_at = now();
|
|
31950
|
+
RETURN v_r;
|
|
31951
|
+
END $$;
|
|
31952
|
+
|
|
31953
|
+
-- B3: laadtoestand alleen bij wijziging in planwachtrij/taken, anders hooguit elke 5 minuten.
|
|
31954
|
+
CREATE OR REPLACE FUNCTION public.logistics_refresh_plan_state_als_nodig()
|
|
31955
|
+
RETURNS jsonb LANGUAGE plpgsql SECURITY DEFINER SET search_path TO 'public' AS $$
|
|
31956
|
+
DECLARE v_wm timestamptz; v_prev timestamptz; v_at timestamptz; v_res jsonb;
|
|
31957
|
+
BEGIN
|
|
31958
|
+
PERFORM public.logistics_acc_guard('server', 'logistics_refresh_plan_state_als_nodig');
|
|
31959
|
+
SELECT greatest((SELECT max(updated_at) FROM public.logistics_plan_queue),
|
|
31960
|
+
(SELECT max(updated_at) FROM public.logistics_tasks)) INTO v_wm;
|
|
31961
|
+
SELECT (value->>'wm')::timestamptz, (value->>'at')::timestamptz INTO v_prev, v_at
|
|
31962
|
+
FROM public.logistics_tick_state WHERE key = 'refresh_plan_state';
|
|
31963
|
+
IF v_at IS NOT NULL AND v_wm IS NOT DISTINCT FROM v_prev AND v_at > now() - interval '5 minutes' THEN
|
|
31964
|
+
RETURN jsonb_build_object('overgeslagen', 'geen wijziging');
|
|
31965
|
+
END IF;
|
|
31966
|
+
v_res := public.logistics_refresh_plan_state(p_limit => 500);
|
|
31967
|
+
INSERT INTO public.logistics_tick_state(key, value, updated_at)
|
|
31968
|
+
VALUES ('refresh_plan_state', jsonb_build_object('wm', v_wm, 'at', now()), now())
|
|
31969
|
+
ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value, updated_at = now();
|
|
31970
|
+
RETURN v_res;
|
|
31971
|
+
END $$;
|
|
31972
|
+
|
|
31973
|
+
-- B1: een wek-aanroep tijdens een lopende ronde oppakken vóór het slot vrijgaat.
|
|
31974
|
+
CREATE OR REPLACE FUNCTION public.logistics_kick_pending_take(p_since timestamptz)
|
|
31975
|
+
RETURNS boolean LANGUAGE plpgsql SECURITY DEFINER SET search_path TO 'public' AS $$
|
|
31976
|
+
DECLARE v_val timestamptz;
|
|
31977
|
+
BEGIN
|
|
31978
|
+
PERFORM public.logistics_acc_guard('server', 'logistics_kick_pending_take');
|
|
31979
|
+
SELECT NULLIF(s.value, '')::timestamptz INTO v_val FROM public.logistics_plan_settings s
|
|
31980
|
+
WHERE s.key = 'kick_pending' FOR UPDATE;
|
|
31981
|
+
IF v_val IS NULL THEN RETURN false; END IF;
|
|
31982
|
+
UPDATE public.logistics_plan_settings s SET value = '', updated_at = now() WHERE s.key = 'kick_pending';
|
|
31983
|
+
RETURN v_val IS NOT NULL AND v_val >= p_since;
|
|
31984
|
+
END $$;
|
|
31985
|
+
|
|
31986
|
+
-- B0: start van de ronde koppelen aan de wek-aanroepen die erop wachtten.
|
|
31987
|
+
CREATE OR REPLACE FUNCTION public.logistics_kick_round_start()
|
|
31988
|
+
RETURNS integer LANGUAGE plpgsql SECURITY DEFINER SET search_path TO 'public' AS $$
|
|
31989
|
+
DECLARE n int;
|
|
31990
|
+
BEGIN
|
|
31991
|
+
PERFORM public.logistics_acc_guard('server', 'logistics_kick_round_start');
|
|
31992
|
+
UPDATE public.logistics_kick_log SET round_started_at = clock_timestamp()
|
|
31993
|
+
WHERE round_started_at IS NULL AND kicked_at > now() - interval '2 minutes';
|
|
31994
|
+
GET DIAGNOSTICS n = ROW_COUNT;
|
|
31995
|
+
RETURN n;
|
|
31996
|
+
END $$;
|
|
31997
|
+
|
|
31998
|
+
-- B0: meting per wek-aanroep: tijd wek, verzoek-id, antwoord van pg_net, start ronde.
|
|
31999
|
+
CREATE OR REPLACE FUNCTION public.logistics_kick_metingen(p_limit integer)
|
|
32000
|
+
RETURNS TABLE(kicked_at timestamptz, request_id bigint, skipped_reason text, antwoord_at timestamptz,
|
|
32001
|
+
status_code integer, time_out boolean, fout text, round_started_at timestamptz,
|
|
32002
|
+
wacht_tot_antwoord_ms integer, wacht_tot_ronde_ms integer)
|
|
32003
|
+
LANGUAGE plpgsql STABLE SECURITY DEFINER SET search_path TO 'public' AS $$
|
|
32004
|
+
BEGIN
|
|
32005
|
+
PERFORM public.logistics_acc_guard('ingelogd', 'logistics_kick_metingen');
|
|
32006
|
+
IF to_regclass('net._http_response') IS NULL THEN
|
|
32007
|
+
RETURN QUERY SELECT k.kicked_at, k.request_id, k.skipped_reason, NULL::timestamptz, NULL::int, NULL::boolean,
|
|
32008
|
+
NULL::text, k.round_started_at, NULL::int,
|
|
32009
|
+
(extract(epoch FROM k.round_started_at - k.kicked_at) * 1000)::int
|
|
32010
|
+
FROM public.logistics_kick_log k ORDER BY k.kicked_at DESC LIMIT least(greatest(coalesce(p_limit, 50), 1), 500);
|
|
32011
|
+
RETURN;
|
|
32012
|
+
END IF;
|
|
32013
|
+
RETURN QUERY EXECUTE $q$
|
|
32014
|
+
SELECT k.kicked_at, k.request_id, k.skipped_reason, r.created, r.status_code, r.timed_out,
|
|
32015
|
+
left(r.error_msg, 200), k.round_started_at,
|
|
32016
|
+
(extract(epoch FROM r.created - k.kicked_at) * 1000)::int,
|
|
32017
|
+
(extract(epoch FROM k.round_started_at - k.kicked_at) * 1000)::int
|
|
32018
|
+
FROM public.logistics_kick_log k
|
|
32019
|
+
LEFT JOIN net._http_response r ON r.id = k.request_id
|
|
32020
|
+
ORDER BY k.kicked_at DESC LIMIT $1$q$ USING least(greatest(coalesce(p_limit, 50), 1), 500);
|
|
32021
|
+
END $$;
|
|
32022
|
+
|
|
32023
|
+
-- A3: uitkomst van de HTTP-ronde (stickers) vastleggen in logistics_tick_runs.steps en rondetoestand.
|
|
32024
|
+
CREATE OR REPLACE FUNCTION public.logistics_tick_http_log(p_steps jsonb)
|
|
32025
|
+
RETURNS void LANGUAGE plpgsql SECURITY DEFINER SET search_path TO 'public' AS $$
|
|
32026
|
+
DECLARE v_fouten jsonb := coalesce(p_steps->'stickerSyncFouten', '[]'::jsonb);
|
|
32027
|
+
BEGIN
|
|
32028
|
+
PERFORM public.logistics_acc_guard('server', 'logistics_tick_http_log');
|
|
32029
|
+
INSERT INTO public.logistics_tick_runs(started_at, finished_at, duration_ms, has_error, steps)
|
|
32030
|
+
VALUES (coalesce((p_steps->>'gestart')::timestamptz, now()), now(),
|
|
32031
|
+
nullif(p_steps->>'duurMs', '')::int,
|
|
32032
|
+
jsonb_typeof(v_fouten) = 'array' AND jsonb_array_length(v_fouten) > 0,
|
|
32033
|
+
jsonb_build_object('bron', 'http') || p_steps);
|
|
32034
|
+
INSERT INTO public.logistics_tick_state(key, value, updated_at)
|
|
32035
|
+
VALUES ('sticker_sync', jsonb_build_object('at', now(), 'fouten', v_fouten), now())
|
|
32036
|
+
ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value, updated_at = now();
|
|
32037
|
+
END $$;
|
|
32038
|
+
|
|
32039
|
+
-- C3: loopt er een (niet verlopen) pakbonopdracht voor deze order? Zelfde filter als logistics_cart_is_frozen.
|
|
32040
|
+
CREATE OR REPLACE FUNCTION public.logistics_order_print_actief(p_order_number text)
|
|
32041
|
+
RETURNS boolean LANGUAGE plpgsql STABLE SECURITY DEFINER SET search_path TO 'public' AS $$
|
|
32042
|
+
DECLARE v boolean;
|
|
32043
|
+
BEGIN
|
|
32044
|
+
PERFORM public.logistics_acc_guard('ingelogd', 'logistics_order_print_actief');
|
|
32045
|
+
IF p_order_number IS NULL OR to_regclass('public.print_jobs') IS NULL THEN RETURN false; END IF;
|
|
32046
|
+
EXECUTE $q$
|
|
32047
|
+
SELECT EXISTS (
|
|
32048
|
+
SELECT 1 FROM public.print_jobs pj
|
|
32049
|
+
WHERE pj.documenttype::text = 'pakbon'
|
|
32050
|
+
AND COALESCE(pj.is_herprint, false) = false
|
|
32051
|
+
AND pj.status::text IN ('wachtend', 'bezig', 'voltooid')
|
|
32052
|
+
AND COALESCE(pj.foutcode, '') NOT IN ('verlopen', 'expired')
|
|
32053
|
+
AND COALESCE(pj.verwerkingsfase, '') NOT IN ('verlopen', 'expired')
|
|
32054
|
+
AND (EXISTS (SELECT 1 FROM public.pick_queue q WHERE q.print_job_id = pj.id AND q.order_number = $1 AND q.cancelled_at IS NULL)
|
|
32055
|
+
OR pj.payload->>'cart_id' IN (SELECT oc.id::text FROM logistics_active.order_carts oc
|
|
32056
|
+
JOIN public.orders o ON o.id = oc.order_id WHERE o.order_number = $1)))$q$
|
|
32057
|
+
INTO v USING p_order_number;
|
|
32058
|
+
RETURN coalesce(v, false);
|
|
32059
|
+
END $$;
|
|
32060
|
+
|
|
32061
|
+
-- C4: printstatus in batch (max. 500 ids, één rij per id).
|
|
32062
|
+
CREATE OR REPLACE FUNCTION public.logistics_print_status(p_queue_item_ids uuid[])
|
|
32063
|
+
RETURNS TABLE(queue_item_id uuid, status text, printnode_status text, fase text, fase_gestart_op timestamptz, melding text)
|
|
32064
|
+
LANGUAGE plpgsql STABLE SECURITY DEFINER SET search_path TO 'public' AS $$
|
|
32065
|
+
DECLARE v_fgo text; v_log text;
|
|
32066
|
+
BEGIN
|
|
32067
|
+
PERFORM public.logistics_acc_guard('ingelogd', 'logistics_print_status');
|
|
32068
|
+
IF coalesce(cardinality(p_queue_item_ids), 0) > 500 THEN
|
|
32069
|
+
RAISE EXCEPTION 'logistics_print_status: maximaal 500 ids per aanroep (gekregen: %)', cardinality(p_queue_item_ids)
|
|
32070
|
+
USING ERRCODE = '22023';
|
|
32071
|
+
END IF;
|
|
32072
|
+
IF coalesce(cardinality(p_queue_item_ids), 0) = 0 THEN RETURN; END IF;
|
|
32073
|
+
IF to_regclass('public.print_jobs') IS NULL THEN
|
|
32074
|
+
RETURN QUERY SELECT u.id, 'geen_printmodule'::text, NULL::text, NULL::text, NULL::timestamptz,
|
|
32075
|
+
'De printmodule is in deze omgeving niet geïnstalleerd.'::text
|
|
32076
|
+
FROM (SELECT DISTINCT x AS id FROM unnest(p_queue_item_ids) x) u;
|
|
32077
|
+
RETURN;
|
|
32078
|
+
END IF;
|
|
32079
|
+
-- fase_gestart_op komt van Layouts; alleen lezen als de kolom bestaat.
|
|
32080
|
+
v_fgo := CASE WHEN EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema='public'
|
|
32081
|
+
AND table_name='print_jobs' AND column_name='fase_gestart_op')
|
|
32082
|
+
THEN 'pj.fase_gestart_op' ELSE 'NULL::timestamptz' END;
|
|
32083
|
+
-- 'fase' = print_jobs.verwerkingsfase; ontbreekt die, dan de laatste print_job_logs-gebeurtenis.
|
|
32084
|
+
v_log := CASE WHEN to_regclass('public.print_job_logs') IS NOT NULL
|
|
32085
|
+
THEN 'SELECT l.gebeurtenis FROM public.print_job_logs l WHERE l.job_id = pj.id ORDER BY l.created_at DESC LIMIT 1'
|
|
32086
|
+
ELSE 'SELECT NULL::text AS gebeurtenis' END;
|
|
32087
|
+
RETURN QUERY EXECUTE format($q$
|
|
32088
|
+
SELECT u.id,
|
|
32089
|
+
CASE WHEN q.id IS NULL THEN 'onbekende_regel'
|
|
32090
|
+
WHEN pj.id IS NULL THEN 'geen_opdracht'
|
|
32091
|
+
ELSE pj.status::text END,
|
|
32092
|
+
pj.printnode_status,
|
|
32093
|
+
COALESCE(NULLIF(pj.verwerkingsfase, ''), lg.gebeurtenis),
|
|
32094
|
+
%s,
|
|
32095
|
+
COALESCE(NULLIF(pj.foutmelding, ''), NULLIF(pj.printnode_bericht, ''))
|
|
32096
|
+
FROM (SELECT DISTINCT x AS id FROM unnest($1) x) u
|
|
32097
|
+
LEFT JOIN public.pick_queue q ON q.id = u.id
|
|
32098
|
+
LEFT JOIN public.print_jobs pj ON pj.id = q.print_job_id
|
|
32099
|
+
LEFT JOIN LATERAL (%s) lg ON true$q$, v_fgo, v_log)
|
|
32100
|
+
USING p_queue_item_ids;
|
|
32101
|
+
END $$;
|
|
32102
|
+
|
|
32103
|
+
-- C2: printed_at/print_count volgen de echte uitkomst van de printopdracht.
|
|
32104
|
+
CREATE OR REPLACE FUNCTION public.logistics_print_job_voltooid_trg()
|
|
32105
|
+
RETURNS trigger LANGUAGE plpgsql SECURITY DEFINER SET search_path TO 'public' AS $$
|
|
32106
|
+
BEGIN
|
|
32107
|
+
IF NEW.status::text = 'voltooid' AND (TG_OP = 'INSERT' OR OLD.status IS DISTINCT FROM NEW.status) THEN
|
|
32108
|
+
UPDATE public.pick_queue q
|
|
32109
|
+
SET printed_at = COALESCE(NEW.voltooid_op, now()),
|
|
32110
|
+
print_count = CASE WHEN q.printed_at IS NULL OR q.printed_at < NEW.created_at
|
|
32111
|
+
THEN COALESCE(q.print_count, 0) + 1 ELSE q.print_count END,
|
|
32112
|
+
updated_at = now()
|
|
32113
|
+
WHERE q.print_job_id = NEW.id
|
|
32114
|
+
AND q.printed_at IS DISTINCT FROM COALESCE(NEW.voltooid_op, now());
|
|
32115
|
+
END IF;
|
|
32116
|
+
RETURN NEW;
|
|
32117
|
+
EXCEPTION WHEN OTHERS THEN
|
|
32118
|
+
-- Een printopdracht mag nooit mislukken door deze bijwerking; wel zichtbaar maken.
|
|
32119
|
+
BEGIN
|
|
32120
|
+
INSERT INTO public.logistics_trace_events(phase, step, status, reason)
|
|
32121
|
+
VALUES ('printen', 'printed_at bijwerken na voltooid', 'mislukt', left(SQLERRM, 300));
|
|
32122
|
+
EXCEPTION WHEN OTHERS THEN NULL; END;
|
|
32123
|
+
RETURN NEW;
|
|
32124
|
+
END $$;
|
|
32125
|
+
|
|
32126
|
+
DO $t$ BEGIN
|
|
32127
|
+
IF to_regclass('public.print_jobs') IS NULL THEN
|
|
32128
|
+
RAISE NOTICE 'logistiek-engine: print_jobs ontbreekt; printstatus-trigger wordt aangemaakt zodra de printmodule er is (opnieuw draaien)';
|
|
32129
|
+
ELSIF NOT EXISTS (SELECT 1 FROM pg_trigger WHERE tgname = 'trg_logistics_print_job_voltooid' AND tgrelid = 'public.print_jobs'::regclass) THEN
|
|
32130
|
+
CREATE TRIGGER trg_logistics_print_job_voltooid
|
|
32131
|
+
AFTER INSERT OR UPDATE OF status ON public.print_jobs
|
|
32132
|
+
FOR EACH ROW EXECUTE FUNCTION public.logistics_print_job_voltooid_trg();
|
|
32133
|
+
END IF;
|
|
32134
|
+
END $t$;
|
|
32135
|
+
|
|
32136
|
+
-- Installatiecontrole 1.0.194.
|
|
32137
|
+
CREATE OR REPLACE FUNCTION public.logistics_install_check_194()
|
|
32138
|
+
RETURNS TABLE(onderdeel text, ok boolean, toelichting text)
|
|
32139
|
+
LANGUAGE plpgsql STABLE SECURITY DEFINER SET search_path TO 'public' AS $$
|
|
32140
|
+
DECLARE v jsonb; v_at timestamptz; n int; v_pc boolean;
|
|
32141
|
+
BEGIN
|
|
32142
|
+
PERFORM public.logistics_acc_guard('ingelogd', 'logistics_install_check_194');
|
|
32143
|
+
SELECT value INTO v FROM public.logistics_tick_state WHERE key = 'sticker_sync';
|
|
32144
|
+
v_at := (v->>'at')::timestamptz;
|
|
32145
|
+
onderdeel := 'Engine: sticker-sync laatste run zonder fout';
|
|
32146
|
+
ok := v IS NOT NULL AND jsonb_array_length(coalesce(v->'fouten', '[]'::jsonb)) = 0 AND v_at > now() - interval '15 minutes';
|
|
32147
|
+
toelichting := CASE WHEN v IS NULL THEN 'nog geen ronde vastgelegd (1.0.194 of nieuwer moet draaien)'
|
|
32148
|
+
WHEN v_at <= now() - interval '15 minutes' THEN 'laatste ronde ' || to_char(v_at, 'DD-MM HH24:MI') || ' (ouder dan 15 min)'
|
|
32149
|
+
WHEN ok THEN 'geen fouten (' || to_char(v_at, 'HH24:MI:SS') || ')'
|
|
32150
|
+
ELSE 'fout: ' || left((v->'fouten')::text, 300) END;
|
|
32151
|
+
RETURN NEXT;
|
|
32152
|
+
|
|
32153
|
+
onderdeel := 'Engine: wek-aanroep gaat niet verloren (kick_pending-mechanisme aanwezig)';
|
|
32154
|
+
ok := EXISTS (SELECT 1 FROM pg_proc WHERE pronamespace='public'::regnamespace AND proname='logistics_plan_kick' AND prosrc LIKE '%kick_pending%')
|
|
32155
|
+
AND to_regprocedure('public.logistics_kick_pending_take(timestamptz)') IS NOT NULL;
|
|
32156
|
+
toelichting := CASE WHEN ok THEN 'aanwezig' ELSE 'ontbreekt: Engine 1.0.194 via schemasync' END; RETURN NEXT;
|
|
32157
|
+
|
|
32158
|
+
onderdeel := 'Engine: tick-aanroep heeft eigen time-out';
|
|
32159
|
+
SELECT count(*) INTO n FROM pg_proc WHERE pronamespace='public'::regnamespace
|
|
32160
|
+
AND proname IN ('logistics_plan_kick', 'logistics_recalc_tick_kick') AND prosrc LIKE '%timeout_milliseconds%';
|
|
32161
|
+
ok := n = 2;
|
|
32162
|
+
BEGIN
|
|
32163
|
+
toelichting := CASE WHEN ok THEN public.logistics_tick_timeout_ms() || ' ms' ELSE n || ' van 2 aanroepen met time-out' END;
|
|
32164
|
+
EXCEPTION WHEN OTHERS THEN ok := false; toelichting := SQLERRM; END;
|
|
32165
|
+
RETURN NEXT;
|
|
32166
|
+
|
|
32167
|
+
onderdeel := 'Engine: claim-timeout ≤ 3 min';
|
|
32168
|
+
BEGIN
|
|
32169
|
+
n := public.logistics_plan_setting('claim_timeout_minutes', 2)::int;
|
|
32170
|
+
ok := n <= 3; toelichting := n || ' min';
|
|
32171
|
+
EXCEPTION WHEN OTHERS THEN ok := false; toelichting := SQLERRM; END;
|
|
32172
|
+
RETURN NEXT;
|
|
32173
|
+
|
|
32174
|
+
onderdeel := 'Engine: printstatus-trigger aanwezig';
|
|
32175
|
+
ok := to_regclass('public.print_jobs') IS NOT NULL
|
|
32176
|
+
AND EXISTS (SELECT 1 FROM pg_trigger WHERE tgname = 'trg_logistics_print_job_voltooid' AND tgrelid = 'public.print_jobs'::regclass);
|
|
32177
|
+
toelichting := CASE WHEN to_regclass('public.print_jobs') IS NULL THEN 'print_jobs ontbreekt (printmodule niet geïnstalleerd)'
|
|
32178
|
+
WHEN ok THEN 'aanwezig' ELSE 'ontbreekt: schemabestand opnieuw draaien' END;
|
|
32179
|
+
RETURN NEXT;
|
|
32180
|
+
|
|
32181
|
+
-- printed_at volgt print_jobs pas echt als geen enkele functie hem nog bij het aanmaken zet.
|
|
32182
|
+
SELECT EXISTS (SELECT 1 FROM pg_proc WHERE pronamespace='public'::regnamespace
|
|
32183
|
+
AND proname IN ('pick_complete_print', 'pick_complete_print_cart', 'pick_complete_print_carts')
|
|
32184
|
+
AND prosrc ~* 'printed_at\s*=\s*(now|clock_timestamp)') INTO v_pc;
|
|
32185
|
+
onderdeel := 'Engine: printed_at volgt print_jobs';
|
|
32186
|
+
ok := to_regprocedure('public.logistics_print_job_voltooid_trg()') IS NOT NULL AND NOT v_pc;
|
|
32187
|
+
toelichting := CASE WHEN ok THEN 'printed_at wordt alleen gezet bij status voltooid'
|
|
32188
|
+
WHEN v_pc THEN 'Engine-trigger aanwezig, maar Pickschermen (pick_complete_print*) zet printed_at nog bij het aanmaken'
|
|
32189
|
+
ELSE 'ontbreekt: Engine 1.0.194' END;
|
|
32190
|
+
RETURN NEXT;
|
|
32191
|
+
|
|
32192
|
+
onderdeel := 'Engine: leverdatum veilig als datum (logistics_order_date)';
|
|
32193
|
+
ok := to_regprocedure('public.logistics_order_date(text)') IS NOT NULL
|
|
32194
|
+
AND NOT EXISTS (SELECT 1 FROM pg_proc WHERE pronamespace='public'::regnamespace
|
|
32195
|
+
AND proname IN ('logistics_sticker_sync','logistics_own_sticker_sync','logistics_own_traysticker_sync')
|
|
32196
|
+
AND prosrc ~ '\mo\.delivery_date\s*>=');
|
|
32197
|
+
toelichting := CASE WHEN ok THEN 'aanwezig' ELSE 'sticker-sync vergelijkt leverdatum nog als tekst' END; RETURN NEXT;
|
|
32198
|
+
END $$;
|
|
32199
|
+
|
|
32200
|
+
CREATE OR REPLACE FUNCTION public.logistics_own_sticker_sync(p_limit integer DEFAULT 200)
|
|
32201
|
+
RETURNS jsonb
|
|
32202
|
+
LANGUAGE plpgsql
|
|
32203
|
+
SECURITY DEFINER
|
|
32204
|
+
SET search_path TO 'public'
|
|
32205
|
+
AS $function$
|
|
32206
|
+
DECLARE
|
|
32207
|
+
r record;
|
|
32208
|
+
v_bron text;
|
|
32209
|
+
v_n int;
|
|
32210
|
+
v_sticker uuid;
|
|
32211
|
+
v_status text;
|
|
32212
|
+
v_reden text;
|
|
32213
|
+
v_bijgewerkt int := 0;
|
|
32214
|
+
v_vrij int := 0;
|
|
32215
|
+
v_uit int := 0;
|
|
32216
|
+
BEGIN
|
|
32217
|
+
PERFORM public.logistics_acc_guard('ingelogd', 'logistics_own_sticker_sync');
|
|
32218
|
+
FOR r IN
|
|
32219
|
+
SELECT o.id AS order_id, o.order_number, o.customer_id, oi.product_id,
|
|
32220
|
+
sum(public.logistics_own_sticker_count(oi.unit, oi.quantity, oi.plants_per_crate, prd.plants_per_crate))::int AS planten,
|
|
32221
|
+
bool_or(public.logistics_own_sticker_count(oi.unit, oi.quantity, oi.plants_per_crate, prd.plants_per_crate) IS NULL) AS onbekend
|
|
32222
|
+
FROM public.orders o
|
|
32223
|
+
JOIN public.order_items oi ON oi.order_id = o.id
|
|
32224
|
+
LEFT JOIN public.products prd ON prd.id = oi.product_id
|
|
32225
|
+
WHERE oi.needs_sticker IS TRUE
|
|
32226
|
+
AND oi.quarantined_at IS NULL
|
|
32227
|
+
AND oi.product_id IS NOT NULL
|
|
32228
|
+
AND coalesce(o.order_source, '') <> 'floriday'
|
|
32229
|
+
AND o.deleted_at IS NULL AND o.cancelled_at IS NULL
|
|
32230
|
+
AND coalesce(o.status, '') <> 'geannuleerd'
|
|
32231
|
+
AND public.logistics_order_date(o.delivery_date) >= current_date - 1
|
|
32232
|
+
GROUP BY o.id, o.order_number, o.customer_id, oi.product_id
|
|
32233
|
+
LIMIT greatest(1, p_limit)
|
|
32234
|
+
LOOP
|
|
32235
|
+
v_bron := r.order_id::text || ':' || r.product_id::text;
|
|
32236
|
+
SELECT count(*), (array_agg(s.id))[1] INTO v_n, v_sticker
|
|
32237
|
+
FROM public.customer_product_stickers s
|
|
32238
|
+
JOIN public.customers c ON c.id = r.customer_id
|
|
32239
|
+
WHERE s.trade_item_key = r.product_id::text
|
|
32240
|
+
AND s.customer_key IN (c.id::text, 'tf:' || coalesce(c.twinfield_code, '-'), 'cn:' || coalesce(c.customer_number, '-'));
|
|
32241
|
+
v_status := 'wacht_op_moment'; v_reden := NULL;
|
|
32242
|
+
IF r.onbekend THEN
|
|
32243
|
+
v_status := 'geblokkeerd'; v_reden := 'Aantal stickers onbekend: regel in fust zonder planten per fust (niet op de regel en niet bij het product). Vul planten per fust in.';
|
|
32244
|
+
ELSIF coalesce(r.planten, 0) < 1 THEN
|
|
32245
|
+
v_status := 'geblokkeerd'; v_reden := 'Aantal planten is 0; geen sticker te printen.';
|
|
32246
|
+
ELSIF v_n = 0 THEN
|
|
32247
|
+
v_status := 'geblokkeerd'; v_reden := 'Geen eigen sticker gevonden voor deze klant en dit product. Voeg hem toe bij de klant.';
|
|
32248
|
+
ELSIF v_n > 1 THEN
|
|
32249
|
+
v_status := 'geblokkeerd'; v_reden := 'Meer dan één eigen sticker voor deze klant en dit product; er mag er maar één zijn.';
|
|
32250
|
+
END IF;
|
|
32251
|
+
|
|
32252
|
+
UPDATE public.logistics_sticker_prints
|
|
32253
|
+
SET kopieen = greatest(coalesce(r.planten, 0), 1),
|
|
32254
|
+
eigen_sticker_id = CASE WHEN v_n = 1 THEN v_sticker ELSE NULL END,
|
|
32255
|
+
status = CASE WHEN status = 'geblokkeerd' AND v_status = 'wacht_op_moment' THEN 'wacht_op_moment'
|
|
32256
|
+
WHEN status = 'niet_printen' THEN v_status
|
|
32257
|
+
WHEN status = 'wacht_op_moment' THEN v_status ELSE status END,
|
|
32258
|
+
reden = CASE WHEN status IN ('wacht_op_moment', 'geblokkeerd', 'niet_printen') THEN v_reden ELSE reden END,
|
|
32259
|
+
updated_at = now()
|
|
32260
|
+
WHERE soort = 'eigen_sticker' AND bron_id = v_bron
|
|
32261
|
+
AND status IN ('wacht_op_moment', 'geblokkeerd', 'niet_printen');
|
|
32262
|
+
IF NOT FOUND AND NOT EXISTS (SELECT 1 FROM public.logistics_sticker_prints WHERE soort = 'eigen_sticker' AND bron_id = v_bron) THEN
|
|
32263
|
+
INSERT INTO public.logistics_sticker_prints (soort, bron_id, order_id, order_number, kopieen, status, reden, eigen_sticker_id)
|
|
32264
|
+
VALUES ('eigen_sticker', v_bron, r.order_id, r.order_number, greatest(coalesce(r.planten, 0), 1), v_status, v_reden,
|
|
32265
|
+
CASE WHEN v_n = 1 THEN v_sticker ELSE NULL END);
|
|
32266
|
+
END IF;
|
|
32267
|
+
v_bijgewerkt := v_bijgewerkt + 1;
|
|
32268
|
+
END LOOP;
|
|
32269
|
+
|
|
32270
|
+
UPDATE public.logistics_sticker_prints p
|
|
32271
|
+
SET status = 'niet_printen', reden = 'Sticker uitgezet op de orderregel of order vervallen.', updated_at = now()
|
|
32272
|
+
WHERE p.soort = 'eigen_sticker' AND p.status IN ('wacht_op_moment', 'geblokkeerd', 'klaar')
|
|
32273
|
+
AND NOT EXISTS (
|
|
32274
|
+
SELECT 1 FROM public.order_items oi JOIN public.orders o ON o.id = oi.order_id
|
|
32275
|
+
WHERE oi.order_id = p.order_id AND p.bron_id = oi.order_id::text || ':' || oi.product_id::text
|
|
32276
|
+
AND oi.needs_sticker IS TRUE AND oi.quarantined_at IS NULL
|
|
32277
|
+
AND o.deleted_at IS NULL AND o.cancelled_at IS NULL AND coalesce(o.status, '') <> 'geannuleerd');
|
|
32278
|
+
GET DIAGNOSTICS v_uit = ROW_COUNT;
|
|
32279
|
+
|
|
32280
|
+
WITH vrij AS (
|
|
32281
|
+
SELECT p.id,
|
|
32282
|
+
(SELECT public.logistics_try_uuid(q.payload->>'cartId')
|
|
32283
|
+
FROM public.pick_queue q
|
|
32284
|
+
WHERE q.cancelled_at IS NULL AND q.print_job_id IS NOT NULL
|
|
32285
|
+
AND (q.payload->>'orderId' = p.order_id::text OR q.payload->'orderIds' ? p.order_id::text)
|
|
32286
|
+
ORDER BY q.created_at LIMIT 1) AS kar,
|
|
32287
|
+
(SELECT k.print_plek_id::text
|
|
32288
|
+
FROM public.pick_queue q
|
|
32289
|
+
JOIN public.print_plek_koppelingen k ON k.bron IN ('pickstation', 'pick_station') AND k.bron_id = q.station_id::text
|
|
32290
|
+
WHERE q.cancelled_at IS NULL AND q.print_job_id IS NOT NULL
|
|
32291
|
+
AND (q.payload->>'orderId' = p.order_id::text OR q.payload->'orderIds' ? p.order_id::text)
|
|
32292
|
+
ORDER BY q.created_at, k.created_at LIMIT 1) AS plek,
|
|
32293
|
+
s.width_mm, s.height_mm,
|
|
32294
|
+
public.logistics_document_for_order(p.order_id, 'eigen_sticker') AS lay
|
|
32295
|
+
FROM public.logistics_sticker_prints p
|
|
32296
|
+
LEFT JOIN public.customer_product_stickers s ON s.id = p.eigen_sticker_id
|
|
32297
|
+
WHERE p.soort = 'eigen_sticker' AND p.status = 'wacht_op_moment'
|
|
32298
|
+
AND EXISTS (SELECT 1 FROM public.pick_queue q
|
|
32299
|
+
WHERE q.cancelled_at IS NULL AND q.print_job_id IS NOT NULL
|
|
32300
|
+
AND (q.payload->>'orderId' = p.order_id::text OR q.payload->'orderIds' ? p.order_id::text))
|
|
32301
|
+
)
|
|
32302
|
+
UPDATE public.logistics_sticker_prints p
|
|
32303
|
+
SET status = 'klaar', cart_id = vrij.kar, print_plek_id = vrij.plek,
|
|
32304
|
+
breedte_mm = coalesce(vrij.width_mm, (vrij.lay->>'breedte_mm')::numeric),
|
|
32305
|
+
hoogte_mm = coalesce(vrij.height_mm, (vrij.lay->>'hoogte_mm')::numeric),
|
|
32306
|
+
updated_at = now()
|
|
32307
|
+
FROM vrij
|
|
32308
|
+
WHERE p.id = vrij.id;
|
|
32309
|
+
GET DIAGNOSTICS v_vrij = ROW_COUNT;
|
|
32310
|
+
|
|
32311
|
+
RETURN jsonb_build_object('regels', v_bijgewerkt, 'vrijgegeven', v_vrij, 'niet_printen', v_uit);
|
|
32312
|
+
END;
|
|
32313
|
+
$function$
|
|
32314
|
+
;
|
|
32315
|
+
|
|
32316
|
+
CREATE OR REPLACE FUNCTION public.logistics_own_traysticker_sync(p_limit integer DEFAULT 200)
|
|
32317
|
+
RETURNS jsonb
|
|
32318
|
+
LANGUAGE plpgsql
|
|
32319
|
+
SECURITY DEFINER
|
|
32320
|
+
SET search_path TO 'public'
|
|
32321
|
+
AS $function$
|
|
32322
|
+
DECLARE
|
|
32323
|
+
s jsonb; v_tray text; v_rel text; v_station text;
|
|
32324
|
+
r record; v_bron text; v_keep text[] := '{}'; v_wacht_orders uuid[] := '{}';
|
|
32325
|
+
n int := 0; n_geblokkeerd int := 0; n_vrij int := 0; n_uit int := 0;
|
|
32326
|
+
BEGIN
|
|
32327
|
+
PERFORM public.logistics_acc_guard('ingelogd', 'logistics_own_traysticker_sync');
|
|
32328
|
+
SELECT settings INTO s FROM public.logistics_rule_configs
|
|
32329
|
+
ORDER BY is_active DESC, (status = 'actief') DESC, updated_at DESC LIMIT 1;
|
|
32330
|
+
v_tray := public.logistics_rule_text(s, 'pickstations', 'traysticker_release', 'uit');
|
|
32331
|
+
IF v_tray = 'uit' THEN
|
|
32332
|
+
RETURN jsonb_build_object('trayRelease', v_tray, 'regels', 0);
|
|
32333
|
+
END IF;
|
|
32334
|
+
v_rel := public.logistics_rule_text(s, 'pickstations', 'sticker_release', 'with_pick_release');
|
|
32335
|
+
v_station := CASE WHEN v_rel = 'with_packing_slip'
|
|
32336
|
+
THEN public.logistics_rule_text(s, 'pickstations', 'packing_slip_station', '')
|
|
32337
|
+
ELSE public.logistics_rule_text(s, 'pickstations', 'sticker_station', '') END;
|
|
32338
|
+
|
|
32339
|
+
FOR r IN
|
|
32340
|
+
WITH regels AS (
|
|
32341
|
+
SELECT o.id AS order_id, o.order_number, ci.cart_id, ci.id AS ci_id, ci.crates, ci.created_at,
|
|
32342
|
+
coalesce(ci.product_name, oi.product_name, 'onbekend') AS regelnaam
|
|
32343
|
+
FROM public.orders o
|
|
32344
|
+
JOIN public.order_items oi ON oi.order_id = o.id
|
|
32345
|
+
JOIN public.order_cart_items ci ON ci.order_item_id = oi.id
|
|
32346
|
+
WHERE coalesce(o.order_source, '') <> 'floriday'
|
|
32347
|
+
AND o.deleted_at IS NULL AND o.cancelled_at IS NULL
|
|
32348
|
+
AND coalesce(o.status, '') <> 'geannuleerd'
|
|
32349
|
+
AND oi.quarantined_at IS NULL
|
|
32350
|
+
AND public.logistics_order_date(o.delivery_date) >= current_date - 1
|
|
32351
|
+
)
|
|
32352
|
+
SELECT g.*,
|
|
32353
|
+
(SELECT min(x.regelnaam) FROM regels x WHERE x.order_id = g.order_id AND coalesce(x.crates, 0) <= 0) AS onbekende_regel
|
|
32354
|
+
FROM regels g
|
|
32355
|
+
ORDER BY g.order_number, g.cart_id, g.created_at, g.ci_id
|
|
32356
|
+
LIMIT greatest(1, p_limit)
|
|
32357
|
+
LOOP
|
|
32358
|
+
IF r.onbekende_regel IS NOT NULL THEN
|
|
32359
|
+
v_wacht_orders := v_wacht_orders || r.order_id;
|
|
32360
|
+
v_bron := r.order_id::text || ':fustaantal-onbekend';
|
|
32361
|
+
v_keep := v_keep || v_bron;
|
|
32362
|
+
INSERT INTO public.logistics_sticker_prints (soort, bron_id, order_id, order_number, cart_id, kopieen, status, reden)
|
|
32363
|
+
VALUES ('eigen_traysticker', v_bron, r.order_id, r.order_number, NULL, 1, 'geblokkeerd',
|
|
32364
|
+
'Fustaantal van regel ' || r.onbekende_regel || ' onbekend. Alle traystickers van deze order wachten, zodat de nummering klopt.')
|
|
32365
|
+
ON CONFLICT DO NOTHING;
|
|
32366
|
+
n_geblokkeerd := n_geblokkeerd + 1;
|
|
32367
|
+
CONTINUE;
|
|
32368
|
+
END IF;
|
|
32369
|
+
FOR k IN 1 .. r.crates LOOP
|
|
32370
|
+
v_bron := r.order_id::text || ':' || r.cart_id::text || ':' || r.ci_id::text || ':' || k::text;
|
|
32371
|
+
v_keep := v_keep || v_bron;
|
|
32372
|
+
INSERT INTO public.logistics_sticker_prints (soort, bron_id, order_id, order_number, cart_id, kopieen, print_plek_id, status, reden)
|
|
32373
|
+
VALUES ('eigen_traysticker', v_bron, r.order_id, r.order_number, r.cart_id, 1,
|
|
32374
|
+
public.logistics_sticker_cart_plek(r.cart_id, v_station), 'wacht_op_moment', 'Wacht op de pakbon van deze kar.')
|
|
32375
|
+
ON CONFLICT DO NOTHING;
|
|
32376
|
+
UPDATE public.logistics_sticker_prints
|
|
32377
|
+
SET status = 'wacht_op_moment', reden = 'Wacht op de pakbon van deze kar.', updated_at = now()
|
|
32378
|
+
WHERE soort = 'eigen_traysticker' AND bron_id = v_bron AND status = 'geblokkeerd'
|
|
32379
|
+
AND reden LIKE 'Fustaantal van regel %';
|
|
32380
|
+
n := n + 1;
|
|
32381
|
+
END LOOP;
|
|
32382
|
+
END LOOP;
|
|
32383
|
+
|
|
32384
|
+
UPDATE public.logistics_sticker_prints p
|
|
32385
|
+
SET status = 'geblokkeerd',
|
|
32386
|
+
reden = (SELECT b.reden FROM public.logistics_sticker_prints b
|
|
32387
|
+
WHERE b.soort = 'eigen_traysticker' AND b.bron_id = p.order_id::text || ':fustaantal-onbekend' LIMIT 1),
|
|
32388
|
+
updated_at = now()
|
|
32389
|
+
WHERE p.soort = 'eigen_traysticker' AND p.status IN ('wacht_op_moment', 'klaar')
|
|
32390
|
+
AND p.order_id = ANY (v_wacht_orders);
|
|
32391
|
+
v_keep := v_keep || ARRAY(SELECT p.bron_id FROM public.logistics_sticker_prints p
|
|
32392
|
+
WHERE p.soort = 'eigen_traysticker' AND p.order_id = ANY (v_wacht_orders)
|
|
32393
|
+
AND p.reden LIKE 'Fustaantal van regel %');
|
|
32394
|
+
|
|
32395
|
+
UPDATE public.logistics_sticker_prints p
|
|
32396
|
+
SET status = 'niet_printen', reden = 'Fust hoort niet meer bij deze kar of order vervallen.', updated_at = now()
|
|
32397
|
+
WHERE p.soort = 'eigen_traysticker' AND p.status IN ('wacht_op_moment', 'geblokkeerd', 'klaar')
|
|
32398
|
+
AND NOT (p.bron_id = ANY (v_keep))
|
|
32399
|
+
AND p.order_id IN (SELECT o.id FROM public.orders o WHERE public.logistics_order_date(o.delivery_date) >= current_date - 1);
|
|
32400
|
+
GET DIAGNOSTICS n_uit = ROW_COUNT;
|
|
32401
|
+
|
|
32402
|
+
UPDATE public.logistics_sticker_prints p
|
|
32403
|
+
SET status = 'klaar', reden = NULL,
|
|
32404
|
+
print_plek_id = coalesce(p.print_plek_id, public.logistics_sticker_cart_plek(p.cart_id, v_station)),
|
|
32405
|
+
updated_at = now()
|
|
32406
|
+
WHERE p.soort = 'eigen_traysticker' AND p.status = 'wacht_op_moment'
|
|
32407
|
+
AND p.cart_id IS NOT NULL AND public.logistics_cart_slip_queued(p.cart_id);
|
|
32408
|
+
GET DIAGNOSTICS n_vrij = ROW_COUNT;
|
|
32409
|
+
|
|
32410
|
+
RETURN jsonb_build_object('trayRelease', v_tray, 'fusten', n, 'geblokkeerd', n_geblokkeerd, 'vrijgegeven', n_vrij, 'niet_printen', n_uit);
|
|
32411
|
+
END; $function$
|
|
32412
|
+
;
|
|
32413
|
+
|
|
32414
|
+
CREATE OR REPLACE FUNCTION public.logistics_sticker_sync(p_limit integer DEFAULT 200)
|
|
32415
|
+
RETURNS jsonb
|
|
32416
|
+
LANGUAGE plpgsql
|
|
32417
|
+
SECURITY DEFINER
|
|
32418
|
+
SET search_path TO 'public'
|
|
32419
|
+
AS $function$
|
|
32420
|
+
DECLARE
|
|
32421
|
+
s jsonb; v_rel text; v_station text; v_tray text;
|
|
32422
|
+
st record; r_ord record; d jsonb; fi jsonb; v_n int; v_order uuid; v_onr text;
|
|
32423
|
+
v_copies int; v_size numeric[]; v_type text; v_should text; v_keep uuid[]; v_del text; v_fo text; v_cart uuid;
|
|
32424
|
+
v_crates_tot int; v_pieces_tot int; r record; v_rows int; v_cp jsonb; v_bron text; v_ncarts int; sv record;
|
|
32425
|
+
n_st int := 0; n_tray int := 0;
|
|
32426
|
+
BEGIN
|
|
32427
|
+
PERFORM public.logistics_acc_guard('ingelogd', 'logistics_sticker_sync');
|
|
32428
|
+
SELECT settings INTO s FROM public.logistics_rule_configs
|
|
32429
|
+
ORDER BY is_active DESC, (status = 'actief') DESC, updated_at DESC LIMIT 1;
|
|
32430
|
+
v_rel := public.logistics_rule_text(s, 'pickstations', 'sticker_release', 'with_pick_release');
|
|
32431
|
+
v_station := CASE WHEN v_rel = 'with_packing_slip'
|
|
32432
|
+
THEN public.logistics_rule_text(s, 'pickstations', 'packing_slip_station', '')
|
|
32433
|
+
ELSE public.logistics_rule_text(s, 'pickstations', 'sticker_station', '') END;
|
|
32434
|
+
v_tray := public.logistics_rule_text(s, 'pickstations', 'traysticker_release', 'uit');
|
|
32435
|
+
|
|
32436
|
+
FOR st IN SELECT * FROM public.floriday_customer_stickers_cache ORDER BY updated_at DESC LIMIT greatest(p_limit,1) LOOP
|
|
32437
|
+
n_st := n_st + 1;
|
|
32438
|
+
d := coalesce(st.data, '{}'::jsonb);
|
|
32439
|
+
fi := coalesce(d->'stickerFileInformation', '{}'::jsonb);
|
|
32440
|
+
v_size := CASE WHEN st.sticker_width_mm IS NOT NULL AND st.sticker_height_mm IS NOT NULL
|
|
32441
|
+
THEN ARRAY[st.sticker_width_mm::numeric, st.sticker_height_mm::numeric]
|
|
32442
|
+
ELSE public.logistics_sticker_parse_size(d->'stickerSize') END;
|
|
32443
|
+
v_type := upper(nullif(btrim(coalesce(st.sticker_type, d->>'stickerType', '')), ''));
|
|
32444
|
+
v_should := lower(coalesce(st.should_be_printed::text, fi->>'shouldBePrinted', ''));
|
|
32445
|
+
|
|
32446
|
+
-- Koppeling volgens Suppliers API 2026v1: alleen via salesOrderId.
|
|
32447
|
+
SELECT count(*), (array_agg(id))[1], (array_agg(order_number))[1] INTO v_n, v_order, v_onr
|
|
32448
|
+
FROM public.orders WHERE deleted_at IS NULL
|
|
32449
|
+
AND nullif(btrim(coalesce(st.sales_order_id, d->>'salesOrderId', '')), '') IS NOT NULL
|
|
32450
|
+
AND floriday_sales_order_id = coalesce(st.sales_order_id, d->>'salesOrderId');
|
|
32451
|
+
|
|
32452
|
+
IF coalesce(st.is_deleted,false) OR lower(coalesce(d->>'isDeleted','')) = 'true' THEN
|
|
32453
|
+
UPDATE public.logistics_sticker_prints SET status = 'niet_printen', reden = 'De koper heeft deze sticker verwijderd.'
|
|
32454
|
+
WHERE soort = 'sticker' AND bron_id = st.floriday_id AND status NOT IN ('in_printwachtrij','afgemeld');
|
|
32455
|
+
CONTINUE;
|
|
32456
|
+
END IF;
|
|
32457
|
+
IF lower(coalesce(d->>'isHandled','')) = 'true' THEN
|
|
32458
|
+
UPDATE public.logistics_sticker_prints SET status = 'afgemeld', reden = 'Al afgemeld in Floriday.', handled_at = coalesce(handled_at, now())
|
|
32459
|
+
WHERE soort = 'sticker' AND bron_id = st.floriday_id AND status <> 'afgemeld';
|
|
32460
|
+
CONTINUE;
|
|
32461
|
+
END IF;
|
|
32462
|
+
IF v_n = 1 AND public.logistics_sales_order_external_fulfilled(v_order, coalesce(st.sales_order_id, d->>'salesOrderId')) THEN
|
|
32463
|
+
UPDATE public.logistics_sticker_prints SET status = 'niet_printen', reden = 'Afgehandeld in Floriday; door ons niet geprint en niet afgemeld.'
|
|
32464
|
+
WHERE soort = 'sticker' AND bron_id = st.floriday_id AND status NOT IN ('in_printwachtrij','afgemeld');
|
|
32465
|
+
PERFORM public.logistics_sticker_upsert('sticker', st.floriday_id, v_order, v_onr, NULL, 0, v_size, NULL, NULL, 'niet_printen', 'Afgehandeld in Floriday; door ons niet geprint en niet afgemeld.');
|
|
32466
|
+
CONTINUE;
|
|
32467
|
+
END IF;
|
|
32468
|
+
IF v_n <> 1 THEN
|
|
32469
|
+
PERFORM public.logistics_sticker_upsert('sticker', st.floriday_id, NULL, NULL, NULL, 0, v_size, NULL, NULL, 'geblokkeerd',
|
|
32470
|
+
format('Geen eenduidige order bij Floriday-verkooporder %s (%s gevonden).', coalesce(st.sales_order_id, d->>'salesOrderId', 'onbekend'), v_n));
|
|
32471
|
+
UPDATE public.logistics_sticker_prints SET sticker_type = v_type WHERE soort='sticker' AND bron_id = st.floriday_id;
|
|
32472
|
+
CONTINUE;
|
|
32473
|
+
END IF;
|
|
32474
|
+
IF v_should = 'false' THEN
|
|
32475
|
+
-- Geen printopdracht; zichtbaar als 'niet_printen'. Wordt ook niet afgemeld in Floriday.
|
|
32476
|
+
UPDATE public.logistics_sticker_prints SET status = 'niet_printen',
|
|
32477
|
+
reden = 'Floriday: deze sticker hoeft niet geprint te worden (shouldBePrinted = false).'
|
|
32478
|
+
WHERE soort = 'sticker' AND bron_id = st.floriday_id AND cart_id IS NOT NULL AND status NOT IN ('in_printwachtrij','afgemeld');
|
|
32479
|
+
PERFORM public.logistics_sticker_upsert('sticker', st.floriday_id, v_order, v_onr, NULL, 0, v_size, NULL, NULL, 'niet_printen',
|
|
32480
|
+
'Floriday: deze sticker hoeft niet geprint te worden (shouldBePrinted = false).');
|
|
32481
|
+
UPDATE public.logistics_sticker_prints SET sticker_type = v_type WHERE soort='sticker' AND bron_id = st.floriday_id;
|
|
32482
|
+
CONTINUE;
|
|
32483
|
+
END IF;
|
|
32484
|
+
IF lower(coalesce(fi->>'deliverSeparately','')) = 'true' THEN
|
|
32485
|
+
PERFORM public.logistics_sticker_upsert('sticker', st.floriday_id, v_order, v_onr, NULL, 0, v_size, NULL, NULL, 'geblokkeerd',
|
|
32486
|
+
'Sticker moet apart worden geleverd (deliverSeparately); kies handmatig hoe die mee gaat.');
|
|
32487
|
+
UPDATE public.logistics_sticker_prints SET sticker_type = v_type WHERE soort='sticker' AND bron_id = st.floriday_id;
|
|
32488
|
+
CONTINUE;
|
|
32489
|
+
END IF;
|
|
32490
|
+
IF nullif(btrim(coalesce(d->>'salesOrderCorrectionRequestId','')),'') IS NOT NULL THEN
|
|
32491
|
+
PERFORM public.logistics_sticker_upsert('sticker', st.floriday_id, v_order, v_onr, NULL, 0, v_size, NULL, NULL, 'wacht_op_moment',
|
|
32492
|
+
'Wacht tot de correctie op de verkooporder is verwerkt.');
|
|
32493
|
+
CONTINUE;
|
|
32494
|
+
END IF;
|
|
32495
|
+
-- 1.0.182: aantal uit numberOfCopies -> additionalServices.numberOfUnits (LABEL) -> afgeleid uit stickersoort.
|
|
32496
|
+
v_cp := public.logistics_sticker_copies(v_order, st.floriday_id, v_type, fi, nullif(btrim(coalesce(st.trade_item_id,'')),''));
|
|
32497
|
+
v_copies := nullif(v_cp->>'aantal','')::int; v_bron := v_cp->>'bron';
|
|
32498
|
+
IF coalesce(v_copies,0) <= 0 THEN
|
|
32499
|
+
PERFORM public.logistics_sticker_upsert('sticker', st.floriday_id, v_order, v_onr, NULL, 0, v_size, NULL, NULL, 'geblokkeerd',
|
|
32500
|
+
'Aantal stickers onbekend: geen numberOfCopies, geen numberOfUnits (LABEL) en niet af te leiden uit de stickersoort; niet geprint.');
|
|
32501
|
+
UPDATE public.logistics_sticker_prints SET sticker_type = v_type, kopieen_bron = NULL WHERE soort='sticker' AND bron_id = st.floriday_id;
|
|
32502
|
+
CONTINUE;
|
|
32503
|
+
END IF;
|
|
32504
|
+
IF v_type = 'DOCUMENT' THEN
|
|
32505
|
+
SELECT oc.id INTO v_cart FROM logistics_active.order_carts oc WHERE oc.order_id = v_order ORDER BY oc.cart_number, oc.created_at LIMIT 1;
|
|
32506
|
+
IF v_cart IS NULL THEN
|
|
32507
|
+
PERFORM public.logistics_sticker_upsert('sticker', st.floriday_id, v_order, v_onr, NULL, v_copies, v_size, NULL, NULL, 'wacht_op_moment',
|
|
32508
|
+
'Wacht tot de order op een kar staat.');
|
|
32509
|
+
ELSE
|
|
32510
|
+
PERFORM public.logistics_sticker_upsert('sticker', st.floriday_id, v_order, v_onr, v_cart, v_copies, v_size,
|
|
32511
|
+
public.logistics_sticker_cart_plek(v_cart, v_station), NULL,
|
|
32512
|
+
CASE WHEN public.logistics_cart_slip_queued(v_cart) THEN 'klaar' ELSE 'wacht_op_moment' END,
|
|
32513
|
+
CASE WHEN public.logistics_cart_slip_queued(v_cart) THEN NULL ELSE 'Wacht op de pakbon van deze kar.' END);
|
|
32514
|
+
UPDATE public.logistics_sticker_prints SET status = 'niet_printen', reden = 'Vervangen door regels per kar.'
|
|
32515
|
+
WHERE soort = 'sticker' AND bron_id = st.floriday_id AND (cart_id IS NULL OR cart_id <> v_cart)
|
|
32516
|
+
AND status NOT IN ('in_printwachtrij','afgemeld','niet_printen');
|
|
32517
|
+
END IF;
|
|
32518
|
+
UPDATE public.logistics_sticker_prints SET sticker_type = v_type, kopieen_bron = v_bron WHERE soort='sticker' AND bron_id = st.floriday_id;
|
|
32519
|
+
CONTINUE;
|
|
32520
|
+
END IF;
|
|
32521
|
+
IF v_type IS NULL OR v_type NOT IN ('POT','PRODUCT','SLEEVE','PACKAGE','LAYER','LOAD_CARRIER') THEN
|
|
32522
|
+
PERFORM public.logistics_sticker_upsert('sticker', st.floriday_id, v_order, v_onr, NULL, v_copies, v_size, NULL, NULL, 'geblokkeerd',
|
|
32523
|
+
format('Onbekende of niet-ondersteunde stickersoort (%s); kies handmatig hoe deze sticker mee gaat.', coalesce(v_type,'leeg')));
|
|
32524
|
+
UPDATE public.logistics_sticker_prints SET sticker_type = v_type WHERE soort='sticker' AND bron_id = st.floriday_id;
|
|
32525
|
+
CONTINUE;
|
|
32526
|
+
END IF;
|
|
32527
|
+
|
|
32528
|
+
v_keep := ARRAY[]::uuid[]; v_rows := 0; v_crates_tot := 0; v_pieces_tot := 0;
|
|
32529
|
+
FOR r IN SELECT * FROM public.logistics_sticker_distribution(v_order, v_type, v_copies, nullif(btrim(coalesce(st.trade_item_id,'')),'')) LOOP
|
|
32530
|
+
v_rows := v_rows + 1; v_keep := v_keep || r.cart_id;
|
|
32531
|
+
v_crates_tot := v_crates_tot + r.crates; v_pieces_tot := v_pieces_tot + r.pieces;
|
|
32532
|
+
PERFORM public.logistics_sticker_upsert('sticker', st.floriday_id, v_order, v_onr, r.cart_id, r.kopieen, v_size,
|
|
32533
|
+
public.logistics_sticker_cart_plek(r.cart_id, v_station), NULL,
|
|
32534
|
+
CASE WHEN public.logistics_cart_slip_queued(r.cart_id) THEN 'klaar' ELSE 'wacht_op_moment' END,
|
|
32535
|
+
CASE WHEN public.logistics_cart_slip_queued(r.cart_id) THEN NULL ELSE 'Wacht op de pakbon van deze kar.' END);
|
|
32536
|
+
END LOOP;
|
|
32537
|
+
IF v_rows = 0 THEN
|
|
32538
|
+
PERFORM public.logistics_sticker_upsert('sticker', st.floriday_id, v_order, v_onr, NULL, v_copies, v_size, NULL, NULL, 'geblokkeerd',
|
|
32539
|
+
'De verdeling over karren sluit niet: de order staat (nog) niet op een kar, of de karren hebben geen fusten/stuks van dit artikel.');
|
|
32540
|
+
UPDATE public.logistics_sticker_prints SET sticker_type = v_type WHERE soort='sticker' AND bron_id = st.floriday_id;
|
|
32541
|
+
CONTINUE;
|
|
32542
|
+
END IF;
|
|
32543
|
+
UPDATE public.logistics_sticker_prints SET sticker_type = v_type, kopieen_bron = v_bron WHERE soort='sticker' AND bron_id = st.floriday_id;
|
|
32544
|
+
IF (v_type IN ('PACKAGE','LAYER') AND v_crates_tot <> v_copies) OR (v_type IN ('POT','PRODUCT','SLEEVE') AND v_pieces_tot <> v_copies) THEN
|
|
32545
|
+
BEGIN
|
|
32546
|
+
INSERT INTO public.logistics_trace_events(phase, step, status, reason, order_ref)
|
|
32547
|
+
SELECT 'stickers', 'Aantal klantstickers wijkt af', 'waarschuwing',
|
|
32548
|
+
format('Floriday vraagt %s (%s); op de karren: %s fusten / %s stuks. Het aantal van Floriday wordt geprint.',
|
|
32549
|
+
v_copies, v_type, v_crates_tot, v_pieces_tot), v_onr
|
|
32550
|
+
WHERE NOT EXISTS (SELECT 1 FROM public.logistics_trace_events e WHERE e.order_ref = v_onr AND e.step = 'Aantal klantstickers wijkt af'
|
|
32551
|
+
AND e.created_at > now() - interval '1 day');
|
|
32552
|
+
EXCEPTION WHEN OTHERS THEN NULL; END;
|
|
32553
|
+
END IF;
|
|
32554
|
+
UPDATE public.logistics_sticker_prints SET status = 'niet_printen',
|
|
32555
|
+
reden = CASE WHEN cart_id IS NULL THEN 'Vervangen door regels per kar.' ELSE 'Kar hoort niet meer bij deze sticker.' END
|
|
32556
|
+
WHERE soort = 'sticker' AND bron_id = st.floriday_id
|
|
32557
|
+
AND (cart_id IS NULL OR NOT (cart_id = ANY (v_keep)))
|
|
32558
|
+
AND status NOT IN ('in_printwachtrij','afgemeld','niet_printen');
|
|
32559
|
+
END LOOP;
|
|
32560
|
+
|
|
32561
|
+
-- 1.0.182: stickers die alleen in additionalServices van de verkooporder staan (stickercache leeg of nog niet bijgewerkt).
|
|
32562
|
+
FOR sv IN
|
|
32563
|
+
SELECT o.id AS order_id, o.order_number, e->>'stickerId' AS sticker_id, sum((e->>'numberOfUnits')::int)::int AS units
|
|
32564
|
+
FROM public.orders o
|
|
32565
|
+
JOIN public.floriday_salesorders_cache s ON s.floriday_id = o.floriday_sales_order_id AND NOT coalesce(s.is_deleted,false)
|
|
32566
|
+
CROSS JOIN LATERAL jsonb_array_elements(CASE WHEN jsonb_typeof(s.data->'additionalServices') = 'array' THEN s.data->'additionalServices' ELSE '[]'::jsonb END) e
|
|
32567
|
+
WHERE o.deleted_at IS NULL AND public.logistics_order_date(o.delivery_date) >= current_date - 2
|
|
32568
|
+
AND upper(coalesce(e->>'unit','')) = 'LABEL' AND (e->>'numberOfUnits') ~ '^\d+$'
|
|
32569
|
+
AND nullif(btrim(coalesce(e->>'stickerId','')),'') IS NOT NULL
|
|
32570
|
+
AND NOT EXISTS (SELECT 1 FROM public.floriday_customer_stickers_cache c WHERE c.floriday_id = e->>'stickerId')
|
|
32571
|
+
AND NOT public.logistics_sales_order_external_fulfilled(o.id, o.floriday_sales_order_id)
|
|
32572
|
+
GROUP BY o.id, o.order_number, e->>'stickerId'
|
|
32573
|
+
LIMIT greatest(p_limit,1)
|
|
32574
|
+
LOOP
|
|
32575
|
+
IF coalesce(sv.units,0) <= 0 THEN CONTINUE; END IF;
|
|
32576
|
+
SELECT count(*), (array_agg(oc.id ORDER BY oc.cart_number, oc.created_at))[1] INTO v_ncarts, v_cart
|
|
32577
|
+
FROM logistics_active.order_carts oc WHERE oc.order_id = sv.order_id;
|
|
32578
|
+
IF v_ncarts = 1 THEN
|
|
32579
|
+
PERFORM public.logistics_sticker_upsert('sticker', sv.sticker_id, sv.order_id, sv.order_number, v_cart, sv.units, NULL,
|
|
32580
|
+
public.logistics_sticker_cart_plek(v_cart, v_station), NULL,
|
|
32581
|
+
CASE WHEN public.logistics_cart_slip_queued(v_cart) THEN 'klaar' ELSE 'wacht_op_moment' END,
|
|
32582
|
+
CASE WHEN public.logistics_cart_slip_queued(v_cart) THEN NULL ELSE 'Wacht op de pakbon van deze kar.' END);
|
|
32583
|
+
UPDATE public.logistics_sticker_prints SET status = 'niet_printen', reden = 'Vervangen door regels per kar.'
|
|
32584
|
+
WHERE soort = 'sticker' AND bron_id = sv.sticker_id AND (cart_id IS NULL OR cart_id <> v_cart)
|
|
32585
|
+
AND status NOT IN ('in_printwachtrij','afgemeld','niet_printen');
|
|
32586
|
+
ELSE
|
|
32587
|
+
PERFORM public.logistics_sticker_upsert('sticker', sv.sticker_id, sv.order_id, sv.order_number, NULL, sv.units, NULL, NULL, NULL, 'wacht_op_moment',
|
|
32588
|
+
CASE WHEN v_ncarts = 0 THEN 'Wacht tot de order op een kar staat.'
|
|
32589
|
+
ELSE format('Stickersoort nog onbekend; wacht op de stickergegevens van Floriday om %s stickers over %s karren te verdelen.', sv.units, v_ncarts) END);
|
|
32590
|
+
END IF;
|
|
32591
|
+
UPDATE public.logistics_sticker_prints SET kopieen_bron = 'numberOfUnits' WHERE soort='sticker' AND bron_id = sv.sticker_id
|
|
32592
|
+
AND status NOT IN ('in_printwachtrij','afgemeld');
|
|
32593
|
+
n_st := n_st + 1;
|
|
32594
|
+
END LOOP;
|
|
32595
|
+
|
|
32596
|
+
IF v_tray <> 'uit' THEN
|
|
32597
|
+
FOR r_ord IN
|
|
32598
|
+
SELECT o.id, o.order_number, nullif(btrim(o.floriday_sales_order_id),'') AS sales
|
|
32599
|
+
FROM public.orders o
|
|
32600
|
+
WHERE o.deleted_at IS NULL AND nullif(btrim(o.floriday_sales_order_id),'') IS NOT NULL
|
|
32601
|
+
AND public.logistics_order_date(o.delivery_date) >= current_date - 2
|
|
32602
|
+
AND EXISTS (SELECT 1 FROM logistics_active.order_carts oc WHERE oc.order_id = o.id)
|
|
32603
|
+
ORDER BY o.delivery_date LIMIT greatest(p_limit,1)
|
|
32604
|
+
LOOP
|
|
32605
|
+
SELECT d2.floriday_id INTO v_del FROM public.floriday_delivery_orders_cache d2
|
|
32606
|
+
WHERE d2.data @> jsonb_build_object('fulfillmentRequests', jsonb_build_array(jsonb_build_object('salesOrderId', r_ord.sales)))
|
|
32607
|
+
ORDER BY d2.fetched_at DESC LIMIT 1;
|
|
32608
|
+
IF v_del IS NULL THEN CONTINUE; END IF;
|
|
32609
|
+
IF EXISTS (SELECT 1 FROM public.logistics_sticker_prints WHERE soort = 'traysticker' AND bron_id = v_del) THEN
|
|
32610
|
+
v_fo := public.logistics_afhandeling_fulfillment_order(r_ord.id)->>'fulfillmentOrderId';
|
|
32611
|
+
IF v_fo IS NOT NULL THEN
|
|
32612
|
+
UPDATE public.logistics_sticker_prints SET fulfillment_order_id = v_fo
|
|
32613
|
+
WHERE soort = 'traysticker' AND bron_id = v_del AND fulfillment_order_id IS DISTINCT FROM v_fo
|
|
32614
|
+
AND status NOT IN ('in_printwachtrij','afgemeld');
|
|
32615
|
+
END IF;
|
|
32616
|
+
CONTINUE;
|
|
32617
|
+
END IF;
|
|
32618
|
+
SELECT oc.id INTO v_cart FROM logistics_active.order_carts oc WHERE oc.order_id = r_ord.id ORDER BY oc.cart_number, oc.created_at LIMIT 1;
|
|
32619
|
+
v_fo := public.logistics_afhandeling_fulfillment_order(r_ord.id)->>'fulfillmentOrderId';
|
|
32620
|
+
PERFORM public.logistics_sticker_upsert('traysticker', v_del, r_ord.id, r_ord.order_number, v_cart, 1, NULL,
|
|
32621
|
+
public.logistics_sticker_cart_plek(v_cart, v_station), v_fo, 'wacht_op_moment',
|
|
32622
|
+
'Wacht op geaccepteerde belading in Floriday.');
|
|
32623
|
+
n_tray := n_tray + 1;
|
|
32624
|
+
END LOOP;
|
|
32625
|
+
END IF;
|
|
32626
|
+
|
|
32627
|
+
RETURN jsonb_build_object('stickers', n_st, 'trayNieuw', n_tray, 'release', v_rel, 'trayRelease', v_tray,
|
|
32628
|
+
'klantstickerMoment', 'with_packing_slip');
|
|
32629
|
+
END; $function$
|
|
32630
|
+
;
|
|
32631
|
+
|
|
32632
|
+
CREATE OR REPLACE FUNCTION public.logistics_plan_kick()
|
|
32633
|
+
RETURNS void
|
|
32634
|
+
LANGUAGE plpgsql
|
|
32635
|
+
SECURITY DEFINER
|
|
32636
|
+
SET search_path TO 'public'
|
|
32637
|
+
AS $function$
|
|
32638
|
+
DECLARE
|
|
32639
|
+
v_base text; v_key text; v_path text; v_last timestamptz;
|
|
32640
|
+
v_debounce int := public.logistics_plan_setting('kick_debounce_seconds', 5)::int;
|
|
32641
|
+
v_running boolean; v_req bigint; v_timeout int := public.logistics_tick_timeout_ms();
|
|
32642
|
+
BEGIN
|
|
32643
|
+
PERFORM public.logistics_acc_guard('server', 'logistics_plan_kick');
|
|
32644
|
+
-- Loopt er al een tick-ronde (slot 'tick:ronde')? Dan geen tweede ronde wekken.
|
|
32645
|
+
-- Fail-closed: kan dit niet worden vastgesteld, dan valt de EXCEPTION-tak
|
|
32646
|
+
-- hieronder en wordt de wek-aanroep overgeslagen; de minuut-cron is het vangnet.
|
|
32647
|
+
SELECT EXISTS (
|
|
32648
|
+
SELECT 1 FROM public.logistics_plan_locks
|
|
32649
|
+
WHERE lock_key = 'tick:ronde' AND locked_until > now()
|
|
32650
|
+
) INTO v_running;
|
|
32651
|
+
IF v_running IS DISTINCT FROM false THEN
|
|
32652
|
+
-- 1.0.194: niet laten vallen; de lopende ronde pakt de vlag vóór het vrijgeven op.
|
|
32653
|
+
INSERT INTO public.logistics_plan_settings(key, value, updated_at)
|
|
32654
|
+
VALUES ('kick_pending', clock_timestamp()::text, now())
|
|
32655
|
+
ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value, updated_at = now();
|
|
32656
|
+
BEGIN
|
|
32657
|
+
INSERT INTO public.logistics_kick_log(skipped_reason) VALUES ('ronde_loopt_vlag_gezet');
|
|
32658
|
+
EXCEPTION WHEN OTHERS THEN NULL; END;
|
|
32659
|
+
RETURN;
|
|
32660
|
+
END IF;
|
|
32661
|
+
|
|
32662
|
+
SELECT value INTO v_base FROM public.module_runtime_config WHERE key = 'logistics_tick_base_url';
|
|
32663
|
+
SELECT value INTO v_key FROM public.module_runtime_config WHERE key = 'logistics_tick_apikey';
|
|
32664
|
+
SELECT value INTO v_path FROM public.module_runtime_config WHERE key = 'logistics_tick_path';
|
|
32665
|
+
IF v_base IS NULL OR v_key IS NULL THEN RETURN; END IF;
|
|
32666
|
+
v_path := COALESCE(v_path, '/api/public/hooks/logistics-recalc-tick');
|
|
32667
|
+
|
|
32668
|
+
SELECT NULLIF(value,'')::timestamptz INTO v_last
|
|
32669
|
+
FROM public.logistics_plan_settings WHERE key = 'last_kick_at';
|
|
32670
|
+
IF v_last IS NOT NULL AND v_last > now() - make_interval(secs => v_debounce) THEN
|
|
32671
|
+
BEGIN INSERT INTO public.logistics_kick_log(skipped_reason) VALUES ('debounce'); EXCEPTION WHEN OTHERS THEN NULL; END;
|
|
32672
|
+
RETURN;
|
|
32673
|
+
END IF;
|
|
32674
|
+
|
|
32675
|
+
INSERT INTO public.logistics_plan_settings(key, value, updated_at)
|
|
32676
|
+
VALUES ('last_kick_at', now()::text, now())
|
|
32677
|
+
ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value, updated_at = now();
|
|
32678
|
+
|
|
32679
|
+
v_req := net.http_post(
|
|
32680
|
+
url := rtrim(v_base,'/') || v_path,
|
|
32681
|
+
headers := jsonb_build_object('Content-Type','application/json','apikey', v_key),
|
|
32682
|
+
body := '{}'::jsonb,
|
|
32683
|
+
timeout_milliseconds := v_timeout
|
|
32684
|
+
);
|
|
32685
|
+
BEGIN INSERT INTO public.logistics_kick_log(request_id) VALUES (v_req); EXCEPTION WHEN OTHERS THEN NULL; END;
|
|
32686
|
+
EXCEPTION WHEN OTHERS THEN
|
|
32687
|
+
NULL;
|
|
32688
|
+
END;
|
|
32689
|
+
$function$
|
|
32690
|
+
;
|
|
32691
|
+
|
|
32692
|
+
CREATE OR REPLACE FUNCTION public.logistics_recalc_tick_kick()
|
|
32693
|
+
RETURNS bigint
|
|
32694
|
+
LANGUAGE plpgsql
|
|
32695
|
+
SECURITY DEFINER
|
|
32696
|
+
SET search_path TO 'public'
|
|
32697
|
+
AS $function$
|
|
32698
|
+
DECLARE
|
|
32699
|
+
v_base text; v_key text; v_path text; v_req bigint; v_host text;
|
|
32700
|
+
BEGIN
|
|
32701
|
+
PERFORM public.logistics_acc_guard('server', 'logistics_recalc_tick_kick');
|
|
32702
|
+
BEGIN PERFORM public.logistics_tick_calls_resolve(); EXCEPTION WHEN others THEN NULL; END;
|
|
32703
|
+
|
|
32704
|
+
SELECT NULLIF(btrim(value),'') INTO v_base FROM public.module_runtime_config WHERE key = 'logistics_tick_base_url';
|
|
32705
|
+
SELECT NULLIF(btrim(value),'') INTO v_key FROM public.module_runtime_config WHERE key = 'logistics_tick_apikey';
|
|
32706
|
+
SELECT NULLIF(btrim(value),'') INTO v_path FROM public.module_runtime_config WHERE key = 'logistics_tick_path';
|
|
32707
|
+
v_host := substring(v_base from '^[a-zA-Z]+://([^/?#]+)');
|
|
32708
|
+
|
|
32709
|
+
IF v_base IS NULL OR v_key IS NULL THEN
|
|
32710
|
+
INSERT INTO public.logistics_tick_calls (target_host, skipped_reason, engine_ok, error, resolved_at)
|
|
32711
|
+
VALUES (v_host,
|
|
32712
|
+
CASE WHEN v_base IS NULL THEN 'geen_adres' ELSE 'geen_sleutel' END,
|
|
32713
|
+
false,
|
|
32714
|
+
CASE WHEN v_base IS NULL THEN 'geen adres ingesteld' ELSE 'geen sleutel ingesteld' END,
|
|
32715
|
+
now());
|
|
32716
|
+
RAISE WARNING 'logistics_recalc_tick_kick: geen aanroep — logistics_tick_base_url en/of logistics_tick_apikey ontbreekt in module_runtime_config. Vul deze voor deze omgeving in.';
|
|
32717
|
+
RETURN NULL;
|
|
32718
|
+
END IF;
|
|
32719
|
+
v_path := COALESCE(v_path, '/api/public/hooks/logistics-recalc-tick');
|
|
32720
|
+
v_req := net.http_post(
|
|
32721
|
+
url := rtrim(v_base,'/') || v_path,
|
|
32722
|
+
headers := jsonb_build_object('Content-Type','application/json','apikey', v_key),
|
|
32723
|
+
body := '{}'::jsonb,
|
|
32724
|
+
timeout_milliseconds := public.logistics_tick_timeout_ms()
|
|
32725
|
+
);
|
|
32726
|
+
INSERT INTO public.logistics_tick_calls (request_id, target_host) VALUES (v_req, v_host);
|
|
32727
|
+
RETURN v_req;
|
|
32728
|
+
END;
|
|
32729
|
+
$function$
|
|
32730
|
+
;
|
|
32731
|
+
|
|
32732
|
+
CREATE OR REPLACE FUNCTION public.logistics_plan_release_stale()
|
|
32733
|
+
RETURNS integer
|
|
32734
|
+
LANGUAGE plpgsql
|
|
32735
|
+
SECURITY DEFINER
|
|
32736
|
+
SET search_path TO 'public'
|
|
32737
|
+
AS $function$
|
|
32738
|
+
DECLARE
|
|
32739
|
+
v_n int := 0;
|
|
32740
|
+
v_min int := public.logistics_plan_setting('claim_timeout_minutes', 2)::int;
|
|
32741
|
+
v_max int := GREATEST(1, public.logistics_plan_setting('max_attempts', 3)::int);
|
|
32742
|
+
v_txt text;
|
|
32743
|
+
r record;
|
|
32744
|
+
BEGIN
|
|
32745
|
+
PERFORM public.logistics_acc_guard('server', 'logistics_plan_release_stale');
|
|
32746
|
+
v_txt := 'de ronde heeft deze indeling na ' || v_min || ' minuten niet afgesloten (claim niet afgerond)';
|
|
32747
|
+
FOR r IN
|
|
32748
|
+
UPDATE public.logistics_plan_queue q
|
|
32749
|
+
SET status = CASE WHEN q.attempts >= v_max OR q.reason_code = 'claim_niet_afgerond' THEN 'geblokkeerd' ELSE 'pending' END,
|
|
32750
|
+
error = v_txt,
|
|
32751
|
+
reason = CASE WHEN q.attempts >= v_max OR q.reason_code = 'claim_niet_afgerond'
|
|
32752
|
+
THEN v_txt || '; herhaald, daarom geblokkeerd' ELSE q.reason END,
|
|
32753
|
+
reason_code = 'claim_niet_afgerond',
|
|
32754
|
+
claimed_at = NULL,
|
|
32755
|
+
next_attempt_at = now() + make_interval(mins => LEAST(30, GREATEST(1, q.attempts))),
|
|
32756
|
+
updated_at = now()
|
|
32757
|
+
WHERE q.status = 'running'
|
|
32758
|
+
AND q.claimed_at < now() - make_interval(mins => v_min)
|
|
32759
|
+
RETURNING q.order_id, q.attempts, q.status
|
|
32760
|
+
LOOP
|
|
32761
|
+
v_n := v_n + 1;
|
|
32762
|
+
BEGIN
|
|
32763
|
+
PERFORM public.logistics_trace_order_step(
|
|
32764
|
+
r.order_id, 'stap:beladingindeling', 'claim niet afgerond',
|
|
32765
|
+
CASE WHEN r.status = 'geblokkeerd' THEN 'mislukt' ELSE 'wacht' END,
|
|
32766
|
+
'ronde sloot de indeling niet af',
|
|
32767
|
+
CASE WHEN r.status = 'geblokkeerd'
|
|
32768
|
+
THEN 'De ronde sloot de indeling voor de tweede keer niet af. De order is geblokkeerd zodat dit niet blijft herhalen; bekijk de reden en bied hem opnieuw aan.'
|
|
32769
|
+
ELSE 'De ronde sloot de indeling niet af binnen ' || v_min || ' minuten. Eén nieuwe poging volgt.'
|
|
32770
|
+
END,
|
|
32771
|
+
'Karrenindeling', v_txt, 'regel');
|
|
32772
|
+
EXCEPTION WHEN OTHERS THEN NULL;
|
|
32773
|
+
END;
|
|
32774
|
+
END LOOP;
|
|
32775
|
+
RETURN v_n;
|
|
32776
|
+
END $function$
|
|
32777
|
+
;
|
|
32778
|
+
|
|
32779
|
+
CREATE OR REPLACE FUNCTION public.logistics_plan_enqueue(p_order_id uuid, p_reason text DEFAULT 'change'::text)
|
|
32780
|
+
RETURNS void
|
|
32781
|
+
LANGUAGE plpgsql
|
|
32782
|
+
SECURITY DEFINER
|
|
32783
|
+
SET search_path TO 'public'
|
|
32784
|
+
AS $function$
|
|
32785
|
+
DECLARE
|
|
32786
|
+
v_ok boolean;
|
|
32787
|
+
v_id uuid;
|
|
32788
|
+
v_status text;
|
|
32789
|
+
v_decision text;
|
|
32790
|
+
v_target uuid := p_order_id;
|
|
32791
|
+
v_wacht jsonb;
|
|
32792
|
+
BEGIN
|
|
32793
|
+
PERFORM public.logistics_acc_guard('ingelogd', 'logistics_plan_enqueue');
|
|
32794
|
+
IF COALESCE(current_setting('logistics.merging', true), '') <> '1' THEN
|
|
32795
|
+
BEGIN
|
|
32796
|
+
PERFORM set_config('logistics.merging', '1', true);
|
|
32797
|
+
v_target := COALESCE(public.logistics_merge_floriday_lines(p_order_id), p_order_id);
|
|
32798
|
+
PERFORM set_config('logistics.merging', '0', true);
|
|
32799
|
+
EXCEPTION WHEN OTHERS THEN
|
|
32800
|
+
v_target := p_order_id;
|
|
32801
|
+
END;
|
|
32802
|
+
END IF;
|
|
32803
|
+
|
|
32804
|
+
-- A2: wachttijd na een mislukte indeling respecteren bij automatische meldingen.
|
|
32805
|
+
v_wacht := public.logistics_plan_wachttijd(v_target, p_reason);
|
|
32806
|
+
IF COALESCE((v_wacht->>'wacht')::boolean, false) THEN
|
|
32807
|
+
PERFORM public.logistics_plan_wachttijd_melden(v_target, p_reason, v_wacht);
|
|
32808
|
+
RETURN;
|
|
32809
|
+
END IF;
|
|
32810
|
+
|
|
32811
|
+
SELECT (o.deleted_at IS NULL AND COALESCE(o.is_reservation, false) = false AND o.plan_locked_at IS NULL)
|
|
32812
|
+
INTO v_ok FROM public.orders o WHERE o.id = v_target;
|
|
32813
|
+
-- 1.0.194: geannuleerde orders niet opnieuw indelen (alleen als de kolommen bestaan).
|
|
32814
|
+
IF COALESCE(v_ok, false) AND public.logistics_order_geannuleerd(v_target) THEN
|
|
32815
|
+
PERFORM public.logistics_trace_order_step(
|
|
32816
|
+
v_target, 'stap:orders_combineren', 'order aanmelden voor indeling', 'overgeslagen',
|
|
32817
|
+
'niet aangemeld', 'Order is geannuleerd', NULL);
|
|
32818
|
+
RETURN;
|
|
32819
|
+
END IF;
|
|
32820
|
+
IF NOT COALESCE(v_ok, false) THEN
|
|
32821
|
+
PERFORM public.logistics_trace_order_step(
|
|
32822
|
+
v_target, 'stap:orders_combineren', 'order aanmelden voor indeling', 'overgeslagen',
|
|
32823
|
+
'niet aangemeld', 'Order is verwijderd, samengevoegd, een reservering of de indeling staat vast', NULL);
|
|
32824
|
+
RETURN;
|
|
32825
|
+
END IF;
|
|
32826
|
+
|
|
32827
|
+
SELECT q.id, q.status INTO v_id, v_status
|
|
32828
|
+
FROM public.logistics_plan_queue q
|
|
32829
|
+
WHERE q.order_id = v_target
|
|
32830
|
+
AND q.status IN ('pending', 'running', 'geblokkeerd')
|
|
32831
|
+
ORDER BY q.updated_at DESC
|
|
32832
|
+
LIMIT 1
|
|
32833
|
+
FOR UPDATE;
|
|
32834
|
+
|
|
32835
|
+
IF v_id IS NULL THEN
|
|
32836
|
+
BEGIN
|
|
32837
|
+
INSERT INTO public.logistics_plan_queue(order_id, reason) VALUES (v_target, p_reason);
|
|
32838
|
+
v_decision := 'nieuw aangemeld voor indeling';
|
|
32839
|
+
EXCEPTION WHEN unique_violation THEN
|
|
32840
|
+
v_decision := 'stond al in de wachtrij';
|
|
32841
|
+
END;
|
|
32842
|
+
ELSIF v_status = 'geblokkeerd' THEN
|
|
32843
|
+
UPDATE public.logistics_plan_queue
|
|
32844
|
+
SET status = 'pending', attempts = 0, reason = p_reason, reason_code = NULL,
|
|
32845
|
+
error = NULL, claimed_at = NULL, next_attempt_at = now(), updated_at = now()
|
|
32846
|
+
WHERE id = v_id;
|
|
32847
|
+
v_decision := 'blokkade opgeheven, opnieuw aangemeld';
|
|
32848
|
+
ELSE
|
|
32849
|
+
UPDATE public.logistics_plan_queue
|
|
32850
|
+
SET reason = p_reason, next_attempt_at = LEAST(next_attempt_at, now()), updated_at = now()
|
|
32851
|
+
WHERE id = v_id;
|
|
32852
|
+
v_decision := 'opnieuw aangemeld';
|
|
32853
|
+
END IF;
|
|
32854
|
+
|
|
32855
|
+
PERFORM public.logistics_trace_order_step(
|
|
32856
|
+
v_target, 'stap:orders_combineren', 'order aanmelden voor indeling', 'geslaagd',
|
|
32857
|
+
v_decision, p_reason, 'Karrenindeling');
|
|
32858
|
+
|
|
32859
|
+
PERFORM public.logistics_plan_kick();
|
|
32860
|
+
END;
|
|
32861
|
+
$function$
|
|
32862
|
+
;
|
|
32863
|
+
|
|
32864
|
+
CREATE OR REPLACE FUNCTION public.logistics_sync_pick_queue_core()
|
|
32865
|
+
RETURNS jsonb
|
|
32866
|
+
LANGUAGE plpgsql
|
|
32867
|
+
SECURITY DEFINER
|
|
32868
|
+
SET search_path TO 'public'
|
|
32869
|
+
AS $function$
|
|
32870
|
+
DECLARE
|
|
32871
|
+
v_type_id uuid; v_station uuid; v_screen uuid; v_status uuid;
|
|
32872
|
+
v_inserted integer := 0; v_updated integer := 0; v_closed integer := 0; v_candidates integer := 0;
|
|
32873
|
+
v_today date; v_forced date; v_pickday date; v_target date; r_unit record; v_start timestamptz; v_due timestamptz;
|
|
32874
|
+
v_carts integer; v_percart boolean := public.logistics_pick_barcode_enabled();
|
|
32875
|
+
v_lines integer; v_breakdown jsonb; v_pickmode text; r_cart record; v_barcode text;
|
|
32876
|
+
v_cart_lines integer; v_fix jsonb; v_cart_payload jsonb; v_cart_rows integer;
|
|
32877
|
+
v_wait timestamptz; v_reason text; v_ok boolean;
|
|
32878
|
+
BEGIN
|
|
32879
|
+
PERFORM public.logistics_acc_guard('ingelogd', 'logistics_sync_pick_queue_core');
|
|
32880
|
+
v_today := public.logistics_local_today();
|
|
32881
|
+
v_forced := NULLIF(current_setting('logistics.forced_pick_day', true), '')::date;
|
|
32882
|
+
SELECT id INTO v_type_id FROM public.pick_document_types WHERE code = 'pakbon' LIMIT 1;
|
|
32883
|
+
IF v_type_id IS NULL THEN RETURN jsonb_build_object('error', 'documenttype pakbon ontbreekt'); END IF;
|
|
32884
|
+
SELECT id INTO v_status FROM public.pick_document_statuses WHERE code = 'queued' LIMIT 1;
|
|
32885
|
+
v_pickmode := COALESCE(public.logistics_pick_mode()->>'pickMode', '');
|
|
32886
|
+
|
|
32887
|
+
v_station := public.logistics_document_station('pakbon');
|
|
32888
|
+
IF v_station IS NULL THEN
|
|
32889
|
+
SELECT s.station_id INTO v_station FROM public.pick_station_screens s WHERE s.name ILIKE '%pakbon%' ORDER BY s.created_at LIMIT 1;
|
|
32890
|
+
END IF;
|
|
32891
|
+
IF v_station IS NULL THEN SELECT id INTO v_station FROM public.pick_stations ORDER BY created_at LIMIT 1; END IF;
|
|
32892
|
+
SELECT s.id INTO v_screen FROM public.pick_station_screens s
|
|
32893
|
+
WHERE s.station_id = v_station
|
|
32894
|
+
AND (s.name ILIKE '%pakbon%' OR EXISTS (SELECT 1 FROM public.pick_screen_document_types d WHERE d.screen_id = s.id AND d.document_type_id = v_type_id))
|
|
32895
|
+
ORDER BY s.created_at LIMIT 1;
|
|
32896
|
+
IF v_screen IS NULL THEN
|
|
32897
|
+
SELECT s.id INTO v_screen FROM public.pick_station_screens s WHERE s.station_id = v_station ORDER BY created_at LIMIT 1;
|
|
32898
|
+
END IF;
|
|
32899
|
+
|
|
32900
|
+
-- Afsluiten in plaats van verwijderen.
|
|
32901
|
+
v_closed := public.logistics_prune_handled_pick_queue();
|
|
32902
|
+
-- Order geannuleerd of verwijderd: annuleren.
|
|
32903
|
+
v_closed := v_closed + public.logistics_pick_queue_close(ARRAY(
|
|
32904
|
+
SELECT q.id FROM public.pick_queue q
|
|
32905
|
+
WHERE q.cancelled_at IS NULL AND q.completed_at IS NULL
|
|
32906
|
+
AND NOT EXISTS (SELECT 1 FROM public.orders o WHERE o.order_number = q.order_number
|
|
32907
|
+
AND o.deleted_at IS NULL AND o.cancelled_at IS NULL AND coalesce(o.status, '') <> 'geannuleerd')),
|
|
32908
|
+
'cancel', 'Order geannuleerd of verwijderd');
|
|
32909
|
+
-- Kar bestaat niet meer: vervallen door herindeling.
|
|
32910
|
+
v_closed := v_closed + public.logistics_pick_queue_close(ARRAY(
|
|
32911
|
+
SELECT q.id FROM public.pick_queue q
|
|
32912
|
+
WHERE q.cancelled_at IS NULL AND q.completed_at IS NULL AND q.printed_at IS NULL
|
|
32913
|
+
AND (q.payload->>'cartRowFor') IS NOT NULL
|
|
32914
|
+
AND NOT EXISTS (SELECT 1 FROM logistics_active.order_carts c WHERE c.id::text = q.reference)),
|
|
32915
|
+
'cancel', 'Kar vervallen door herindeling');
|
|
32916
|
+
-- Pickmethode niet meer "per kar": karregels vervallen.
|
|
32917
|
+
IF v_pickmode <> 'cart' THEN
|
|
32918
|
+
v_closed := v_closed + public.logistics_pick_queue_close(ARRAY(
|
|
32919
|
+
SELECT q.id FROM public.pick_queue q
|
|
32920
|
+
WHERE q.cancelled_at IS NULL AND q.completed_at IS NULL AND q.printed_at IS NULL
|
|
32921
|
+
AND (q.payload->>'cartRowFor') IS NOT NULL),
|
|
32922
|
+
'cancel', 'Kar vervallen: pickmethode is niet meer per kar');
|
|
32923
|
+
END IF;
|
|
32924
|
+
|
|
32925
|
+
SELECT min(d) INTO v_pickday FROM (
|
|
32926
|
+
SELECT public.logistics_order_pick_day(ord.order_number) AS d FROM public.orders ord
|
|
32927
|
+
WHERE ord.order_number IS NOT NULL
|
|
32928
|
+
AND public.logistics_order_is_topick(ord.id, ord.order_number)
|
|
32929
|
+
AND NOT public.logistics_order_handled(ord.id)
|
|
32930
|
+
AND public.logistics_order_released(ord.order_number)
|
|
32931
|
+
AND EXISTS (SELECT 1 FROM public.logistics_tasks lt WHERE lt.order_ref = ord.order_number AND lt.task_type = 'pick.execute'
|
|
32932
|
+
AND lt.status NOT IN ('done','cancelled','superseded','skipped'))
|
|
32933
|
+
) s WHERE d >= v_today;
|
|
32934
|
+
IF v_forced IS NOT NULL THEN v_pickday := v_forced; END IF;
|
|
32935
|
+
v_target := v_pickday;
|
|
32936
|
+
|
|
32937
|
+
IF v_pickday IS NOT NULL THEN
|
|
32938
|
+
SELECT count(*) INTO v_candidates FROM public.orders ord
|
|
32939
|
+
WHERE ord.order_number IS NOT NULL
|
|
32940
|
+
AND public.logistics_order_is_topick(ord.id, ord.order_number)
|
|
32941
|
+
AND NOT public.logistics_order_handled(ord.id)
|
|
32942
|
+
AND (v_forced IS NOT NULL OR public.logistics_order_released(ord.order_number))
|
|
32943
|
+
AND public.logistics_order_pick_day(ord.order_number) = v_target
|
|
32944
|
+
AND EXISTS (SELECT 1 FROM public.logistics_tasks lt WHERE lt.order_ref = ord.order_number AND lt.task_type = 'pick.execute'
|
|
32945
|
+
AND lt.status NOT IN ('done','cancelled','superseded','skipped'));
|
|
32946
|
+
END IF;
|
|
32947
|
+
|
|
32948
|
+
IF v_pickday IS NULL OR COALESCE(v_candidates, 0) = 0 THEN
|
|
32949
|
+
RETURN jsonb_build_object('station', v_station, 'screen', v_screen, 'pickdag', v_pickday, 'toegevoegd', 0,
|
|
32950
|
+
'afgesloten', v_closed, 'kandidaten', COALESCE(v_candidates, 0),
|
|
32951
|
+
'per_kar', v_pickmode = 'cart', 'kleinsteTelt', v_percart, 'pickmethode', v_pickmode,
|
|
32952
|
+
'guard', 'geen kandidaten');
|
|
32953
|
+
END IF;
|
|
32954
|
+
|
|
32955
|
+
-- 1.0.152: een geprinte regel blijft open tot scan, annulering of vervallen kar.
|
|
32956
|
+
|
|
32957
|
+
FOR r_unit IN
|
|
32958
|
+
WITH kandidaat AS (
|
|
32959
|
+
SELECT ord.id, ord.order_number, ord.customer_name, ord.delivery_date, ord.buffer_id, ord.custom_fields
|
|
32960
|
+
FROM public.orders ord
|
|
32961
|
+
WHERE ord.order_number IS NOT NULL
|
|
32962
|
+
AND public.logistics_order_is_topick(ord.id, ord.order_number)
|
|
32963
|
+
AND NOT public.logistics_order_handled(ord.id)
|
|
32964
|
+
AND (v_forced IS NOT NULL OR public.logistics_order_released(ord.order_number))
|
|
32965
|
+
AND public.logistics_order_pick_day(ord.order_number) = v_target
|
|
32966
|
+
AND EXISTS (SELECT 1 FROM public.logistics_tasks lt WHERE lt.order_ref = ord.order_number AND lt.task_type = 'pick.execute'
|
|
32967
|
+
AND lt.status NOT IN ('done','cancelled','superseded','skipped'))
|
|
32968
|
+
)
|
|
32969
|
+
SELECT min(k.order_number) AS order_number,
|
|
32970
|
+
(array_agg(k.id ORDER BY k.order_number))[1] AS lead_id,
|
|
32971
|
+
array_agg(k.order_number ORDER BY k.order_number) AS refs,
|
|
32972
|
+
array_agg(k.id ORDER BY k.order_number) AS ids,
|
|
32973
|
+
(array_agg(k.buffer_id ORDER BY k.order_number) FILTER (WHERE k.buffer_id IS NOT NULL))[1] AS buffer_id,
|
|
32974
|
+
count(*)::int AS leden,
|
|
32975
|
+
(array_agg(k.customer_name ORDER BY k.order_number))[1] AS customer_name,
|
|
32976
|
+
(array_agg(k.delivery_date ORDER BY k.order_number))[1] AS delivery_date,
|
|
32977
|
+
(array_agg(k.custom_fields ORDER BY k.order_number))[1] AS custom_fields
|
|
32978
|
+
FROM kandidaat k GROUP BY COALESCE(k.buffer_id::text, k.id::text) ORDER BY 1
|
|
32979
|
+
LOOP
|
|
32980
|
+
IF v_pickmode = 'cart' AND public.logistics_pakbon_old_order_row(r_unit.ids) THEN CONTINUE; END IF;
|
|
32981
|
+
SELECT min(lt.planned_start), max(COALESCE(lt.due_at, lt.planned_end)) INTO v_start, v_due
|
|
32982
|
+
FROM public.logistics_tasks lt WHERE lt.order_ref = ANY(r_unit.refs) AND lt.task_type IN ('print.packing_slip','pick.execute');
|
|
32983
|
+
SELECT count(DISTINCT c.id)::integer INTO v_carts FROM logistics_active.order_carts c
|
|
32984
|
+
WHERE c.order_id = ANY(r_unit.ids) OR (r_unit.buffer_id IS NOT NULL AND r_unit.leden > 1 AND c.buffer_id = r_unit.buffer_id AND c.order_id IS NULL);
|
|
32985
|
+
SELECT count(*) INTO v_lines FROM logistics_active.order_items oi WHERE oi.order_id = ANY(r_unit.ids);
|
|
32986
|
+
v_breakdown := public.pick_cart_breakdown(r_unit.lead_id);
|
|
32987
|
+
|
|
32988
|
+
IF v_pickmode = 'cart' THEN
|
|
32989
|
+
v_cart_rows := 0;
|
|
32990
|
+
FOR r_cart IN
|
|
32991
|
+
SELECT DISTINCT c.id AS cart_id, c.cart_number FROM logistics_active.order_carts c
|
|
32992
|
+
WHERE c.order_id = ANY(r_unit.ids)
|
|
32993
|
+
OR (r_unit.buffer_id IS NOT NULL AND r_unit.leden > 1 AND c.buffer_id = r_unit.buffer_id AND c.order_id IS NULL)
|
|
32994
|
+
OR EXISTS (SELECT 1 FROM logistics_active.order_cart_items ci JOIN logistics_active.order_items oi ON oi.id = ci.order_item_id
|
|
32995
|
+
WHERE ci.cart_id = c.id AND oi.order_id = ANY(r_unit.ids))
|
|
32996
|
+
ORDER BY c.cart_number
|
|
32997
|
+
LOOP
|
|
32998
|
+
-- Kar al gescand: niets nieuws aanmaken.
|
|
32999
|
+
IF public.logistics_cart_scanned(r_cart.cart_id) THEN CONTINUE; END IF;
|
|
33000
|
+
v_ok := v_forced IS NOT NULL OR public.logistics_cart_released(r_cart.cart_id);
|
|
33001
|
+
v_reason := CASE WHEN v_ok THEN NULL ELSE public.logistics_cart_release_reason(r_cart.cart_id) END;
|
|
33002
|
+
v_wait := CASE WHEN v_ok THEN NULL ELSE public.logistics_cart_wait_until(r_cart.cart_id) END;
|
|
33003
|
+
|
|
33004
|
+
v_barcode := public.logistics_ensure_cart_barcode(r_cart.cart_id);
|
|
33005
|
+
v_fix := public.logistics_cart_fixed_numbering(r_cart.cart_id);
|
|
33006
|
+
SELECT count(*) INTO v_cart_lines FROM logistics_active.order_cart_items ci WHERE ci.cart_id = r_cart.cart_id;
|
|
33007
|
+
|
|
33008
|
+
v_cart_payload := jsonb_build_object(
|
|
33009
|
+
'source','logistiek-engine','orderId', r_unit.lead_id, 'documentType','pakbon',
|
|
33010
|
+
'pickDate', v_target, 'perCart', true, 'pickMode', 'cart',
|
|
33011
|
+
'cartRowFor', r_unit.order_number, 'cartId', r_cart.cart_id,
|
|
33012
|
+
'isFullCart', public.logistics_cart_is_full(r_cart.cart_id),
|
|
33013
|
+
'barcode', v_barcode, 'cartCount', v_fix->'cartTotal', 'totalCarts', v_fix->'cartTotal',
|
|
33014
|
+
'bufferId', r_unit.buffer_id, 'isBuffer', r_unit.leden > 1,
|
|
33015
|
+
'bufferOrders', to_jsonb(r_unit.refs), 'orderIds', to_jsonb(r_unit.ids),
|
|
33016
|
+
'printbaar', v_ok, 'wachtTot', v_wait, 'wachtReden', v_reason)
|
|
33017
|
+
|| COALESCE(v_breakdown, '{}'::jsonb) || v_fix;
|
|
33018
|
+
|
|
33019
|
+
IF EXISTS (SELECT 1 FROM public.pick_queue q WHERE q.reference = r_cart.cart_id::text
|
|
33020
|
+
AND q.document_type_id = v_type_id AND q.cancelled_at IS NULL) THEN
|
|
33021
|
+
UPDATE public.pick_queue q
|
|
33022
|
+
SET screen_id = COALESCE(v_screen, q.screen_id), station_id = COALESCE(v_station, q.station_id),
|
|
33023
|
+
customer_name = r_unit.customer_name, order_number = r_unit.order_number,
|
|
33024
|
+
document_number = 'PB-' || r_unit.order_number || '-K' || (v_fix->>'cartSeq'),
|
|
33025
|
+
line_count = v_cart_lines, cart_count = 1,
|
|
33026
|
+
due_at = COALESCE(v_wait, v_due, q.due_at),
|
|
33027
|
+
payload = COALESCE(q.payload, '{}'::jsonb) || v_cart_payload, updated_at = now()
|
|
33028
|
+
WHERE q.reference = r_cart.cart_id::text AND q.document_type_id = v_type_id AND q.cancelled_at IS NULL
|
|
33029
|
+
AND (q.screen_id, q.station_id, q.customer_name, q.order_number, q.document_number, q.line_count, q.cart_count, q.due_at, q.payload)
|
|
33030
|
+
IS DISTINCT FROM
|
|
33031
|
+
(COALESCE(v_screen, q.screen_id), COALESCE(v_station, q.station_id), r_unit.customer_name, r_unit.order_number,
|
|
33032
|
+
'PB-' || r_unit.order_number || '-K' || (v_fix->>'cartSeq'), v_cart_lines, 1,
|
|
33033
|
+
COALESCE(v_wait, v_due, q.due_at), COALESCE(q.payload, '{}'::jsonb) || v_cart_payload);
|
|
33034
|
+
IF FOUND THEN v_updated := v_updated + 1; END IF;
|
|
33035
|
+
ELSE
|
|
33036
|
+
INSERT INTO public.pick_queue (station_id, screen_id, document_type_id, status_id, document_number, order_number, reference,
|
|
33037
|
+
customer_name, delivery_address, line_count, cart_count, priority, announced_at, due_at, payload)
|
|
33038
|
+
VALUES (v_station, v_screen, v_type_id, v_status,
|
|
33039
|
+
'PB-' || r_unit.order_number || '-K' || (v_fix->>'cartSeq'), r_unit.order_number, r_cart.cart_id::text,
|
|
33040
|
+
r_unit.customer_name, NULLIF(r_unit.custom_fields ->> 'delivery_address', ''),
|
|
33041
|
+
v_cart_lines, 1, 30, now(), COALESCE(v_wait, v_due, (r_unit.delivery_date)::timestamptz), v_cart_payload)
|
|
33042
|
+
ON CONFLICT DO NOTHING;
|
|
33043
|
+
v_inserted := v_inserted + 1;
|
|
33044
|
+
END IF;
|
|
33045
|
+
v_cart_rows := v_cart_rows + 1;
|
|
33046
|
+
END LOOP;
|
|
33047
|
+
|
|
33048
|
+
IF v_cart_rows > 0 THEN
|
|
33049
|
+
v_closed := v_closed + public.logistics_pick_queue_close(ARRAY(
|
|
33050
|
+
SELECT q.id FROM public.pick_queue q
|
|
33051
|
+
WHERE q.order_number = ANY(r_unit.refs) AND q.document_type_id = v_type_id
|
|
33052
|
+
AND (q.payload->>'cartRowFor') IS NULL AND q.printed_at IS NULL
|
|
33053
|
+
AND q.cancelled_at IS NULL AND q.completed_at IS NULL),
|
|
33054
|
+
'cancel', 'Orderregel vervangen door één regel per kar');
|
|
33055
|
+
END IF;
|
|
33056
|
+
-- Bij picken per kar nooit terugvallen op een orderregel.
|
|
33057
|
+
CONTINUE;
|
|
33058
|
+
END IF;
|
|
33059
|
+
|
|
33060
|
+
IF EXISTS (SELECT 1 FROM public.pick_queue q WHERE q.order_number = r_unit.order_number AND q.document_type_id = v_type_id
|
|
33061
|
+
AND (q.payload ->> 'cartRowFor') IS NULL AND q.cancelled_at IS NULL) THEN
|
|
33062
|
+
UPDATE public.pick_queue q
|
|
33063
|
+
SET screen_id = COALESCE(v_screen, q.screen_id), station_id = COALESCE(v_station, q.station_id),
|
|
33064
|
+
customer_name = r_unit.customer_name, document_number = 'PB-' || r_unit.order_number,
|
|
33065
|
+
line_count = v_lines, cart_count = COALESCE(v_carts, 0), due_at = COALESCE(v_due, q.due_at),
|
|
33066
|
+
payload = COALESCE(q.payload, '{}'::jsonb)
|
|
33067
|
+
|| jsonb_build_object('source','logistiek-engine','orderId', r_unit.lead_id, 'documentType','pakbon',
|
|
33068
|
+
'pickDate', v_target, 'perCart', v_percart, 'cartCount', COALESCE(v_carts, 0),
|
|
33069
|
+
'bufferId', r_unit.buffer_id, 'isBuffer', r_unit.leden > 1,
|
|
33070
|
+
'bufferOrders', to_jsonb(r_unit.refs), 'orderIds', to_jsonb(r_unit.ids))
|
|
33071
|
+
|| COALESCE(v_breakdown, '{}'::jsonb),
|
|
33072
|
+
updated_at = now()
|
|
33073
|
+
WHERE q.order_number = r_unit.order_number AND q.document_type_id = v_type_id
|
|
33074
|
+
AND (q.payload ->> 'cartRowFor') IS NULL AND q.cancelled_at IS NULL
|
|
33075
|
+
AND (q.screen_id, q.station_id, q.customer_name, q.document_number, q.line_count, q.cart_count, q.due_at, q.payload)
|
|
33076
|
+
IS DISTINCT FROM
|
|
33077
|
+
(COALESCE(v_screen, q.screen_id), COALESCE(v_station, q.station_id), r_unit.customer_name, 'PB-' || r_unit.order_number,
|
|
33078
|
+
v_lines, COALESCE(v_carts, 0), COALESCE(v_due, q.due_at),
|
|
33079
|
+
COALESCE(q.payload, '{}'::jsonb)
|
|
33080
|
+
|| jsonb_build_object('source','logistiek-engine','orderId', r_unit.lead_id, 'documentType','pakbon',
|
|
33081
|
+
'pickDate', v_target, 'perCart', v_percart, 'cartCount', COALESCE(v_carts, 0),
|
|
33082
|
+
'bufferId', r_unit.buffer_id, 'isBuffer', r_unit.leden > 1,
|
|
33083
|
+
'bufferOrders', to_jsonb(r_unit.refs), 'orderIds', to_jsonb(r_unit.ids))
|
|
33084
|
+
|| COALESCE(v_breakdown, '{}'::jsonb));
|
|
33085
|
+
IF FOUND THEN v_updated := v_updated + 1; END IF;
|
|
33086
|
+
CONTINUE;
|
|
33087
|
+
END IF;
|
|
33088
|
+
|
|
33089
|
+
INSERT INTO public.pick_queue (station_id, screen_id, document_type_id, status_id, document_number, order_number, reference,
|
|
33090
|
+
customer_name, delivery_address, line_count, cart_count, priority, announced_at, due_at, payload)
|
|
33091
|
+
VALUES (v_station, v_screen, v_type_id, v_status, 'PB-' || r_unit.order_number, r_unit.order_number, r_unit.order_number,
|
|
33092
|
+
r_unit.customer_name, NULLIF(r_unit.custom_fields ->> 'delivery_address', ''), v_lines, COALESCE(v_carts, 0), 30, now(),
|
|
33093
|
+
COALESCE(v_due, (r_unit.delivery_date)::timestamptz),
|
|
33094
|
+
jsonb_build_object('source','logistiek-engine','orderId', r_unit.lead_id, 'documentType','pakbon', 'pickDate', v_target,
|
|
33095
|
+
'perCart', v_percart, 'cartCount', COALESCE(v_carts, 0), 'bufferId', r_unit.buffer_id,
|
|
33096
|
+
'isBuffer', r_unit.leden > 1, 'bufferOrders', to_jsonb(r_unit.refs), 'orderIds', to_jsonb(r_unit.ids))
|
|
33097
|
+
|| COALESCE(v_breakdown, '{}'::jsonb))
|
|
33098
|
+
ON CONFLICT DO NOTHING;
|
|
33099
|
+
v_inserted := v_inserted + 1;
|
|
33100
|
+
END LOOP;
|
|
33101
|
+
|
|
33102
|
+
RETURN jsonb_build_object('station', v_station, 'screen', v_screen, 'pickdag', v_pickday,
|
|
33103
|
+
'kandidaten', v_candidates, 'per_kar', v_pickmode = 'cart', 'kleinsteTelt', v_percart, 'pickmethode', v_pickmode,
|
|
33104
|
+
'toegevoegd', v_inserted, 'bijgewerkt', v_updated, 'afgesloten', v_closed);
|
|
33105
|
+
END;
|
|
33106
|
+
$function$
|
|
33107
|
+
;
|
|
33108
|
+
|
|
33109
|
+
CREATE OR REPLACE FUNCTION public.logistics_sync_pick_queue_backlog()
|
|
33110
|
+
RETURNS jsonb
|
|
33111
|
+
LANGUAGE plpgsql
|
|
33112
|
+
SECURITY DEFINER
|
|
33113
|
+
SET search_path TO 'public'
|
|
33114
|
+
AS $function$
|
|
33115
|
+
DECLARE
|
|
33116
|
+
v_max integer := public.logistics_pick_backlog_max_days();
|
|
33117
|
+
v_type_id uuid; v_status uuid; v_station uuid; v_screen uuid; v_pickmode text;
|
|
33118
|
+
r_unit record; r_cart record; v_barcode text; v_seq integer; v_total integer;
|
|
33119
|
+
v_lines integer; v_carts integer; v_payload jsonb; v_breakdown jsonb;
|
|
33120
|
+
v_ins integer := 0; v_upd integer := 0; v_orders integer := 0;
|
|
33121
|
+
BEGIN
|
|
33122
|
+
PERFORM public.logistics_acc_guard('ingelogd', 'logistics_sync_pick_queue_backlog');
|
|
33123
|
+
IF v_max IS NULL THEN RETURN jsonb_build_object('achterstand', 'uit'); END IF;
|
|
33124
|
+
v_pickmode := COALESCE(public.logistics_pick_mode()->>'pickMode', '');
|
|
33125
|
+
IF v_pickmode <> 'cart' THEN
|
|
33126
|
+
RETURN jsonb_build_object('achterstand', 'overgeslagen', 'reden', 'alleen bij picken per kar');
|
|
33127
|
+
END IF;
|
|
33128
|
+
SELECT id INTO v_type_id FROM public.pick_document_types WHERE code = 'pakbon' LIMIT 1;
|
|
33129
|
+
SELECT id INTO v_status FROM public.pick_document_statuses WHERE code = 'queued' LIMIT 1;
|
|
33130
|
+
IF v_type_id IS NULL THEN RETURN jsonb_build_object('achterstand', 'fout', 'reden', 'documenttype pakbon ontbreekt'); END IF;
|
|
33131
|
+
v_station := public.logistics_document_station('pakbon');
|
|
33132
|
+
IF v_station IS NULL THEN
|
|
33133
|
+
SELECT s.station_id INTO v_station FROM public.pick_station_screens s WHERE s.name ILIKE '%pakbon%' ORDER BY s.created_at LIMIT 1;
|
|
33134
|
+
END IF;
|
|
33135
|
+
IF v_station IS NULL THEN SELECT id INTO v_station FROM public.pick_stations ORDER BY created_at LIMIT 1; END IF;
|
|
33136
|
+
SELECT s.id INTO v_screen FROM public.pick_station_screens s
|
|
33137
|
+
WHERE s.station_id = v_station
|
|
33138
|
+
AND (s.name ILIKE '%pakbon%' OR EXISTS (SELECT 1 FROM public.pick_screen_document_types d WHERE d.screen_id = s.id AND d.document_type_id = v_type_id))
|
|
33139
|
+
ORDER BY s.created_at LIMIT 1;
|
|
33140
|
+
|
|
33141
|
+
FOR r_unit IN
|
|
33142
|
+
WITH k AS (
|
|
33143
|
+
SELECT o.id, o.order_number, o.customer_name, o.delivery_date, o.buffer_id, o.custom_fields
|
|
33144
|
+
FROM public.orders o WHERE o.order_number IS NOT NULL AND public.logistics_order_is_backlog(o.id))
|
|
33145
|
+
SELECT min(k.order_number) AS order_number,
|
|
33146
|
+
(array_agg(k.id ORDER BY k.order_number))[1] AS lead_id,
|
|
33147
|
+
array_agg(k.order_number ORDER BY k.order_number) AS refs,
|
|
33148
|
+
array_agg(k.id ORDER BY k.order_number) AS ids,
|
|
33149
|
+
(array_agg(k.buffer_id ORDER BY k.order_number) FILTER (WHERE k.buffer_id IS NOT NULL))[1] AS buffer_id,
|
|
33150
|
+
count(*)::int AS leden,
|
|
33151
|
+
(array_agg(k.customer_name ORDER BY k.order_number))[1] AS customer_name,
|
|
33152
|
+
min((k.delivery_date::text)::date) AS delivery_date,
|
|
33153
|
+
(array_agg(k.custom_fields ORDER BY k.order_number))[1] AS custom_fields
|
|
33154
|
+
FROM k GROUP BY COALESCE(k.buffer_id::text, k.id::text) ORDER BY 1
|
|
33155
|
+
LOOP
|
|
33156
|
+
IF public.logistics_pakbon_old_order_row(r_unit.ids) THEN CONTINUE; END IF;
|
|
33157
|
+
v_orders := v_orders + 1;
|
|
33158
|
+
SELECT count(DISTINCT c.id)::int INTO v_carts FROM logistics_active.order_carts c
|
|
33159
|
+
WHERE c.order_id = ANY(r_unit.ids) OR (r_unit.buffer_id IS NOT NULL AND c.buffer_id = r_unit.buffer_id);
|
|
33160
|
+
v_breakdown := public.pick_cart_breakdown(r_unit.lead_id);
|
|
33161
|
+
FOR r_cart IN
|
|
33162
|
+
SELECT DISTINCT c.id AS cart_id, c.cart_number, c.total_carts FROM logistics_active.order_carts c
|
|
33163
|
+
WHERE c.order_id = ANY(r_unit.ids) OR (r_unit.buffer_id IS NOT NULL AND c.buffer_id = r_unit.buffer_id)
|
|
33164
|
+
ORDER BY c.cart_number
|
|
33165
|
+
LOOP
|
|
33166
|
+
IF NOT public.logistics_cart_released(r_cart.cart_id) THEN CONTINUE; END IF;
|
|
33167
|
+
v_barcode := public.logistics_ensure_cart_barcode(r_cart.cart_id);
|
|
33168
|
+
v_seq := NULL; v_total := NULL;
|
|
33169
|
+
SELECT s.cart_seq, s.cart_total INTO v_seq, v_total FROM public.logistics_cart_seq(r_unit.lead_id) s WHERE s.cart_id = r_cart.cart_id;
|
|
33170
|
+
v_seq := COALESCE(v_seq, r_cart.cart_number);
|
|
33171
|
+
v_total := COALESCE(v_total, r_cart.total_carts, v_carts);
|
|
33172
|
+
SELECT count(*) INTO v_lines FROM logistics_active.order_cart_items ci WHERE ci.cart_id = r_cart.cart_id;
|
|
33173
|
+
v_payload := jsonb_build_object(
|
|
33174
|
+
'source','logistiek-engine','orderId', r_unit.lead_id, 'documentType','pakbon',
|
|
33175
|
+
'pickDate', r_unit.delivery_date, 'perCart', true, 'pickMode', 'cart',
|
|
33176
|
+
'cartRowFor', r_unit.order_number, 'cartId', r_cart.cart_id,
|
|
33177
|
+
'cartSeq', v_seq, 'cartIndex', r_cart.cart_number, 'cartTotal', v_total,
|
|
33178
|
+
'cartLabel', 'Kar ' || v_seq || ' van ' || COALESCE(v_total, v_seq),
|
|
33179
|
+
'isFullCart', public.logistics_cart_is_full(r_cart.cart_id),
|
|
33180
|
+
'barcode', v_barcode, 'cartCount', COALESCE(v_carts, 0),
|
|
33181
|
+
'bufferId', r_unit.buffer_id, 'isBuffer', r_unit.leden > 1,
|
|
33182
|
+
'bufferOrders', to_jsonb(r_unit.refs), 'orderIds', to_jsonb(r_unit.ids),
|
|
33183
|
+
'backlog', true,
|
|
33184
|
+
'backlogDays', (now() AT TIME ZONE public.logistics_tz())::date - r_unit.delivery_date,
|
|
33185
|
+
'backlogLabel', 'Achterstand, leverdatum ' || to_char(r_unit.delivery_date, 'DD-MM'))
|
|
33186
|
+
|| COALESCE(v_breakdown, '{}'::jsonb);
|
|
33187
|
+
IF EXISTS (SELECT 1 FROM public.pick_queue q WHERE q.reference = r_cart.cart_id::text AND q.document_type_id = v_type_id AND q.cancelled_at IS NULL) THEN
|
|
33188
|
+
UPDATE public.pick_queue q
|
|
33189
|
+
SET customer_name = r_unit.customer_name, order_number = r_unit.order_number,
|
|
33190
|
+
document_number = 'PB-' || r_unit.order_number || '-K' || v_seq,
|
|
33191
|
+
line_count = v_lines, cart_count = 1, is_urgent = true,
|
|
33192
|
+
payload = COALESCE(q.payload, '{}'::jsonb) || v_payload, updated_at = now()
|
|
33193
|
+
WHERE q.reference = r_cart.cart_id::text AND q.document_type_id = v_type_id AND q.cancelled_at IS NULL
|
|
33194
|
+
AND q.printed_at IS NULL AND q.completed_at IS NULL
|
|
33195
|
+
AND (q.customer_name, q.order_number, q.document_number, q.line_count, q.cart_count, q.is_urgent, q.payload)
|
|
33196
|
+
IS DISTINCT FROM
|
|
33197
|
+
(r_unit.customer_name, r_unit.order_number, 'PB-' || r_unit.order_number || '-K' || v_seq, v_lines, 1, true,
|
|
33198
|
+
COALESCE(q.payload, '{}'::jsonb) || v_payload);
|
|
33199
|
+
IF FOUND THEN v_upd := v_upd + 1; END IF;
|
|
33200
|
+
ELSE
|
|
33201
|
+
INSERT INTO public.pick_queue (
|
|
33202
|
+
station_id, screen_id, document_type_id, status_id, document_number, order_number, reference,
|
|
33203
|
+
customer_name, delivery_address, line_count, cart_count, priority, is_urgent, announced_at, due_at, payload
|
|
33204
|
+
) VALUES (
|
|
33205
|
+
v_station, v_screen, v_type_id, v_status,
|
|
33206
|
+
'PB-' || r_unit.order_number || '-K' || v_seq, r_unit.order_number, r_cart.cart_id::text,
|
|
33207
|
+
r_unit.customer_name, NULLIF(r_unit.custom_fields ->> 'delivery_address', ''),
|
|
33208
|
+
v_lines, 1, 30, true, now(), (r_unit.delivery_date)::timestamptz, v_payload
|
|
33209
|
+
) ON CONFLICT DO NOTHING;
|
|
33210
|
+
v_ins := v_ins + 1;
|
|
33211
|
+
END IF;
|
|
33212
|
+
END LOOP;
|
|
33213
|
+
END LOOP;
|
|
33214
|
+
RETURN jsonb_build_object('achterstand', 'aan', 'maxDagen', v_max, 'eenheden', v_orders, 'toegevoegd', v_ins, 'bijgewerkt', v_upd);
|
|
33215
|
+
END $function$
|
|
33216
|
+
;
|
|
33217
|
+
|
|
33218
|
+
CREATE OR REPLACE FUNCTION public.logistics_order_frozen(p_order_id uuid)
|
|
33219
|
+
RETURNS boolean
|
|
33220
|
+
LANGUAGE plpgsql
|
|
33221
|
+
STABLE SECURITY DEFINER
|
|
33222
|
+
SET search_path TO 'public'
|
|
33223
|
+
AS $function$
|
|
33224
|
+
DECLARE v_number text;
|
|
33225
|
+
BEGIN
|
|
33226
|
+
PERFORM public.logistics_acc_guard('ingelogd', 'logistics_order_frozen');
|
|
33227
|
+
SELECT o.order_number INTO v_number FROM public.orders o WHERE o.id = p_order_id;
|
|
33228
|
+
IF v_number IS NULL THEN RETURN false; END IF;
|
|
33229
|
+
IF EXISTS (SELECT 1 FROM public.logistics_order_plan_exclusion x WHERE x.order_id = p_order_id) THEN RETURN true; END IF;
|
|
33230
|
+
IF public.logistics_order_afgemeld(p_order_id) THEN RETURN true; END IF;
|
|
33231
|
+
IF EXISTS (SELECT 1 FROM public.pick_queue q WHERE q.order_number = v_number AND q.cancelled_at IS NULL
|
|
33232
|
+
AND (q.printed_at IS NOT NULL OR q.completed_at IS NOT NULL)) THEN RETURN true; END IF;
|
|
33233
|
+
IF EXISTS (SELECT 1 FROM public.cart_control_items ci WHERE ci.order_number = v_number) THEN RETURN true; END IF;
|
|
33234
|
+
IF public.logistics_order_print_actief(v_number) THEN RETURN true; END IF;
|
|
33235
|
+
RETURN false;
|
|
33236
|
+
END;
|
|
33237
|
+
$function$
|
|
33238
|
+
;
|
|
33239
|
+
|
|
33240
|
+
CREATE OR REPLACE FUNCTION public.logistics_order_mergeable(p_order_id uuid)
|
|
33241
|
+
RETURNS boolean
|
|
33242
|
+
LANGUAGE plpgsql
|
|
33243
|
+
STABLE SECURITY DEFINER
|
|
33244
|
+
SET search_path TO 'public'
|
|
33245
|
+
AS $function$
|
|
33246
|
+
DECLARE
|
|
33247
|
+
v_ok boolean;
|
|
33248
|
+
BEGIN
|
|
33249
|
+
PERFORM public.logistics_acc_guard('ingelogd', 'logistics_order_mergeable');
|
|
33250
|
+
SELECT (o.deleted_at IS NULL
|
|
33251
|
+
AND o.cancelled_at IS NULL
|
|
33252
|
+
AND o.plan_locked_at IS NULL
|
|
33253
|
+
AND COALESCE(o.is_reservation, false) = false
|
|
33254
|
+
AND o.order_source = 'floriday'
|
|
33255
|
+
AND o.floriday_order_group_key IS NOT NULL
|
|
33256
|
+
AND NOT EXISTS (
|
|
33257
|
+
SELECT 1
|
|
33258
|
+
FROM public.pick_queue q
|
|
33259
|
+
WHERE q.order_number = o.order_number
|
|
33260
|
+
AND (q.printed_at IS NOT NULL OR q.completed_at IS NOT NULL)
|
|
33261
|
+
)
|
|
33262
|
+
AND NOT public.logistics_order_print_actief(o.order_number))
|
|
33263
|
+
INTO v_ok
|
|
33264
|
+
FROM public.orders o
|
|
33265
|
+
WHERE o.id = p_order_id;
|
|
33266
|
+
|
|
33267
|
+
RETURN COALESCE(v_ok, false);
|
|
33268
|
+
EXCEPTION WHEN OTHERS THEN
|
|
33269
|
+
RETURN false;
|
|
33270
|
+
END $function$
|
|
33271
|
+
;
|
|
33272
|
+
|
|
33273
|
+
CREATE OR REPLACE FUNCTION public.logistics_merge_partial_carts(p_limit integer DEFAULT 50)
|
|
33274
|
+
RETURNS jsonb
|
|
33275
|
+
LANGUAGE plpgsql
|
|
33276
|
+
SECURITY DEFINER
|
|
33277
|
+
SET search_path TO 'public'
|
|
33278
|
+
AS $function$
|
|
33279
|
+
declare r record; v_count integer := 0;
|
|
33280
|
+
begin
|
|
33281
|
+
PERFORM public.logistics_acc_guard('ingelogd', 'logistics_merge_partial_carts');
|
|
33282
|
+
if coalesce(public.logistics_pick_mode()->>'pickMode','') <> 'cart' then
|
|
33283
|
+
return jsonb_build_object('overgeslagen', 'pickmethode is niet per kar');
|
|
33284
|
+
end if;
|
|
33285
|
+
if public.logistics_pick_partial_mode() <> 'after_checkin' then
|
|
33286
|
+
return jsonb_build_object('overgeslagen', 'deelkarren mogen ook voor het aanmeldmoment');
|
|
33287
|
+
end if;
|
|
33288
|
+
|
|
33289
|
+
for r in
|
|
33290
|
+
select distinct o.id as order_id
|
|
33291
|
+
from logistics_active.order_carts c
|
|
33292
|
+
join lateral unnest(public.logistics_cart_order_refs(c.id)) ref on true
|
|
33293
|
+
join public.orders o on o.order_number = ref
|
|
33294
|
+
where not public.logistics_cart_is_full(c.id)
|
|
33295
|
+
and not exists (select 1 from public.pick_queue q
|
|
33296
|
+
where q.reference = c.id::text and q.printed_at is not null)
|
|
33297
|
+
and not public.logistics_order_print_actief(o.order_number)
|
|
33298
|
+
and public.logistics_cart_checkin_at(c.id) is not null
|
|
33299
|
+
limit greatest(coalesce(p_limit, 50), 1)
|
|
33300
|
+
loop
|
|
33301
|
+
begin
|
|
33302
|
+
perform public.enqueue_cart_replan_if_unlocked(r.order_id);
|
|
33303
|
+
v_count := v_count + 1;
|
|
33304
|
+
exception when others then null;
|
|
33305
|
+
end;
|
|
33306
|
+
end loop;
|
|
33307
|
+
|
|
33308
|
+
return jsonb_build_object('opnieuw_ingedeeld', v_count);
|
|
33309
|
+
end; $function$
|
|
33310
|
+
;
|
|
33311
|
+
|
|
33312
|
+
CREATE OR REPLACE FUNCTION public.logistics_order_is_frozen(p_order_id uuid)
|
|
33313
|
+
RETURNS boolean
|
|
33314
|
+
LANGUAGE sql
|
|
33315
|
+
STABLE SECURITY DEFINER
|
|
33316
|
+
SET search_path TO 'public'
|
|
33317
|
+
AS $function$
|
|
33318
|
+
SELECT public.logistics_acc_guard('ingelogd', 'logistics_order_is_frozen');
|
|
33319
|
+
SELECT EXISTS (
|
|
33320
|
+
SELECT 1 FROM public.pick_queue p
|
|
33321
|
+
WHERE p.cancelled_at IS NULL
|
|
33322
|
+
AND (p.printed_at IS NOT NULL OR p.completed_at IS NOT NULL
|
|
33323
|
+
OR jsonb_array_length(CASE WHEN jsonb_typeof(p.printed_cart_ids) = 'array' THEN p.printed_cart_ids ELSE '[]'::jsonb END) > 0)
|
|
33324
|
+
AND (p.payload->>'orderId' = p_order_id::text
|
|
33325
|
+
OR COALESCE(p.payload->'orderIds', '[]'::jsonb) ? p_order_id::text)
|
|
33326
|
+
)
|
|
33327
|
+
OR EXISTS (
|
|
33328
|
+
SELECT 1 FROM logistics_active.order_carts oc
|
|
33329
|
+
WHERE oc.order_id = p_order_id AND public.logistics_cart_is_frozen(oc.id)
|
|
33330
|
+
)
|
|
33331
|
+
OR public.logistics_order_print_actief((SELECT o.order_number FROM public.orders o WHERE o.id = p_order_id))
|
|
33332
|
+
OR EXISTS (
|
|
33333
|
+
SELECT 1 FROM logistics_active.order_carts oc
|
|
33334
|
+
JOIN logistics_active.order_cart_items oci ON oci.cart_id = oc.id
|
|
33335
|
+
JOIN public.order_items oi ON oi.id = oci.order_item_id
|
|
33336
|
+
WHERE oc.order_id IS NULL AND oi.order_id = p_order_id
|
|
33337
|
+
AND public.logistics_cart_is_frozen(oc.id)
|
|
33338
|
+
)
|
|
33339
|
+
$function$
|
|
33340
|
+
;
|
|
33341
|
+
|
|
33342
|
+
CREATE OR REPLACE FUNCTION public.logistics_order_on_frozen_shared_cart(p_order_id uuid)
|
|
33343
|
+
RETURNS boolean
|
|
33344
|
+
LANGUAGE sql
|
|
33345
|
+
STABLE SECURITY DEFINER
|
|
33346
|
+
SET search_path TO 'public'
|
|
33347
|
+
AS $function$
|
|
33348
|
+
SELECT public.logistics_acc_guard('server', 'logistics_order_on_frozen_shared_cart');
|
|
33349
|
+
SELECT EXISTS (
|
|
33350
|
+
SELECT 1 FROM public.logistics_shared_cart_peers(p_order_id) p
|
|
33351
|
+
JOIN logistics_active.order_carts c ON c.id = p.cart_id
|
|
33352
|
+
LEFT JOIN public.orders po ON po.id = p.peer_order_id
|
|
33353
|
+
WHERE c.pickup_locked IS TRUE OR c.is_complete IS TRUE
|
|
33354
|
+
OR public.logistics_cart_handled(COALESCE(po.order_number, p.peer_order_id::text), p.cart_number)
|
|
33355
|
+
OR public.logistics_order_afgemeld(p.peer_order_id)
|
|
33356
|
+
OR public.logistics_order_print_actief(po.order_number)
|
|
33357
|
+
OR public.logistics_cart_is_frozen(p.cart_id)
|
|
33358
|
+
OR EXISTS (SELECT 1 FROM public.pick_queue q WHERE q.cancelled_at IS NULL
|
|
33359
|
+
AND q.payload->>'cartId' = p.cart_id::text
|
|
33360
|
+
AND (q.printed_at IS NOT NULL OR q.completed_at IS NOT NULL)))
|
|
33361
|
+
$function$
|
|
33362
|
+
;
|
|
33363
|
+
|
|
33364
|
+
CREATE OR REPLACE FUNCTION public.logistics_tick()
|
|
33365
|
+
RETURNS jsonb
|
|
33366
|
+
LANGUAGE plpgsql
|
|
33367
|
+
SECURITY DEFINER
|
|
33368
|
+
SET search_path TO 'public'
|
|
33369
|
+
AS $function$
|
|
33370
|
+
DECLARE v_result jsonb := '{}'::jsonb; v_start timestamptz := clock_timestamp(); v_t timestamptz; v_err boolean := false;
|
|
33371
|
+
BEGIN
|
|
33372
|
+
PERFORM public.logistics_acc_guard('server', 'logistics_tick');
|
|
33373
|
+
v_t := clock_timestamp();
|
|
33374
|
+
BEGIN
|
|
33375
|
+
PERFORM set_config('statement_timeout', '10000', true);
|
|
33376
|
+
v_result := v_result || jsonb_build_object('vervoerder', public.logistics_carrier_recalc_run(200));
|
|
33377
|
+
EXCEPTION WHEN OTHERS THEN
|
|
33378
|
+
v_err := true; v_result := v_result || jsonb_build_object('vervoerder', left(SQLERRM, 200));
|
|
33379
|
+
END;
|
|
33380
|
+
v_result := v_result || jsonb_build_object('vervoerder_ms', (extract(epoch from clock_timestamp()-v_t)*1000)::int);
|
|
33381
|
+
|
|
33382
|
+
v_t := clock_timestamp();
|
|
33383
|
+
BEGIN
|
|
33384
|
+
PERFORM set_config('statement_timeout', '15000', true);
|
|
33385
|
+
PERFORM public.logistics_rebuffer_pending();
|
|
33386
|
+
v_result := v_result || jsonb_build_object('rebuffer', 'ok');
|
|
33387
|
+
EXCEPTION WHEN OTHERS THEN
|
|
33388
|
+
v_err := true;
|
|
33389
|
+
IF SQLERRM ILIKE '%tijdzone%' THEN
|
|
33390
|
+
v_result := v_result || jsonb_build_object('rebuffer', 'overgeslagen: tijdzone ontbreekt in Werktijden');
|
|
33391
|
+
BEGIN
|
|
33392
|
+
UPDATE public.logistics_rebuffer_state SET last_error = 'Herindelen overgeslagen: de tijdzone ontbreekt in Werktijden.', updated_at = now() WHERE id;
|
|
33393
|
+
EXCEPTION WHEN OTHERS THEN NULL; END;
|
|
33394
|
+
ELSE
|
|
33395
|
+
v_result := v_result || jsonb_build_object('rebuffer', left(SQLERRM, 200));
|
|
33396
|
+
END IF;
|
|
33397
|
+
END;
|
|
33398
|
+
v_result := v_result || jsonb_build_object('rebuffer_ms', (extract(epoch from clock_timestamp()-v_t)*1000)::int);
|
|
33399
|
+
|
|
33400
|
+
v_t := clock_timestamp();
|
|
33401
|
+
BEGIN
|
|
33402
|
+
PERFORM set_config('statement_timeout', '20000', true);
|
|
33403
|
+
PERFORM public.run_logistics_tasks_dispatch();
|
|
33404
|
+
v_result := v_result || jsonb_build_object('taken', 'ok');
|
|
33405
|
+
EXCEPTION WHEN OTHERS THEN
|
|
33406
|
+
v_err := true; v_result := v_result || jsonb_build_object('taken', left(SQLERRM, 200));
|
|
33407
|
+
END;
|
|
33408
|
+
v_result := v_result || jsonb_build_object('taken_ms', (extract(epoch from clock_timestamp()-v_t)*1000)::int);
|
|
33409
|
+
|
|
33410
|
+
v_t := clock_timestamp();
|
|
33411
|
+
BEGIN
|
|
33412
|
+
PERFORM set_config('statement_timeout', '20000', true);
|
|
33413
|
+
v_result := v_result || jsonb_build_object('afgerond', public.logistics_order_finalize_run(25, 120));
|
|
33414
|
+
EXCEPTION WHEN OTHERS THEN
|
|
33415
|
+
v_err := true; v_result := v_result || jsonb_build_object('afgerond', left(SQLERRM, 200));
|
|
33416
|
+
END;
|
|
33417
|
+
v_result := v_result || jsonb_build_object('afgerond_ms', (extract(epoch from clock_timestamp()-v_t)*1000)::int);
|
|
33418
|
+
|
|
33419
|
+
-- 1.0.194: lichte ronde — pickwachtrij en laadtoestand in SQL, niet meer via HTTP.
|
|
33420
|
+
v_t := clock_timestamp();
|
|
33421
|
+
BEGIN
|
|
33422
|
+
PERFORM set_config('statement_timeout', '15000', true);
|
|
33423
|
+
v_result := v_result || jsonb_build_object('pickwachtrij', public.logistics_sync_pick_queue());
|
|
33424
|
+
EXCEPTION WHEN OTHERS THEN
|
|
33425
|
+
v_err := true; v_result := v_result || jsonb_build_object('pickwachtrij', left(SQLERRM, 200));
|
|
33426
|
+
END;
|
|
33427
|
+
v_result := v_result || jsonb_build_object('pickwachtrij_ms', (extract(epoch from clock_timestamp()-v_t)*1000)::int);
|
|
33428
|
+
|
|
33429
|
+
v_t := clock_timestamp();
|
|
33430
|
+
BEGIN
|
|
33431
|
+
PERFORM set_config('statement_timeout', '10000', true);
|
|
33432
|
+
v_result := v_result || jsonb_build_object('laadtoestand', public.logistics_refresh_plan_state_als_nodig());
|
|
33433
|
+
EXCEPTION WHEN OTHERS THEN
|
|
33434
|
+
v_err := true; v_result := v_result || jsonb_build_object('laadtoestand', left(SQLERRM, 200));
|
|
33435
|
+
END;
|
|
33436
|
+
v_result := v_result || jsonb_build_object('laadtoestand_ms', (extract(epoch from clock_timestamp()-v_t)*1000)::int);
|
|
33437
|
+
|
|
33438
|
+
v_t := clock_timestamp();
|
|
33439
|
+
BEGIN
|
|
33440
|
+
PERFORM set_config('statement_timeout', '10000', true);
|
|
33441
|
+
v_result := v_result || jsonb_build_object('instellingen', public.logistics_ensure_periodiek());
|
|
33442
|
+
EXCEPTION WHEN OTHERS THEN
|
|
33443
|
+
v_err := true; v_result := v_result || jsonb_build_object('instellingen', left(SQLERRM, 200));
|
|
33444
|
+
END;
|
|
33445
|
+
v_result := v_result || jsonb_build_object('instellingen_ms', (extract(epoch from clock_timestamp()-v_t)*1000)::int);
|
|
33446
|
+
|
|
33447
|
+
BEGIN
|
|
33448
|
+
INSERT INTO public.logistics_tick_runs(started_at, finished_at, duration_ms, has_error, steps)
|
|
33449
|
+
VALUES (v_start, clock_timestamp(), (extract(epoch from clock_timestamp()-v_start)*1000)::int, v_err, v_result);
|
|
33450
|
+
EXCEPTION WHEN OTHERS THEN NULL;
|
|
33451
|
+
END;
|
|
33452
|
+
PERFORM set_config('statement_timeout', '30000', true);
|
|
33453
|
+
RETURN v_result;
|
|
33454
|
+
END $function$
|
|
33455
|
+
;
|
|
33456
|
+
|
|
33457
|
+
CREATE OR REPLACE FUNCTION public.logistics_install_check()
|
|
33458
|
+
RETURNS TABLE(onderdeel text, ok boolean, toelichting text)
|
|
33459
|
+
LANGUAGE plpgsql
|
|
33460
|
+
SECURITY DEFINER
|
|
33461
|
+
SET search_path TO 'public'
|
|
33462
|
+
AS $function$
|
|
33463
|
+
DECLARE c text; f text; t text; v_fav text; n bigint; s record;
|
|
33464
|
+
BEGIN
|
|
33465
|
+
PERFORM public.logistics_acc_guard('ingelogd', 'logistics_install_check');
|
|
33466
|
+
FOREACH f IN ARRAY ARRAY[
|
|
33467
|
+
'f:logistics_try_uuid','f:logistics_tz','f:logistics_is_beheer','f:logistics_buffer_payload','f:logistics_apply_order_buffer',
|
|
33468
|
+
'f:logistics_apply_buffer_decision','f:logistics_rebuffer_pending','f:logistics_departure_calc','f:logistics_own_transport_departure',
|
|
33469
|
+
'f:logistics_order_departure','f:logistics_brief_transport','f:logistics_order_frozen','f:logistics_order_is_frozen',
|
|
33470
|
+
'f:logistics_order_finalize_run','f:run_logistics_tasks_dispatch','f:enqueue_cart_replan_if_unlocked','f:logistics_tick',
|
|
33471
|
+
't:logistics_tick_runs',
|
|
33472
|
+
'f:logistics_belading_bevestig','f:logistics_cart_seq_dubbel','f:logistics_plan_retry_later','f:logistics_own_traysticker_sync',
|
|
33473
|
+
't:logistics_belading_bevestigingen',
|
|
33474
|
+
'f:logistics_sticker_copies','f:logistics_sticker_etiket_status','f:logistics_sticker_order_counts','f:logistics_sticker_sync',
|
|
33475
|
+
'c:logistics_sticker_prints.kopieen_bron',
|
|
33476
|
+
'f:logistics_carrier_settings_ensure','t:logistics_carrier_settings','t:logistics_carrier_recalc_queue','t:logistics_carrier_events',
|
|
33477
|
+
'f:logistics_carrier_status','f:logistics_carrier_set_backfill','f:logistics_carrier_preview_existing','c:control_get_todays_carts.carrier_name',
|
|
33478
|
+
'f:logistics_plan_fail_log'] LOOP
|
|
33479
|
+
onderdeel := 'Engine: ' || substr(f, 3);
|
|
33480
|
+
ok := CASE left(f, 1)
|
|
33481
|
+
WHEN 'f' THEN EXISTS (SELECT 1 FROM pg_proc p WHERE p.pronamespace = 'public'::regnamespace AND p.proname = substr(f, 3))
|
|
33482
|
+
WHEN 't' THEN to_regclass('public.' || substr(f, 3)) IS NOT NULL
|
|
33483
|
+
ELSE CASE WHEN split_part(substr(f, 3), '.', 1) = 'control_get_todays_carts'
|
|
33484
|
+
THEN coalesce(pg_get_function_result(to_regprocedure('public.control_get_todays_carts()')), '') LIKE '%carrier_name%'
|
|
33485
|
+
ELSE EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema='public'
|
|
33486
|
+
AND table_name = split_part(substr(f, 3), '.', 1) AND column_name = split_part(substr(f, 3), '.', 2)) END
|
|
33487
|
+
END;
|
|
33488
|
+
toelichting := CASE WHEN ok THEN 'aanwezig' ELSE 'ontbreekt: Engine-migratie (1.0.182 t/m 1.0.188) in dev uitvoeren en via schemasync verspreiden' END;
|
|
33489
|
+
RETURN NEXT;
|
|
33490
|
+
END LOOP;
|
|
33491
|
+
FOREACH c IN ARRAY ARRAY['carrier_id','carrier_source','carrier_reason','carrier_resolved_at','carrier_set_by','carrier_set_at'] LOOP
|
|
33492
|
+
onderdeel := 'orders.' || c;
|
|
33493
|
+
ok := EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema='public' AND table_name='orders' AND column_name=c);
|
|
33494
|
+
toelichting := CASE WHEN ok THEN 'aanwezig' ELSE 'ontbreekt: Engine-migratie' END; RETURN NEXT;
|
|
33495
|
+
END LOOP;
|
|
33496
|
+
FOREACH f IN ARRAY ARRAY['logistics_resolve_carrier(uuid)','logistics_resolve_carrier_many(uuid[])',
|
|
33497
|
+
'logistics_resolve_carrier_for_location(uuid,uuid)','logistics_resolve_carrier_for_location_many(uuid[],uuid)',
|
|
33498
|
+
'logistics_resolve_carrier_for_location_many(jsonb)',
|
|
33499
|
+
'logistics_set_order_carrier(uuid,uuid)','logistics_carrier_recalc_run(integer)'] LOOP
|
|
33500
|
+
onderdeel := 'functie ' || f; ok := to_regprocedure('public.' || f) IS NOT NULL;
|
|
33501
|
+
toelichting := CASE WHEN ok THEN 'aanwezig' ELSE 'ontbreekt: Engine-migratie' END; RETURN NEXT;
|
|
33502
|
+
END LOOP;
|
|
33503
|
+
FOREACH t IN ARRAY ARRAY['orders:trg_logistics_orders_carrier_fill','orders:trg_logistics_orders_carrier_changed',
|
|
33504
|
+
'carriers:trg_logistics_carriers_enqueue','customer_delivery_locations:trg_logistics_locations_enqueue'] LOOP
|
|
33505
|
+
onderdeel := 'trigger ' || split_part(t, ':', 2);
|
|
33506
|
+
ok := EXISTS (SELECT 1 FROM pg_trigger g WHERE g.tgname = split_part(t, ':', 2)
|
|
33507
|
+
AND g.tgrelid = to_regclass('public.' || split_part(t, ':', 1)));
|
|
33508
|
+
toelichting := CASE WHEN ok THEN 'aanwezig' ELSE 'ontbreekt: Engine-migratie' END; RETURN NEXT;
|
|
33509
|
+
END LOOP;
|
|
33510
|
+
onderdeel := 'minuutronde rekent vervoerders';
|
|
33511
|
+
ok := EXISTS (SELECT 1 FROM pg_proc WHERE proname = 'logistics_tick' AND prosrc LIKE '%logistics_carrier_recalc_run%');
|
|
33512
|
+
toelichting := CASE WHEN ok THEN 'ja' ELSE 'nee' END; RETURN NEXT;
|
|
33513
|
+
FOREACH c IN ARRAY ARRAY['carrier_kind','is_favorite','archived_at','delivery_location_glns'] LOOP
|
|
33514
|
+
onderdeel := 'Afhandeling: carriers.' || c;
|
|
33515
|
+
ok := EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema='public' AND table_name='carriers' AND column_name=c);
|
|
33516
|
+
toelichting := CASE WHEN ok THEN 'aanwezig' ELSE 'ontbreekt: Afhandeling' END; RETURN NEXT;
|
|
33517
|
+
END LOOP;
|
|
33518
|
+
FOREACH f IN ARRAY ARRAY['afh_favorite_carrier','afh_city_code','afh_normalize_country','afh_normalize_nl_province'] LOOP
|
|
33519
|
+
onderdeel := 'Afhandeling: ' || f || '()';
|
|
33520
|
+
ok := EXISTS (SELECT 1 FROM pg_proc p WHERE p.pronamespace = 'public'::regnamespace AND p.proname = f);
|
|
33521
|
+
toelichting := CASE WHEN ok THEN 'aanwezig' ELSE 'ontbreekt: Afhandeling' END; RETURN NEXT;
|
|
33522
|
+
END LOOP;
|
|
33523
|
+
onderdeel := 'Favoriete vervoerder ingesteld en bruikbaar';
|
|
33524
|
+
IF to_regprocedure('public.afh_favorite_carrier()') IS NULL THEN ok := false; toelichting := 'functie ontbreekt';
|
|
33525
|
+
ELSE
|
|
33526
|
+
BEGIN v_fav := public.logistics_carrier_favorite_id()::text; EXCEPTION WHEN OTHERS THEN v_fav := NULL; END;
|
|
33527
|
+
ok := public.logistics_try_uuid(v_fav) IS NOT NULL AND public.logistics_carrier_skip_reason(public.logistics_try_uuid(v_fav)) IS NULL;
|
|
33528
|
+
toelichting := CASE WHEN ok THEN 'ja' WHEN v_fav IS NULL THEN 'geen favoriet ingesteld (Afhandeling → Vervoerders)'
|
|
33529
|
+
ELSE public.logistics_carrier_skip_reason(public.logistics_try_uuid(v_fav)) END;
|
|
33530
|
+
END IF;
|
|
33531
|
+
RETURN NEXT;
|
|
33532
|
+
onderdeel := 'Klanten: customers.default_carrier_id';
|
|
33533
|
+
ok := EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema='public' AND table_name='customers' AND column_name='default_carrier_id');
|
|
33534
|
+
toelichting := CASE WHEN ok THEN 'aanwezig' ELSE 'ontbreekt: Klanten' END; RETURN NEXT;
|
|
33535
|
+
IF to_regclass('public.logistics_carrier_settings') IS NOT NULL AND to_regclass('public.orders') IS NOT NULL
|
|
33536
|
+
AND EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema='public' AND table_name='orders' AND column_name='carrier_source') THEN
|
|
33537
|
+
SELECT * INTO s FROM public.logistics_carrier_settings_ensure();
|
|
33538
|
+
EXECUTE $q$SELECT count(*) FROM public.orders o
|
|
33539
|
+
WHERE o.deleted_at IS NULL AND o.cancelled_at IS NULL
|
|
33540
|
+
AND coalesce(o.delivery_date, '') ~ '^\d{4}-\d{2}-\d{2}' AND left(o.delivery_date, 10)::date >= current_date
|
|
33541
|
+
AND (o.carrier_source = 'geen' OR (o.carrier_source IS NULL AND o.created_at >= $1))$q$ INTO n USING s.installed_at;
|
|
33542
|
+
onderdeel := 'Open orders zonder vervoerder'; ok := n = 0; toelichting := n || ' order(s)'; RETURN NEXT;
|
|
33543
|
+
onderdeel := 'Schakelaar bestaande orders'; ok := true;
|
|
33544
|
+
toelichting := CASE WHEN coalesce(s.backfill_existing, false) THEN 'aan' ELSE 'uit (bestaande orders wachten op akkoord)' END; RETURN NEXT;
|
|
33545
|
+
END IF;
|
|
33546
|
+
-- 1.0.188: mislukte aanmelding voor (her)planning vanuit de ordertriggers.
|
|
33547
|
+
SELECT count(*) INTO n FROM public.logistics_trace_events e
|
|
33548
|
+
WHERE e.actor IN ('trg_logistics_plan_order_items', 'trg_logistics_plan_orders')
|
|
33549
|
+
AND e.status = 'mislukt' AND e.created_at > now() - interval '24 hours';
|
|
33550
|
+
onderdeel := 'Mislukte herplanning (laatste 24 uur)'; ok := n = 0;
|
|
33551
|
+
toelichting := CASE WHEN n = 0 THEN 'geen' ELSE n || ' keer; laatste: ' || coalesce((SELECT coalesce(e.order_ref, '?') || ': ' || coalesce(e.error, '') FROM public.logistics_trace_events e
|
|
33552
|
+
WHERE e.actor IN ('trg_logistics_plan_order_items', 'trg_logistics_plan_orders') AND e.status = 'mislukt'
|
|
33553
|
+
ORDER BY e.created_at DESC LIMIT 1), '') END;
|
|
33554
|
+
RETURN NEXT;
|
|
33555
|
+
RETURN QUERY SELECT * FROM public.logistics_install_check_194();
|
|
33556
|
+
RETURN QUERY SELECT * FROM public.logistics_access_check();
|
|
33557
|
+
RETURN QUERY SELECT * FROM public.logistics_overload_check();
|
|
33558
|
+
RETURN QUERY SELECT * FROM public.logistics_check_190();
|
|
33559
|
+
END $function$
|
|
33560
|
+
;
|
|
33561
|
+
|
|
33562
|
+
-- Bij installeren: instellingen aanvullen (idempotent).
|
|
33563
|
+
DO $e$ BEGIN
|
|
33564
|
+
PERFORM public.logistics_ensure_tick_settings();
|
|
33565
|
+
PERFORM public.logistics_ensure_rebuffer_setting();
|
|
33566
|
+
EXCEPTION WHEN OTHERS THEN
|
|
33567
|
+
RAISE NOTICE 'logistiek-engine 1.0.194: instellingen aanvullen overgeslagen (%); de minuutronde doet dit alsnog', SQLERRM;
|
|
33568
|
+
END $e$;
|
|
33569
|
+
|
|
33570
|
+
GRANT EXECUTE ON FUNCTION public.logistics_order_date(text) TO authenticated, service_role;
|
|
33571
|
+
GRANT EXECUTE ON FUNCTION public.logistics_order_date(date) TO authenticated, service_role;
|
|
33572
|
+
GRANT EXECUTE ON FUNCTION public.logistics_print_status(uuid[]) TO authenticated, service_role;
|
|
33573
|
+
GRANT EXECUTE ON FUNCTION public.logistics_kick_metingen(integer) TO authenticated, service_role;
|
|
33574
|
+
GRANT EXECUTE ON FUNCTION public.logistics_install_check_194() TO authenticated, service_role;
|