@flowselections/logistiek-engine 1.0.237 → 1.0.238

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.
@@ -40615,4 +40615,473 @@ BEGIN
40615
40615
  ELSE ok := EXISTS (SELECT 1 FROM pg_attribute WHERE attrelid = 'public.pick_stations'::regclass AND attname = 'system_code' AND NOT attisdropped);
40616
40616
  toelichting := CASE WHEN ok THEN 'aanwezig' ELSE 'ontbreekt: werk Pickschermen bij; tot dan geen standaardstations' END; END IF;
40617
40617
  RETURN NEXT;
40618
- END $function$;
40618
+ END $function$;-- Samenvoeggroepen (orderbuffers): geannuleerde/verwijderde orders nooit (meer) in een groep.
40619
+
40620
+ CREATE OR REPLACE FUNCTION public.logistics_order_vervallen(p_order_id uuid)
40621
+ RETURNS boolean LANGUAGE plpgsql STABLE SECURITY DEFINER SET search_path TO 'public' AS $$
40622
+ DECLARE v boolean := false;
40623
+ BEGIN
40624
+ SELECT (o.deleted_at IS NOT NULL OR o.cancelled_at IS NOT NULL
40625
+ OR lower(btrim(coalesce(o.status, ''))) IN ('geannuleerd', 'cancelled'))
40626
+ INTO v FROM public.orders o WHERE o.id = p_order_id;
40627
+ IF coalesce(v, false) THEN RETURN true; END IF;
40628
+ IF to_regclass('public.voorraad_order_terugdraaiing') IS NOT NULL THEN
40629
+ EXECUTE 'SELECT EXISTS (SELECT 1 FROM public.voorraad_order_terugdraaiing t WHERE t.order_id = $1)'
40630
+ INTO v USING p_order_id;
40631
+ END IF;
40632
+ RETURN coalesce(v, false);
40633
+ END $$;
40634
+ REVOKE ALL ON FUNCTION public.logistics_order_vervallen(uuid) FROM PUBLIC, anon;
40635
+ GRANT EXECUTE ON FUNCTION public.logistics_order_vervallen(uuid) TO authenticated, service_role;
40636
+
40637
+ CREATE OR REPLACE FUNCTION public.logistics_order_buffer_excluded(p_order_id uuid)
40638
+ RETURNS boolean
40639
+ LANGUAGE sql
40640
+ STABLE SECURITY DEFINER
40641
+ SET search_path TO 'public'
40642
+ AS $function$
40643
+ SELECT public.logistics_acc_guard('server', 'logistics_order_buffer_excluded');
40644
+ SELECT EXISTS (SELECT 1 FROM public.logistics_order_plan_exclusion x WHERE x.order_id = p_order_id)
40645
+ OR public.logistics_order_afgemeld(p_order_id)
40646
+ OR public.logistics_order_vervallen(p_order_id)
40647
+ $function$;
40648
+
40649
+ CREATE OR REPLACE FUNCTION public._logistics_buffers_vervallen_herstel(p_order_id uuid)
40650
+ RETURNS jsonb LANGUAGE plpgsql SECURITY DEFINER SET search_path TO 'public' AS $$
40651
+ DECLARE r record; v_groepen uuid[] := '{}'; v_orders int := 0; v_oud text; v_stand jsonb;
40652
+ BEGIN
40653
+ v_oud := current_setting('logistics.buffer_writer', true);
40654
+ FOR r IN
40655
+ SELECT o.id, o.order_number, o.buffer_id,
40656
+ CASE WHEN o.deleted_at IS NOT NULL THEN 'verwijderd' ELSE 'geannuleerd' END AS reden
40657
+ FROM public.orders o
40658
+ WHERE o.buffer_id IS NOT NULL AND (p_order_id IS NULL OR o.id = p_order_id)
40659
+ AND public.logistics_order_vervallen(o.id)
40660
+ ORDER BY o.order_number
40661
+ FOR UPDATE OF o
40662
+ LOOP
40663
+ PERFORM set_config('logistics.buffer_writer', 'engine', true);
40664
+ UPDATE public.orders SET buffer_id = NULL WHERE id = r.id AND buffer_id = r.buffer_id;
40665
+ INSERT INTO public.logistics_correctie_log (correctie, object_id, omschrijving, oud, nieuw)
40666
+ VALUES ('buffers_geannuleerd', r.id, coalesce(r.order_number, r.id::text) || ' (' || r.reden || ') losgemaakt uit samenvoeggroep',
40667
+ jsonb_build_object('buffer_id', r.buffer_id), jsonb_build_object('buffer_id', NULL));
40668
+ v_orders := v_orders + 1;
40669
+ IF NOT r.buffer_id = ANY (v_groepen) THEN v_groepen := v_groepen || r.buffer_id; END IF;
40670
+ END LOOP;
40671
+ PERFORM set_config('logistics.buffer_writer', coalesce(v_oud, ''), true);
40672
+
40673
+ IF cardinality(v_groepen) > 0 THEN
40674
+ FOR r IN SELECT x.id, x.order_number FROM public.orders x
40675
+ WHERE x.buffer_id = ANY (v_groepen) AND x.deleted_at IS NULL AND x.cancelled_at IS NULL LOOP
40676
+ BEGIN
40677
+ PERFORM public.logistics_plan_enqueue(r.id, 'correctie_buffers_geannuleerd');
40678
+ EXCEPTION WHEN others THEN
40679
+ INSERT INTO public.logistics_correctie_log (correctie, object_id, omschrijving)
40680
+ VALUES ('buffers_geannuleerd_fout', r.id, 'Opnieuw plannen van ' || coalesce(r.order_number, r.id::text) || ' mislukt: ' || SQLERRM);
40681
+ END;
40682
+ END LOOP;
40683
+ SELECT value::jsonb INTO v_stand FROM public.logistics_plan_settings WHERE key = 'buffers_geannuleerd_herstel';
40684
+ v_stand := jsonb_build_object(
40685
+ 'totaalGroepen', coalesce((v_stand->>'totaalGroepen')::int, 0) + cardinality(v_groepen),
40686
+ 'totaalOrders', coalesce((v_stand->>'totaalOrders')::int, 0) + v_orders,
40687
+ 'laatsteGroepen', cardinality(v_groepen), 'laatsteOrders', v_orders, 'laatsteOp', now());
40688
+ INSERT INTO public.logistics_plan_settings (key, value) VALUES ('buffers_geannuleerd_herstel', v_stand::text)
40689
+ ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value, updated_at = now();
40690
+ END IF;
40691
+ RETURN jsonb_build_object('groepen', cardinality(v_groepen), 'orders', v_orders);
40692
+ END $$;
40693
+ REVOKE ALL ON FUNCTION public._logistics_buffers_vervallen_herstel(uuid) FROM PUBLIC, anon, authenticated;
40694
+ GRANT EXECUTE ON FUNCTION public._logistics_buffers_vervallen_herstel(uuid) TO service_role;
40695
+
40696
+ CREATE OR REPLACE FUNCTION public.logistics_correctie_buffers_geannuleerd(p_uitvoeren boolean DEFAULT false)
40697
+ RETURNS TABLE(order_id uuid, order_number text, reden text, buffer_id uuid, actieve_karren integer,
40698
+ andere_open_orders text, uitgevoerd boolean)
40699
+ LANGUAGE plpgsql SECURITY DEFINER SET search_path TO 'public' AS $$
40700
+ #variable_conflict use_column
40701
+ DECLARE r record;
40702
+ BEGIN
40703
+ PERFORM public.logistics_acc_guard('server', 'logistics_correctie_buffers_geannuleerd');
40704
+ FOR r IN
40705
+ SELECT o.id, o.order_number, o.buffer_id,
40706
+ CASE WHEN o.deleted_at IS NOT NULL THEN 'verwijderd' ELSE 'geannuleerd' END AS reden,
40707
+ (SELECT count(*)::int FROM public.order_carts c WHERE c.order_id = o.id AND c.archived_at IS NULL) AS karren,
40708
+ (SELECT string_agg(x.order_number, ' ' ORDER BY x.order_number) FROM public.orders x
40709
+ WHERE x.buffer_id = o.buffer_id AND x.id <> o.id AND x.deleted_at IS NULL AND x.cancelled_at IS NULL) AS anderen
40710
+ FROM public.orders o
40711
+ WHERE o.buffer_id IS NOT NULL AND public.logistics_order_vervallen(o.id)
40712
+ ORDER BY o.order_number
40713
+ LOOP
40714
+ order_id := r.id; order_number := r.order_number; reden := r.reden; buffer_id := r.buffer_id;
40715
+ actieve_karren := r.karren; andere_open_orders := r.anderen; uitgevoerd := p_uitvoeren;
40716
+ RETURN NEXT;
40717
+ END LOOP;
40718
+ IF p_uitvoeren THEN PERFORM public._logistics_buffers_vervallen_herstel(NULL); END IF;
40719
+ END $$;
40720
+ REVOKE ALL ON FUNCTION public.logistics_correctie_buffers_geannuleerd(boolean) FROM PUBLIC, anon, authenticated;
40721
+ GRANT EXECUTE ON FUNCTION public.logistics_correctie_buffers_geannuleerd(boolean) TO service_role;
40722
+
40723
+ CREATE OR REPLACE FUNCTION public.logistics_orders_vervallen_buffer_trg()
40724
+ RETURNS trigger LANGUAGE plpgsql SECURITY DEFINER SET search_path TO 'public' AS $$
40725
+ BEGIN
40726
+ BEGIN
40727
+ PERFORM public._logistics_buffers_vervallen_herstel(NEW.id);
40728
+ EXCEPTION WHEN others THEN
40729
+ BEGIN
40730
+ INSERT INTO public.logistics_correctie_log (correctie, object_id, omschrijving)
40731
+ VALUES ('buffers_geannuleerd_fout', NEW.id, 'Losmaken uit samenvoeggroep mislukt: ' || SQLERRM);
40732
+ EXCEPTION WHEN others THEN RAISE WARNING 'logistics_orders_vervallen_buffer_trg: %', SQLERRM;
40733
+ END;
40734
+ END;
40735
+ RETURN NULL;
40736
+ END $$;
40737
+ REVOKE ALL ON FUNCTION public.logistics_orders_vervallen_buffer_trg() FROM PUBLIC, anon, authenticated;
40738
+
40739
+ DROP TRIGGER IF EXISTS trg_logistics_orders_vervallen_buffer ON public.orders;
40740
+ CREATE TRIGGER trg_logistics_orders_vervallen_buffer
40741
+ AFTER UPDATE OF cancelled_at, deleted_at, status, buffer_id ON public.orders
40742
+ FOR EACH ROW
40743
+ WHEN (NEW.buffer_id IS NOT NULL AND (NEW.cancelled_at IS NOT NULL OR NEW.deleted_at IS NOT NULL
40744
+ OR lower(btrim(coalesce(NEW.status, ''))) IN ('geannuleerd', 'cancelled')))
40745
+ EXECUTE FUNCTION public.logistics_orders_vervallen_buffer_trg();
40746
+
40747
+ CREATE OR REPLACE FUNCTION public.logistics_terugdraaiing_buffer_trg()
40748
+ RETURNS trigger LANGUAGE plpgsql SECURITY DEFINER SET search_path TO 'public' AS $$
40749
+ BEGIN
40750
+ BEGIN
40751
+ PERFORM public._logistics_buffers_vervallen_herstel(NEW.order_id);
40752
+ EXCEPTION WHEN others THEN
40753
+ BEGIN
40754
+ INSERT INTO public.logistics_correctie_log (correctie, object_id, omschrijving)
40755
+ VALUES ('buffers_geannuleerd_fout', NEW.order_id, 'Losmaken uit samenvoeggroep na terugdraaien mislukt: ' || SQLERRM);
40756
+ EXCEPTION WHEN others THEN RAISE WARNING 'logistics_terugdraaiing_buffer_trg: %', SQLERRM;
40757
+ END;
40758
+ END;
40759
+ RETURN NULL;
40760
+ END $$;
40761
+ REVOKE ALL ON FUNCTION public.logistics_terugdraaiing_buffer_trg() FROM PUBLIC, anon, authenticated;
40762
+
40763
+ DO $do$ BEGIN
40764
+ IF to_regclass('public.voorraad_order_terugdraaiing') IS NOT NULL THEN
40765
+ DROP TRIGGER IF EXISTS trg_logistics_terugdraaiing_buffer ON public.voorraad_order_terugdraaiing;
40766
+ CREATE TRIGGER trg_logistics_terugdraaiing_buffer AFTER INSERT ON public.voorraad_order_terugdraaiing
40767
+ FOR EACH ROW EXECUTE FUNCTION public.logistics_terugdraaiing_buffer_trg();
40768
+ END IF;
40769
+ END $do$;
40770
+
40771
+ CREATE OR REPLACE FUNCTION public.logistics_buffers_herstel_status()
40772
+ RETURNS jsonb LANGUAGE plpgsql STABLE SECURITY DEFINER SET search_path TO 'public' AS $$
40773
+ BEGIN
40774
+ PERFORM public.logistics_acc_guard('ingelogd', 'logistics_buffers_herstel_status');
40775
+ RETURN jsonb_build_object(
40776
+ 'stand', (SELECT value::jsonb FROM public.logistics_plan_settings WHERE key = 'buffers_geannuleerd_herstel'),
40777
+ 'nogInGroep', (SELECT count(*) FROM public.orders o WHERE o.buffer_id IS NOT NULL
40778
+ AND (o.deleted_at IS NOT NULL OR o.cancelled_at IS NOT NULL)),
40779
+ 'fouten', coalesce((SELECT jsonb_agg(jsonb_build_object('op', l.created_at, 'melding', l.omschrijving) ORDER BY l.created_at DESC)
40780
+ FROM (SELECT * FROM public.logistics_correctie_log WHERE correctie = 'buffers_geannuleerd_fout'
40781
+ AND created_at > now() - interval '7 days' ORDER BY created_at DESC LIMIT 5) l), '[]'::jsonb));
40782
+ END $$;
40783
+ REVOKE ALL ON FUNCTION public.logistics_buffers_herstel_status() FROM PUBLIC, anon;
40784
+ GRANT EXECUTE ON FUNCTION public.logistics_buffers_herstel_status() TO authenticated, service_role;
40785
+
40786
+ CREATE OR REPLACE FUNCTION public.logistics_install_check_buffers_vervallen()
40787
+ RETURNS TABLE(onderdeel text, ok boolean, toelichting text) LANGUAGE sql STABLE SECURITY DEFINER SET search_path TO 'public' AS $$
40788
+ SELECT 'functie ' || f, to_regprocedure('public.' || f) IS NOT NULL,
40789
+ CASE WHEN to_regprocedure('public.' || f) IS NOT NULL THEN 'aanwezig' ELSE 'ontbreekt: werk de Engine bij via Supabase bijwerken' END
40790
+ FROM unnest(ARRAY['logistics_order_vervallen(uuid)', '_logistics_buffers_vervallen_herstel(uuid)',
40791
+ 'logistics_buffers_herstel_status()', 'logistics_orders_vervallen_buffer_trg()']) f
40792
+ UNION ALL
40793
+ SELECT 'trigger trg_logistics_orders_vervallen_buffer',
40794
+ EXISTS (SELECT 1 FROM pg_trigger g WHERE g.tgname = 'trg_logistics_orders_vervallen_buffer' AND g.tgrelid = 'public.orders'::regclass),
40795
+ 'Maakt geannuleerde orders los uit hun samenvoeggroep'
40796
+ $$;
40797
+ REVOKE ALL ON FUNCTION public.logistics_install_check_buffers_vervallen() FROM PUBLIC, anon;
40798
+ GRANT EXECUTE ON FUNCTION public.logistics_install_check_buffers_vervallen() TO authenticated, service_role;
40799
+
40800
+ CREATE OR REPLACE FUNCTION public.logistics_buffer_frozen(p_buffer uuid)
40801
+ RETURNS boolean
40802
+ LANGUAGE sql
40803
+ STABLE SECURITY DEFINER
40804
+ SET search_path TO 'public'
40805
+ AS $function$
40806
+ SELECT public.logistics_acc_guard('server', 'logistics_buffer_frozen');
40807
+ SELECT p_buffer IS NOT NULL AND (
40808
+ EXISTS (SELECT 1 FROM logistics_active.order_carts oc
40809
+ WHERE oc.buffer_id = p_buffer
40810
+ AND (public.logistics_cart_is_frozen(oc.id)
40811
+ OR EXISTS (SELECT 1 FROM public.logistics_control_scan s WHERE s.cart_id = oc.id AND s.status <> 'geannuleerd')
40812
+ OR EXISTS (SELECT 1 FROM public.logistics_slip_shipping_note n
40813
+ WHERE n.cart_id = oc.id AND (n.status IN ('in_printwachtrij','geprint') OR n.fulfillment_order_id IS NOT NULL))))
40814
+ OR EXISTS (SELECT 1 FROM public.orders x
40815
+ WHERE x.buffer_id = p_buffer AND x.deleted_at IS NULL AND x.cancelled_at IS NULL
40816
+ AND (EXISTS (SELECT 1 FROM public.logistics_control_scan s WHERE s.order_id = x.id AND s.status <> 'geannuleerd')
40817
+ OR EXISTS (SELECT 1 FROM public.logistics_slip_shipping_note n
40818
+ WHERE n.order_id = x.id AND (n.status IN ('in_printwachtrij','geprint') OR n.fulfillment_order_id IS NOT NULL))
40819
+ OR EXISTS (SELECT 1 FROM public.floriday_status_sync_queue q WHERE q.order_id = x.id AND q.state = 'done')))
40820
+ )
40821
+ $function$;
40822
+
40823
+ CREATE OR REPLACE FUNCTION public.logistics_buffer_handled(p_buffer uuid)
40824
+ RETURNS boolean
40825
+ LANGUAGE sql
40826
+ STABLE SECURITY DEFINER
40827
+ SET search_path TO 'public'
40828
+ AS $function$
40829
+ SELECT public.logistics_acc_guard('ingelogd', 'logistics_buffer_handled');
40830
+ WITH karren AS (
40831
+ SELECT oc.id, oc.cart_number, oc.order_id
40832
+ FROM logistics_active.order_carts oc
40833
+ WHERE oc.buffer_id = p_buffer
40834
+ ), leden AS (
40835
+ SELECT COALESCE(o.order_number, o.id::text) AS ref, o.id
40836
+ FROM public.orders o
40837
+ WHERE o.buffer_id = p_buffer
40838
+ AND o.deleted_at IS NULL AND o.cancelled_at IS NULL
40839
+ ), op_kar AS (
40840
+ SELECT DISTINCT k.id AS cart_id, COALESCE(o.order_number, o.id::text) AS ref
40841
+ FROM karren k
40842
+ JOIN logistics_active.order_cart_items ci ON ci.cart_id = k.id
40843
+ JOIN public.order_items oi ON oi.id = ci.order_item_id
40844
+ JOIN public.orders o ON o.id = oi.order_id AND o.deleted_at IS NULL AND o.cancelled_at IS NULL
40845
+ WHERE k.order_id IS NULL
40846
+ )
40847
+ SELECT EXISTS (SELECT 1 FROM karren)
40848
+ AND NOT EXISTS (
40849
+ SELECT 1 FROM karren k
40850
+ WHERE k.order_id IS NOT NULL
40851
+ AND NOT EXISTS (
40852
+ SELECT 1 FROM leden l
40853
+ WHERE l.id = k.order_id
40854
+ AND public.logistics_cart_handled(l.ref, k.cart_number))
40855
+ )
40856
+ AND NOT EXISTS (
40857
+ SELECT 1 FROM karren k
40858
+ WHERE k.order_id IS NULL
40859
+ AND (
40860
+ NOT EXISTS (SELECT 1 FROM op_kar ok WHERE ok.cart_id = k.id)
40861
+ OR EXISTS (
40862
+ SELECT 1 FROM op_kar ok
40863
+ WHERE ok.cart_id = k.id
40864
+ AND NOT public.logistics_cart_handled(ok.ref, k.cart_number))
40865
+ )
40866
+ );
40867
+ $function$;
40868
+
40869
+ CREATE OR REPLACE FUNCTION public.logistics_buffer_open_key(p_base_key text)
40870
+ RETURNS text
40871
+ LANGUAGE plpgsql
40872
+ SECURITY DEFINER
40873
+ SET search_path TO 'public'
40874
+ AS $function$
40875
+ DECLARE
40876
+ v_try int := 0;
40877
+ v_key text;
40878
+ v_id uuid;
40879
+ v_closed boolean;
40880
+ BEGIN
40881
+ PERFORM public.logistics_acc_guard('ingelogd', 'logistics_buffer_open_key');
40882
+ IF p_base_key IS NULL OR btrim(p_base_key) = '' THEN
40883
+ RETURN p_base_key;
40884
+ END IF;
40885
+
40886
+ LOOP
40887
+ v_key := CASE WHEN v_try = 0 THEN p_base_key ELSE p_base_key || '#' || v_try END;
40888
+ SELECT id INTO v_id FROM public.order_buffers WHERE buffer_key = v_key;
40889
+ IF v_id IS NULL THEN
40890
+ RETURN v_key;
40891
+ END IF;
40892
+
40893
+ SELECT b.closed_at IS NOT NULL
40894
+ OR (b.close_at IS NOT NULL AND b.close_at <= now())
40895
+ OR public.logistics_buffer_handled(b.id)
40896
+ OR EXISTS (SELECT 1 FROM public.orders x WHERE x.buffer_id = b.id AND x.plan_locked_at IS NOT NULL)
40897
+ OR EXISTS (SELECT 1 FROM public.orders x WHERE x.buffer_id = b.id AND public.logistics_order_vervallen(x.id))
40898
+ OR EXISTS (SELECT 1 FROM logistics_active.order_carts oc
40899
+ WHERE oc.buffer_id = b.id AND (oc.pickup_locked IS TRUE OR oc.is_complete IS TRUE))
40900
+ INTO v_closed
40901
+ FROM public.order_buffers b WHERE b.id = v_id;
40902
+
40903
+ IF NOT COALESCE(v_closed, false) THEN
40904
+ RETURN v_key;
40905
+ END IF;
40906
+
40907
+ UPDATE public.order_buffers
40908
+ SET closed_at = COALESCE(closed_at, now()), updated_at = now()
40909
+ WHERE id = v_id;
40910
+
40911
+ v_try := v_try + 1;
40912
+ IF v_try > 200 THEN
40913
+ RAISE EXCEPTION 'Geen open orderbuffer beschikbaar voor sleutel %', p_base_key;
40914
+ END IF;
40915
+ END LOOP;
40916
+ END;
40917
+ $function$;
40918
+
40919
+ CREATE OR REPLACE FUNCTION public.logistics_ensure_defaults()
40920
+ RETURNS jsonb
40921
+ LANGUAGE plpgsql
40922
+ SECURITY DEFINER
40923
+ SET search_path TO 'public'
40924
+ AS $function$
40925
+ DECLARE
40926
+ v_ladingdragers text[] := '{}';
40927
+ v_ladingdrager_velden text[] := '{}';
40928
+ v_vertalingen text[] := '{}';
40929
+ v_instellingen text[] := '{}';
40930
+ v_tijd text := NULL;
40931
+ v_strategie boolean := false;
40932
+ v_meldingen text[] := '{}';
40933
+ v_gewijzigd boolean;
40934
+ v_res jsonb;
40935
+ v_heeft_orders boolean;
40936
+ v_herstel jsonb := NULL;
40937
+ BEGIN
40938
+ PERFORM public.logistics_acc_guard('ingelogd', 'logistics_ensure_defaults');
40939
+ PERFORM pg_advisory_xact_lock(hashtext('logistics_ensure_defaults'));
40940
+
40941
+ WITH std(code, name, capacity_model, layers, len, wid, max_crates, max_boxes, max_kg, max_cm, mix, sort_order) AS (VALUES
40942
+ ('trolley','Stapelwagen','layered',5,135,56,NULL::int,NULL::int,500::numeric,190,true,10),
40943
+ ('danish_container','Deense container','layered',5,135,56,NULL,NULL,500,190,true,20),
40944
+ ('euro_rolcontainer','Euro-rolcontainer','layered',6,120,80,NULL,NULL,600,200,true,30),
40945
+ ('pallet','Pallet','stacked',4,120,80,NULL,40,800,180,true,40),
40946
+ ('euro_pallet','Euro-pallet','single',1,60,40,1,1,20,40,false,50),
40947
+ ('loose','Geen ladingdrager','none',1,0,0,NULL,NULL,NULL,NULL,false,60)
40948
+ ), ins AS (
40949
+ INSERT INTO public.load_carrier_types (code, name, capacity_model, layers_per_carrier, layer_length_cm, layer_width_cm,
40950
+ max_crates_per_layer, max_boxes, max_weight_kg, max_height_cm, allow_mixing, is_active, is_default, sort_order)
40951
+ SELECT s.code, s.name, s.capacity_model, s.layers, s.len, s.wid, s.max_crates, s.max_boxes, s.max_kg, s.max_cm, s.mix, true,
40952
+ (s.code = 'trolley' AND NOT EXISTS (SELECT 1 FROM public.load_carrier_types d WHERE d.is_default)), s.sort_order
40953
+ FROM std s
40954
+ WHERE NOT EXISTS (SELECT 1 FROM public.load_carrier_types l WHERE l.code = s.code)
40955
+ ON CONFLICT (code) DO NOTHING
40956
+ RETURNING code
40957
+ )
40958
+ SELECT coalesce(array_agg(code ORDER BY code), '{}') INTO v_ladingdragers FROM ins;
40959
+
40960
+ WITH std(code, name, capacity_model, layers, len, wid, max_crates, max_boxes, max_kg, max_cm) AS (VALUES
40961
+ ('trolley','Stapelwagen','layered',5,135,56,NULL::int,NULL::int,500::numeric,190),
40962
+ ('danish_container','Deense container','layered',5,135,56,NULL,NULL,500,190),
40963
+ ('euro_rolcontainer','Euro-rolcontainer','layered',6,120,80,NULL,NULL,600,200),
40964
+ ('pallet','Pallet','stacked',4,120,80,NULL,40,800,180),
40965
+ ('euro_pallet','Euro-pallet','single',1,60,40,1,1,20,40),
40966
+ ('loose','Geen ladingdrager','none',1,0,0,NULL,NULL,NULL,NULL)
40967
+ ), upd AS (
40968
+ UPDATE public.load_carrier_types l SET
40969
+ name = coalesce(nullif(btrim(l.name),''), s.name),
40970
+ capacity_model = coalesce(l.capacity_model, s.capacity_model),
40971
+ layers_per_carrier = coalesce(l.layers_per_carrier, s.layers),
40972
+ layer_length_cm = coalesce(l.layer_length_cm, s.len),
40973
+ layer_width_cm = coalesce(l.layer_width_cm, s.wid),
40974
+ max_crates_per_layer = coalesce(l.max_crates_per_layer, s.max_crates),
40975
+ max_boxes = coalesce(l.max_boxes, s.max_boxes),
40976
+ max_weight_kg = coalesce(l.max_weight_kg, s.max_kg),
40977
+ max_height_cm = coalesce(l.max_height_cm, s.max_cm),
40978
+ updated_at = now()
40979
+ FROM std s
40980
+ WHERE l.code = s.code AND NOT (l.code = ANY(v_ladingdragers))
40981
+ AND ( nullif(btrim(l.name),'') IS NULL
40982
+ OR l.capacity_model IS NULL OR l.layers_per_carrier IS NULL OR l.layer_length_cm IS NULL OR l.layer_width_cm IS NULL
40983
+ OR (l.max_crates_per_layer IS NULL AND s.max_crates IS NOT NULL)
40984
+ OR (l.max_boxes IS NULL AND s.max_boxes IS NOT NULL)
40985
+ OR (l.max_weight_kg IS NULL AND s.max_kg IS NOT NULL)
40986
+ OR (l.max_height_cm IS NULL AND s.max_cm IS NOT NULL))
40987
+ RETURNING l.code
40988
+ )
40989
+ SELECT coalesce(array_agg(code ORDER BY code), '{}') INTO v_ladingdrager_velden FROM upd;
40990
+
40991
+ IF to_regclass('public.logistics_floriday_carrier_map') IS NOT NULL THEN
40992
+ WITH v(f, c) AS (VALUES
40993
+ ('DANISH_TROLLEY','danish_container'), ('DANISH_TROLLEY_NON_CC_RFID','danish_container'),
40994
+ ('DANISH_TROLLEY_WATER_TRAY','danish_container'), ('ONE_THIRD_DANISH_TROLLEY','danish_container'),
40995
+ ('AUCTION_TROLLEY','trolley'),
40996
+ ('EURO_TROLLEY','euro_rolcontainer'), ('WATERING_CONTAINER','euro_rolcontainer'),
40997
+ ('EURO_PALLET','euro_pallet'), ('HT_EURO_PALLET','euro_pallet'),
40998
+ ('PALLET','pallet'), ('BLOCK_PALLET','pallet'), ('IFCO_PALLET','pallet'),
40999
+ ('CHEP_PALLET_BLUE','pallet'), ('CHEP_PALLET_RED','pallet'),
41000
+ ('NONE','loose')
41001
+ ), ins AS (
41002
+ INSERT INTO public.logistics_floriday_carrier_map (floriday_code, carrier_code)
41003
+ SELECT v.f, v.c FROM v
41004
+ WHERE NOT EXISTS (SELECT 1 FROM public.logistics_floriday_carrier_map m WHERE upper(m.floriday_code) = v.f)
41005
+ ON CONFLICT DO NOTHING
41006
+ RETURNING floriday_code
41007
+ )
41008
+ SELECT coalesce(array_agg(floriday_code ORDER BY floriday_code), '{}') INTO v_vertalingen FROM ins;
41009
+ END IF;
41010
+
41011
+ WITH v(k, w) AS (VALUES ('batch_size','10'), ('retention_days','7'), ('kick_debounce_seconds','5')),
41012
+ ins AS (
41013
+ INSERT INTO public.logistics_plan_settings (key, value)
41014
+ SELECT v.k, v.w FROM v WHERE NOT EXISTS (SELECT 1 FROM public.logistics_plan_settings p WHERE p.key = v.k)
41015
+ ON CONFLICT (key) DO NOTHING
41016
+ RETURNING key
41017
+ )
41018
+ SELECT coalesce(array_agg(key ORDER BY key), '{}') INTO v_instellingen FROM ins;
41019
+
41020
+ IF NOT EXISTS (SELECT 1 FROM public.logistics_time_settings) THEN
41021
+ v_heeft_orders := EXISTS (SELECT 1 FROM public.orders);
41022
+ IF v_heeft_orders THEN
41023
+ INSERT INTO public.logistics_time_settings (id, timezone, departure_rule_since, rule_160_since)
41024
+ VALUES (true, 'Europe/Amsterdam', NULL, NULL) ON CONFLICT DO NOTHING;
41025
+ v_tijd := 'aangemaakt zonder ingangsdata (er zijn al orders)';
41026
+ ELSE
41027
+ INSERT INTO public.logistics_time_settings (id, timezone, departure_rule_since, rule_160_since)
41028
+ VALUES (true, 'Europe/Amsterdam', now(), now()) ON CONFLICT DO NOTHING;
41029
+ v_tijd := 'aangemaakt met ingangsdata nu (lege shell)';
41030
+ END IF;
41031
+ END IF;
41032
+
41033
+ IF NOT EXISTS (SELECT 1 FROM public.logistics_strategies) THEN
41034
+ IF auth.uid() IS NULL THEN
41035
+ v_meldingen := array_append(v_meldingen, 'Basisstrategie wordt aangemaakt bij het openen van de Engine door een gebruiker'::text);
41036
+ ELSE
41037
+ WITH s AS (
41038
+ INSERT INTO public.logistics_strategies (name, is_active, user_id)
41039
+ VALUES ('Basisstrategie', true, auth.uid()) RETURNING id
41040
+ )
41041
+ INSERT INTO public.logistics_rule_configs (name, status, is_active, settings, strategy_id, user_id)
41042
+ SELECT 'Werkwijze Basisstrategie', 'actief',
41043
+ NOT EXISTS (SELECT 1 FROM public.logistics_rule_configs r WHERE r.is_active),
41044
+ '{}'::jsonb, s.id, auth.uid() FROM s;
41045
+ v_strategie := true;
41046
+ END IF;
41047
+ END IF;
41048
+ IF NOT EXISTS (SELECT 1 FROM public.logistics_strategies WHERE is_active) THEN
41049
+ v_meldingen := array_append(v_meldingen, 'Geen actieve strategie'::text);
41050
+ END IF;
41051
+
41052
+ IF to_regclass('public.pick_station_types') IS NOT NULL AND NOT EXISTS (SELECT 1 FROM public.pick_station_types) THEN
41053
+ v_meldingen := array_append(v_meldingen, 'Pickstation-soorten leeg (Pickmodule)'::text);
41054
+ END IF;
41055
+ IF to_regclass('public.pick_screen_document_types') IS NOT NULL AND NOT EXISTS (SELECT 1 FROM public.pick_screen_document_types) THEN
41056
+ v_meldingen := array_append(v_meldingen, 'Pickscherm-documentsoorten leeg (Pickmodule)'::text);
41057
+ END IF;
41058
+ IF to_regclass('public.cart_planning_settings') IS NOT NULL AND NOT EXISTS (SELECT 1 FROM public.cart_planning_settings) THEN
41059
+ v_meldingen := array_append(v_meldingen, 'Karplanning-instellingen leeg (Verkoop)'::text);
41060
+ END IF;
41061
+
41062
+ -- Samenvoeggroepen: geannuleerde/verwijderde orders losmaken (herhaalbaar; tweede keer zonder effect).
41063
+ BEGIN
41064
+ v_herstel := public._logistics_buffers_vervallen_herstel(NULL);
41065
+ EXCEPTION WHEN others THEN
41066
+ v_meldingen := array_append(v_meldingen, ('Herstel samenvoeggroepen mislukt: ' || SQLERRM)::text);
41067
+ END;
41068
+
41069
+ v_gewijzigd := cardinality(v_ladingdragers) + cardinality(v_ladingdrager_velden) + cardinality(v_vertalingen)
41070
+ + cardinality(v_instellingen) > 0 OR v_tijd IS NOT NULL OR v_strategie;
41071
+ v_res := jsonb_build_object(
41072
+ 'gewijzigd', v_gewijzigd, 'op', now(),
41073
+ 'ladingdragers', to_jsonb(v_ladingdragers),
41074
+ 'ladingdrager_velden', to_jsonb(v_ladingdrager_velden),
41075
+ 'floriday_vertalingen', to_jsonb(v_vertalingen),
41076
+ 'plan_instellingen', to_jsonb(v_instellingen),
41077
+ 'tijdinstellingen', v_tijd,
41078
+ 'basisstrategie', v_strategie,
41079
+ 'meldingen', to_jsonb(v_meldingen),
41080
+ 'samenvoeggroepen_hersteld', coalesce((v_herstel->>'groepen')::int, 0));
41081
+
41082
+ IF v_gewijzigd THEN
41083
+ INSERT INTO public.logistics_plan_settings (key, value) VALUES ('defaults_last_result', v_res::text)
41084
+ ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value, updated_at = now();
41085
+ END IF;
41086
+ RETURN v_res;
41087
+ END $function$;
@@ -1 +1 @@
1
- {"version":3,"file":"StandaardgegevensCard.d.ts","sourceRoot":"","sources":["../../src/components/StandaardgegevensCard.tsx"],"names":[],"mappings":"AAaA,wBAAgB,qBAAqB,gCA8CpC"}
1
+ {"version":3,"file":"StandaardgegevensCard.d.ts","sourceRoot":"","sources":["../../src/components/StandaardgegevensCard.tsx"],"names":[],"mappings":"AAaA,wBAAgB,qBAAqB,gCA+CpC"}
@@ -24,7 +24,7 @@ export function StandaardgegevensCard() {
24
24
  const eerder = aangevuldeOnderdelen(laatste);
25
25
  const meldingen = Array.isArray(resultaat?.meldingen) ? resultaat.meldingen : [];
26
26
  return (_jsxs(Card, { children: [_jsxs(CardHeader, { children: [_jsxs(CardTitle, { className: "flex items-center gap-2 text-base", children: [_jsx(Database, { className: "h-4 w-4" }), " Standaardgegevens", fout ? _jsx(Badge, { variant: "destructive", children: "Controle mislukt" })
27
- : resultaat ? _jsx(Badge, { variant: "secondary", children: nu.length ? "Aangevuld" : "Compleet" }) : null] }), _jsx(CardDescription, { children: "Ladingdragers, Floriday-vertalingen, planinstellingen, tijdinstelling en strategie." })] }), _jsxs(CardContent, { className: "space-y-2 text-sm", children: [fout && _jsxs("p", { className: "text-destructive", children: ["Kon niet controleren: ", fout] }), nu.length > 0 && (_jsxs("div", { children: [_jsx("p", { className: "font-medium", children: "Zojuist aangevuld:" }), _jsx("ul", { className: "list-disc pl-5", children: nu.map((t) => _jsx("li", { children: t }, t)) })] })), nu.length === 0 && eerder.length > 0 && laatste?.op && (_jsxs("div", { children: [_jsxs("p", { className: "text-muted-foreground", children: ["Laatst aangevuld op ", new Date(laatste.op).toLocaleString("nl-NL"), ":"] }), _jsx("ul", { className: "list-disc pl-5 text-muted-foreground", children: eerder.map((t) => _jsx("li", { children: t }, t)) })] })), resultaat && nu.length === 0 && eerder.length === 0 && _jsx("p", { className: "text-muted-foreground", children: "Alle standaardgegevens zijn aanwezig." }), meldingen.map((m) => _jsx("p", { className: "text-destructive", children: m }, m)), _jsx(OnderhoudStatus, {}), _jsx(ProductiebonStatus, {})] })] }));
27
+ : resultaat ? _jsx(Badge, { variant: "secondary", children: nu.length ? "Aangevuld" : "Compleet" }) : null] }), _jsx(CardDescription, { children: "Ladingdragers, Floriday-vertalingen, planinstellingen, tijdinstelling en strategie." })] }), _jsxs(CardContent, { className: "space-y-2 text-sm", children: [fout && _jsxs("p", { className: "text-destructive", children: ["Kon niet controleren: ", fout] }), nu.length > 0 && (_jsxs("div", { children: [_jsx("p", { className: "font-medium", children: "Zojuist aangevuld:" }), _jsx("ul", { className: "list-disc pl-5", children: nu.map((t) => _jsx("li", { children: t }, t)) })] })), nu.length === 0 && eerder.length > 0 && laatste?.op && (_jsxs("div", { children: [_jsxs("p", { className: "text-muted-foreground", children: ["Laatst aangevuld op ", new Date(laatste.op).toLocaleString("nl-NL"), ":"] }), _jsx("ul", { className: "list-disc pl-5 text-muted-foreground", children: eerder.map((t) => _jsx("li", { children: t }, t)) })] })), resultaat && nu.length === 0 && eerder.length === 0 && _jsx("p", { className: "text-muted-foreground", children: "Alle standaardgegevens zijn aanwezig." }), meldingen.map((m) => _jsx("p", { className: "text-destructive", children: m }, m)), _jsx(OnderhoudStatus, {}), _jsx(ProductiebonStatus, {}), _jsx(SamenvoeggroepenStatus, {})] })] }));
28
28
  }
