@flowselections/logistiek-engine 1.0.240 → 1.0.242
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 +1632 -0
- package/dist-lib/components/StandaardgegevensCard.d.ts.map +1 -1
- package/dist-lib/components/StandaardgegevensCard.js +49 -2
- package/dist-lib/integrations/supabase/types.d.ts +154 -0
- package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
- package/dist-lib/lib/engineStatusTeksten.d.ts +1 -1
- package/dist-lib/lib/engineStatusTeksten.js +1 -1
- package/dist-lib/lib/karindelingBewakingTeksten.d.ts +9 -0
- package/dist-lib/lib/karindelingBewakingTeksten.d.ts.map +1 -0
- package/dist-lib/lib/karindelingBewakingTeksten.js +26 -0
- package/dist-lib/lib/modulePrompts.d.ts.map +1 -1
- package/dist-lib/lib/modulePrompts.js +44 -0
- package/dist-lib/lib/moduleVersion.d.ts +1 -1
- package/dist-lib/lib/moduleVersion.js +1 -1
- package/dist-lib/lib/ophaalmomentTeksten.d.ts +9 -0
- package/dist-lib/lib/ophaalmomentTeksten.d.ts.map +1 -0
- package/dist-lib/lib/ophaalmomentTeksten.js +29 -0
- package/dist-lib/lib/ruleEngineSchema.d.ts.map +1 -1
- package/dist-lib/lib/ruleEngineSchema.js +26 -1
- package/package.json +1 -1
|
@@ -43510,3 +43510,1635 @@ BEGIN
|
|
|
43510
43510
|
FROM (SELECT categorie, count(*) n FROM public.logistics_merge_herstel_log GROUP BY 1) h));
|
|
43511
43511
|
END $$;
|
|
43512
43512
|
|
|
43513
|
+
-- ===== 2026-11-10c: ophaalmoment, samenvoegen volgens buffergroep =====
|
|
43514
|
+
-- 1.0.239 (schema 2026-11-10c): ophaalmoment per order uit de werkwijze, samenvoegen volgens de buffergroep.
|
|
43515
|
+
|
|
43516
|
+
ALTER TABLE public.logistics_pickup_moments ADD COLUMN IF NOT EXISTS carrier_id uuid;
|
|
43517
|
+
ALTER TABLE public.logistics_pickup_moments ADD COLUMN IF NOT EXISTS pickup_days integer;
|
|
43518
|
+
ALTER TABLE public.logistics_pickup_moments ADD COLUMN IF NOT EXISTS signup_days integer;
|
|
43519
|
+
ALTER TABLE public.logistics_pickup_moments ADD COLUMN IF NOT EXISTS signup_time time without time zone;
|
|
43520
|
+
CREATE INDEX IF NOT EXISTS logistics_pickup_moments_carrier_dag_idx ON public.logistics_pickup_moments (carrier_id, moment_date);
|
|
43521
|
+
|
|
43522
|
+
CREATE OR REPLACE FUNCTION public.logistics_pickup_carrier_uit_naam(p_naam text)
|
|
43523
|
+
RETURNS uuid LANGUAGE sql STABLE SECURITY DEFINER SET search_path TO 'public' AS $$
|
|
43524
|
+
SELECT CASE WHEN count(*) = 1 THEN (array_agg(c.id))[1] END
|
|
43525
|
+
FROM public.carriers c
|
|
43526
|
+
WHERE c.archived_at IS NULL
|
|
43527
|
+
AND nullif(btrim(p_naam), '') IS NOT NULL
|
|
43528
|
+
AND lower(btrim(c.name)) = lower(btrim(p_naam));
|
|
43529
|
+
$$;
|
|
43530
|
+
REVOKE ALL ON FUNCTION public.logistics_pickup_carrier_uit_naam(text) FROM PUBLIC, anon, authenticated;
|
|
43531
|
+
GRANT EXECUTE ON FUNCTION public.logistics_pickup_carrier_uit_naam(text) TO service_role;
|
|
43532
|
+
|
|
43533
|
+
CREATE OR REPLACE FUNCTION public.logistics_pickup_moments_koppel_trg()
|
|
43534
|
+
RETURNS trigger LANGUAGE plpgsql SECURITY DEFINER SET search_path TO 'public' AS $$
|
|
43535
|
+
BEGIN
|
|
43536
|
+
IF NEW.carrier_id IS NULL AND NEW.carrier_ref IS NOT NULL THEN
|
|
43537
|
+
NEW.carrier_id := public.logistics_pickup_carrier_uit_naam(NEW.carrier_ref);
|
|
43538
|
+
END IF;
|
|
43539
|
+
RETURN NEW;
|
|
43540
|
+
END $$;
|
|
43541
|
+
REVOKE ALL ON FUNCTION public.logistics_pickup_moments_koppel_trg() FROM PUBLIC, anon, authenticated;
|
|
43542
|
+
DROP TRIGGER IF EXISTS trg_logistics_pickup_moments_koppel ON public.logistics_pickup_moments;
|
|
43543
|
+
CREATE TRIGGER trg_logistics_pickup_moments_koppel
|
|
43544
|
+
BEFORE INSERT OR UPDATE OF carrier_ref, carrier_id ON public.logistics_pickup_moments
|
|
43545
|
+
FOR EACH ROW EXECUTE FUNCTION public.logistics_pickup_moments_koppel_trg();
|
|
43546
|
+
|
|
43547
|
+
CREATE OR REPLACE FUNCTION public.logistics_pickup_moments_koppel()
|
|
43548
|
+
RETURNS integer LANGUAGE plpgsql SECURITY DEFINER SET search_path TO 'public' AS $$
|
|
43549
|
+
DECLARE n int;
|
|
43550
|
+
BEGIN
|
|
43551
|
+
UPDATE public.logistics_pickup_moments p
|
|
43552
|
+
SET carrier_id = public.logistics_pickup_carrier_uit_naam(p.carrier_ref)
|
|
43553
|
+
WHERE p.carrier_id IS NULL AND p.carrier_ref IS NOT NULL
|
|
43554
|
+
AND public.logistics_pickup_carrier_uit_naam(p.carrier_ref) IS NOT NULL;
|
|
43555
|
+
GET DIAGNOSTICS n = ROW_COUNT;
|
|
43556
|
+
RETURN n;
|
|
43557
|
+
END $$;
|
|
43558
|
+
REVOKE ALL ON FUNCTION public.logistics_pickup_moments_koppel() FROM PUBLIC, anon, authenticated;
|
|
43559
|
+
GRANT EXECUTE ON FUNCTION public.logistics_pickup_moments_koppel() TO service_role;
|
|
43560
|
+
|
|
43561
|
+
CREATE OR REPLACE FUNCTION public.logistics_pickup_regel()
|
|
43562
|
+
RETURNS jsonb LANGUAGE plpgsql STABLE SECURITY DEFINER SET search_path TO 'public' AS $$
|
|
43563
|
+
DECLARE s jsonb; v_regel text; v_marge numeric;
|
|
43564
|
+
BEGIN
|
|
43565
|
+
SELECT c.settings INTO s FROM public.logistics_rule_configs c WHERE c.is_active ORDER BY c.updated_at DESC LIMIT 1;
|
|
43566
|
+
v_regel := public.logistics_rule_text(COALESCE(s, '{}'::jsonb), 'order_verwerking', 'pickup_moment_rule', 'eerste');
|
|
43567
|
+
v_marge := public.logistics_rule_num(COALESCE(s, '{}'::jsonb), 'order_verwerking', 'pickup_moment_marge_uren', 2);
|
|
43568
|
+
IF v_regel NOT IN ('eerste', 'laatste_voor_levertijd') THEN v_regel := 'eerste'; END IF;
|
|
43569
|
+
RETURN jsonb_build_object('regel', v_regel, 'margeUren', COALESCE(v_marge, 2), 'werkwijzeGevonden', s IS NOT NULL);
|
|
43570
|
+
END $$;
|
|
43571
|
+
REVOKE ALL ON FUNCTION public.logistics_pickup_regel() FROM PUBLIC, anon;
|
|
43572
|
+
GRANT EXECUTE ON FUNCTION public.logistics_pickup_regel() TO authenticated, service_role;
|
|
43573
|
+
|
|
43574
|
+
CREATE OR REPLACE FUNCTION public.logistics_pickup_moment_kies(
|
|
43575
|
+
p_carrier_id uuid, p_leverdag date, p_levertijd timestamptz, p_regel text, p_marge_uren numeric)
|
|
43576
|
+
RETURNS jsonb LANGUAGE plpgsql STABLE SECURITY DEFINER SET search_path TO 'public' AS $$
|
|
43577
|
+
DECLARE v_tz text; r record; n int;
|
|
43578
|
+
BEGIN
|
|
43579
|
+
IF p_carrier_id IS NULL THEN
|
|
43580
|
+
RETURN jsonb_build_object('ok', false, 'redenCode', 'geen_vervoerder', 'reden', 'De order heeft geen vervoerder.');
|
|
43581
|
+
END IF;
|
|
43582
|
+
IF p_leverdag IS NULL THEN
|
|
43583
|
+
RETURN jsonb_build_object('ok', false, 'redenCode', 'geen_leverdag', 'reden', 'De order heeft geen leverdag.');
|
|
43584
|
+
END IF;
|
|
43585
|
+
v_tz := public.logistics_tz();
|
|
43586
|
+
IF v_tz IS NULL THEN
|
|
43587
|
+
RETURN jsonb_build_object('ok', false, 'redenCode', 'geen_tijdzone', 'reden', 'De tijdzone van de vestiging ontbreekt. Stel die in bij Werktijden.');
|
|
43588
|
+
END IF;
|
|
43589
|
+
|
|
43590
|
+
SELECT count(*) INTO n
|
|
43591
|
+
FROM public.logistics_pickup_moments p
|
|
43592
|
+
WHERE p.carrier_id = p_carrier_id AND p.moment_date = p_leverdag AND p.pickup_time IS NOT NULL
|
|
43593
|
+
AND public.logistics_subtract_pick_workdays(p_leverdag, COALESCE(p.pickup_days, 0)) IS NOT NULL;
|
|
43594
|
+
IF n = 0 THEN
|
|
43595
|
+
RETURN jsonb_build_object('ok', false, 'redenCode', 'geen_moment_op_dag',
|
|
43596
|
+
'reden', 'De vervoerder heeft geen ophaalmoment op ' || to_char(p_leverdag, 'DD-MM-YYYY') || '.');
|
|
43597
|
+
END IF;
|
|
43598
|
+
|
|
43599
|
+
IF p_regel = 'laatste_voor_levertijd' AND p_levertijd IS NOT NULL THEN
|
|
43600
|
+
SELECT k2.* INTO r FROM (
|
|
43601
|
+
SELECT p.pickup_time, COALESCE(p.pickup_days, 0) AS pd, p.signup_days AS sd, p.signup_time AS st,
|
|
43602
|
+
public.logistics_subtract_pick_workdays(p_leverdag, COALESCE(p.pickup_days, 0)) AS pdag,
|
|
43603
|
+
((public.logistics_subtract_pick_workdays(p_leverdag, COALESCE(p.pickup_days, 0))::timestamp + p.pickup_time) AT TIME ZONE v_tz) AS pickup_at
|
|
43604
|
+
FROM public.logistics_pickup_moments p
|
|
43605
|
+
WHERE p.carrier_id = p_carrier_id AND p.moment_date = p_leverdag AND p.pickup_time IS NOT NULL) k2
|
|
43606
|
+
WHERE k2.pdag IS NOT NULL
|
|
43607
|
+
AND k2.pickup_at <= p_levertijd - make_interval(secs => (COALESCE(p_marge_uren, 2) * 3600)::double precision)
|
|
43608
|
+
ORDER BY k2.pickup_at DESC LIMIT 1;
|
|
43609
|
+
IF NOT FOUND THEN
|
|
43610
|
+
RETURN jsonb_build_object('ok', false, 'redenCode', 'geen_moment_voor_levertijd',
|
|
43611
|
+
'reden', 'Er is geen ophaalmoment minstens ' || COALESCE(p_marge_uren, 2) || ' uur vóór de levertijd van de klant ('
|
|
43612
|
+
|| to_char(p_levertijd AT TIME ZONE v_tz, 'DD-MM-YYYY HH24:MI') || ').');
|
|
43613
|
+
END IF;
|
|
43614
|
+
ELSE
|
|
43615
|
+
SELECT k2.* INTO r FROM (
|
|
43616
|
+
SELECT p.pickup_time, COALESCE(p.pickup_days, 0) AS pd, p.signup_days AS sd, p.signup_time AS st,
|
|
43617
|
+
public.logistics_subtract_pick_workdays(p_leverdag, COALESCE(p.pickup_days, 0)) AS pdag,
|
|
43618
|
+
((public.logistics_subtract_pick_workdays(p_leverdag, COALESCE(p.pickup_days, 0))::timestamp + p.pickup_time) AT TIME ZONE v_tz) AS pickup_at
|
|
43619
|
+
FROM public.logistics_pickup_moments p
|
|
43620
|
+
WHERE p.carrier_id = p_carrier_id AND p.moment_date = p_leverdag AND p.pickup_time IS NOT NULL) k2
|
|
43621
|
+
WHERE k2.pdag IS NOT NULL
|
|
43622
|
+
ORDER BY k2.pickup_at ASC LIMIT 1;
|
|
43623
|
+
END IF;
|
|
43624
|
+
|
|
43625
|
+
RETURN jsonb_build_object('ok', true,
|
|
43626
|
+
'pickupMoment', to_char(r.pdag, 'YYYY-MM-DD') || '|' || to_char(r.pickup_time, 'HH24:MI'),
|
|
43627
|
+
'momentKey', CASE WHEN r.st IS NOT NULL
|
|
43628
|
+
THEN r.pd::text || '@' || to_char(r.pickup_time, 'HH24:MI') || '|' || COALESCE(r.sd, 0)::text || '@' || to_char(r.st, 'HH24:MI') END,
|
|
43629
|
+
'pickupAt', r.pickup_at,
|
|
43630
|
+
'regel', CASE WHEN p_regel = 'laatste_voor_levertijd' AND p_levertijd IS NOT NULL THEN 'laatste_voor_levertijd' ELSE 'eerste' END);
|
|
43631
|
+
END $$;
|
|
43632
|
+
REVOKE ALL ON FUNCTION public.logistics_pickup_moment_kies(uuid, date, timestamptz, text, numeric) FROM PUBLIC, anon;
|
|
43633
|
+
GRANT EXECUTE ON FUNCTION public.logistics_pickup_moment_kies(uuid, date, timestamptz, text, numeric) TO authenticated, service_role;
|
|
43634
|
+
|
|
43635
|
+
CREATE OR REPLACE FUNCTION public.logistics_pickup_cf_nieuw(
|
|
43636
|
+
p_cf jsonb, p_carrier_id uuid, p_delivery_date text, p_deleted_at timestamptz, p_cancelled_at timestamptz,
|
|
43637
|
+
p_plan_locked_at timestamptz, p_regel jsonb DEFAULT NULL)
|
|
43638
|
+
RETURNS jsonb LANGUAGE plpgsql STABLE SECURITY DEFINER SET search_path TO 'public' AS $$
|
|
43639
|
+
DECLARE cf jsonb := COALESCE(p_cf, '{}'::jsonb); v_dag date; v_lt timestamptz; v_regel jsonb; k jsonb; n jsonb;
|
|
43640
|
+
BEGIN
|
|
43641
|
+
IF jsonb_typeof(cf) <> 'object' THEN RETURN NULL; END IF;
|
|
43642
|
+
IF p_deleted_at IS NOT NULL OR p_cancelled_at IS NOT NULL OR p_plan_locked_at IS NOT NULL THEN RETURN NULL; END IF;
|
|
43643
|
+
IF (cf->'backfill') IS NOT NULL AND jsonb_typeof(cf->'backfill') <> 'null'
|
|
43644
|
+
AND (CASE WHEN jsonb_typeof(cf->'backfill') = 'boolean' THEN (cf->>'backfill')::boolean
|
|
43645
|
+
ELSE lower(btrim(cf->>'backfill')) NOT IN ('', 'false', '0', 'nee', 'no') END) THEN
|
|
43646
|
+
RETURN NULL;
|
|
43647
|
+
END IF;
|
|
43648
|
+
IF nullif(btrim(COALESCE(cf->>'pickup_moment', '')), '') IS NOT NULL
|
|
43649
|
+
AND COALESCE(cf->>'pickup_moment_bron', '') <> 'auto' THEN
|
|
43650
|
+
RETURN NULL;
|
|
43651
|
+
END IF;
|
|
43652
|
+
BEGIN v_dag := public.logistics_order_date(p_delivery_date); EXCEPTION WHEN OTHERS THEN v_dag := NULL; END;
|
|
43653
|
+
IF v_dag IS NOT NULL AND v_dag < (now() AT TIME ZONE 'Europe/Amsterdam')::date THEN RETURN NULL; END IF;
|
|
43654
|
+
BEGIN v_lt := nullif(btrim(COALESCE(cf->>'floriday_latest_delivery_at', '')), '')::timestamptz;
|
|
43655
|
+
EXCEPTION WHEN OTHERS THEN v_lt := NULL; END;
|
|
43656
|
+
v_regel := COALESCE(p_regel, public.logistics_pickup_regel());
|
|
43657
|
+
k := public.logistics_pickup_moment_kies(p_carrier_id, v_dag, v_lt, v_regel->>'regel', (v_regel->>'margeUren')::numeric);
|
|
43658
|
+
IF COALESCE((k->>'ok')::boolean, false) THEN
|
|
43659
|
+
n := (cf - 'pickup_moment_reden') || jsonb_build_object('pickup_moment', k->>'pickupMoment', 'pickup_moment_bron', 'auto');
|
|
43660
|
+
n := CASE WHEN k->>'momentKey' IS NOT NULL THEN n || jsonb_build_object('pickup_moment_key', k->>'momentKey')
|
|
43661
|
+
ELSE n - 'pickup_moment_key' END;
|
|
43662
|
+
ELSE
|
|
43663
|
+
n := (cf - 'pickup_moment' - 'pickup_moment_key')
|
|
43664
|
+
|| jsonb_build_object('pickup_moment_bron', 'auto', 'pickup_moment_reden', k->>'reden');
|
|
43665
|
+
END IF;
|
|
43666
|
+
IF n = cf THEN RETURN NULL; END IF;
|
|
43667
|
+
RETURN n;
|
|
43668
|
+
END;
|
|
43669
|
+
$$;
|
|
43670
|
+
REVOKE ALL ON FUNCTION public.logistics_pickup_cf_nieuw(jsonb, uuid, text, timestamptz, timestamptz, timestamptz, jsonb) FROM PUBLIC, anon, authenticated;
|
|
43671
|
+
GRANT EXECUTE ON FUNCTION public.logistics_pickup_cf_nieuw(jsonb, uuid, text, timestamptz, timestamptz, timestamptz, jsonb) TO service_role;
|
|
43672
|
+
|
|
43673
|
+
CREATE OR REPLACE FUNCTION public.logistics_orders_pickup_moment()
|
|
43674
|
+
RETURNS trigger LANGUAGE plpgsql SECURITY DEFINER SET search_path TO 'public' AS $$
|
|
43675
|
+
DECLARE n jsonb;
|
|
43676
|
+
BEGIN
|
|
43677
|
+
IF COALESCE(current_setting('logistics.pickup_writer', true), '') = 'engine' THEN RETURN NEW; END IF;
|
|
43678
|
+
IF TG_OP = 'UPDATE'
|
|
43679
|
+
AND NEW.carrier_id IS NOT DISTINCT FROM OLD.carrier_id
|
|
43680
|
+
AND NEW.delivery_date IS NOT DISTINCT FROM OLD.delivery_date
|
|
43681
|
+
AND (NEW.custom_fields->>'floriday_latest_delivery_at') IS NOT DISTINCT FROM (OLD.custom_fields->>'floriday_latest_delivery_at')
|
|
43682
|
+
AND NEW.deleted_at IS NOT DISTINCT FROM OLD.deleted_at
|
|
43683
|
+
AND (nullif(btrim(COALESCE(NEW.custom_fields->>'pickup_moment', '')), '') IS NOT NULL
|
|
43684
|
+
OR (NEW.custom_fields->'pickup_moment_reden') IS NOT NULL) THEN
|
|
43685
|
+
RETURN NEW;
|
|
43686
|
+
END IF;
|
|
43687
|
+
BEGIN
|
|
43688
|
+
n := public.logistics_pickup_cf_nieuw(NEW.custom_fields, NEW.carrier_id, NEW.delivery_date::text,
|
|
43689
|
+
NEW.deleted_at, NEW.cancelled_at, NEW.plan_locked_at, NULL);
|
|
43690
|
+
IF n IS NOT NULL THEN NEW.custom_fields := n; END IF;
|
|
43691
|
+
EXCEPTION WHEN OTHERS THEN
|
|
43692
|
+
RAISE WARNING 'logistiek-engine: ophaalmoment bepalen mislukt voor order %: %', NEW.id, SQLERRM;
|
|
43693
|
+
BEGIN
|
|
43694
|
+
PERFORM public._logistics_merge_melding('ophaalmoment_fout', NEW.id, NULL, NULL, 'ophaalmoment bepalen: ' || SQLERRM);
|
|
43695
|
+
EXCEPTION WHEN OTHERS THEN RAISE WARNING 'logistiek-engine: melding ophaalmoment mislukt: %', SQLERRM; END;
|
|
43696
|
+
END;
|
|
43697
|
+
RETURN NEW;
|
|
43698
|
+
END $$;
|
|
43699
|
+
REVOKE ALL ON FUNCTION public.logistics_orders_pickup_moment() FROM PUBLIC, anon, authenticated;
|
|
43700
|
+
DROP TRIGGER IF EXISTS trg_logistics_orders_zpickup_moment ON public.orders;
|
|
43701
|
+
CREATE TRIGGER trg_logistics_orders_zpickup_moment
|
|
43702
|
+
BEFORE INSERT OR UPDATE ON public.orders
|
|
43703
|
+
FOR EACH ROW EXECUTE FUNCTION public.logistics_orders_pickup_moment();
|
|
43704
|
+
|
|
43705
|
+
CREATE OR REPLACE FUNCTION public.logistics_pickup_herwijs(p_carrier_ids uuid[], p_max integer DEFAULT 5000)
|
|
43706
|
+
RETURNS jsonb LANGUAGE plpgsql SECURITY DEFINER SET search_path TO 'public' AS $$
|
|
43707
|
+
DECLARE r record; n jsonb; v_regel jsonb; v_gewijzigd int := 0; v_fouten jsonb := '[]'::jsonb; v_vorig text;
|
|
43708
|
+
BEGIN
|
|
43709
|
+
IF p_carrier_ids IS NULL OR cardinality(p_carrier_ids) = 0 THEN RETURN jsonb_build_object('gewijzigd', 0); END IF;
|
|
43710
|
+
v_regel := public.logistics_pickup_regel();
|
|
43711
|
+
v_vorig := current_setting('logistics.pickup_writer', true);
|
|
43712
|
+
FOR r IN
|
|
43713
|
+
SELECT o.id, o.order_number, o.custom_fields, o.carrier_id, o.delivery_date::text AS dd, o.deleted_at, o.cancelled_at, o.plan_locked_at
|
|
43714
|
+
FROM public.orders o
|
|
43715
|
+
WHERE o.carrier_id = ANY (p_carrier_ids) AND o.deleted_at IS NULL AND o.cancelled_at IS NULL
|
|
43716
|
+
AND public.logistics_order_date(o.delivery_date::text) >= (now() AT TIME ZONE 'Europe/Amsterdam')::date
|
|
43717
|
+
AND (nullif(btrim(COALESCE(o.custom_fields->>'pickup_moment', '')), '') IS NULL
|
|
43718
|
+
OR COALESCE(o.custom_fields->>'pickup_moment_bron', '') = 'auto')
|
|
43719
|
+
ORDER BY o.delivery_date LIMIT greatest(COALESCE(p_max, 5000), 1)
|
|
43720
|
+
LOOP
|
|
43721
|
+
BEGIN
|
|
43722
|
+
n := public.logistics_pickup_cf_nieuw(r.custom_fields, r.carrier_id, r.dd, r.deleted_at, r.cancelled_at, r.plan_locked_at, v_regel);
|
|
43723
|
+
IF n IS NOT NULL THEN
|
|
43724
|
+
PERFORM set_config('logistics.pickup_writer', 'engine', true);
|
|
43725
|
+
UPDATE public.orders SET custom_fields = n, updated_at = now() WHERE id = r.id;
|
|
43726
|
+
PERFORM set_config('logistics.pickup_writer', COALESCE(v_vorig, ''), true);
|
|
43727
|
+
v_gewijzigd := v_gewijzigd + 1;
|
|
43728
|
+
END IF;
|
|
43729
|
+
EXCEPTION WHEN OTHERS THEN
|
|
43730
|
+
PERFORM set_config('logistics.pickup_writer', COALESCE(v_vorig, ''), true);
|
|
43731
|
+
v_fouten := v_fouten || jsonb_build_array(jsonb_build_object('order', r.order_number, 'fout', SQLERRM));
|
|
43732
|
+
PERFORM public._logistics_merge_melding('ophaalmoment_fout', r.id, NULL, NULL, 'ophaalmoment herzien: ' || SQLERRM);
|
|
43733
|
+
END;
|
|
43734
|
+
END LOOP;
|
|
43735
|
+
RETURN jsonb_build_object('gewijzigd', v_gewijzigd, 'fouten', v_fouten);
|
|
43736
|
+
END $$;
|
|
43737
|
+
REVOKE ALL ON FUNCTION public.logistics_pickup_herwijs(uuid[], integer) FROM PUBLIC, anon, authenticated;
|
|
43738
|
+
GRANT EXECUTE ON FUNCTION public.logistics_pickup_herwijs(uuid[], integer) TO service_role;
|
|
43739
|
+
|
|
43740
|
+
CREATE OR REPLACE FUNCTION public.logistics_pickup_moments_gewijzigd_trg()
|
|
43741
|
+
RETURNS trigger LANGUAGE plpgsql SECURITY DEFINER SET search_path TO 'public' AS $$
|
|
43742
|
+
DECLARE v_ids uuid[];
|
|
43743
|
+
BEGIN
|
|
43744
|
+
IF TG_OP = 'DELETE' THEN
|
|
43745
|
+
SELECT array_agg(DISTINCT carrier_id) INTO v_ids FROM oud WHERE carrier_id IS NOT NULL;
|
|
43746
|
+
ELSIF TG_OP = 'INSERT' THEN
|
|
43747
|
+
SELECT array_agg(DISTINCT carrier_id) INTO v_ids FROM nieuw WHERE carrier_id IS NOT NULL;
|
|
43748
|
+
ELSE
|
|
43749
|
+
SELECT array_agg(DISTINCT x) INTO v_ids FROM (
|
|
43750
|
+
SELECT carrier_id x FROM nieuw UNION SELECT carrier_id FROM oud) t WHERE x IS NOT NULL;
|
|
43751
|
+
END IF;
|
|
43752
|
+
BEGIN
|
|
43753
|
+
PERFORM public.logistics_pickup_herwijs(v_ids, 5000);
|
|
43754
|
+
EXCEPTION WHEN OTHERS THEN
|
|
43755
|
+
RAISE WARNING 'logistiek-engine: ophaalmomenten herzien mislukt: %', SQLERRM;
|
|
43756
|
+
PERFORM public._logistics_merge_melding('ophaalmoment_fout', NULL, NULL, NULL, 'ophaalmomenten herzien: ' || SQLERRM);
|
|
43757
|
+
END;
|
|
43758
|
+
RETURN NULL;
|
|
43759
|
+
END $$;
|
|
43760
|
+
REVOKE ALL ON FUNCTION public.logistics_pickup_moments_gewijzigd_trg() FROM PUBLIC, anon, authenticated;
|
|
43761
|
+
DROP TRIGGER IF EXISTS trg_logistics_pickup_moments_ins ON public.logistics_pickup_moments;
|
|
43762
|
+
DROP TRIGGER IF EXISTS trg_logistics_pickup_moments_upd ON public.logistics_pickup_moments;
|
|
43763
|
+
DROP TRIGGER IF EXISTS trg_logistics_pickup_moments_del ON public.logistics_pickup_moments;
|
|
43764
|
+
CREATE TRIGGER trg_logistics_pickup_moments_ins AFTER INSERT ON public.logistics_pickup_moments
|
|
43765
|
+
REFERENCING NEW TABLE AS nieuw FOR EACH STATEMENT EXECUTE FUNCTION public.logistics_pickup_moments_gewijzigd_trg();
|
|
43766
|
+
CREATE TRIGGER trg_logistics_pickup_moments_upd AFTER UPDATE ON public.logistics_pickup_moments
|
|
43767
|
+
REFERENCING NEW TABLE AS nieuw OLD TABLE AS oud FOR EACH STATEMENT EXECUTE FUNCTION public.logistics_pickup_moments_gewijzigd_trg();
|
|
43768
|
+
CREATE TRIGGER trg_logistics_pickup_moments_del AFTER DELETE ON public.logistics_pickup_moments
|
|
43769
|
+
REFERENCING OLD TABLE AS oud FOR EACH STATEMENT EXECUTE FUNCTION public.logistics_pickup_moments_gewijzigd_trg();
|
|
43770
|
+
|
|
43771
|
+
CREATE OR REPLACE FUNCTION public.logistics_pickup_vul_voorbeeld()
|
|
43772
|
+
RETURNS jsonb LANGUAGE plpgsql STABLE SECURITY DEFINER SET search_path TO 'public' AS $$
|
|
43773
|
+
DECLARE v_regel jsonb := public.logistics_pickup_regel(); v jsonb;
|
|
43774
|
+
BEGIN
|
|
43775
|
+
WITH o AS (
|
|
43776
|
+
SELECT o.id, o.order_number, o.carrier_id, public.logistics_order_date(o.delivery_date::text) AS dag,
|
|
43777
|
+
public.logistics_pickup_cf_nieuw(o.custom_fields, o.carrier_id, o.delivery_date::text, o.deleted_at, o.cancelled_at, o.plan_locked_at, v_regel) AS n
|
|
43778
|
+
FROM public.orders o
|
|
43779
|
+
WHERE o.deleted_at IS NULL AND o.cancelled_at IS NULL
|
|
43780
|
+
AND public.logistics_order_date(o.delivery_date::text) >= (now() AT TIME ZONE 'Europe/Amsterdam')::date
|
|
43781
|
+
AND NOT public.logistics_order_is_backfill(o.id)
|
|
43782
|
+
AND (nullif(btrim(COALESCE(o.custom_fields->>'pickup_moment', '')), '') IS NULL
|
|
43783
|
+
OR COALESCE(o.custom_fields->>'pickup_moment_bron', '') = 'auto')
|
|
43784
|
+
), u AS (
|
|
43785
|
+
SELECT o.*, COALESCE(c.name, '(geen vervoerder)') AS vervoerder,
|
|
43786
|
+
CASE WHEN o.n IS NULL THEN 'ongewijzigd'
|
|
43787
|
+
WHEN (o.n->'pickup_moment') IS NOT NULL THEN 'krijgt moment'
|
|
43788
|
+
ELSE 'geen moment: ' || COALESCE(o.n->>'pickup_moment_reden', '?') END AS uitkomst
|
|
43789
|
+
FROM o LEFT JOIN public.carriers c ON c.id = o.carrier_id
|
|
43790
|
+
)
|
|
43791
|
+
SELECT jsonb_build_object('regel', v_regel,
|
|
43792
|
+
'totaal', (SELECT count(*) FROM u),
|
|
43793
|
+
'krijgtMoment', (SELECT count(*) FROM u WHERE uitkomst = 'krijgt moment'),
|
|
43794
|
+
'ongewijzigd', (SELECT count(*) FROM u WHERE uitkomst = 'ongewijzigd'),
|
|
43795
|
+
'zonderMoment', (SELECT count(*) FROM u WHERE uitkomst LIKE 'geen moment%'),
|
|
43796
|
+
'perVervoerderEnDag', COALESCE((SELECT jsonb_agg(jsonb_build_object('vervoerder', vervoerder, 'leverdag', dag,
|
|
43797
|
+
'uitkomst', regexp_replace(uitkomst, ' op \d{2}-\d{2}-\d{4}\.$', ''), 'aantal', n, 'voorbeelden', vb)
|
|
43798
|
+
ORDER BY dag, vervoerder)
|
|
43799
|
+
FROM (SELECT vervoerder, dag, uitkomst, count(*) n, (array_agg(order_number ORDER BY order_number))[1:3] vb
|
|
43800
|
+
FROM u WHERE uitkomst <> 'ongewijzigd' GROUP BY 1, 2, 3) g), '[]'::jsonb))
|
|
43801
|
+
INTO v;
|
|
43802
|
+
RETURN v;
|
|
43803
|
+
END $$;
|
|
43804
|
+
REVOKE ALL ON FUNCTION public.logistics_pickup_vul_voorbeeld() FROM PUBLIC, anon, authenticated;
|
|
43805
|
+
GRANT EXECUTE ON FUNCTION public.logistics_pickup_vul_voorbeeld() TO service_role;
|
|
43806
|
+
|
|
43807
|
+
CREATE OR REPLACE FUNCTION public.logistics_pickup_vul_uitvoeren(p_max integer DEFAULT 500)
|
|
43808
|
+
RETURNS jsonb LANGUAGE plpgsql SECURITY DEFINER SET search_path TO 'public' AS $$
|
|
43809
|
+
DECLARE r record; n jsonb; v_regel jsonb; v_gevuld int := 0; v_zonder int := 0; v_bekeken int := 0;
|
|
43810
|
+
v_fouten jsonb := '[]'::jsonb; v_vorig text; v_rest int;
|
|
43811
|
+
BEGIN
|
|
43812
|
+
PERFORM public.logistics_pickup_moments_koppel();
|
|
43813
|
+
v_regel := public.logistics_pickup_regel();
|
|
43814
|
+
v_vorig := current_setting('logistics.pickup_writer', true);
|
|
43815
|
+
FOR r IN
|
|
43816
|
+
SELECT o.id, o.order_number, o.custom_fields, o.carrier_id, o.delivery_date::text AS dd, o.deleted_at, o.cancelled_at, o.plan_locked_at
|
|
43817
|
+
FROM public.orders o
|
|
43818
|
+
WHERE o.deleted_at IS NULL AND o.cancelled_at IS NULL
|
|
43819
|
+
AND public.logistics_order_date(o.delivery_date::text) >= (now() AT TIME ZONE 'Europe/Amsterdam')::date
|
|
43820
|
+
AND NOT public.logistics_order_is_backfill(o.id)
|
|
43821
|
+
AND (nullif(btrim(COALESCE(o.custom_fields->>'pickup_moment', '')), '') IS NULL
|
|
43822
|
+
OR COALESCE(o.custom_fields->>'pickup_moment_bron', '') = 'auto')
|
|
43823
|
+
ORDER BY o.delivery_date, o.order_number
|
|
43824
|
+
LOOP
|
|
43825
|
+
EXIT WHEN v_gevuld + v_zonder >= greatest(COALESCE(p_max, 500), 1);
|
|
43826
|
+
v_bekeken := v_bekeken + 1;
|
|
43827
|
+
BEGIN
|
|
43828
|
+
n := public.logistics_pickup_cf_nieuw(r.custom_fields, r.carrier_id, r.dd, r.deleted_at, r.cancelled_at, r.plan_locked_at, v_regel);
|
|
43829
|
+
IF n IS NOT NULL THEN
|
|
43830
|
+
PERFORM set_config('logistics.pickup_writer', 'engine', true);
|
|
43831
|
+
UPDATE public.orders SET custom_fields = n, updated_at = now() WHERE id = r.id;
|
|
43832
|
+
PERFORM set_config('logistics.pickup_writer', COALESCE(v_vorig, ''), true);
|
|
43833
|
+
IF (n->'pickup_moment') IS NOT NULL THEN v_gevuld := v_gevuld + 1; ELSE v_zonder := v_zonder + 1; END IF;
|
|
43834
|
+
END IF;
|
|
43835
|
+
EXCEPTION WHEN OTHERS THEN
|
|
43836
|
+
PERFORM set_config('logistics.pickup_writer', COALESCE(v_vorig, ''), true);
|
|
43837
|
+
v_fouten := v_fouten || jsonb_build_array(jsonb_build_object('order', r.order_number, 'fout', SQLERRM));
|
|
43838
|
+
END;
|
|
43839
|
+
END LOOP;
|
|
43840
|
+
v_rest := COALESCE((public.logistics_pickup_vul_voorbeeld()->>'krijgtMoment')::int, 0);
|
|
43841
|
+
RETURN jsonb_build_object('gevuld', v_gevuld, 'zonderMomentGemarkeerd', v_zonder, 'bekeken', v_bekeken,
|
|
43842
|
+
'fouten', v_fouten, 'nogTeDoen', v_rest);
|
|
43843
|
+
END $$;
|
|
43844
|
+
REVOKE ALL ON FUNCTION public.logistics_pickup_vul_uitvoeren(integer) FROM PUBLIC, anon, authenticated;
|
|
43845
|
+
GRANT EXECUTE ON FUNCTION public.logistics_pickup_vul_uitvoeren(integer) TO service_role;
|
|
43846
|
+
|
|
43847
|
+
CREATE OR REPLACE FUNCTION public.logistics_pickup_status()
|
|
43848
|
+
RETURNS jsonb LANGUAGE plpgsql STABLE SECURITY DEFINER SET search_path TO 'public' AS $$
|
|
43849
|
+
DECLARE v_vandaag date := (now() AT TIME ZONE 'Europe/Amsterdam')::date; v_tot date; v_heeft boolean; v jsonb; v_regel jsonb;
|
|
43850
|
+
BEGIN
|
|
43851
|
+
PERFORM public.logistics_acc_guard('ingelogd', 'logistics_pickup_status');
|
|
43852
|
+
v_regel := public.logistics_pickup_regel();
|
|
43853
|
+
SELECT max(moment_date) INTO v_tot FROM public.logistics_pickup_moments;
|
|
43854
|
+
SELECT EXISTS (SELECT 1 FROM public.carriers c WHERE c.archived_at IS NULL
|
|
43855
|
+
AND jsonb_typeof(c.pickup_moments) = 'array' AND jsonb_array_length(c.pickup_moments) > 0) INTO v_heeft;
|
|
43856
|
+
WITH o AS (
|
|
43857
|
+
SELECT o.order_number, o.carrier_id, public.logistics_order_date(o.delivery_date::text) AS dag, o.custom_fields cf
|
|
43858
|
+
FROM public.orders o
|
|
43859
|
+
WHERE o.deleted_at IS NULL AND o.cancelled_at IS NULL
|
|
43860
|
+
AND public.logistics_order_date(o.delivery_date::text) BETWEEN v_vandaag AND v_vandaag + 6
|
|
43861
|
+
AND NOT public.logistics_order_is_backfill(o.id)
|
|
43862
|
+
AND lower(btrim(COALESCE(o.status, ''))) NOT IN ('verzonden', 'afgehandeld', 'geannuleerd', 'cancelled', 'canceled')
|
|
43863
|
+
AND nullif(btrim(COALESCE(o.custom_fields->>'pickup_moment', '')), '') IS NULL
|
|
43864
|
+
), r AS (
|
|
43865
|
+
SELECT o.*, public.logistics_pickup_moment_kies(o.carrier_id, o.dag,
|
|
43866
|
+
CASE WHEN o.cf->>'floriday_latest_delivery_at' ~ '^\d{4}-' THEN (o.cf->>'floriday_latest_delivery_at')::timestamptz END,
|
|
43867
|
+
v_regel->>'regel', (v_regel->>'margeUren')::numeric) AS k
|
|
43868
|
+
FROM o
|
|
43869
|
+
)
|
|
43870
|
+
SELECT jsonb_build_object(
|
|
43871
|
+
'zonderMoment', (SELECT count(*) FROM r),
|
|
43872
|
+
'redenen', COALESCE((SELECT jsonb_object_agg(code, n) FROM (
|
|
43873
|
+
SELECT COALESCE(CASE WHEN (k->>'ok')::boolean THEN 'nog_niet_gevuld' END, k->>'redenCode', 'onbekend') code, count(*) n
|
|
43874
|
+
FROM r GROUP BY 1) g), '{}'::jsonb),
|
|
43875
|
+
'voorbeelden', COALESCE((SELECT jsonb_agg(order_number) FROM (SELECT order_number FROM r ORDER BY dag, order_number LIMIT 5) x), '[]'::jsonb),
|
|
43876
|
+
'momentenTot', v_tot,
|
|
43877
|
+
'momentenTeKort', v_heeft AND (v_tot IS NULL OR v_tot < v_vandaag + 14),
|
|
43878
|
+
'ongekoppeld', (SELECT count(*) FROM public.logistics_pickup_moments p WHERE p.moment_date >= v_vandaag AND p.carrier_id IS NULL),
|
|
43879
|
+
'regel', v_regel)
|
|
43880
|
+
INTO v;
|
|
43881
|
+
RETURN v;
|
|
43882
|
+
END $$;
|
|
43883
|
+
REVOKE ALL ON FUNCTION public.logistics_pickup_status() FROM PUBLIC, anon;
|
|
43884
|
+
GRANT EXECUTE ON FUNCTION public.logistics_pickup_status() TO authenticated, service_role;
|
|
43885
|
+
|
|
43886
|
+
CREATE OR REPLACE FUNCTION public.logistics_refresh_pickup_moments(p_weeks integer DEFAULT 4)
|
|
43887
|
+
RETURNS jsonb LANGUAGE plpgsql SECURITY DEFINER SET search_path TO 'public' AS $$
|
|
43888
|
+
declare
|
|
43889
|
+
v_weeks integer := greatest(1, least(coalesce(p_weeks, 4), 26));
|
|
43890
|
+
v_from date := current_date;
|
|
43891
|
+
v_to date := current_date + (v_weeks * 7);
|
|
43892
|
+
v_inserted integer := 0;
|
|
43893
|
+
v_updated integer := 0;
|
|
43894
|
+
v_removed integer := 0;
|
|
43895
|
+
begin
|
|
43896
|
+
PERFORM public.logistics_acc_guard('ingelogd', 'logistics_refresh_pickup_moments');
|
|
43897
|
+
create temp table _wanted on commit drop as
|
|
43898
|
+
with dagen as (
|
|
43899
|
+
select d::date as moment_date from generate_series(v_from, v_to, interval '1 day') d
|
|
43900
|
+
),
|
|
43901
|
+
momenten as (
|
|
43902
|
+
select c.user_id, c.id as carrier_id, c.name as carrier_ref, c.pickup_weekdays,
|
|
43903
|
+
nullif(m->>'pickup_time','')::time as pickup_time,
|
|
43904
|
+
greatest(coalesce(nullif(m->>'pickup_days','')::int, 0), 0) as pickup_days,
|
|
43905
|
+
greatest(coalesce(nullif(m->>'signup_days','')::int, 0), 0) as signup_days,
|
|
43906
|
+
nullif(m->>'signup_time','')::time as signup_time,
|
|
43907
|
+
nullif(m->>'label','') as label
|
|
43908
|
+
from public.carriers c
|
|
43909
|
+
cross join lateral jsonb_array_elements(
|
|
43910
|
+
case when jsonb_typeof(c.pickup_moments) = 'array' then c.pickup_moments else '[]'::jsonb end) m
|
|
43911
|
+
where c.archived_at is null
|
|
43912
|
+
),
|
|
43913
|
+
geldig as (
|
|
43914
|
+
select d.moment_date, mo.user_id, mo.carrier_id, mo.carrier_ref, mo.pickup_time, mo.pickup_days,
|
|
43915
|
+
mo.signup_days, mo.signup_time, mo.label
|
|
43916
|
+
from dagen d
|
|
43917
|
+
join momenten mo
|
|
43918
|
+
on mo.pickup_time is not null
|
|
43919
|
+
and (mo.pickup_weekdays is null or array_length(mo.pickup_weekdays, 1) is null
|
|
43920
|
+
or extract(isodow from d.moment_date)::int = any(mo.pickup_weekdays))
|
|
43921
|
+
),
|
|
43922
|
+
uniek as (
|
|
43923
|
+
select distinct on (user_id, moment_date, carrier_id, pickup_time)
|
|
43924
|
+
user_id, moment_date, carrier_id, carrier_ref, pickup_time, pickup_days, signup_days, signup_time, label
|
|
43925
|
+
from geldig
|
|
43926
|
+
order by user_id, moment_date, carrier_id, pickup_time, pickup_days, signup_time
|
|
43927
|
+
)
|
|
43928
|
+
select u.*,
|
|
43929
|
+
row_number() over (partition by user_id, moment_date order by pickup_time, carrier_ref, carrier_id)::int as sequence
|
|
43930
|
+
from uniek u;
|
|
43931
|
+
|
|
43932
|
+
if not exists (select 1 from _wanted) then
|
|
43933
|
+
return jsonb_build_object('ok', true, 'reason', 'geen_ophaalmomenten_ingesteld', 'inserted', 0, 'updated', 0, 'removed', 0);
|
|
43934
|
+
end if;
|
|
43935
|
+
|
|
43936
|
+
with weg as (
|
|
43937
|
+
delete from public.logistics_pickup_moments p
|
|
43938
|
+
where p.moment_date between v_from and v_to
|
|
43939
|
+
and not exists (select 1 from _wanted w
|
|
43940
|
+
where w.user_id = p.user_id and w.moment_date = p.moment_date and w.sequence = p.sequence)
|
|
43941
|
+
returning 1
|
|
43942
|
+
)
|
|
43943
|
+
select count(*) into v_removed from weg;
|
|
43944
|
+
|
|
43945
|
+
with upd as (
|
|
43946
|
+
update public.logistics_pickup_moments p
|
|
43947
|
+
set pickup_time = w.pickup_time, carrier_ref = w.carrier_ref, carrier_id = w.carrier_id,
|
|
43948
|
+
pickup_days = w.pickup_days, signup_days = w.signup_days, signup_time = w.signup_time,
|
|
43949
|
+
label = w.label, updated_at = now()
|
|
43950
|
+
from _wanted w
|
|
43951
|
+
where p.user_id = w.user_id and p.moment_date = w.moment_date and p.sequence = w.sequence
|
|
43952
|
+
and (p.pickup_time is distinct from w.pickup_time or p.carrier_ref is distinct from w.carrier_ref
|
|
43953
|
+
or p.carrier_id is distinct from w.carrier_id or p.pickup_days is distinct from w.pickup_days
|
|
43954
|
+
or p.signup_days is distinct from w.signup_days or p.signup_time is distinct from w.signup_time
|
|
43955
|
+
or p.label is distinct from w.label)
|
|
43956
|
+
returning 1
|
|
43957
|
+
)
|
|
43958
|
+
select count(*) into v_updated from upd;
|
|
43959
|
+
|
|
43960
|
+
with ins as (
|
|
43961
|
+
insert into public.logistics_pickup_moments
|
|
43962
|
+
(user_id, moment_date, sequence, pickup_time, carrier_ref, carrier_id, pickup_days, signup_days, signup_time, label)
|
|
43963
|
+
select w.user_id, w.moment_date, w.sequence, w.pickup_time, w.carrier_ref, w.carrier_id, w.pickup_days, w.signup_days, w.signup_time, w.label
|
|
43964
|
+
from _wanted w
|
|
43965
|
+
where not exists (select 1 from public.logistics_pickup_moments p
|
|
43966
|
+
where p.user_id = w.user_id and p.moment_date = w.moment_date and p.sequence = w.sequence)
|
|
43967
|
+
returning 1
|
|
43968
|
+
)
|
|
43969
|
+
select count(*) into v_inserted from ins;
|
|
43970
|
+
|
|
43971
|
+
return jsonb_build_object('ok', true, 'inserted', v_inserted, 'updated', v_updated, 'removed', v_removed,
|
|
43972
|
+
'from', v_from, 'to', v_to);
|
|
43973
|
+
end $$;
|
|
43974
|
+
|
|
43975
|
+
CREATE OR REPLACE FUNCTION public.logistics_order_buffer_ref(p_order_id uuid)
|
|
43976
|
+
RETURNS text LANGUAGE plpgsql STABLE SECURITY DEFINER SET search_path TO 'public' AS $$
|
|
43977
|
+
DECLARE d jsonb;
|
|
43978
|
+
BEGIN
|
|
43979
|
+
d := public.logistics_buffer_decision(public.logistics_buffer_payload(p_order_id), NULL);
|
|
43980
|
+
IF COALESCE((d->>'buffer')::boolean, false) IS NOT TRUE OR NOT COALESCE((d->>'complete')::boolean, false) THEN RETURN NULL; END IF;
|
|
43981
|
+
RETURN nullif(d->>'bufferRef', '');
|
|
43982
|
+
END $$;
|
|
43983
|
+
REVOKE ALL ON FUNCTION public.logistics_order_buffer_ref(uuid) FROM PUBLIC, anon, authenticated;
|
|
43984
|
+
GRANT EXECUTE ON FUNCTION public.logistics_order_buffer_ref(uuid) TO service_role;
|
|
43985
|
+
|
|
43986
|
+
CREATE OR REPLACE FUNCTION public.logistics_merge_floriday_lines(p_order_id uuid)
|
|
43987
|
+
RETURNS uuid LANGUAGE plpgsql SECURITY DEFINER SET search_path TO 'public' AS $$
|
|
43988
|
+
DECLARE
|
|
43989
|
+
v_ref text; v_key text; v_keep uuid; v_keepnr text; v_cust uuid; v_dag date; r record; v_ids uuid[];
|
|
43990
|
+
v_moved int; v_logged int; v_total int := 0; v_count int := 0; v_groep int; s jsonb;
|
|
43991
|
+
v_lines text[] := ARRAY[]::text[];
|
|
43992
|
+
BEGIN
|
|
43993
|
+
PERFORM public.logistics_acc_guard('ingelogd', 'logistics_merge_floriday_lines');
|
|
43994
|
+
IF p_order_id IS NULL THEN RETURN p_order_id; END IF;
|
|
43995
|
+
IF COALESCE(current_setting('logistics.merge_uit', true), '') = '1' THEN RETURN p_order_id; END IF;
|
|
43996
|
+
IF NOT public.logistics_merge_lines_enabled() THEN RETURN p_order_id; END IF;
|
|
43997
|
+
SELECT c.settings INTO s FROM public.logistics_rule_configs c WHERE c.is_active ORDER BY c.updated_at DESC LIMIT 1;
|
|
43998
|
+
IF NOT public.logistics_rule_bool(COALESCE(s, '{}'::jsonb), 'order_verwerking', 'buffer_enabled', true) THEN RETURN p_order_id; END IF;
|
|
43999
|
+
IF NOT public.logistics_order_mergeable(p_order_id) THEN RETURN p_order_id; END IF;
|
|
44000
|
+
IF NOT EXISTS (SELECT 1 FROM logistics_active.order_items i WHERE i.order_id = p_order_id) THEN RETURN p_order_id; END IF;
|
|
44001
|
+
|
|
44002
|
+
v_ref := public.logistics_order_buffer_ref(p_order_id);
|
|
44003
|
+
IF v_ref IS NULL THEN RETURN p_order_id; END IF;
|
|
44004
|
+
SELECT customer_id, public.logistics_order_date(delivery_date::text) INTO v_cust, v_dag FROM public.orders WHERE id = p_order_id;
|
|
44005
|
+
IF v_cust IS NULL OR v_dag IS NULL THEN RETURN p_order_id; END IF;
|
|
44006
|
+
v_key := 'buf|' || v_ref || '|' || v_cust::text || '|' || v_dag::text;
|
|
44007
|
+
IF NOT pg_try_advisory_xact_lock(hashtext('logistics_merge:' || v_key)) THEN RETURN p_order_id; END IF;
|
|
44008
|
+
|
|
44009
|
+
SELECT array_agg(o.id ORDER BY o.order_number ASC NULLS LAST, o.created_at ASC) INTO v_ids
|
|
44010
|
+
FROM public.orders o
|
|
44011
|
+
WHERE o.customer_id = v_cust AND o.deleted_at IS NULL AND o.order_source = 'floriday'
|
|
44012
|
+
AND public.logistics_order_date(o.delivery_date::text) = v_dag
|
|
44013
|
+
AND EXISTS (SELECT 1 FROM logistics_active.order_items i WHERE i.order_id = o.id)
|
|
44014
|
+
AND public.logistics_order_mergeable(o.id)
|
|
44015
|
+
AND public.logistics_order_buffer_ref(o.id) = v_ref;
|
|
44016
|
+
v_groep := COALESCE(cardinality(v_ids), 0);
|
|
44017
|
+
IF v_groep < 2 THEN RETURN p_order_id; END IF;
|
|
44018
|
+
v_keep := v_ids[1];
|
|
44019
|
+
v_groep := v_groep + (SELECT count(*) FROM public.logistics_order_merges m WHERE m.kept_order_id = v_keep AND m.group_key = v_key);
|
|
44020
|
+
IF v_groep > 20 THEN
|
|
44021
|
+
IF NOT EXISTS (SELECT 1 FROM public.logistics_merge_meldingen m
|
|
44022
|
+
WHERE m.soort = 'groep_te_groot' AND m.group_key = v_key AND m.created_at > now() - interval '1 day') THEN
|
|
44023
|
+
PERFORM public._logistics_merge_melding('groep_te_groot', p_order_id, v_key, v_groep,
|
|
44024
|
+
v_groep || ' orders in dezelfde buffergroep; niet samengevoegd.');
|
|
44025
|
+
END IF;
|
|
44026
|
+
RETURN p_order_id;
|
|
44027
|
+
END IF;
|
|
44028
|
+
|
|
44029
|
+
SELECT order_number INTO v_keepnr FROM public.orders WHERE id = v_keep;
|
|
44030
|
+
PERFORM set_config('logistics.merge_uit', '1', true);
|
|
44031
|
+
|
|
44032
|
+
FOR r IN SELECT o.id, o.order_number, o.floriday_sales_order_id AS sales, o.created_at
|
|
44033
|
+
FROM public.orders o WHERE o.id = ANY (v_ids) AND o.id <> v_keep
|
|
44034
|
+
ORDER BY o.order_number ASC NULLS LAST LOOP
|
|
44035
|
+
BEGIN
|
|
44036
|
+
INSERT INTO public.logistics_order_merge_lines(
|
|
44037
|
+
group_key, from_order_id, from_order_number, to_order_id, to_order_number,
|
|
44038
|
+
order_item_id, product_id, product_name, quantity, source_line_key,
|
|
44039
|
+
sales_order_id, ordered_at, reason)
|
|
44040
|
+
SELECT v_key, r.id, r.order_number, v_keep, v_keepnr,
|
|
44041
|
+
i.id, i.product_id, i.product_name, i.quantity, i.source_line_key,
|
|
44042
|
+
r.sales, r.created_at, 'Zelfde buffergroep volgens de werkwijze'
|
|
44043
|
+
FROM logistics_active.order_items i WHERE i.order_id = r.id;
|
|
44044
|
+
GET DIAGNOSTICS v_logged = ROW_COUNT;
|
|
44045
|
+
UPDATE public.order_items SET order_id = v_keep WHERE order_id = r.id AND quarantined_at IS NULL;
|
|
44046
|
+
GET DIAGNOSTICS v_moved = ROW_COUNT;
|
|
44047
|
+
IF v_moved <> v_logged THEN RAISE EXCEPTION 'aantal verplaatste regels (%) wijkt af van het logboek (%)', v_moved, v_logged; END IF;
|
|
44048
|
+
IF v_moved = 0 THEN RAISE EXCEPTION 'geen regels verplaatst; order blijft staan'; END IF;
|
|
44049
|
+
INSERT INTO public.logistics_order_merges(group_key, kept_order_id, kept_order_number,
|
|
44050
|
+
merged_order_id, merged_order_number, sales_order_id, items_moved)
|
|
44051
|
+
VALUES (v_key, v_keep, v_keepnr, r.id, r.order_number, r.sales, v_moved)
|
|
44052
|
+
ON CONFLICT (merged_order_id) DO NOTHING;
|
|
44053
|
+
UPDATE public.orders SET deleted_at = now(),
|
|
44054
|
+
notes = COALESCE(NULLIF(notes, ''), '')
|
|
44055
|
+
|| CASE WHEN COALESCE(notes, '') = '' THEN '' ELSE E'\n' END
|
|
44056
|
+
|| 'Samengevoegd met ' || COALESCE(v_keepnr, '') || '.',
|
|
44057
|
+
updated_at = now()
|
|
44058
|
+
WHERE id = r.id;
|
|
44059
|
+
v_total := v_total + v_moved; v_count := v_count + 1;
|
|
44060
|
+
v_lines := v_lines || (COALESCE(r.order_number, '') || ': ' || v_moved::text || ' regel(s)');
|
|
44061
|
+
EXCEPTION WHEN OTHERS THEN
|
|
44062
|
+
PERFORM public._logistics_merge_melding('fout', r.id, v_key, NULL,
|
|
44063
|
+
'samenvoegen ' || COALESCE(r.order_number, '?') || ' met ' || COALESCE(v_keepnr, '?') || ': ' || SQLERRM);
|
|
44064
|
+
END;
|
|
44065
|
+
END LOOP;
|
|
44066
|
+
PERFORM set_config('logistics.merge_uit', '0', true);
|
|
44067
|
+
|
|
44068
|
+
IF v_count > 0 THEN
|
|
44069
|
+
UPDATE public.orders o SET total = to_char(COALESCE((
|
|
44070
|
+
SELECT SUM(COALESCE(i.quantity, 0) * COALESCE(i.unit_price, 0))
|
|
44071
|
+
FROM logistics_active.order_items i WHERE i.order_id = o.id), 0), 'FM9999999990.00'),
|
|
44072
|
+
updated_at = now()
|
|
44073
|
+
WHERE o.id = v_keep;
|
|
44074
|
+
PERFORM public.logistics_trace_order_step(
|
|
44075
|
+
v_keep, 'stap:bestelregels_samenvoegen', 'bestelregels samenvoegen', 'geslaagd',
|
|
44076
|
+
'samengevoegd tot ' || COALESCE(v_keepnr, 'één bestelling'),
|
|
44077
|
+
v_count::text || ' order(s), ' || v_total::text || ' regel(s) verplaatst: ' || array_to_string(v_lines, '; '),
|
|
44078
|
+
'Orders combineren');
|
|
44079
|
+
END IF;
|
|
44080
|
+
IF EXISTS (SELECT 1 FROM public.orders WHERE id = p_order_id AND deleted_at IS NOT NULL) THEN RETURN v_keep; END IF;
|
|
44081
|
+
RETURN p_order_id;
|
|
44082
|
+
EXCEPTION WHEN OTHERS THEN
|
|
44083
|
+
PERFORM set_config('logistics.merge_uit', '0', true);
|
|
44084
|
+
PERFORM public._logistics_merge_melding('fout', p_order_id, v_key, NULL, 'samenvoegen: ' || SQLERRM);
|
|
44085
|
+
RETURN p_order_id;
|
|
44086
|
+
END $$;
|
|
44087
|
+
|
|
44088
|
+
CREATE OR REPLACE FUNCTION public.logistics_install_check_samenvoegen()
|
|
44089
|
+
RETURNS TABLE(onderdeel text, ok boolean, toelichting text)
|
|
44090
|
+
LANGUAGE plpgsql STABLE SECURITY DEFINER SET search_path TO 'public'
|
|
44091
|
+
AS $function$
|
|
44092
|
+
DECLARE f text;
|
|
44093
|
+
BEGIN
|
|
44094
|
+
FOREACH f IN ARRAY ARRAY['logistics_order_is_backfill(uuid)', 'logistics_order_merge_key_v2(uuid)',
|
|
44095
|
+
'logistics_order_mergeable(uuid)', 'logistics_merge_floriday_lines(uuid)', 'logistics_order_buffer_ref(uuid)',
|
|
44096
|
+
'logistics_merge_herstel_voorbeeld()', 'logistics_merge_herstel_uitvoeren(text,integer)',
|
|
44097
|
+
'logistics_merge_status()', 'logistics_trace_event(uuid,text,text,text)', 'logistics_schema_versie()',
|
|
44098
|
+
'logistics_pickup_regel()', 'logistics_pickup_moment_kies(uuid,date,timestamp with time zone,text,numeric)',
|
|
44099
|
+
'logistics_pickup_vul_voorbeeld()', 'logistics_pickup_vul_uitvoeren(integer)', 'logistics_pickup_status()',
|
|
44100
|
+
'logistics_pickup_herwijs(uuid[],integer)', 'logistics_refresh_pickup_moments(integer)'] LOOP
|
|
44101
|
+
onderdeel := 'functie ' || f;
|
|
44102
|
+
ok := to_regprocedure('public.' || f) IS NOT NULL;
|
|
44103
|
+
toelichting := CASE WHEN ok THEN 'aanwezig' ELSE 'ontbreekt: werk de Engine bij via Supabase bijwerken' END;
|
|
44104
|
+
RETURN NEXT;
|
|
44105
|
+
END LOOP;
|
|
44106
|
+
onderdeel := 'tabel logistics_merge_herstel_log';
|
|
44107
|
+
ok := to_regclass('public.logistics_merge_herstel_log') IS NOT NULL;
|
|
44108
|
+
toelichting := CASE WHEN ok THEN 'aanwezig' ELSE 'ontbreekt: werk de Engine bij via Supabase bijwerken' END;
|
|
44109
|
+
RETURN NEXT;
|
|
44110
|
+
onderdeel := 'kolom logistics_pickup_moments.carrier_id';
|
|
44111
|
+
ok := EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = 'public'
|
|
44112
|
+
AND table_name = 'logistics_pickup_moments' AND column_name = 'carrier_id');
|
|
44113
|
+
toelichting := CASE WHEN ok THEN 'aanwezig' ELSE 'ontbreekt: werk de Engine bij via Supabase bijwerken' END;
|
|
44114
|
+
RETURN NEXT;
|
|
44115
|
+
onderdeel := 'trigger ophaalmoment op orders';
|
|
44116
|
+
ok := EXISTS (SELECT 1 FROM pg_trigger WHERE tgname = 'trg_logistics_orders_zpickup_moment');
|
|
44117
|
+
toelichting := CASE WHEN ok THEN 'aanwezig' ELSE 'ontbreekt: werk de Engine bij via Supabase bijwerken' END;
|
|
44118
|
+
RETURN NEXT;
|
|
44119
|
+
END $function$;
|
|
44120
|
+
|
|
44121
|
+
CREATE OR REPLACE FUNCTION public.logistics_schema_versie()
|
|
44122
|
+
RETURNS text LANGUAGE sql IMMUTABLE SET search_path TO 'public'
|
|
44123
|
+
AS $function$ SELECT '2026-11-10c'::text $function$;
|
|
44124
|
+
|
|
44125
|
+
CREATE OR REPLACE FUNCTION public.logistics_buffer_decision_for_settings(p_order jsonb, p_settings jsonb, p_user_id uuid DEFAULT NULL::uuid)
|
|
44126
|
+
RETURNS jsonb
|
|
44127
|
+
LANGUAGE plpgsql
|
|
44128
|
+
STABLE
|
|
44129
|
+
SET search_path TO 'public'
|
|
44130
|
+
AS $function$
|
|
44131
|
+
DECLARE
|
|
44132
|
+
v_user uuid := p_user_id;
|
|
44133
|
+
v_settings jsonb := COALESCE(p_settings, '{}'::jsonb);
|
|
44134
|
+
v_enabled boolean; v_include_res boolean; v_is_res boolean;
|
|
44135
|
+
v_minutes int; v_reference text; v_release text; v_criteria text[];
|
|
44136
|
+
v_checkin timestamptz; v_pick_start timestamptz; v_close timestamptz;
|
|
44137
|
+
v_key text; v_parts text[] := ARRAY[]::text[]; v_trace jsonb := '[]'::jsonb;
|
|
44138
|
+
v_missing boolean := false; v_missing_list text[] := ARRAY[]::text[];
|
|
44139
|
+
v_day text; v_ref text; c text; eff text; val text;
|
|
44140
|
+
v_is_fd boolean; v_skip text[]; v_skip_raw jsonb;
|
|
44141
|
+
BEGIN
|
|
44142
|
+
p_order := COALESCE(p_order, '{}'::jsonb);
|
|
44143
|
+
v_enabled := public.logistics_rule_bool(v_settings, 'order_verwerking', 'buffer_enabled', true);
|
|
44144
|
+
v_include_res := public.logistics_rule_bool(v_settings, 'order_verwerking', 'buffer_include_reservations', false);
|
|
44145
|
+
v_minutes := public.logistics_rule_num(v_settings, 'order_verwerking', 'buffer_close_before_minutes', 60)::int;
|
|
44146
|
+
v_reference := public.logistics_rule_text(v_settings, 'order_verwerking', 'buffer_close_reference', 'checkin');
|
|
44147
|
+
v_release := public.logistics_rule_text(v_settings, 'order_verwerking', 'buffer_release', 'auto');
|
|
44148
|
+
v_criteria := public.logistics_buffer_criteria(v_settings);
|
|
44149
|
+
|
|
44150
|
+
v_skip_raw := public.logistics_rule_value(v_settings, 'order_verwerking', 'buffer_skip_for_floriday');
|
|
44151
|
+
IF v_skip_raw IS NOT NULL AND jsonb_typeof(v_skip_raw) = 'array' THEN
|
|
44152
|
+
v_skip := ARRAY(SELECT jsonb_array_elements_text(v_skip_raw));
|
|
44153
|
+
ELSE
|
|
44154
|
+
-- 1.0.239: geen verborgen standaard meer; alleen overslaan wat de werkwijze zelf aangeeft.
|
|
44155
|
+
v_skip := ARRAY[]::text[];
|
|
44156
|
+
END IF;
|
|
44157
|
+
|
|
44158
|
+
v_is_fd := NULLIF(TRIM(COALESCE(p_order ->> 'floridaySalesOrderRef', p_order ->> 'floriday_sales_order_id', '')), '') IS NOT NULL;
|
|
44159
|
+
v_is_res := COALESCE((p_order ->> 'isReservation')::boolean, (p_order ->> 'is_reservation')::boolean, false);
|
|
44160
|
+
v_ref := COALESCE(NULLIF(p_order ->> 'orderRef', ''), NULLIF(p_order ->> 'order_ref', ''), md5(p_order::text));
|
|
44161
|
+
|
|
44162
|
+
IF NOT v_enabled THEN
|
|
44163
|
+
RETURN jsonb_build_object('buffer', false, 'reason', 'buffering_disabled', 'bufferRef', NULL, 'bufferKey', NULL,
|
|
44164
|
+
'matchCriteria', to_jsonb(v_criteria), 'parts', '[]'::jsonb, 'complete', true, 'missingCriteria', '[]'::jsonb,
|
|
44165
|
+
'isReservation', v_is_res, 'isFloriday', v_is_fd, 'closeBeforeMinutes', NULL, 'closeReference', v_reference,
|
|
44166
|
+
'bufferCloseAt', NULL, 'release', 'none',
|
|
44167
|
+
'trace', jsonb_build_array(jsonb_build_object('rule', 'buffer_enabled', 'result', false,
|
|
44168
|
+
'message', 'Orderbuffer staat uit in de Rule Engine; elke order wordt los verwerkt.')));
|
|
44169
|
+
END IF;
|
|
44170
|
+
|
|
44171
|
+
IF v_is_res AND NOT v_include_res THEN
|
|
44172
|
+
RETURN jsonb_build_object('buffer', false, 'reason', 'reservation_excluded', 'bufferRef', NULL, 'bufferKey', NULL,
|
|
44173
|
+
'matchCriteria', to_jsonb(v_criteria), 'parts', '[]'::jsonb, 'complete', true, 'missingCriteria', '[]'::jsonb,
|
|
44174
|
+
'isReservation', true, 'isFloriday', v_is_fd, 'closeBeforeMinutes', NULL, 'closeReference', v_reference,
|
|
44175
|
+
'bufferCloseAt', NULL, 'release', 'none',
|
|
44176
|
+
'trace', jsonb_build_array(jsonb_build_object('rule', 'buffer_include_reservations', 'result', false,
|
|
44177
|
+
'message', 'Gereserveerde bestellingen worden volgens de instelling buiten de buffers gehouden.')));
|
|
44178
|
+
END IF;
|
|
44179
|
+
|
|
44180
|
+
BEGIN v_checkin := (p_order ->> 'checkinAt')::timestamptz; EXCEPTION WHEN others THEN v_checkin := NULL; END;
|
|
44181
|
+
|
|
44182
|
+
IF v_reference = 'pick_start' THEN
|
|
44183
|
+
v_pick_start := public.logistics_pick_start_estimate(v_settings, p_order);
|
|
44184
|
+
IF v_pick_start IS NOT NULL THEN
|
|
44185
|
+
v_close := v_pick_start - make_interval(mins => v_minutes);
|
|
44186
|
+
v_trace := v_trace || jsonb_build_array(jsonb_build_object('rule', 'buffer_close_reference', 'value', 'pick_start', 'result', true,
|
|
44187
|
+
'message', 'De buffer sluit ' || v_minutes || ' minuten vóór de start van het picken.'));
|
|
44188
|
+
ELSIF v_checkin IS NOT NULL THEN
|
|
44189
|
+
v_close := v_checkin - make_interval(mins => v_minutes);
|
|
44190
|
+
v_trace := v_trace || jsonb_build_array(jsonb_build_object('rule', 'buffer_close_reference', 'value', 'checkin_fallback', 'result', true,
|
|
44191
|
+
'message', 'De pickstart is nog niet te berekenen; er wordt teruggerekend vanaf de aanmeldtijd.'));
|
|
44192
|
+
END IF;
|
|
44193
|
+
ELSIF v_checkin IS NOT NULL THEN
|
|
44194
|
+
v_close := v_checkin - make_interval(mins => v_minutes);
|
|
44195
|
+
END IF;
|
|
44196
|
+
|
|
44197
|
+
v_day := LEFT(COALESCE(p_order ->> 'deliveryDate', p_order ->> 'delivery_date', p_order ->> 'checkinAt', ''), 10);
|
|
44198
|
+
IF v_day <> '' THEN
|
|
44199
|
+
v_parts := v_parts || ('day=' || v_day);
|
|
44200
|
+
v_trace := v_trace || jsonb_build_array(jsonb_build_object('rule', 'day_bucket', 'value', v_day, 'result', true,
|
|
44201
|
+
'message', 'Orders worden alleen binnen dezelfde dag gecombineerd.'));
|
|
44202
|
+
END IF;
|
|
44203
|
+
|
|
44204
|
+
IF array_length(v_criteria, 1) IS NULL THEN
|
|
44205
|
+
val := NULLIF(TRIM(COALESCE(p_order ->> 'customerRef', '')), '');
|
|
44206
|
+
IF val IS NULL THEN
|
|
44207
|
+
v_missing := true; v_missing_list := v_missing_list || 'customer'::text;
|
|
44208
|
+
v_parts := v_parts || ('missing=customer:' || v_ref);
|
|
44209
|
+
v_trace := v_trace || jsonb_build_array(jsonb_build_object('rule', 'no_criteria_selected', 'value', NULL, 'result', false,
|
|
44210
|
+
'message', 'Geen combineercriteria aangevinkt en de klant ontbreekt; deze order wordt niet gecombineerd.'));
|
|
44211
|
+
ELSE
|
|
44212
|
+
v_parts := v_parts || ('customer=' || val);
|
|
44213
|
+
v_trace := v_trace || jsonb_build_array(jsonb_build_object('rule', 'no_criteria_selected', 'result', false,
|
|
44214
|
+
'message', 'Er zijn geen combineercriteria aangevinkt; er wordt teruggevallen op dezelfde klant.'));
|
|
44215
|
+
END IF;
|
|
44216
|
+
ELSE
|
|
44217
|
+
FOREACH c IN ARRAY v_criteria LOOP
|
|
44218
|
+
eff := c;
|
|
44219
|
+
IF v_is_fd AND c = ANY(v_skip) THEN
|
|
44220
|
+
v_trace := v_trace || jsonb_build_array(jsonb_build_object('rule', c, 'value', NULL, 'result', true,
|
|
44221
|
+
'message', 'Niet van toepassing bij Floriday-orders; Floriday levert deze waarde niet.'));
|
|
44222
|
+
CONTINUE;
|
|
44223
|
+
END IF;
|
|
44224
|
+
IF v_is_fd AND c = 'customer_bvo' THEN
|
|
44225
|
+
eff := 'customer_floriday';
|
|
44226
|
+
v_trace := v_trace || jsonb_build_array(jsonb_build_object('rule', c, 'value', NULL, 'result', true,
|
|
44227
|
+
'message', 'Bij Floriday-orders wordt de Floriday-koper gebruikt in plaats van de BVO-klant.'));
|
|
44228
|
+
END IF;
|
|
44229
|
+
val := public.logistics_buffer_value(p_order, eff);
|
|
44230
|
+
IF val IS NULL THEN
|
|
44231
|
+
v_missing := true; v_missing_list := v_missing_list || eff;
|
|
44232
|
+
v_parts := v_parts || ('missing=' || eff || ':' || v_ref);
|
|
44233
|
+
v_trace := v_trace || jsonb_build_array(jsonb_build_object('rule', eff, 'value', NULL, 'result', false,
|
|
44234
|
+
'message', 'Waarde ontbreekt; deze order wordt niet gecombineerd.'));
|
|
44235
|
+
ELSE
|
|
44236
|
+
v_parts := v_parts || (eff || '=' || lower(val));
|
|
44237
|
+
v_trace := v_trace || jsonb_build_array(jsonb_build_object('rule', eff, 'value', val, 'result', true,
|
|
44238
|
+
'message', 'Criterium meegenomen in de buffersleutel.'));
|
|
44239
|
+
END IF;
|
|
44240
|
+
END LOOP;
|
|
44241
|
+
END IF;
|
|
44242
|
+
|
|
44243
|
+
IF coalesce(p_order ->> 'transportGroup', 'extern') IN ('eigen_vervoer', 'klant_haalt_op') THEN
|
|
44244
|
+
v_parts := v_parts || ('vervoer=' || (p_order ->> 'transportGroup'));
|
|
44245
|
+
v_trace := v_trace || jsonb_build_array(jsonb_build_object('rule', 'transport_group', 'value', p_order ->> 'transportGroup', 'result', true,
|
|
44246
|
+
'message', CASE p_order ->> 'transportGroup' WHEN 'eigen_vervoer' THEN 'Eigen vervoer wordt alleen samen gebufferd met andere orders met eigen vervoer.'
|
|
44247
|
+
ELSE 'Klant haalt op wordt alleen samen gebufferd met andere orders die de klant ophaalt.' END));
|
|
44248
|
+
END IF;
|
|
44249
|
+
v_parts := ARRAY(SELECT DISTINCT ON (p) p FROM unnest(v_parts) WITH ORDINALITY AS t(p, n) ORDER BY p, n);
|
|
44250
|
+
v_key := array_to_string(v_parts, '|');
|
|
44251
|
+
|
|
44252
|
+
RETURN jsonb_build_object(
|
|
44253
|
+
'buffer', true,
|
|
44254
|
+
'reason', CASE WHEN v_missing THEN 'buffering_enabled_missing_data' ELSE 'buffering_enabled' END,
|
|
44255
|
+
'bufferRef', md5(COALESCE(v_user::text, 'anon') || '|' || v_key),
|
|
44256
|
+
'bufferKey', v_key, 'parts', to_jsonb(v_parts), 'complete', NOT v_missing,
|
|
44257
|
+
'missingCriteria', to_jsonb(v_missing_list), 'isReservation', v_is_res, 'isFloriday', v_is_fd,
|
|
44258
|
+
'skippedForFloriday', CASE WHEN v_is_fd THEN to_jsonb(v_skip) ELSE '[]'::jsonb END,
|
|
44259
|
+
'matchCriteria', to_jsonb(v_criteria), 'closeBeforeMinutes', v_minutes, 'closeReference', v_reference,
|
|
44260
|
+
'bufferCloseAt', v_close, 'release', v_release, 'trace', v_trace);
|
|
44261
|
+
END;
|
|
44262
|
+
$function$;
|
|
44263
|
+
|
|
44264
|
+
SELECT public.logistics_pickup_moments_koppel();
|
|
44265
|
+
|
|
44266
|
+
-- ===== 2026-11-10c: rechten hulponderdelen ophaalmoment =====
|
|
44267
|
+
REVOKE EXECUTE ON FUNCTION public.logistics_pickup_regel() FROM authenticated;
|
|
44268
|
+
REVOKE EXECUTE ON FUNCTION public.logistics_pickup_moment_kies(uuid, date, timestamptz, text, numeric) FROM authenticated;
|
|
44269
|
+
|
|
44270
|
+
-- ===== 2026-11-10d: karindeling snel en betrouwbaar bij productievolume =====
|
|
44271
|
+
-- Karindeling snel en betrouwbaar bij productievolume (schema 2026-11-10d)
|
|
44272
|
+
CREATE OR REPLACE FUNCTION public.logistics_product_maten(p_product_ids uuid[])
|
|
44273
|
+
RETURNS TABLE(product_id uuid, product_name text, plants_per_crate integer, fust_type_id uuid, fust_name text, fust_bron text,
|
|
44274
|
+
fust_length_mm numeric, fust_width_mm numeric, fust_height_mm numeric,
|
|
44275
|
+
lengte_bron text, breedte_bron text, hoogte_bron text,
|
|
44276
|
+
plant_height_cm numeric, height_source text, weight_kg numeric,
|
|
44277
|
+
pad_fust text, pad_artikel text, missing_reason text)
|
|
44278
|
+
LANGUAGE plpgsql STABLE SECURITY DEFINER SET search_path TO 'public' AS $$
|
|
44279
|
+
#variable_conflict use_column
|
|
44280
|
+
BEGIN
|
|
44281
|
+
PERFORM public.logistics_acc_guard('ingelogd', 'logistics_product_maten');
|
|
44282
|
+
IF p_product_ids IS NOT NULL AND cardinality(p_product_ids) > 2000 THEN
|
|
44283
|
+
RAISE EXCEPTION 'Maximaal 2000 producten per aanvraag.' USING ERRCODE = '22023';
|
|
44284
|
+
END IF;
|
|
44285
|
+
RETURN QUERY
|
|
44286
|
+
WITH fm AS MATERIALIZED (SELECT * FROM public.logistics_fust_maten()),
|
|
44287
|
+
pr AS MATERIALIZED (
|
|
44288
|
+
SELECT p.id, p.product, p.plants_per_crate, p.fust_type_id AS hand_fid, p.plant_height_cm AS hand_h, p.weight,
|
|
44289
|
+
public.logistics_artikel_vbn(p.id) AS a
|
|
44290
|
+
FROM public.products p
|
|
44291
|
+
WHERE p_product_ids IS NULL OR p.id = ANY(p_product_ids)
|
|
44292
|
+
),
|
|
44293
|
+
keuze AS (
|
|
44294
|
+
SELECT pr.*,
|
|
44295
|
+
(SELECT m.fust_type_id FROM fm m
|
|
44296
|
+
WHERE (pr.a->>'vbn_package_code') ~ '^[0-9]+$'
|
|
44297
|
+
AND (m.vbn_code = (pr.a->>'vbn_package_code')::int OR m.fust_key = 'vbn:' || (pr.a->>'vbn_package_code'))
|
|
44298
|
+
ORDER BY ((m.length_mm IS NOT NULL)::int + (m.width_mm IS NOT NULL)::int + (m.height_mm IS NOT NULL)::int) DESC, m.fust_type_id
|
|
44299
|
+
LIMIT 1) AS vbn_fid,
|
|
44300
|
+
(SELECT f.id FROM public.fust_types f
|
|
44301
|
+
WHERE nullif(pr.a->>'custom_package_id', '') IS NOT NULL AND f.custom_package_id = pr.a->>'custom_package_id'
|
|
44302
|
+
ORDER BY f.created_at, f.id LIMIT 1) AS cp_fid
|
|
44303
|
+
FROM pr
|
|
44304
|
+
),
|
|
44305
|
+
k2 AS (
|
|
44306
|
+
SELECT k.*, coalesce(k.hand_fid, k.vbn_fid, k.cp_fid) AS fid,
|
|
44307
|
+
CASE WHEN k.hand_fid IS NOT NULL THEN 'handmatig' WHEN k.vbn_fid IS NOT NULL THEN 'vbn' WHEN k.cp_fid IS NOT NULL THEN 'floriday' END AS fbron
|
|
44308
|
+
FROM keuze k
|
|
44309
|
+
),
|
|
44310
|
+
maat AS (
|
|
44311
|
+
SELECT k.*, m.name AS mname, m.pad AS mpad,
|
|
44312
|
+
coalesce(m.length_mm, lx.length_mm) AS l, CASE WHEN m.length_mm IS NOT NULL THEN m.lengte_bron ELSE coalesce(lx.lengte_bron, 'onbekend') END AS lb,
|
|
44313
|
+
coalesce(m.width_mm, wx.width_mm) AS w, CASE WHEN m.width_mm IS NOT NULL THEN m.breedte_bron ELSE coalesce(wx.breedte_bron, 'onbekend') END AS wb,
|
|
44314
|
+
coalesce(m.height_mm, hx.height_mm) AS h, CASE WHEN m.height_mm IS NOT NULL THEN m.hoogte_bron ELSE coalesce(hx.hoogte_bron, 'onbekend') END AS hb
|
|
44315
|
+
FROM k2 k
|
|
44316
|
+
LEFT JOIN fm m ON m.fust_type_id = k.fid
|
|
44317
|
+
LEFT JOIN LATERAL (SELECT m2.length_mm, m2.lengte_bron FROM fm m2 WHERE m.fust_key IS NOT NULL AND m2.fust_key = m.fust_key AND m2.length_mm IS NOT NULL ORDER BY m2.fust_type_id LIMIT 1) lx ON true
|
|
44318
|
+
LEFT JOIN LATERAL (SELECT m2.width_mm, m2.breedte_bron FROM fm m2 WHERE m.fust_key IS NOT NULL AND m2.fust_key = m.fust_key AND m2.width_mm IS NOT NULL ORDER BY m2.fust_type_id LIMIT 1) wx ON true
|
|
44319
|
+
LEFT JOIN LATERAL (SELECT m2.height_mm, m2.hoogte_bron FROM fm m2 WHERE m.fust_key IS NOT NULL AND m2.fust_key = m.fust_key AND m2.height_mm IS NOT NULL ORDER BY m2.fust_type_id LIMIT 1) hx ON true
|
|
44320
|
+
),
|
|
44321
|
+
hoogte AS (
|
|
44322
|
+
SELECT x.*,
|
|
44323
|
+
public.logistics_pos_num(x.hand_h::text) AS hh,
|
|
44324
|
+
public.logistics_pos_num(x.a->>'s15_cm') AS s15,
|
|
44325
|
+
public.logistics_pos_num(x.a->>'s02_cm') AS s02,
|
|
44326
|
+
(SELECT b.bron FROM public.logistics_gegevens_bron b
|
|
44327
|
+
WHERE b.onderwerp = 'product' AND b.sleutel = x.id::text AND b.veld = 'plant_height_cm'
|
|
44328
|
+
AND public.logistics_pos_num(b.waarde) = public.logistics_pos_num(x.hand_h::text) LIMIT 1) AS hand_bron
|
|
44329
|
+
FROM maat x
|
|
44330
|
+
)
|
|
44331
|
+
SELECT h.id, h.product, h.plants_per_crate::int, h.fid, h.mname, h.fbron,
|
|
44332
|
+
h.l, h.w, h.h, h.lb, h.wb, h.hb,
|
|
44333
|
+
coalesce(h.hh, h.s15, h.s02),
|
|
44334
|
+
CASE WHEN h.hh IS NOT NULL THEN coalesce(h.hand_bron, 'handmatig')
|
|
44335
|
+
WHEN h.s15 IS NOT NULL THEN 'floricode_s15'
|
|
44336
|
+
WHEN h.s02 IS NOT NULL THEN 'floricode_s02' END,
|
|
44337
|
+
public.logistics_pos_num(regexp_replace(coalesce(h.weight::text, ''), '[^0-9.,]', '', 'g')),
|
|
44338
|
+
coalesce(h.mpad, (SELECT fm.pad FROM fm LIMIT 1)), h.a->>'pad',
|
|
44339
|
+
CASE
|
|
44340
|
+
WHEN h.fid IS NULL AND h.a->>'vbn_package_code' IS NULL AND h.a->>'custom_package_id' IS NULL
|
|
44341
|
+
THEN format('Bij "%s" is geen fustsoort bekend: kies de fustsoort bij het product in Voorraad.', h.product)
|
|
44342
|
+
WHEN h.fid IS NULL
|
|
44343
|
+
THEN format('Bij "%s" bestaat de fustsoort (VBN %s) niet in Voorraad → Fusten.', h.product, coalesce(h.a->>'vbn_package_code', h.a->>'custom_package_id'))
|
|
44344
|
+
WHEN h.l IS NULL OR h.w IS NULL OR h.h IS NULL
|
|
44345
|
+
THEN format('Fust %s heeft geen volledige maten in Floricode, Floriday of Voorraad → Fusten (ontbreekt: %s).', coalesce(h.mname, 'onbekend'),
|
|
44346
|
+
array_to_string(array_remove(ARRAY[CASE WHEN h.l IS NULL THEN 'lengte' END, CASE WHEN h.w IS NULL THEN 'breedte' END, CASE WHEN h.h IS NULL THEN 'hoogte' END], NULL), ', '))
|
|
44347
|
+
WHEN coalesce(h.hh, h.s15, h.s02) IS NULL
|
|
44348
|
+
THEN format('Bij "%s" is de planthoogte inclusief pot onbekend (geen S15/S02 in Floricode of Floriday, niet handmatig ingevuld).', h.product)
|
|
44349
|
+
END
|
|
44350
|
+
FROM hoogte h;
|
|
44351
|
+
END $$;
|
|
44352
|
+
GRANT EXECUTE ON FUNCTION public.logistics_product_maten(uuid[]) TO authenticated, service_role;
|
|
44353
|
+
|
|
44354
|
+
-- ===== Karindeling: tijdelijke fouten opnieuw proberen =====
|
|
44355
|
+
CREATE OR REPLACE FUNCTION public.logistics_plan_is_tijdelijk(p_text text)
|
|
44356
|
+
RETURNS boolean LANGUAGE sql IMMUTABLE SET search_path TO 'public'
|
|
44357
|
+
AS $function$
|
|
44358
|
+
SELECT coalesce(p_text, '') ~* '(statement timeout|canceling statement|niet afgesloten|claim niet afgerond|lock timeout|could not obtain lock|deadlock detected)'
|
|
44359
|
+
$function$;
|
|
44360
|
+
REVOKE EXECUTE ON FUNCTION public.logistics_plan_is_tijdelijk(text) FROM PUBLIC, anon;
|
|
44361
|
+
GRANT EXECUTE ON FUNCTION public.logistics_plan_is_tijdelijk(text) TO authenticated, service_role;
|
|
44362
|
+
|
|
44363
|
+
CREATE OR REPLACE FUNCTION public.logistics_plan_tijdelijk_wacht(p_attempts integer)
|
|
44364
|
+
RETURNS interval LANGUAGE sql IMMUTABLE SET search_path TO 'public'
|
|
44365
|
+
AS $function$
|
|
44366
|
+
SELECT make_interval(mins => (2 ^ (LEAST(5, GREATEST(1, coalesce(p_attempts, 1))) - 1))::int)
|
|
44367
|
+
$function$;
|
|
44368
|
+
REVOKE EXECUTE ON FUNCTION public.logistics_plan_tijdelijk_wacht(integer) FROM PUBLIC, anon;
|
|
44369
|
+
GRANT EXECUTE ON FUNCTION public.logistics_plan_tijdelijk_wacht(integer) TO authenticated, service_role;
|
|
44370
|
+
|
|
44371
|
+
CREATE OR REPLACE FUNCTION public.logistics_plan_tijdelijk_opnieuw(p_order_ids uuid[], p_reason text)
|
|
44372
|
+
RETURNS uuid[] LANGUAGE plpgsql SECURITY DEFINER SET search_path TO 'public'
|
|
44373
|
+
AS $function$
|
|
44374
|
+
-- Zet wachtrijregels met een tijdelijke fout terug met oplopende wachttijd.
|
|
44375
|
+
-- Geeft de orders terug die al het maximum aantal pogingen hadden (die worden echt geblokkeerd).
|
|
44376
|
+
DECLARE
|
|
44377
|
+
v_max int := GREATEST(1, public.logistics_plan_setting('max_attempts', 5)::int);
|
|
44378
|
+
v_op uuid[];
|
|
44379
|
+
r record;
|
|
44380
|
+
BEGIN
|
|
44381
|
+
PERFORM public.logistics_acc_guard('ingelogd', 'logistics_plan_tijdelijk_opnieuw');
|
|
44382
|
+
FOR r IN
|
|
44383
|
+
UPDATE public.logistics_plan_queue q
|
|
44384
|
+
SET status = 'pending', reason_code = 'tijdelijk', error = left(p_reason, 500),
|
|
44385
|
+
claimed_at = NULL, next_attempt_at = now() + public.logistics_plan_tijdelijk_wacht(q.attempts),
|
|
44386
|
+
updated_at = now()
|
|
44387
|
+
WHERE q.order_id = ANY(p_order_ids) AND q.status IN ('pending', 'running') AND q.attempts < v_max
|
|
44388
|
+
RETURNING q.order_id, q.attempts, q.next_attempt_at
|
|
44389
|
+
LOOP
|
|
44390
|
+
BEGIN
|
|
44391
|
+
PERFORM public.logistics_trace_order_step(
|
|
44392
|
+
r.order_id, 'stap:beladingindeling', 'karrenindeling opnieuw proberen', 'wachtend',
|
|
44393
|
+
'tijdelijke fout, poging ' || r.attempts || ' van ' || v_max,
|
|
44394
|
+
'De indeling liep tegen een tijdslimiet aan. Bestaande karren blijven staan. Volgende poging om '
|
|
44395
|
+
|| to_char(r.next_attempt_at AT TIME ZONE public.logistics_tz(), 'HH24:MI') || ' uur.',
|
|
44396
|
+
'Karrenindeling', left(p_reason, 500), 'tijdelijk');
|
|
44397
|
+
EXCEPTION WHEN OTHERS THEN
|
|
44398
|
+
RAISE WARNING 'logistiek-engine: verloop niet vastgelegd voor order %: %', r.order_id, SQLERRM;
|
|
44399
|
+
END;
|
|
44400
|
+
END LOOP;
|
|
44401
|
+
SELECT array_agg(q.order_id) INTO v_op FROM public.logistics_plan_queue q
|
|
44402
|
+
WHERE q.order_id = ANY(p_order_ids) AND q.status IN ('pending', 'running') AND q.attempts >= v_max;
|
|
44403
|
+
RETURN coalesce(v_op, ARRAY[]::uuid[]);
|
|
44404
|
+
END $function$;
|
|
44405
|
+
REVOKE EXECUTE ON FUNCTION public.logistics_plan_tijdelijk_opnieuw(uuid[], text) FROM PUBLIC, anon, authenticated;
|
|
44406
|
+
GRANT EXECUTE ON FUNCTION public.logistics_plan_tijdelijk_opnieuw(uuid[], text) TO service_role;
|
|
44407
|
+
|
|
44408
|
+
CREATE OR REPLACE FUNCTION public.logistics_plan_block(p_order_ids uuid[], p_reason text, p_details jsonb DEFAULT '{}'::jsonb)
|
|
44409
|
+
RETURNS integer
|
|
44410
|
+
LANGUAGE plpgsql
|
|
44411
|
+
SECURITY DEFINER
|
|
44412
|
+
SET search_path TO 'public'
|
|
44413
|
+
AS $function$
|
|
44414
|
+
DECLARE
|
|
44415
|
+
v_user uuid;
|
|
44416
|
+
o record;
|
|
44417
|
+
v_count integer := 0;
|
|
44418
|
+
v_code text;
|
|
44419
|
+
v_details jsonb := COALESCE(p_details, '{}'::jsonb);
|
|
44420
|
+
v_reason text := left(COALESCE(p_reason, 'belading onbekend'), 500);
|
|
44421
|
+
v_buffer text;
|
|
44422
|
+
v_subject_type text;
|
|
44423
|
+
v_subject_id text;
|
|
44424
|
+
v_result jsonb;
|
|
44425
|
+
v_last jsonb;
|
|
44426
|
+
v_members text[];
|
|
44427
|
+
BEGIN
|
|
44428
|
+
PERFORM public.logistics_acc_guard('ingelogd', 'logistics_plan_block');
|
|
44429
|
+
IF p_order_ids IS NULL OR array_length(p_order_ids, 1) IS NULL THEN
|
|
44430
|
+
RETURN 0;
|
|
44431
|
+
END IF;
|
|
44432
|
+
|
|
44433
|
+
-- Tijdelijke fout (time-out): eerst opnieuw proberen, pas na het maximum blokkeren.
|
|
44434
|
+
IF public.logistics_plan_is_tijdelijk(p_reason) THEN
|
|
44435
|
+
v_count := cardinality(p_order_ids);
|
|
44436
|
+
p_order_ids := public.logistics_plan_tijdelijk_opnieuw(p_order_ids, v_reason);
|
|
44437
|
+
IF cardinality(p_order_ids) = 0 THEN
|
|
44438
|
+
RETURN v_count;
|
|
44439
|
+
END IF;
|
|
44440
|
+
v_reason := left(v_reason || ' (na ' || public.logistics_plan_setting('max_attempts', 5)::int || ' pogingen)', 500);
|
|
44441
|
+
v_count := 0;
|
|
44442
|
+
END IF;
|
|
44443
|
+
|
|
44444
|
+
v_user := COALESCE(
|
|
44445
|
+
auth.uid(),
|
|
44446
|
+
(SELECT user_id FROM public.logistics_strategies WHERE is_active ORDER BY updated_at DESC LIMIT 1),
|
|
44447
|
+
(SELECT user_id FROM public.logistics_strategies ORDER BY updated_at DESC LIMIT 1)
|
|
44448
|
+
);
|
|
44449
|
+
|
|
44450
|
+
v_code := public.logistics_block_reason_code(p_reason, v_details);
|
|
44451
|
+
v_buffer := NULLIF(v_details ->> 'bufferId', '');
|
|
44452
|
+
|
|
44453
|
+
UPDATE public.logistics_plan_queue
|
|
44454
|
+
SET status = 'geblokkeerd',
|
|
44455
|
+
reason = v_reason,
|
|
44456
|
+
reason_code = v_code,
|
|
44457
|
+
error = v_reason,
|
|
44458
|
+
claimed_at = NULL,
|
|
44459
|
+
updated_at = now()
|
|
44460
|
+
WHERE order_id = ANY(p_order_ids)
|
|
44461
|
+
AND status IN ('pending', 'running');
|
|
44462
|
+
|
|
44463
|
+
FOR o IN
|
|
44464
|
+
SELECT id, order_number FROM public.orders
|
|
44465
|
+
WHERE id = ANY(p_order_ids) AND deleted_at IS NULL
|
|
44466
|
+
LOOP
|
|
44467
|
+
v_count := v_count + 1;
|
|
44468
|
+
|
|
44469
|
+
IF v_user IS NOT NULL AND o.order_number IS NOT NULL THEN
|
|
44470
|
+
INSERT INTO public.logistics_events (
|
|
44471
|
+
user_id, source_module, event_type, order_ref, payload, shadow,
|
|
44472
|
+
status, processed_at, idempotency_key
|
|
44473
|
+
) VALUES (
|
|
44474
|
+
v_user, 'logistiek-engine', 'plan.blocked', o.order_number,
|
|
44475
|
+
jsonb_build_object('reason', p_reason, 'reasonCode', v_code, 'orderId', o.id) || v_details,
|
|
44476
|
+
false, 'processed', now(),
|
|
44477
|
+
'plan.blocked|' || o.id::text || '|' || v_code
|
|
44478
|
+
)
|
|
44479
|
+
ON CONFLICT DO NOTHING;
|
|
44480
|
+
END IF;
|
|
44481
|
+
END LOOP;
|
|
44482
|
+
|
|
44483
|
+
IF v_user IS NOT NULL THEN
|
|
44484
|
+
SELECT array_agg(COALESCE(ord.order_number, ord.id::text) ORDER BY COALESCE(ord.order_number, ord.id::text))
|
|
44485
|
+
INTO v_members
|
|
44486
|
+
FROM public.orders ord
|
|
44487
|
+
WHERE ord.id = ANY(p_order_ids) AND ord.deleted_at IS NULL;
|
|
44488
|
+
|
|
44489
|
+
IF v_buffer IS NOT NULL THEN
|
|
44490
|
+
v_subject_type := 'buffer';
|
|
44491
|
+
v_subject_id := v_buffer;
|
|
44492
|
+
ELSE
|
|
44493
|
+
v_subject_type := 'order';
|
|
44494
|
+
v_subject_id := p_order_ids[1]::text;
|
|
44495
|
+
END IF;
|
|
44496
|
+
|
|
44497
|
+
v_result := jsonb_build_object(
|
|
44498
|
+
'blocked', true,
|
|
44499
|
+
'reason_code', v_code,
|
|
44500
|
+
'path', COALESCE(v_details ->> 'path', 'onbekend')
|
|
44501
|
+
);
|
|
44502
|
+
|
|
44503
|
+
SELECT d.result INTO v_last
|
|
44504
|
+
FROM public.logistics_decisions d
|
|
44505
|
+
WHERE d.user_id = v_user
|
|
44506
|
+
AND d.subject_type = v_subject_type
|
|
44507
|
+
AND d.subject_id = v_subject_id
|
|
44508
|
+
AND d.slot = 'beladingindeling'
|
|
44509
|
+
ORDER BY d.created_at DESC
|
|
44510
|
+
LIMIT 1;
|
|
44511
|
+
|
|
44512
|
+
IF v_last IS DISTINCT FROM v_result THEN
|
|
44513
|
+
INSERT INTO public.logistics_decisions (
|
|
44514
|
+
user_id, subject_type, subject_id, slot, result, explanation, input_snapshot
|
|
44515
|
+
) VALUES (
|
|
44516
|
+
v_user, v_subject_type, v_subject_id, 'beladingindeling',
|
|
44517
|
+
v_result,
|
|
44518
|
+
left(COALESCE(p_reason, 'Belading onbekend'), 2000),
|
|
44519
|
+
v_details || jsonb_build_object(
|
|
44520
|
+
'members', to_jsonb(COALESCE(v_members, ARRAY[]::text[])),
|
|
44521
|
+
'memberCount', COALESCE(array_length(v_members, 1), 0)
|
|
44522
|
+
)
|
|
44523
|
+
);
|
|
44524
|
+
END IF;
|
|
44525
|
+
END IF;
|
|
44526
|
+
|
|
44527
|
+
RETURN v_count;
|
|
44528
|
+
END;
|
|
44529
|
+
$function$;
|
|
44530
|
+
|
|
44531
|
+
CREATE OR REPLACE FUNCTION public.logistics_plan_close_claim(p_id uuid, p_reason text, p_reason_code text DEFAULT NULL::text)
|
|
44532
|
+
RETURNS boolean
|
|
44533
|
+
LANGUAGE plpgsql
|
|
44534
|
+
SECURITY DEFINER
|
|
44535
|
+
SET search_path TO 'public'
|
|
44536
|
+
AS $function$
|
|
44537
|
+
DECLARE v_n int; v_order uuid;
|
|
44538
|
+
BEGIN
|
|
44539
|
+
PERFORM public.logistics_acc_guard('server', 'logistics_plan_close_claim');
|
|
44540
|
+
IF public.logistics_plan_is_tijdelijk(p_reason) THEN
|
|
44541
|
+
SELECT q.order_id INTO v_order FROM public.logistics_plan_queue q WHERE q.id = p_id AND q.status = 'running';
|
|
44542
|
+
IF v_order IS NOT NULL AND cardinality(public.logistics_plan_tijdelijk_opnieuw(ARRAY[v_order], p_reason)) = 0 THEN
|
|
44543
|
+
RETURN true;
|
|
44544
|
+
END IF;
|
|
44545
|
+
END IF;
|
|
44546
|
+
UPDATE public.logistics_plan_queue q
|
|
44547
|
+
SET status='geblokkeerd', reason=COALESCE(p_reason, q.reason), error=p_reason,
|
|
44548
|
+
reason_code=COALESCE(p_reason_code, public.logistics_block_reason_code(p_reason, '{}'::jsonb)),
|
|
44549
|
+
claimed_at=NULL, updated_at=now()
|
|
44550
|
+
WHERE q.id = p_id AND q.status = 'running';
|
|
44551
|
+
GET DIAGNOSTICS v_n = ROW_COUNT;
|
|
44552
|
+
RETURN v_n > 0;
|
|
44553
|
+
END $function$;
|
|
44554
|
+
|
|
44555
|
+
CREATE OR REPLACE FUNCTION public.logistics_plan_release_stale()
|
|
44556
|
+
RETURNS integer
|
|
44557
|
+
LANGUAGE plpgsql
|
|
44558
|
+
SECURITY DEFINER
|
|
44559
|
+
SET search_path TO 'public'
|
|
44560
|
+
AS $function$
|
|
44561
|
+
DECLARE
|
|
44562
|
+
v_n int := 0;
|
|
44563
|
+
v_min int := public.logistics_plan_setting('claim_timeout_minutes', 2)::int;
|
|
44564
|
+
v_max int := GREATEST(1, public.logistics_plan_setting('max_attempts', 5)::int);
|
|
44565
|
+
v_txt text;
|
|
44566
|
+
r record;
|
|
44567
|
+
BEGIN
|
|
44568
|
+
PERFORM public.logistics_acc_guard('server', 'logistics_plan_release_stale');
|
|
44569
|
+
v_txt := 'de ronde heeft deze indeling na ' || v_min || ' minuten niet afgesloten (claim niet afgerond)';
|
|
44570
|
+
FOR r IN
|
|
44571
|
+
UPDATE public.logistics_plan_queue q
|
|
44572
|
+
SET status = CASE WHEN q.attempts >= v_max THEN 'geblokkeerd' ELSE 'pending' END,
|
|
44573
|
+
error = v_txt,
|
|
44574
|
+
reason = CASE WHEN q.attempts >= v_max
|
|
44575
|
+
THEN v_txt || '; ' || q.attempts || ' keer achter elkaar, daarom geblokkeerd' ELSE q.reason END,
|
|
44576
|
+
reason_code = 'claim_niet_afgerond',
|
|
44577
|
+
claimed_at = NULL,
|
|
44578
|
+
next_attempt_at = now() + public.logistics_plan_tijdelijk_wacht(q.attempts),
|
|
44579
|
+
updated_at = now()
|
|
44580
|
+
WHERE q.status = 'running'
|
|
44581
|
+
AND q.claimed_at < now() - make_interval(mins => v_min)
|
|
44582
|
+
RETURNING q.order_id, q.attempts, q.status
|
|
44583
|
+
LOOP
|
|
44584
|
+
v_n := v_n + 1;
|
|
44585
|
+
BEGIN
|
|
44586
|
+
PERFORM public.logistics_trace_order_step(
|
|
44587
|
+
r.order_id, 'stap:beladingindeling', 'claim niet afgerond',
|
|
44588
|
+
CASE WHEN r.status = 'geblokkeerd' THEN 'mislukt' ELSE 'wacht' END,
|
|
44589
|
+
'ronde sloot de indeling niet af',
|
|
44590
|
+
CASE WHEN r.status = 'geblokkeerd'
|
|
44591
|
+
THEN 'De ronde sloot de indeling ' || r.attempts || ' keer niet af. De order is geblokkeerd zodat dit niet blijft herhalen; bekijk de reden en bied hem opnieuw aan.'
|
|
44592
|
+
ELSE 'De ronde sloot de indeling niet af binnen ' || v_min || ' minuten. Er volgt automatisch een nieuwe poging (' || r.attempts || ' van ' || v_max || ').'
|
|
44593
|
+
END,
|
|
44594
|
+
'Karrenindeling', v_txt, 'regel');
|
|
44595
|
+
EXCEPTION WHEN OTHERS THEN
|
|
44596
|
+
RAISE WARNING 'logistiek-engine: verloop niet vastgelegd voor order %: %', r.order_id, SQLERRM;
|
|
44597
|
+
END;
|
|
44598
|
+
END LOOP;
|
|
44599
|
+
RETURN v_n;
|
|
44600
|
+
END $function$;
|
|
44601
|
+
|
|
44602
|
+
-- ===== Gegevensblokkades automatisch opnieuw =====
|
|
44603
|
+
CREATE OR REPLACE FUNCTION public.logistics_plan_gegevens_codes()
|
|
44604
|
+
RETURNS text[] LANGUAGE sql IMMUTABLE SET search_path TO 'public'
|
|
44605
|
+
AS $function$
|
|
44606
|
+
SELECT ARRAY['belading_onbekend','belading_onvolledig','beladingsprofiel_ontbreekt','afmetingen_onbekend',
|
|
44607
|
+
'afmetingen_plan_ongeldig','ladingdrager_onbekend','artikel_ontbreekt','belading_onwaarschijnlijk',
|
|
44608
|
+
'karindeling_geblokkeerd']
|
|
44609
|
+
$function$;
|
|
44610
|
+
REVOKE EXECUTE ON FUNCTION public.logistics_plan_gegevens_codes() FROM PUBLIC, anon;
|
|
44611
|
+
GRANT EXECUTE ON FUNCTION public.logistics_plan_gegevens_codes() TO authenticated, service_role;
|
|
44612
|
+
|
|
44613
|
+
CREATE OR REPLACE FUNCTION public.logistics_plan_gegevens_opnieuw(p_order_ids uuid[], p_reden text)
|
|
44614
|
+
RETURNS integer LANGUAGE plpgsql SECURITY DEFINER SET search_path TO 'public'
|
|
44615
|
+
AS $function$
|
|
44616
|
+
-- Biedt alleen de geblokkeerde orders met een gegevensblokkade opnieuw aan. Per order een eigen poging.
|
|
44617
|
+
DECLARE v_id uuid; v_n int := 0;
|
|
44618
|
+
BEGIN
|
|
44619
|
+
PERFORM public.logistics_acc_guard('ingelogd', 'logistics_plan_gegevens_opnieuw');
|
|
44620
|
+
FOR v_id IN
|
|
44621
|
+
SELECT DISTINCT q.order_id FROM public.logistics_plan_queue q
|
|
44622
|
+
WHERE q.order_id = ANY(p_order_ids) AND q.status = 'geblokkeerd'
|
|
44623
|
+
AND q.reason_code = ANY(public.logistics_plan_gegevens_codes())
|
|
44624
|
+
LOOP
|
|
44625
|
+
BEGIN
|
|
44626
|
+
PERFORM public.logistics_plan_enqueue(v_id, p_reden);
|
|
44627
|
+
v_n := v_n + 1;
|
|
44628
|
+
EXCEPTION WHEN OTHERS THEN
|
|
44629
|
+
PERFORM public.logistics_plan_fail_log('logistics_plan_gegevens_opnieuw', v_id, SQLSTATE, SQLERRM);
|
|
44630
|
+
END;
|
|
44631
|
+
END LOOP;
|
|
44632
|
+
RETURN v_n;
|
|
44633
|
+
END $function$;
|
|
44634
|
+
REVOKE EXECUTE ON FUNCTION public.logistics_plan_gegevens_opnieuw(uuid[], text) FROM PUBLIC, anon, authenticated;
|
|
44635
|
+
GRANT EXECUTE ON FUNCTION public.logistics_plan_gegevens_opnieuw(uuid[], text) TO service_role;
|
|
44636
|
+
|
|
44637
|
+
CREATE OR REPLACE FUNCTION public.logistics_fust_maten_gewijzigd_trg()
|
|
44638
|
+
RETURNS trigger LANGUAGE plpgsql SECURITY DEFINER SET search_path TO 'public'
|
|
44639
|
+
AS $function$
|
|
44640
|
+
DECLARE v_orders uuid[];
|
|
44641
|
+
BEGIN
|
|
44642
|
+
BEGIN
|
|
44643
|
+
IF NEW.length_mm IS NOT DISTINCT FROM OLD.length_mm AND NEW.width_mm IS NOT DISTINCT FROM OLD.width_mm
|
|
44644
|
+
AND NEW.height_mm IS NOT DISTINCT FROM OLD.height_mm AND NEW.vbn_code IS NOT DISTINCT FROM OLD.vbn_code
|
|
44645
|
+
AND NEW.custom_package_id IS NOT DISTINCT FROM OLD.custom_package_id AND NEW.fust_key IS NOT DISTINCT FROM OLD.fust_key THEN
|
|
44646
|
+
RETURN NEW;
|
|
44647
|
+
END IF;
|
|
44648
|
+
WITH geblokkeerd AS MATERIALIZED (
|
|
44649
|
+
SELECT DISTINCT q.order_id FROM public.logistics_plan_queue q
|
|
44650
|
+
WHERE q.status = 'geblokkeerd' AND q.reason_code = ANY(public.logistics_plan_gegevens_codes())
|
|
44651
|
+
), prod AS MATERIALIZED (
|
|
44652
|
+
SELECT DISTINCT oi.order_id, oi.product_id FROM public.order_items oi JOIN geblokkeerd g ON g.order_id = oi.order_id
|
|
44653
|
+
WHERE oi.product_id IS NOT NULL
|
|
44654
|
+
), maten AS MATERIALIZED (
|
|
44655
|
+
SELECT m.product_id, m.fust_type_id FROM public.logistics_product_maten((SELECT array_agg(DISTINCT product_id) FROM prod)) m
|
|
44656
|
+
WHERE EXISTS (SELECT 1 FROM prod)
|
|
44657
|
+
)
|
|
44658
|
+
SELECT array_agg(DISTINCT p.order_id) INTO v_orders
|
|
44659
|
+
FROM prod p LEFT JOIN maten m ON m.product_id = p.product_id
|
|
44660
|
+
LEFT JOIN public.products pr ON pr.id = p.product_id
|
|
44661
|
+
WHERE m.fust_type_id = NEW.id OR pr.fust_type_id = NEW.id;
|
|
44662
|
+
IF v_orders IS NOT NULL THEN
|
|
44663
|
+
PERFORM public.logistics_plan_gegevens_opnieuw(v_orders, 'fustmaten aangepast');
|
|
44664
|
+
END IF;
|
|
44665
|
+
EXCEPTION WHEN OTHERS THEN
|
|
44666
|
+
RAISE WARNING 'logistiek-engine: opnieuw aanbieden na fustwijziging mislukt: %', SQLERRM;
|
|
44667
|
+
BEGIN
|
|
44668
|
+
PERFORM public.logistics_plan_fail_log('trg_logistics_fust_maten_gewijzigd', NULL, SQLSTATE, SQLERRM);
|
|
44669
|
+
EXCEPTION WHEN OTHERS THEN
|
|
44670
|
+
RAISE WARNING 'logistiek-engine: fout niet vastgelegd: %', SQLERRM;
|
|
44671
|
+
END;
|
|
44672
|
+
END;
|
|
44673
|
+
RETURN NEW;
|
|
44674
|
+
END $function$;
|
|
44675
|
+
REVOKE EXECUTE ON FUNCTION public.logistics_fust_maten_gewijzigd_trg() FROM PUBLIC, anon, authenticated;
|
|
44676
|
+
DO $fs$ BEGIN
|
|
44677
|
+
IF to_regclass('public.fust_types') IS NOT NULL THEN
|
|
44678
|
+
EXECUTE 'DROP TRIGGER IF EXISTS trg_logistics_fust_maten_gewijzigd ON public.fust_types';
|
|
44679
|
+
EXECUTE 'CREATE TRIGGER trg_logistics_fust_maten_gewijzigd AFTER UPDATE OF length_mm, width_mm, height_mm, vbn_code, custom_package_id, fust_key ON public.fust_types FOR EACH ROW EXECUTE FUNCTION public.logistics_fust_maten_gewijzigd_trg()';
|
|
44680
|
+
ELSE
|
|
44681
|
+
RAISE NOTICE 'logistiek-engine: trigger fustmaten overgeslagen (tabel fust_types van Voorraad ontbreekt)';
|
|
44682
|
+
END IF;
|
|
44683
|
+
EXCEPTION WHEN undefined_column THEN
|
|
44684
|
+
RAISE NOTICE 'logistiek-engine: trigger fustmaten overgeslagen: %', SQLERRM;
|
|
44685
|
+
END $fs$;
|
|
44686
|
+
|
|
44687
|
+
CREATE OR REPLACE FUNCTION public.logistics_plan_products_trg()
|
|
44688
|
+
RETURNS trigger
|
|
44689
|
+
LANGUAGE plpgsql
|
|
44690
|
+
SECURITY DEFINER
|
|
44691
|
+
SET search_path TO 'public'
|
|
44692
|
+
AS $function$
|
|
44693
|
+
DECLARE r record;
|
|
44694
|
+
BEGIN
|
|
44695
|
+
IF NEW.plant_height_cm IS NOT DISTINCT FROM OLD.plant_height_cm
|
|
44696
|
+
AND NEW.fust_type_id IS NOT DISTINCT FROM OLD.fust_type_id
|
|
44697
|
+
AND NEW.plants_per_crate IS NOT DISTINCT FROM OLD.plants_per_crate
|
|
44698
|
+
AND NEW.crates_per_layer IS NOT DISTINCT FROM OLD.crates_per_layer
|
|
44699
|
+
AND NEW.layers_per_cart IS NOT DISTINCT FROM OLD.layers_per_cart
|
|
44700
|
+
AND NEW.is_layer IS NOT DISTINCT FROM OLD.is_layer THEN
|
|
44701
|
+
RETURN NEW;
|
|
44702
|
+
END IF;
|
|
44703
|
+
|
|
44704
|
+
BEGIN
|
|
44705
|
+
FOR r IN
|
|
44706
|
+
SELECT DISTINCT o.id
|
|
44707
|
+
FROM logistics_active.order_items oi
|
|
44708
|
+
JOIN public.orders o ON o.id = oi.order_id
|
|
44709
|
+
WHERE oi.product_id = NEW.id
|
|
44710
|
+
AND o.deleted_at IS NULL
|
|
44711
|
+
AND COALESCE(o.is_reservation, false) = false
|
|
44712
|
+
AND o.plan_locked_at IS NULL
|
|
44713
|
+
AND (o.delivery_date IS NULL OR public.logistics_order_date(o.delivery_date::text) >= (now() AT TIME ZONE coalesce(public.logistics_tz(), 'Europe/Amsterdam'))::date)
|
|
44714
|
+
LOOP
|
|
44715
|
+
BEGIN
|
|
44716
|
+
PERFORM public.logistics_plan_enqueue(r.id, 'product');
|
|
44717
|
+
EXCEPTION WHEN OTHERS THEN
|
|
44718
|
+
PERFORM public.logistics_plan_fail_log('trg_logistics_plan_products', r.id, SQLSTATE, SQLERRM);
|
|
44719
|
+
END;
|
|
44720
|
+
END LOOP;
|
|
44721
|
+
EXCEPTION WHEN OTHERS THEN
|
|
44722
|
+
RAISE WARNING 'logistiek-engine: opnieuw aanbieden na productwijziging mislukt: %', SQLERRM;
|
|
44723
|
+
END;
|
|
44724
|
+
|
|
44725
|
+
RETURN NEW;
|
|
44726
|
+
END;
|
|
44727
|
+
$function$;
|
|
44728
|
+
|
|
44729
|
+
-- ===== Onderhoud in de minuutronde =====
|
|
44730
|
+
CREATE OR REPLACE FUNCTION public.logistics_plan_herstel_tijdelijk()
|
|
44731
|
+
RETURNS jsonb LANGUAGE plpgsql SECURITY DEFINER SET search_path TO 'public'
|
|
44732
|
+
AS $function$
|
|
44733
|
+
-- Eenmalig: orders die vóór deze versie geblokkeerd raakten door een time-out of een niet afgesloten ronde
|
|
44734
|
+
-- worden opnieuw aangeboden. Een tweede keer doet niets.
|
|
44735
|
+
DECLARE v_key text := 'herstel_tijdelijk_2026_11_10'; v_val text; v_n int := 0; v_id uuid;
|
|
44736
|
+
BEGIN
|
|
44737
|
+
PERFORM public.logistics_acc_guard('server', 'logistics_plan_herstel_tijdelijk');
|
|
44738
|
+
SELECT value INTO v_val FROM public.logistics_plan_settings WHERE key = v_key;
|
|
44739
|
+
IF v_val IS NOT NULL THEN
|
|
44740
|
+
RETURN jsonb_build_object('gedaan', true, 'aantal', coalesce(nullif(v_val, '')::int, 0), 'nu', 0);
|
|
44741
|
+
END IF;
|
|
44742
|
+
FOR v_id IN
|
|
44743
|
+
SELECT DISTINCT q.order_id FROM public.logistics_plan_queue q JOIN public.orders o ON o.id = q.order_id
|
|
44744
|
+
WHERE q.status IN ('geblokkeerd', 'failed')
|
|
44745
|
+
AND o.deleted_at IS NULL AND o.cancelled_at IS NULL
|
|
44746
|
+
AND (q.reason_code = 'claim_niet_afgerond'
|
|
44747
|
+
OR public.logistics_plan_is_tijdelijk(coalesce(q.error, '') || ' ' || coalesce(q.reason, '')))
|
|
44748
|
+
LOOP
|
|
44749
|
+
BEGIN
|
|
44750
|
+
UPDATE public.logistics_plan_queue q
|
|
44751
|
+
SET status = 'pending', attempts = 0, reason = 'automatisch opnieuw aangeboden na een time-out',
|
|
44752
|
+
reason_code = NULL, error = NULL, claimed_at = NULL, next_attempt_at = now(), updated_at = now()
|
|
44753
|
+
WHERE q.order_id = v_id AND q.status IN ('geblokkeerd', 'failed');
|
|
44754
|
+
v_n := v_n + 1;
|
|
44755
|
+
EXCEPTION WHEN OTHERS THEN
|
|
44756
|
+
PERFORM public.logistics_plan_fail_log('logistics_plan_herstel_tijdelijk', v_id, SQLSTATE, SQLERRM);
|
|
44757
|
+
END;
|
|
44758
|
+
END LOOP;
|
|
44759
|
+
INSERT INTO public.logistics_plan_settings(key, value, updated_at) VALUES (v_key, v_n::text, now())
|
|
44760
|
+
ON CONFLICT (key) DO NOTHING;
|
|
44761
|
+
RETURN jsonb_build_object('gedaan', true, 'aantal', v_n, 'nu', v_n);
|
|
44762
|
+
END $function$;
|
|
44763
|
+
REVOKE EXECUTE ON FUNCTION public.logistics_plan_herstel_tijdelijk() FROM PUBLIC, anon, authenticated;
|
|
44764
|
+
GRANT EXECUTE ON FUNCTION public.logistics_plan_herstel_tijdelijk() TO service_role;
|
|
44765
|
+
|
|
44766
|
+
CREATE OR REPLACE FUNCTION public.logistics_plan_gegevens_vangnet(p_force boolean DEFAULT false)
|
|
44767
|
+
RETURNS jsonb LANGUAGE plpgsql SECURITY DEFINER SET search_path TO 'public'
|
|
44768
|
+
AS $function$
|
|
44769
|
+
-- Hooguit elke 15 minuten: gegevensblokkade waarvan de gegevens na de blokkade zijn aangepast -> opnieuw.
|
|
44770
|
+
-- Meet meteen hoe lang het lezen van alle productmaten duurt.
|
|
44771
|
+
DECLARE v_last timestamptz; v_orders uuid[]; v_n int := 0; v_t timestamptz; v_ms int; v_cnt int;
|
|
44772
|
+
BEGIN
|
|
44773
|
+
PERFORM public.logistics_acc_guard('server', 'logistics_plan_gegevens_vangnet');
|
|
44774
|
+
SELECT nullif(value, '')::timestamptz INTO v_last FROM public.logistics_plan_settings WHERE key = 'gegevens_vangnet_at';
|
|
44775
|
+
IF NOT p_force AND v_last IS NOT NULL AND v_last > now() - interval '15 minutes' THEN
|
|
44776
|
+
RETURN jsonb_build_object('overgeslagen', true, 'laatst', v_last);
|
|
44777
|
+
END IF;
|
|
44778
|
+
INSERT INTO public.logistics_plan_settings(key, value, updated_at) VALUES ('gegevens_vangnet_at', now()::text, now())
|
|
44779
|
+
ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value, updated_at = now();
|
|
44780
|
+
|
|
44781
|
+
SELECT array_agg(DISTINCT q.order_id) INTO v_orders
|
|
44782
|
+
FROM public.logistics_plan_queue q
|
|
44783
|
+
WHERE q.status = 'geblokkeerd' AND q.reason_code = ANY(public.logistics_plan_gegevens_codes())
|
|
44784
|
+
AND (EXISTS (SELECT 1 FROM public.order_items oi JOIN public.products p ON p.id = oi.product_id
|
|
44785
|
+
WHERE oi.order_id = q.order_id AND p.updated_at > q.updated_at)
|
|
44786
|
+
OR EXISTS (SELECT 1 FROM public.order_items oi JOIN public.products p ON p.id = oi.product_id
|
|
44787
|
+
JOIN public.fust_types f ON f.id = p.fust_type_id
|
|
44788
|
+
WHERE oi.order_id = q.order_id AND f.updated_at > q.updated_at)
|
|
44789
|
+
OR EXISTS (SELECT 1 FROM public.logistics_belading_bevestigingen b
|
|
44790
|
+
WHERE b.order_id = q.order_id AND b.created_at > q.updated_at)
|
|
44791
|
+
OR (q.reason_code = 'artikel_ontbreekt'
|
|
44792
|
+
AND NOT EXISTS (SELECT 1 FROM public.order_items oi WHERE oi.order_id = q.order_id AND oi.product_id IS NULL)));
|
|
44793
|
+
IF v_orders IS NOT NULL THEN
|
|
44794
|
+
v_n := public.logistics_plan_gegevens_opnieuw(v_orders, 'gegevens aangepast (controle minuutronde)');
|
|
44795
|
+
END IF;
|
|
44796
|
+
|
|
44797
|
+
v_t := clock_timestamp();
|
|
44798
|
+
BEGIN
|
|
44799
|
+
SELECT count(*) INTO v_cnt FROM public.logistics_product_maten(NULL);
|
|
44800
|
+
v_ms := (extract(epoch FROM clock_timestamp() - v_t) * 1000)::int;
|
|
44801
|
+
EXCEPTION WHEN OTHERS THEN
|
|
44802
|
+
v_ms := (extract(epoch FROM clock_timestamp() - v_t) * 1000)::int;
|
|
44803
|
+
RAISE WARNING 'logistiek-engine: meting productmaten mislukt: %', SQLERRM;
|
|
44804
|
+
END;
|
|
44805
|
+
INSERT INTO public.logistics_plan_settings(key, value, updated_at) VALUES ('productmaten_ms', v_ms::text, now())
|
|
44806
|
+
ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value, updated_at = now();
|
|
44807
|
+
RETURN jsonb_build_object('opnieuw', v_n, 'productmatenMs', v_ms);
|
|
44808
|
+
END $function$;
|
|
44809
|
+
REVOKE EXECUTE ON FUNCTION public.logistics_plan_gegevens_vangnet(boolean) FROM PUBLIC, anon, authenticated;
|
|
44810
|
+
GRANT EXECUTE ON FUNCTION public.logistics_plan_gegevens_vangnet(boolean) TO service_role;
|
|
44811
|
+
|
|
44812
|
+
CREATE OR REPLACE FUNCTION public.logistics_karindeling_bewaking()
|
|
44813
|
+
RETURNS jsonb LANGUAGE plpgsql STABLE SECURITY DEFINER SET search_path TO 'public'
|
|
44814
|
+
AS $function$
|
|
44815
|
+
DECLARE v jsonb;
|
|
44816
|
+
BEGIN
|
|
44817
|
+
PERFORM public.logistics_acc_guard('ingelogd', 'logistics_karindeling_bewaking');
|
|
44818
|
+
SELECT jsonb_build_object(
|
|
44819
|
+
'achterstand', (SELECT count(DISTINCT q.order_id) FROM public.logistics_plan_queue q JOIN public.orders o ON o.id = q.order_id
|
|
44820
|
+
WHERE q.status IN ('pending', 'running') AND q.enqueued_at < now() - interval '10 minutes'
|
|
44821
|
+
AND o.deleted_at IS NULL),
|
|
44822
|
+
'productmatenMs', (SELECT nullif(value, '')::int FROM public.logistics_plan_settings WHERE key = 'productmaten_ms'),
|
|
44823
|
+
'productmatenOp', (SELECT updated_at FROM public.logistics_plan_settings WHERE key = 'productmaten_ms'),
|
|
44824
|
+
'tijdelijkOpnieuw', (SELECT count(*) FROM public.logistics_plan_queue q WHERE q.status = 'pending' AND q.reason_code IN ('tijdelijk', 'claim_niet_afgerond')),
|
|
44825
|
+
'herstelTijdelijk', (SELECT nullif(value, '')::int FROM public.logistics_plan_settings WHERE key = 'herstel_tijdelijk_2026_11_10')
|
|
44826
|
+
) INTO v;
|
|
44827
|
+
RETURN v;
|
|
44828
|
+
END $function$;
|
|
44829
|
+
REVOKE EXECUTE ON FUNCTION public.logistics_karindeling_bewaking() FROM PUBLIC, anon;
|
|
44830
|
+
GRANT EXECUTE ON FUNCTION public.logistics_karindeling_bewaking() TO authenticated, service_role;
|
|
44831
|
+
|
|
44832
|
+
CREATE OR REPLACE FUNCTION public.logistics_karindeling_onderhoud()
|
|
44833
|
+
RETURNS jsonb LANGUAGE plpgsql SECURITY DEFINER SET search_path TO 'public'
|
|
44834
|
+
AS $function$
|
|
44835
|
+
DECLARE v jsonb := '{}'::jsonb;
|
|
44836
|
+
BEGIN
|
|
44837
|
+
PERFORM public.logistics_acc_guard('server', 'logistics_karindeling_onderhoud');
|
|
44838
|
+
BEGIN
|
|
44839
|
+
v := v || jsonb_build_object('herstel', public.logistics_plan_herstel_tijdelijk());
|
|
44840
|
+
EXCEPTION WHEN OTHERS THEN
|
|
44841
|
+
v := v || jsonb_build_object('herstel', left(SQLERRM, 200));
|
|
44842
|
+
END;
|
|
44843
|
+
BEGIN
|
|
44844
|
+
v := v || jsonb_build_object('vangnet', public.logistics_plan_gegevens_vangnet(false));
|
|
44845
|
+
EXCEPTION WHEN OTHERS THEN
|
|
44846
|
+
v := v || jsonb_build_object('vangnet', left(SQLERRM, 200));
|
|
44847
|
+
END;
|
|
44848
|
+
RETURN v;
|
|
44849
|
+
END $function$;
|
|
44850
|
+
REVOKE EXECUTE ON FUNCTION public.logistics_karindeling_onderhoud() FROM PUBLIC, anon, authenticated;
|
|
44851
|
+
GRANT EXECUTE ON FUNCTION public.logistics_karindeling_onderhoud() TO service_role;
|
|
44852
|
+
|
|
44853
|
+
CREATE OR REPLACE FUNCTION public.logistics_plan_finish(p_id uuid, p_error text DEFAULT NULL::text)
|
|
44854
|
+
RETURNS void
|
|
44855
|
+
LANGUAGE plpgsql
|
|
44856
|
+
SECURITY DEFINER
|
|
44857
|
+
SET search_path TO 'public'
|
|
44858
|
+
AS $function$
|
|
44859
|
+
DECLARE
|
|
44860
|
+
v_max int := public.logistics_plan_setting('max_attempts', 5)::int;
|
|
44861
|
+
v_attempts int;
|
|
44862
|
+
v_order uuid;
|
|
44863
|
+
v_text text;
|
|
44864
|
+
v_class text;
|
|
44865
|
+
v_next timestamptz;
|
|
44866
|
+
v_min numeric;
|
|
44867
|
+
BEGIN
|
|
44868
|
+
PERFORM public.logistics_acc_guard('ingelogd', 'logistics_plan_finish');
|
|
44869
|
+
SELECT order_id, attempts INTO v_order, v_attempts
|
|
44870
|
+
FROM public.logistics_plan_queue WHERE id = p_id;
|
|
44871
|
+
|
|
44872
|
+
IF p_error IS NULL THEN
|
|
44873
|
+
UPDATE public.logistics_plan_queue SET status='done', error=NULL, claimed_at=NULL, updated_at=now() WHERE id = p_id;
|
|
44874
|
+
PERFORM public.logistics_trace_order_step(
|
|
44875
|
+
v_order, 'stap:beladingindeling', 'karrenindeling afgerond', 'geslaagd',
|
|
44876
|
+
'indeling klaar', NULL, 'Order picken', NULL, NULL);
|
|
44877
|
+
BEGIN
|
|
44878
|
+
PERFORM public.logistics_trace_progress_order(v_order::text);
|
|
44879
|
+
EXCEPTION WHEN OTHERS THEN
|
|
44880
|
+
NULL;
|
|
44881
|
+
END;
|
|
44882
|
+
RETURN;
|
|
44883
|
+
END IF;
|
|
44884
|
+
|
|
44885
|
+
IF p_error = 'deadline' THEN
|
|
44886
|
+
PERFORM public.logistics_plan_defer(p_id);
|
|
44887
|
+
RETURN;
|
|
44888
|
+
END IF;
|
|
44889
|
+
|
|
44890
|
+
v_text := public.logistics_plan_error_text(p_error);
|
|
44891
|
+
v_class := public.logistics_plan_error_class(p_error);
|
|
44892
|
+
|
|
44893
|
+
IF COALESCE(v_attempts,0) >= v_max THEN
|
|
44894
|
+
UPDATE public.logistics_plan_queue SET status='failed', error=p_error, claimed_at=NULL, updated_at=now() WHERE id = p_id;
|
|
44895
|
+
PERFORM public.logistics_trace_order_step(
|
|
44896
|
+
v_order, 'stap:beladingindeling', 'karrenindeling mislukt', 'mislukt',
|
|
44897
|
+
'gestopt na ' || COALESCE(v_attempts,0) || ' van ' || v_max || ' pogingen',
|
|
44898
|
+
v_text || ' Dit ging ' || COALESCE(v_attempts,0) || ' keer achter elkaar mis; de engine probeert het niet meer vanzelf. Bestaande karren blijven staan. Los de oorzaak op en start de order opnieuw via de wachtrij.',
|
|
44899
|
+
'Oorzaak oplossen en opnieuw aanmelden in de wachtrij', v_text, v_class);
|
|
44900
|
+
ELSE
|
|
44901
|
+
-- Tijdelijke fout (time-out, ronde niet afgesloten): oplopend 1, 2, 4, 8, 16 minuten.
|
|
44902
|
+
v_min := CASE
|
|
44903
|
+
WHEN public.logistics_plan_is_tijdelijk(p_error) THEN EXTRACT(epoch FROM public.logistics_plan_tijdelijk_wacht(v_attempts)) / 60
|
|
44904
|
+
WHEN COALESCE(v_attempts,1) <= 1 THEN public.logistics_plan_setting('retry_1_min', 5)
|
|
44905
|
+
WHEN v_attempts = 2 THEN public.logistics_plan_setting('retry_2_min', 15)
|
|
44906
|
+
ELSE public.logistics_plan_setting('retry_3_min', 60) END;
|
|
44907
|
+
v_next := now() + make_interval(mins => GREATEST(1, v_min)::int);
|
|
44908
|
+
UPDATE public.logistics_plan_queue
|
|
44909
|
+
SET status='pending', error=p_error, claimed_at=NULL, updated_at=now(),
|
|
44910
|
+
next_attempt_at = v_next
|
|
44911
|
+
WHERE id = p_id;
|
|
44912
|
+
PERFORM public.logistics_trace_order_step(
|
|
44913
|
+
v_order, 'stap:beladingindeling', 'karrenindeling opnieuw proberen', 'wachtend',
|
|
44914
|
+
'nieuwe poging ingepland (poging ' || (COALESCE(v_attempts,0) + 1) || ' van ' || v_max || ')',
|
|
44915
|
+
v_text || ' Bestaande karren blijven staan. Volgende poging om ' || to_char(v_next AT TIME ZONE public.logistics_tz(), 'HH24:MI') || ' uur.',
|
|
44916
|
+
'Karrenindeling', v_text, v_class);
|
|
44917
|
+
END IF;
|
|
44918
|
+
END;
|
|
44919
|
+
$function$;
|
|
44920
|
+
|
|
44921
|
+
CREATE OR REPLACE FUNCTION public.logistics_tick()
|
|
44922
|
+
RETURNS jsonb
|
|
44923
|
+
LANGUAGE plpgsql
|
|
44924
|
+
SECURITY DEFINER
|
|
44925
|
+
SET search_path TO 'public'
|
|
44926
|
+
AS $function$
|
|
44927
|
+
DECLARE v_result jsonb := '{}'::jsonb; v_start timestamptz := clock_timestamp(); v_t timestamptz; v_err boolean := false;
|
|
44928
|
+
BEGIN
|
|
44929
|
+
PERFORM public.logistics_acc_guard('server', 'logistics_tick');
|
|
44930
|
+
v_t := clock_timestamp();
|
|
44931
|
+
BEGIN
|
|
44932
|
+
PERFORM set_config('statement_timeout', '10000', true);
|
|
44933
|
+
v_result := v_result || jsonb_build_object('vervoerder', public.logistics_carrier_recalc_run(200));
|
|
44934
|
+
EXCEPTION WHEN OTHERS THEN
|
|
44935
|
+
v_err := true; v_result := v_result || jsonb_build_object('vervoerder', left(SQLERRM, 200));
|
|
44936
|
+
END;
|
|
44937
|
+
v_result := v_result || jsonb_build_object('vervoerder_ms', (extract(epoch from clock_timestamp()-v_t)*1000)::int);
|
|
44938
|
+
|
|
44939
|
+
v_t := clock_timestamp();
|
|
44940
|
+
BEGIN
|
|
44941
|
+
PERFORM set_config('statement_timeout', '15000', true);
|
|
44942
|
+
PERFORM public.logistics_rebuffer_pending();
|
|
44943
|
+
v_result := v_result || jsonb_build_object('rebuffer', 'ok');
|
|
44944
|
+
EXCEPTION WHEN OTHERS THEN
|
|
44945
|
+
v_err := true;
|
|
44946
|
+
IF SQLERRM ILIKE '%tijdzone%' THEN
|
|
44947
|
+
v_result := v_result || jsonb_build_object('rebuffer', 'overgeslagen: tijdzone ontbreekt in Werktijden');
|
|
44948
|
+
BEGIN
|
|
44949
|
+
UPDATE public.logistics_rebuffer_state SET last_error = 'Herindelen overgeslagen: de tijdzone ontbreekt in Werktijden.', updated_at = now() WHERE id;
|
|
44950
|
+
EXCEPTION WHEN OTHERS THEN NULL; END;
|
|
44951
|
+
ELSE
|
|
44952
|
+
v_result := v_result || jsonb_build_object('rebuffer', left(SQLERRM, 200));
|
|
44953
|
+
END IF;
|
|
44954
|
+
END;
|
|
44955
|
+
v_result := v_result || jsonb_build_object('rebuffer_ms', (extract(epoch from clock_timestamp()-v_t)*1000)::int);
|
|
44956
|
+
|
|
44957
|
+
v_t := clock_timestamp();
|
|
44958
|
+
BEGIN
|
|
44959
|
+
PERFORM set_config('statement_timeout', '20000', true);
|
|
44960
|
+
PERFORM public.run_logistics_tasks_dispatch();
|
|
44961
|
+
v_result := v_result || jsonb_build_object('taken', 'ok');
|
|
44962
|
+
EXCEPTION WHEN OTHERS THEN
|
|
44963
|
+
v_err := true; v_result := v_result || jsonb_build_object('taken', left(SQLERRM, 200));
|
|
44964
|
+
END;
|
|
44965
|
+
v_result := v_result || jsonb_build_object('taken_ms', (extract(epoch from clock_timestamp()-v_t)*1000)::int);
|
|
44966
|
+
|
|
44967
|
+
v_t := clock_timestamp();
|
|
44968
|
+
BEGIN
|
|
44969
|
+
PERFORM set_config('statement_timeout', '20000', true);
|
|
44970
|
+
v_result := v_result || jsonb_build_object('afgerond', public.logistics_order_finalize_run(25, 120));
|
|
44971
|
+
EXCEPTION WHEN OTHERS THEN
|
|
44972
|
+
v_err := true; v_result := v_result || jsonb_build_object('afgerond', left(SQLERRM, 200));
|
|
44973
|
+
END;
|
|
44974
|
+
v_result := v_result || jsonb_build_object('afgerond_ms', (extract(epoch from clock_timestamp()-v_t)*1000)::int);
|
|
44975
|
+
|
|
44976
|
+
v_t := clock_timestamp();
|
|
44977
|
+
BEGIN
|
|
44978
|
+
PERFORM set_config('statement_timeout', '15000', true);
|
|
44979
|
+
v_result := v_result || jsonb_build_object('pickwachtrij', public.logistics_sync_pick_queue());
|
|
44980
|
+
EXCEPTION WHEN OTHERS THEN
|
|
44981
|
+
v_err := true; v_result := v_result || jsonb_build_object('pickwachtrij', left(SQLERRM, 200));
|
|
44982
|
+
END;
|
|
44983
|
+
v_result := v_result || jsonb_build_object('pickwachtrij_ms', (extract(epoch from clock_timestamp()-v_t)*1000)::int);
|
|
44984
|
+
|
|
44985
|
+
v_t := clock_timestamp();
|
|
44986
|
+
BEGIN
|
|
44987
|
+
PERFORM set_config('statement_timeout', '10000', true);
|
|
44988
|
+
v_result := v_result || jsonb_build_object('laadtoestand', public.logistics_refresh_plan_state_als_nodig());
|
|
44989
|
+
EXCEPTION WHEN OTHERS THEN
|
|
44990
|
+
v_err := true; v_result := v_result || jsonb_build_object('laadtoestand', left(SQLERRM, 200));
|
|
44991
|
+
END;
|
|
44992
|
+
v_result := v_result || jsonb_build_object('laadtoestand_ms', (extract(epoch from clock_timestamp()-v_t)*1000)::int);
|
|
44993
|
+
|
|
44994
|
+
v_t := clock_timestamp();
|
|
44995
|
+
BEGIN
|
|
44996
|
+
PERFORM set_config('statement_timeout', '10000', true);
|
|
44997
|
+
v_result := v_result || jsonb_build_object('instellingen', public.logistics_ensure_periodiek());
|
|
44998
|
+
EXCEPTION WHEN OTHERS THEN
|
|
44999
|
+
v_err := true; v_result := v_result || jsonb_build_object('instellingen', left(SQLERRM, 200));
|
|
45000
|
+
END;
|
|
45001
|
+
v_result := v_result || jsonb_build_object('instellingen_ms', (extract(epoch from clock_timestamp()-v_t)*1000)::int);
|
|
45002
|
+
|
|
45003
|
+
-- Karindeling: eenmalig herstel na time-outs en (hooguit elke 15 min) vangnet voor gegevensblokkades.
|
|
45004
|
+
v_t := clock_timestamp();
|
|
45005
|
+
BEGIN
|
|
45006
|
+
PERFORM set_config('statement_timeout', '10000', true);
|
|
45007
|
+
v_result := v_result || jsonb_build_object('karindeling', public.logistics_karindeling_onderhoud());
|
|
45008
|
+
EXCEPTION WHEN OTHERS THEN
|
|
45009
|
+
v_err := true; v_result := v_result || jsonb_build_object('karindeling', left(SQLERRM, 200));
|
|
45010
|
+
END;
|
|
45011
|
+
v_result := v_result || jsonb_build_object('karindeling_ms', (extract(epoch from clock_timestamp()-v_t)*1000)::int);
|
|
45012
|
+
|
|
45013
|
+
BEGIN
|
|
45014
|
+
INSERT INTO public.logistics_tick_runs(started_at, finished_at, duration_ms, has_error, steps)
|
|
45015
|
+
VALUES (v_start, clock_timestamp(), (extract(epoch from clock_timestamp()-v_start)*1000)::int, v_err, v_result);
|
|
45016
|
+
EXCEPTION WHEN OTHERS THEN NULL;
|
|
45017
|
+
END;
|
|
45018
|
+
PERFORM set_config('statement_timeout', '30000', true);
|
|
45019
|
+
RETURN v_result;
|
|
45020
|
+
END $function$;
|
|
45021
|
+
|
|
45022
|
+
CREATE OR REPLACE FUNCTION public.logistics_install_check_samenvoegen()
|
|
45023
|
+
RETURNS TABLE(onderdeel text, ok boolean, toelichting text)
|
|
45024
|
+
LANGUAGE plpgsql
|
|
45025
|
+
STABLE SECURITY DEFINER
|
|
45026
|
+
SET search_path TO 'public'
|
|
45027
|
+
AS $function$
|
|
45028
|
+
DECLARE f text;
|
|
45029
|
+
BEGIN
|
|
45030
|
+
FOREACH f IN ARRAY ARRAY['logistics_order_is_backfill(uuid)', 'logistics_order_merge_key_v2(uuid)',
|
|
45031
|
+
'logistics_order_mergeable(uuid)', 'logistics_merge_floriday_lines(uuid)', 'logistics_order_buffer_ref(uuid)',
|
|
45032
|
+
'logistics_merge_herstel_voorbeeld()', 'logistics_merge_herstel_uitvoeren(text,integer)',
|
|
45033
|
+
'logistics_merge_status()', 'logistics_trace_event(uuid,text,text,text)', 'logistics_schema_versie()',
|
|
45034
|
+
'logistics_pickup_regel()', 'logistics_pickup_moment_kies(uuid,date,timestamp with time zone,text,numeric)',
|
|
45035
|
+
'logistics_pickup_vul_voorbeeld()', 'logistics_pickup_vul_uitvoeren(integer)', 'logistics_pickup_status()',
|
|
45036
|
+
'logistics_pickup_herwijs(uuid[],integer)', 'logistics_refresh_pickup_moments(integer)',
|
|
45037
|
+
'logistics_plan_is_tijdelijk(text)', 'logistics_plan_tijdelijk_wacht(integer)',
|
|
45038
|
+
'logistics_plan_tijdelijk_opnieuw(uuid[],text)', 'logistics_plan_gegevens_codes()',
|
|
45039
|
+
'logistics_plan_gegevens_opnieuw(uuid[],text)', 'logistics_plan_herstel_tijdelijk()',
|
|
45040
|
+
'logistics_plan_gegevens_vangnet(boolean)', 'logistics_karindeling_bewaking()',
|
|
45041
|
+
'logistics_karindeling_onderhoud()', 'logistics_fust_maten_gewijzigd_trg()'] LOOP
|
|
45042
|
+
onderdeel := 'functie ' || f;
|
|
45043
|
+
ok := to_regprocedure('public.' || f) IS NOT NULL;
|
|
45044
|
+
toelichting := CASE WHEN ok THEN 'aanwezig' ELSE 'ontbreekt: werk de Engine bij via Supabase bijwerken' END;
|
|
45045
|
+
RETURN NEXT;
|
|
45046
|
+
END LOOP;
|
|
45047
|
+
onderdeel := 'tabel logistics_merge_herstel_log';
|
|
45048
|
+
ok := to_regclass('public.logistics_merge_herstel_log') IS NOT NULL;
|
|
45049
|
+
toelichting := CASE WHEN ok THEN 'aanwezig' ELSE 'ontbreekt: werk de Engine bij via Supabase bijwerken' END;
|
|
45050
|
+
RETURN NEXT;
|
|
45051
|
+
onderdeel := 'kolom logistics_pickup_moments.carrier_id';
|
|
45052
|
+
ok := EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = 'public'
|
|
45053
|
+
AND table_name = 'logistics_pickup_moments' AND column_name = 'carrier_id');
|
|
45054
|
+
toelichting := CASE WHEN ok THEN 'aanwezig' ELSE 'ontbreekt: werk de Engine bij via Supabase bijwerken' END;
|
|
45055
|
+
RETURN NEXT;
|
|
45056
|
+
onderdeel := 'trigger ophaalmoment op orders';
|
|
45057
|
+
ok := EXISTS (SELECT 1 FROM pg_trigger WHERE tgname = 'trg_logistics_orders_zpickup_moment');
|
|
45058
|
+
toelichting := CASE WHEN ok THEN 'aanwezig' ELSE 'ontbreekt: werk de Engine bij via Supabase bijwerken' END;
|
|
45059
|
+
RETURN NEXT;
|
|
45060
|
+
onderdeel := 'trigger fustmaten gewijzigd';
|
|
45061
|
+
ok := EXISTS (SELECT 1 FROM pg_trigger WHERE tgname = 'trg_logistics_fust_maten_gewijzigd');
|
|
45062
|
+
toelichting := CASE WHEN ok THEN 'aanwezig' ELSE 'ontbreekt: werk de Engine bij via Supabase bijwerken' END;
|
|
45063
|
+
RETURN NEXT;
|
|
45064
|
+
END $function$;
|
|
45065
|
+
|
|
45066
|
+
CREATE OR REPLACE FUNCTION public.logistics_schema_versie()
|
|
45067
|
+
RETURNS text LANGUAGE sql IMMUTABLE SET search_path TO 'public'
|
|
45068
|
+
AS $function$ SELECT '2026-11-10d'::text $function$;
|
|
45069
|
+
-- ===== 2026-11-10d: tijdbudget planstatus pickscherm =====
|
|
45070
|
+
CREATE OR REPLACE FUNCTION public.logistics_refresh_plan_state(p_limit integer DEFAULT 500)
|
|
45071
|
+
RETURNS jsonb
|
|
45072
|
+
LANGUAGE plpgsql
|
|
45073
|
+
SECURITY DEFINER
|
|
45074
|
+
SET search_path TO 'public'
|
|
45075
|
+
AS $function$
|
|
45076
|
+
-- Per pickwachtrijregel wordt de planstatus opnieuw bepaald. Dat kost per regel enkele honderden ms,
|
|
45077
|
+
-- daarom geldt een tijdbudget van 2 s per ronde; de volgende ronde gaat verder waar deze stopte.
|
|
45078
|
+
declare rec record; v jsonb; v_ref text; v_updated integer := 0; v_checked integer := 0;
|
|
45079
|
+
v_start timestamptz := clock_timestamp(); v_cursor timestamptz; v_last timestamptz; v_klaar boolean := true;
|
|
45080
|
+
begin
|
|
45081
|
+
PERFORM public.logistics_acc_guard('ingelogd', 'logistics_refresh_plan_state');
|
|
45082
|
+
SELECT nullif(value->>'cursor', '')::timestamptz INTO v_cursor FROM public.logistics_tick_state WHERE key = 'refresh_plan_state_cursor';
|
|
45083
|
+
for rec in
|
|
45084
|
+
select q.id, q.payload, q.order_number, q.document_number, q.created_at
|
|
45085
|
+
from public.pick_queue q
|
|
45086
|
+
where q.cancelled_at is null and q.completed_at is null
|
|
45087
|
+
and (v_cursor is null or q.created_at < v_cursor)
|
|
45088
|
+
order by q.created_at desc
|
|
45089
|
+
limit greatest(coalesce(p_limit, 500), 1)
|
|
45090
|
+
loop
|
|
45091
|
+
if clock_timestamp() - v_start > interval '2 seconds' then
|
|
45092
|
+
v_klaar := false;
|
|
45093
|
+
exit;
|
|
45094
|
+
end if;
|
|
45095
|
+
v_last := rec.created_at;
|
|
45096
|
+
v_checked := v_checked + 1;
|
|
45097
|
+
v_ref := coalesce(nullif(rec.payload->>'orderRef', ''), rec.order_number, rec.document_number);
|
|
45098
|
+
if v_ref is null then continue; end if;
|
|
45099
|
+
v := public.logistics_plan_state_for(v_ref);
|
|
45100
|
+
update public.pick_queue q
|
|
45101
|
+
set payload = coalesce(q.payload, '{}'::jsonb) || jsonb_build_object(
|
|
45102
|
+
'planState', v->>'planState',
|
|
45103
|
+
'planProgress', v->'planProgress',
|
|
45104
|
+
'planStateSince', v->>'planStateSince',
|
|
45105
|
+
'cartPlanReady', coalesce((v->>'cartPlanReady')::boolean, false),
|
|
45106
|
+
'cartPlanReleased', coalesce((v->>'cartPlanReleased')::boolean, false),
|
|
45107
|
+
'cartPlanReason', v->>'cartPlanReason')
|
|
45108
|
+
where q.id = rec.id
|
|
45109
|
+
and coalesce(q.payload->>'planState', '') is distinct from coalesce(v->>'planState', '');
|
|
45110
|
+
if found then v_updated := v_updated + 1; end if;
|
|
45111
|
+
end loop;
|
|
45112
|
+
begin
|
|
45113
|
+
INSERT INTO public.logistics_tick_state(key, value, updated_at)
|
|
45114
|
+
VALUES ('refresh_plan_state_cursor', jsonb_build_object('cursor', CASE WHEN v_klaar THEN NULL ELSE v_last END), now())
|
|
45115
|
+
ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value, updated_at = now();
|
|
45116
|
+
exception when others then
|
|
45117
|
+
raise warning 'logistiek-engine: voortgang planstatus niet opgeslagen: %', SQLERRM;
|
|
45118
|
+
end;
|
|
45119
|
+
return jsonb_build_object('updated', v_updated, 'gecontroleerd', v_checked, 'klaar', v_klaar);
|
|
45120
|
+
end;
|
|
45121
|
+
$function$;
|
|
45122
|
+
CREATE OR REPLACE FUNCTION public.logistics_refresh_plan_state_als_nodig()
|
|
45123
|
+
RETURNS jsonb
|
|
45124
|
+
LANGUAGE plpgsql
|
|
45125
|
+
SECURITY DEFINER
|
|
45126
|
+
SET search_path TO 'public'
|
|
45127
|
+
AS $function$
|
|
45128
|
+
DECLARE v_wm timestamptz; v_prev timestamptz; v_at timestamptz; v_res jsonb; v_bezig boolean;
|
|
45129
|
+
BEGIN
|
|
45130
|
+
PERFORM public.logistics_acc_guard('server', 'logistics_refresh_plan_state_als_nodig');
|
|
45131
|
+
SELECT greatest((SELECT max(updated_at) FROM public.logistics_plan_queue),
|
|
45132
|
+
(SELECT max(updated_at) FROM public.logistics_tasks)) INTO v_wm;
|
|
45133
|
+
SELECT (value->>'wm')::timestamptz, (value->>'at')::timestamptz INTO v_prev, v_at
|
|
45134
|
+
FROM public.logistics_tick_state WHERE key = 'refresh_plan_state';
|
|
45135
|
+
SELECT (value->>'cursor') IS NOT NULL INTO v_bezig FROM public.logistics_tick_state WHERE key = 'refresh_plan_state_cursor';
|
|
45136
|
+
IF NOT coalesce(v_bezig, false) AND v_at IS NOT NULL AND v_wm IS NOT DISTINCT FROM v_prev AND v_at > now() - interval '5 minutes' THEN
|
|
45137
|
+
RETURN jsonb_build_object('overgeslagen', 'geen wijziging');
|
|
45138
|
+
END IF;
|
|
45139
|
+
v_res := public.logistics_refresh_plan_state(p_limit => 500);
|
|
45140
|
+
INSERT INTO public.logistics_tick_state(key, value, updated_at)
|
|
45141
|
+
VALUES ('refresh_plan_state', jsonb_build_object('wm', v_wm, 'at', now()), now())
|
|
45142
|
+
ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value, updated_at = now();
|
|
45143
|
+
RETURN v_res;
|
|
45144
|
+
END $function$;
|