29
29
  // 1.0.228: wachtrij per status, oudste trace-regel en laatste opruimronde.
30
30
  function OnderhoudStatus() {
@@ -40,6 +40,30 @@ function OnderhoudStatus() {
40
40
  const prune = o.laatsteOpruimronde;
41
41
  return (_jsxs("div", { className: "border-t pt-2 text-muted-foreground", children: [_jsx("p", { className: "font-medium text-foreground", children: "Onderhoud" }), _jsxs("p", { children: ["Wachtrij: ", wachtrij.length ? wachtrij.map(([s, n]) => `${s} ${n}`).join(" · ") : "leeg"] }), _jsxs("p", { children: ["Oudste trace-regel: ", o.oudsteTrace ? new Date(o.oudsteTrace).toLocaleString("nl-NL") : "geen"] }), _jsxs("p", { children: ["Laatste opruimronde: ", prune?.op ? `${new Date(prune.op).toLocaleString("nl-NL")} (${prune.verwijderd ?? 0} verwijderd)` : "nog niet"] })] }));
42
42
  }
43
+ // Geannuleerde/verwijderde orders die uit een samenvoeggroep zijn gehaald.
44
+ function SamenvoeggroepenStatus() {
45
+ const [info, setInfo] = useState(null);
46
+ const [fout, setFout] = useState(null);
47
+ useEffect(() => {
48
+ supabase.rpc("logistics_buffers_herstel_status").then(({ data, error }) => {
49
+ if (error) {
50
+ console.error("logistics_buffers_herstel_status", error);
51
+ setFout(error.message);
52
+ return;
53
+ }
54
+ setInfo(data && typeof data === "object" ? data : null);
55
+ });
56
+ }, []);
57
+ if (fout)
58
+ return _jsx("p", { className: "text-destructive", children: "Samenvoeggroepen: status kon niet worden gelezen." });
59
+ if (!info)
60
+ return null;
61
+ const fouten = Array.isArray(info.fouten) ? info.fouten : [];
62
+ const groepen = info.stand?.totaalGroepen ?? 0;
63
+ return (_jsxs("div", { className: "border-t pt-2", children: [_jsx("p", { className: "font-medium", children: "Samenvoeggroepen" }), _jsx("p", { className: "text-muted-foreground", children: groepen > 0
64
+ ? `${groepen} groep(en) hersteld: ${info.stand?.totaalOrders ?? 0} geannuleerde of verwijderde order(s) eruit gehaald${info.stand?.laatsteOp ? ` (laatst ${formatDatumTijd(info.stand.laatsteOp)})` : ""}.`
65
+ : "Geen groepen met geannuleerde orders gevonden." }), (info.nogInGroep ?? 0) > 0 && (_jsxs("p", { className: "text-destructive", children: ["Nog ", info.nogInGroep, " geannuleerde order(s) in een groep."] })), fouten.map((f, i) => (_jsxs("p", { className: "text-destructive", children: [formatDatumTijd(f.op), ": ", f.melding] }, i)))] }));
66
+ }
43
67
  function ProductiebonStatus() {
44
68
  const [info, setInfo] = useState(null);
45
69
  const [fout, setFout] = useState(null);