@flowselections/logistiek-engine 1.0.202 → 1.0.203
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 +316 -0
- package/dist-lib/components/BeladingIndelingCard.d.ts.map +1 -1
- package/dist-lib/components/BeladingIndelingCard.js +2 -2
- package/dist-lib/integrations/supabase/types.d.ts +175 -0
- package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
- package/dist-lib/lib/extraPlaten.d.ts +9 -0
- package/dist-lib/lib/extraPlaten.d.ts.map +1 -1
- package/dist-lib/lib/extraPlaten.js +15 -0
- package/dist-lib/lib/floridayCartCarriers.d.ts +3 -0
- package/dist-lib/lib/floridayCartCarriers.d.ts.map +1 -1
- package/dist-lib/lib/floridayCartCarriers.js +6 -1
- package/dist-lib/lib/floridayPush.server.d.ts.map +1 -1
- package/dist-lib/lib/floridayPush.server.js +2 -1
- package/dist-lib/lib/moduleVersion.d.ts +1 -1
- package/dist-lib/lib/moduleVersion.js +1 -1
- package/dist-lib/lib/shippingNoteQueue.server.d.ts.map +1 -1
- package/dist-lib/lib/shippingNoteQueue.server.js +11 -0
- package/dist-lib/lib/stickerQueue.server.d.ts.map +1 -1
- package/dist-lib/lib/stickerQueue.server.js +13 -0
- package/package.json +1 -1
|
@@ -34940,3 +34940,319 @@ DO $d$ BEGIN
|
|
|
34940
34940
|
SELECT id, order_number, buffer_id INTO o FROM public.orders WHERE id = p_order_id;');
|
|
34941
34941
|
END IF;
|
|
34942
34942
|
END $d$;
|
|
34943
|
+
|
|
34944
|
+
-- 1.0.201 vervolg — kar_te_hoog, guards pakbonfuncties, productoordeel, Beheerlink (additief, idempotent)
|
|
34945
|
+
-- 1.0.201 vervolg: te hoge karren, guards op pakbonfuncties, productoordeel, Beheerlink altijd gevuld.
|
|
34946
|
+
|
|
34947
|
+
-- Rekenhulp zonder gegevens: blokkade voor één kar.
|
|
34948
|
+
CREATE OR REPLACE FUNCTION public.logistics_kar_blokkade(p_kar text, p_lagen numeric, p_max numeric)
|
|
34949
|
+
RETURNS jsonb LANGUAGE sql IMMUTABLE SET search_path TO 'public' AS $$
|
|
34950
|
+
SELECT CASE WHEN p_lagen IS NOT NULL AND coalesce(p_max, 0) > 0 AND p_lagen > p_max + 0.001
|
|
34951
|
+
THEN jsonb_build_object('teHoog', true, 'blokkade', jsonb_build_object('code', 'kar_te_hoog',
|
|
34952
|
+
'tekst', format('Kar %s heeft %s lagen, maximaal %s. Deel de order opnieuw in of pas de belading aan.',
|
|
34953
|
+
coalesce(nullif(btrim(p_kar), ''), '?'),
|
|
34954
|
+
replace(trim_scale(round(p_lagen, 2))::text, '.', ','),
|
|
34955
|
+
replace(trim_scale(round(p_max, 2))::text, '.', ','))))
|
|
34956
|
+
ELSE jsonb_build_object('teHoog', false, 'blokkade', NULL) END
|
|
34957
|
+
$$;
|
|
34958
|
+
|
|
34959
|
+
-- Karren aanvullen (platen + blokkade). Geeft alleen de meegegeven kar terug, maar leest ladingdragernamen: guard.
|
|
34960
|
+
CREATE OR REPLACE FUNCTION public.logistics_cart_platen_verrijk(p_cart jsonb)
|
|
34961
|
+
RETURNS jsonb LANGUAGE plpgsql STABLE SECURITY DEFINER SET search_path TO 'public' AS $$
|
|
34962
|
+
DECLARE v_l numeric := nullif(p_cart->>'layersUsed', '')::numeric;
|
|
34963
|
+
BEGIN
|
|
34964
|
+
PERFORM public.logistics_acc_guard('ingelogd', 'logistics_cart_platen_verrijk');
|
|
34965
|
+
RETURN p_cart || jsonb_build_object(
|
|
34966
|
+
'additionalLayers', public.logistics_extra_platen(v_l),
|
|
34967
|
+
'ladingdragerTekst', public.logistics_ladingdrager_tekst(p_cart->>'carrierType', v_l,
|
|
34968
|
+
coalesce(nullif(p_cart->>'layerUtilizationPct', '')::numeric, nullif(p_cart->>'occupancyPct', '')::numeric)))
|
|
34969
|
+
|| public.logistics_kar_blokkade(coalesce(p_cart->>'cartNumber', p_cart->>'cart_number', p_cart->>'seq'), v_l,
|
|
34970
|
+
nullif(p_cart->>'maxLayers', '')::numeric);
|
|
34971
|
+
END $$;
|
|
34972
|
+
|
|
34973
|
+
-- Guard op de kopie van de pakbonfunctie.
|
|
34974
|
+
DO $d$ BEGIN
|
|
34975
|
+
IF to_regprocedure('public.logistics_cart_slip_plan_v200(uuid)') IS NOT NULL
|
|
34976
|
+
AND position('logistics_acc_guard' IN pg_get_functiondef('public.logistics_cart_slip_plan_v200(uuid)'::regprocedure)) = 0 THEN
|
|
34977
|
+
EXECUTE regexp_replace(pg_get_functiondef('public.logistics_cart_slip_plan_v200(uuid)'::regprocedure),
|
|
34978
|
+
'\nBEGIN\n', E'\nBEGIN\n PERFORM public.logistics_acc_guard(''ingelogd'', ''logistics_cart_slip_plan_v200'');\n');
|
|
34979
|
+
END IF;
|
|
34980
|
+
END $d$;
|
|
34981
|
+
|
|
34982
|
+
-- Pakbonfunctie: guard + per kar teHoog/blokkade.
|
|
34983
|
+
DO $do$ BEGIN
|
|
34984
|
+
IF to_regprocedure('public.logistics_cart_slip_plan_v200(uuid)') IS NOT NULL THEN
|
|
34985
|
+
EXECUTE $f$
|
|
34986
|
+
CREATE OR REPLACE FUNCTION public.logistics_cart_slip_plan(p_order_id uuid)
|
|
34987
|
+
RETURNS jsonb LANGUAGE plpgsql STABLE SECURITY DEFINER SET search_path TO 'public' AS $b$
|
|
34988
|
+
DECLARE b jsonb;
|
|
34989
|
+
BEGIN
|
|
34990
|
+
PERFORM public.logistics_acc_guard('ingelogd', 'logistics_cart_slip_plan');
|
|
34991
|
+
b := public.logistics_cart_slip_plan_v200(p_order_id);
|
|
34992
|
+
IF b IS NULL OR jsonb_typeof(b->'carts') IS DISTINCT FROM 'array' THEN RETURN b; END IF;
|
|
34993
|
+
RETURN b || jsonb_build_object('carts', (
|
|
34994
|
+
SELECT coalesce(jsonb_agg(public.logistics_cart_platen_verrijk(t.e) ORDER BY t.o), '[]'::jsonb)
|
|
34995
|
+
FROM jsonb_array_elements(b->'carts') WITH ORDINALITY t(e, o)));
|
|
34996
|
+
END $b$;
|
|
34997
|
+
$f$;
|
|
34998
|
+
END IF;
|
|
34999
|
+
END $do$;
|
|
35000
|
+
|
|
35001
|
+
-- Te hoge karren per order (via de buffer, dus ook gedeelde karren).
|
|
35002
|
+
CREATE OR REPLACE FUNCTION public.logistics_order_te_hoge_karren(p_order_ids uuid[])
|
|
35003
|
+
RETURNS TABLE(order_id uuid, cart_id uuid, cart_number integer, lagen numeric, max_lagen numeric, blokkade jsonb)
|
|
35004
|
+
LANGUAGE plpgsql STABLE SET search_path TO 'public' AS $$
|
|
35005
|
+
BEGIN
|
|
35006
|
+
PERFORM public.logistics_acc_guard('ingelogd', 'logistics_order_te_hoge_karren');
|
|
35007
|
+
RETURN QUERY
|
|
35008
|
+
SELECT u.order_id, u.cart_id, coalesce(u.display_seq, u.cart_number), c.total_layers_used::numeric, c.max_layers::numeric,
|
|
35009
|
+
public.logistics_kar_blokkade(coalesce(u.display_seq, u.cart_number)::text, c.total_layers_used, c.max_layers)->'blokkade'
|
|
35010
|
+
FROM public.logistics_order_unit_carts_core(p_order_ids) u
|
|
35011
|
+
JOIN logistics_active.order_carts c ON c.id = u.cart_id
|
|
35012
|
+
WHERE (public.logistics_kar_blokkade(NULL, c.total_layers_used, c.max_layers)->>'teHoog')::boolean;
|
|
35013
|
+
END $$;
|
|
35014
|
+
|
|
35015
|
+
-- Reden bij geen indeling: kar_te_hoog eerst, actieUrl altijd gevuld.
|
|
35016
|
+
CREATE OR REPLACE FUNCTION public.logistics_order_geen_indeling_core(p_order_id uuid)
|
|
35017
|
+
RETURNS jsonb LANGUAGE plpgsql STABLE SECURITY DEFINER SET search_path TO 'public' AS $$
|
|
35018
|
+
DECLARE o record; q record; d record; t record; v_code text; v_tekst text; v_soort text; v_actie text;
|
|
35019
|
+
c_beheer constant text := '/logistiek-engine/beheer';
|
|
35020
|
+
BEGIN
|
|
35021
|
+
PERFORM public.logistics_acc_guard('ingelogd', 'logistics_order_geen_indeling_core');
|
|
35022
|
+
SELECT id, order_number, buffer_id INTO o FROM public.orders WHERE id = p_order_id;
|
|
35023
|
+
IF NOT FOUND THEN RETURN NULL; END IF;
|
|
35024
|
+
SELECT * INTO t FROM public.logistics_order_te_hoge_karren(ARRAY[p_order_id]) ORDER BY cart_number LIMIT 1;
|
|
35025
|
+
IF FOUND THEN
|
|
35026
|
+
v_code := 'kar_te_hoog'; v_tekst := t.blokkade->>'tekst'; v_actie := 'belading_aanpassen';
|
|
35027
|
+
ELSE
|
|
35028
|
+
SELECT status, reason, reason_code INTO q FROM public.logistics_plan_queue
|
|
35029
|
+
WHERE order_id = p_order_id ORDER BY updated_at DESC NULLS LAST LIMIT 1;
|
|
35030
|
+
IF FOUND AND q.status = 'geblokkeerd' THEN
|
|
35031
|
+
v_code := coalesce(q.reason_code, 'geblokkeerd'); v_tekst := q.reason;
|
|
35032
|
+
ELSE
|
|
35033
|
+
SELECT dd.result, dd.explanation, dd.input_snapshot INTO d
|
|
35034
|
+
FROM public.logistics_decisions dd
|
|
35035
|
+
WHERE dd.slot = 'beladingindeling'
|
|
35036
|
+
AND ((dd.subject_type = 'order' AND dd.subject_id = o.id::text)
|
|
35037
|
+
OR (dd.subject_type = 'buffer' AND o.buffer_id IS NOT NULL AND dd.subject_id = o.buffer_id::text)
|
|
35038
|
+
OR (o.order_number IS NOT NULL AND coalesce(dd.input_snapshot->'members', '[]'::jsonb) ? o.order_number))
|
|
35039
|
+
ORDER BY dd.created_at DESC LIMIT 1;
|
|
35040
|
+
IF FOUND AND coalesce((d.result->>'blocked')::boolean, false) THEN
|
|
35041
|
+
v_code := coalesce(d.result->>'reason_code', 'geblokkeerd'); v_tekst := d.explanation;
|
|
35042
|
+
v_soort := d.input_snapshot->>'soort';
|
|
35043
|
+
ELSIF q.status IN ('pending', 'running') THEN
|
|
35044
|
+
v_code := 'wacht_op_indeling'; v_tekst := 'De karindeling wordt gemaakt; dit duurt meestal enkele seconden.';
|
|
35045
|
+
ELSE
|
|
35046
|
+
v_code := 'onbekend';
|
|
35047
|
+
v_tekst := 'Er is nog geen karindeling en er is geen blokkade vastgelegd. De minuutronde probeert het opnieuw; blijft dit staan, meld het aan beheer.';
|
|
35048
|
+
END IF;
|
|
35049
|
+
END IF;
|
|
35050
|
+
IF v_code LIKE 'belading%' OR v_code = 'ladingdrager_niet_in_beheer' THEN v_actie := 'belading_aanpassen'; END IF;
|
|
35051
|
+
END IF;
|
|
35052
|
+
RETURN jsonb_build_object('code', v_code, 'soort', v_soort,
|
|
35053
|
+
'tekst', coalesce(nullif(btrim(v_tekst), ''), 'Geen indeling; de reden is niet bekend.'),
|
|
35054
|
+
'actie', coalesce(v_actie, 'beheer'),
|
|
35055
|
+
'actieLabel', CASE WHEN v_actie IS NOT NULL THEN 'Belading aanpassen' ELSE 'Naar Engine-beheer' END,
|
|
35056
|
+
'actieUrl', CASE WHEN v_actie IS NOT NULL THEN c_beheer || '#gegevens-voor-karindeling' ELSE c_beheer END);
|
|
35057
|
+
END $$;
|
|
35058
|
+
|
|
35059
|
+
CREATE OR REPLACE FUNCTION public.logistics_order_geen_indeling_many(p_order_ids uuid[])
|
|
35060
|
+
RETURNS TABLE(order_id uuid, geen_indeling jsonb) LANGUAGE plpgsql STABLE SECURITY DEFINER SET search_path TO 'public' AS $$
|
|
35061
|
+
BEGIN
|
|
35062
|
+
PERFORM public.logistics_acc_guard('ingelogd', 'logistics_order_geen_indeling_many');
|
|
35063
|
+
IF coalesce(cardinality(p_order_ids), 0) > 1000 THEN
|
|
35064
|
+
RAISE EXCEPTION 'Maximaal 1000 orders per aanroep (ontvangen: %).', cardinality(p_order_ids) USING ERRCODE = '22023';
|
|
35065
|
+
END IF;
|
|
35066
|
+
RETURN QUERY
|
|
35067
|
+
WITH th AS (SELECT DISTINCT h.order_id FROM public.logistics_order_te_hoge_karren(p_order_ids) h)
|
|
35068
|
+
SELECT b.order_id,
|
|
35069
|
+
CASE WHEN (b.state IN ('geen_indeling', 'deels') AND NOT coalesce(x.full_fulfilled, false)) OR th.order_id IS NOT NULL
|
|
35070
|
+
THEN public.logistics_order_geen_indeling_core(b.order_id) END
|
|
35071
|
+
FROM public.logistics_order_plan_coverage_list_v152(p_order_ids) b
|
|
35072
|
+
LEFT JOIN public.logistics_external_fulfillment x ON x.order_id = b.order_id
|
|
35073
|
+
LEFT JOIN th ON th.order_id = b.order_id;
|
|
35074
|
+
END $$;
|
|
35075
|
+
|
|
35076
|
+
CREATE OR REPLACE FUNCTION public.logistics_order_plan_coverage_list_v201(p_order_ids uuid[])
|
|
35077
|
+
RETURNS TABLE(order_id uuid, order_number text, buffer_id uuid, state text, label text, progress_pct integer, reason text,
|
|
35078
|
+
cart_count integer, pick_row_count integer, pick_status text, picked_at timestamptz,
|
|
35079
|
+
buffer_order_count integer, buffer_cart_count integer, geen_indeling jsonb)
|
|
35080
|
+
LANGUAGE plpgsql STABLE SECURITY DEFINER SET search_path TO 'public' AS $$
|
|
35081
|
+
BEGIN
|
|
35082
|
+
PERFORM public.logistics_acc_guard('ingelogd', 'logistics_order_plan_coverage_list_v201');
|
|
35083
|
+
IF coalesce(cardinality(p_order_ids), 0) > 1000 THEN
|
|
35084
|
+
RAISE EXCEPTION 'Maximaal 1000 orders per aanroep (ontvangen: %).', cardinality(p_order_ids) USING ERRCODE = '22023';
|
|
35085
|
+
END IF;
|
|
35086
|
+
RETURN QUERY
|
|
35087
|
+
WITH th AS (SELECT DISTINCT h.order_id FROM public.logistics_order_te_hoge_karren(p_order_ids) h)
|
|
35088
|
+
SELECT l.order_id, l.order_number, l.buffer_id, l.state,
|
|
35089
|
+
CASE WHEN th.order_id IS NOT NULL THEN 'Ingedeeld — kar te hoog' ELSE l.label END,
|
|
35090
|
+
l.progress_pct, l.reason, l.cart_count, l.pick_row_count, l.pick_status, l.picked_at,
|
|
35091
|
+
l.buffer_order_count, l.buffer_cart_count,
|
|
35092
|
+
CASE WHEN l.state IN ('geen_indeling', 'deels') OR th.order_id IS NOT NULL THEN public.logistics_order_geen_indeling_core(l.order_id) END
|
|
35093
|
+
FROM public.logistics_order_plan_coverage_list(p_order_ids) l
|
|
35094
|
+
LEFT JOIN th ON th.order_id = l.order_id;
|
|
35095
|
+
END $$;
|
|
35096
|
+
|
|
35097
|
+
CREATE OR REPLACE FUNCTION public.logistics_order_plan_coverage(p_order_id uuid)
|
|
35098
|
+
RETURNS jsonb LANGUAGE plpgsql STABLE SECURITY DEFINER SET search_path TO 'public' AS $$
|
|
35099
|
+
DECLARE b jsonb; x jsonb; v_hoog boolean;
|
|
35100
|
+
BEGIN
|
|
35101
|
+
PERFORM public.logistics_acc_guard('ingelogd', 'logistics_order_plan_coverage');
|
|
35102
|
+
b := public.logistics_order_plan_coverage_v165(p_order_id);
|
|
35103
|
+
x := public.logistics_order_external_state(p_order_id);
|
|
35104
|
+
IF jsonb_typeof(b->'carts') = 'array' THEN
|
|
35105
|
+
b := b || jsonb_build_object('carts', (
|
|
35106
|
+
SELECT coalesce(jsonb_agg(
|
|
35107
|
+
CASE WHEN c.id IS NULL THEN t.e || jsonb_build_object('additionalLayers', NULL, 'layersUsed', NULL, 'teHoog', false, 'blokkade', NULL)
|
|
35108
|
+
ELSE public.logistics_cart_platen_verrijk(t.e || jsonb_build_object(
|
|
35109
|
+
'layersUsed', c.total_layers_used, 'occupancyPct', c.occupancy_pct, 'maxLayers', c.max_layers,
|
|
35110
|
+
'layerUtilizationPct', CASE WHEN coalesce(c.max_layers, 0) > 0 AND c.total_layers_used IS NOT NULL
|
|
35111
|
+
THEN round(100.0 * c.total_layers_used / c.max_layers, 1) END)) END
|
|
35112
|
+
ORDER BY t.o), '[]'::jsonb)
|
|
35113
|
+
FROM jsonb_array_elements(b->'carts') WITH ORDINALITY t(e, o)
|
|
35114
|
+
LEFT JOIN logistics_active.order_carts c ON c.id::text = t.e->>'cartId'));
|
|
35115
|
+
END IF;
|
|
35116
|
+
IF x IS NOT NULL AND (x->>'full')::boolean THEN
|
|
35117
|
+
b := b || jsonb_build_object('state', 'afgehandeld_extern', 'label', 'Afgehandeld in Floriday',
|
|
35118
|
+
'reason', 'De order is in Floriday afgehandeld; de Engine plant en print er niets meer voor.');
|
|
35119
|
+
END IF;
|
|
35120
|
+
v_hoog := EXISTS (SELECT 1 FROM jsonb_array_elements(coalesce(b->'carts', '[]'::jsonb)) e WHERE (e->>'teHoog')::boolean)
|
|
35121
|
+
OR EXISTS (SELECT 1 FROM public.logistics_order_te_hoge_karren(ARRAY[p_order_id]));
|
|
35122
|
+
IF v_hoog AND b->>'state' IS DISTINCT FROM 'afgehandeld_extern' THEN
|
|
35123
|
+
b := b || jsonb_build_object('label', 'Ingedeeld — kar te hoog');
|
|
35124
|
+
END IF;
|
|
35125
|
+
b := b || jsonb_build_object('geenIndeling',
|
|
35126
|
+
CASE WHEN b->>'state' IN ('geen_indeling', 'deels') OR (v_hoog AND b->>'state' IS DISTINCT FROM 'afgehandeld_extern')
|
|
35127
|
+
THEN public.logistics_order_geen_indeling_core(p_order_id) END);
|
|
35128
|
+
RETURN b || jsonb_build_object('externalFulfilled', x);
|
|
35129
|
+
END $$;
|
|
35130
|
+
|
|
35131
|
+
-- Kar-check voor de server (sticker- en verzendbriefronde).
|
|
35132
|
+
CREATE OR REPLACE FUNCTION public.logistics_cart_te_hoog(p_cart_id uuid)
|
|
35133
|
+
RETURNS jsonb LANGUAGE plpgsql STABLE SECURITY DEFINER SET search_path TO 'public' AS $$
|
|
35134
|
+
DECLARE c record;
|
|
35135
|
+
BEGIN
|
|
35136
|
+
PERFORM public.logistics_acc_guard('ingelogd', 'logistics_cart_te_hoog');
|
|
35137
|
+
SELECT oc.cart_number, oc.total_layers_used, oc.max_layers INTO c FROM logistics_active.order_carts oc WHERE oc.id = p_cart_id;
|
|
35138
|
+
IF NOT FOUND THEN RETURN jsonb_build_object('teHoog', false, 'blokkade', NULL); END IF;
|
|
35139
|
+
RETURN public.logistics_kar_blokkade(c.cart_number::text, c.total_layers_used, c.max_layers);
|
|
35140
|
+
END $$;
|
|
35141
|
+
|
|
35142
|
+
-- LayerSpace (zelfde algoritme als dimensionPlanner.ts): maximaal aantal fusten per laag, beide startrichtingen.
|
|
35143
|
+
CREATE OR REPLACE FUNCTION public.logistics_max_fusten_per_laag(p_ll numeric, p_lw numeric, p_fl numeric, p_fw numeric)
|
|
35144
|
+
RETURNS integer LANGUAGE plpgsql IMMUTABLE SET search_path TO 'public' AS $$
|
|
35145
|
+
DECLARE best int := 0; rnd int; a0 numeric; b0 numeric; n int; placed boolean;
|
|
35146
|
+
dep numeric[]; used numeric[]; total numeric; i int; k int; oa numeric; ob numeric; e numeric := 1e-9;
|
|
35147
|
+
BEGIN
|
|
35148
|
+
IF coalesce(p_ll,0) <= 0 OR coalesce(p_lw,0) <= 0 OR coalesce(p_fl,0) <= 0 OR coalesce(p_fw,0) <= 0 THEN RETURN NULL; END IF;
|
|
35149
|
+
FOR rnd IN 1..2 LOOP
|
|
35150
|
+
IF rnd = 1 THEN a0 := p_fl; b0 := p_fw; ELSE a0 := p_fw; b0 := p_fl; END IF;
|
|
35151
|
+
dep := '{}'; used := '{}'; total := 0; n := 0;
|
|
35152
|
+
LOOP
|
|
35153
|
+
EXIT WHEN n >= 100000;
|
|
35154
|
+
placed := false;
|
|
35155
|
+
FOR k IN 1..2 LOOP
|
|
35156
|
+
IF k = 1 THEN oa := a0; ob := b0; ELSE oa := b0; ob := a0; END IF;
|
|
35157
|
+
CONTINUE WHEN oa > p_ll OR ob > p_lw;
|
|
35158
|
+
FOR i IN 1..coalesce(array_length(dep,1),0) LOOP
|
|
35159
|
+
CONTINUE WHEN NOT (used[i] + oa <= p_ll + e);
|
|
35160
|
+
IF ob <= dep[i] + e THEN used[i] := used[i] + oa; placed := true; EXIT; END IF;
|
|
35161
|
+
IF i = array_length(dep,1) AND total - dep[i] + ob <= p_lw + e THEN
|
|
35162
|
+
total := total - dep[i] + ob; dep[i] := ob; used[i] := used[i] + oa; placed := true; EXIT;
|
|
35163
|
+
END IF;
|
|
35164
|
+
END LOOP;
|
|
35165
|
+
EXIT WHEN placed;
|
|
35166
|
+
IF total + ob <= p_lw + e THEN dep := dep || ob; used := used || oa; total := total + ob; placed := true; EXIT; END IF;
|
|
35167
|
+
END LOOP;
|
|
35168
|
+
EXIT WHEN NOT placed;
|
|
35169
|
+
n := n + 1;
|
|
35170
|
+
END LOOP;
|
|
35171
|
+
best := greatest(best, n);
|
|
35172
|
+
END LOOP;
|
|
35173
|
+
RETURN best;
|
|
35174
|
+
END $$;
|
|
35175
|
+
|
|
35176
|
+
-- Productoordeel (zelfde regel als "Gegevens voor karindeling").
|
|
35177
|
+
CREATE OR REPLACE FUNCTION public.logistics_product_oordeel_many(p_product_ids uuid[])
|
|
35178
|
+
RETURNS TABLE(product_id uuid, oordeel text, tekst text, "actieUrl" text)
|
|
35179
|
+
LANGUAGE plpgsql STABLE SECURITY DEFINER SET search_path TO 'public' AS $$
|
|
35180
|
+
#variable_conflict use_column
|
|
35181
|
+
DECLARE r record; c record; v_carrier text; ls int; fh numeric; ph numeric; cpl numeric; lpc numeric; laag numeric;
|
|
35182
|
+
blok text[]; waar text[]; mist text[]; hmist text[]; naam text; bron text;
|
|
35183
|
+
c_url constant text := '/logistiek-engine/beheer#gegevens-voor-karindeling';
|
|
35184
|
+
BEGIN
|
|
35185
|
+
PERFORM public.logistics_acc_guard('ingelogd', 'logistics_product_oordeel_many');
|
|
35186
|
+
IF coalesce(cardinality(p_product_ids), 0) = 0 THEN RETURN; END IF;
|
|
35187
|
+
IF cardinality(p_product_ids) > 1000 THEN
|
|
35188
|
+
RAISE EXCEPTION 'Maximaal 1000 producten per aanroep (ontvangen: %).', cardinality(p_product_ids) USING ERRCODE = '22023';
|
|
35189
|
+
END IF;
|
|
35190
|
+
SELECT nullif(btrim(coalesce(s.settings->'karrenindeling'->'fields'->>'default_carrier_type', s.settings->'karrenindeling'->>'default_carrier_type')), '')
|
|
35191
|
+
INTO v_carrier FROM public.logistics_rule_configs s WHERE s.is_active ORDER BY s.updated_at DESC LIMIT 1;
|
|
35192
|
+
SELECT t.code, t.name, t.layer_length_cm, t.layer_width_cm, t.max_height_cm, t.max_crates_per_layer, t.layers_per_carrier INTO c
|
|
35193
|
+
FROM public.load_carrier_types t
|
|
35194
|
+
WHERE t.is_active AND (t.code = v_carrier OR (v_carrier IS NULL AND t.is_default))
|
|
35195
|
+
ORDER BY (t.code = v_carrier) DESC NULLS LAST LIMIT 1;
|
|
35196
|
+
naam := coalesce(c.name, c.code, 'de ladingdrager');
|
|
35197
|
+
|
|
35198
|
+
FOR r IN SELECT * FROM public.logistics_data_gereedheid(p_product_ids) LOOP
|
|
35199
|
+
blok := '{}'; waar := '{}'; hmist := '{}';
|
|
35200
|
+
mist := array_remove(coalesce(r.ontbrekend, '{}'), 'weight');
|
|
35201
|
+
bron := coalesce(r.velden->'crates_per_layer'->>'bron', 'product');
|
|
35202
|
+
cpl := coalesce(nullif(r.velden->'crates_per_layer'->>'waarde', '')::numeric, 0);
|
|
35203
|
+
lpc := coalesce(nullif(r.velden->'layers_per_cart'->>'waarde', '')::numeric, 0);
|
|
35204
|
+
fh := nullif(r.velden->'fust_height_mm'->>'waarde', '')::numeric / 10.0;
|
|
35205
|
+
ph := nullif(r.velden->'plant_height_cm'->>'waarde', '')::numeric;
|
|
35206
|
+
ls := public.logistics_max_fusten_per_laag(c.layer_length_cm, c.layer_width_cm,
|
|
35207
|
+
nullif(r.velden->'fust_length_mm'->>'waarde', '')::numeric / 10.0, nullif(r.velden->'fust_width_mm'->>'waarde', '')::numeric / 10.0);
|
|
35208
|
+
IF coalesce(fh,0) > 0 AND coalesce(ph,0) > 0 AND coalesce(c.max_height_cm,0) > 0 THEN
|
|
35209
|
+
laag := greatest(fh, ph);
|
|
35210
|
+
IF lpc * laag > c.max_height_cm + 1e-9 THEN
|
|
35211
|
+
blok := blok || format('%s lagen × %s cm = %s cm, terwijl %s %s cm hoog is; er passen maximaal %s lagen',
|
|
35212
|
+
lpc, trim_scale(round(laag,1)), trim_scale(round(lpc*laag,1)), naam, trim_scale(round(c.max_height_cm::numeric,1)), floor(c.max_height_cm / laag));
|
|
35213
|
+
END IF;
|
|
35214
|
+
ELSE
|
|
35215
|
+
IF coalesce(fh,0) <= 0 THEN hmist := hmist || 'fusthoogte'::text; END IF;
|
|
35216
|
+
IF coalesce(ph,0) <= 0 THEN hmist := hmist || 'planthoogte'::text; END IF;
|
|
35217
|
+
IF coalesce(c.max_height_cm,0) <= 0 THEN hmist := hmist || ('hoogte van ' || naam); END IF;
|
|
35218
|
+
END IF;
|
|
35219
|
+
IF ls IS NOT NULL AND cpl > ls THEN
|
|
35220
|
+
blok := blok || format('%s fust per laag (bron: %s), maar op een laag van %s passen er maximaal %s', cpl, bron, naam, ls);
|
|
35221
|
+
END IF;
|
|
35222
|
+
IF coalesce(c.max_crates_per_layer,0) > 0 AND cpl > c.max_crates_per_layer THEN
|
|
35223
|
+
blok := blok || format('%s fust per laag (bron: %s), maar %s staat maximaal %s fust per laag toe', cpl, bron, naam, c.max_crates_per_layer);
|
|
35224
|
+
END IF;
|
|
35225
|
+
IF cardinality(blok) = 0 AND cardinality(hmist) > 0 AND coalesce(c.layers_per_carrier,0) > 0 AND lpc > c.layers_per_carrier THEN
|
|
35226
|
+
waar := waar || format('%s lagen opgegeven, standaard %s; niet te controleren omdat de %s ontbreekt', lpc, c.layers_per_carrier, array_to_string(hmist, ', '));
|
|
35227
|
+
END IF;
|
|
35228
|
+
IF 'weight' = ANY(coalesce(r.ontbrekend, '{}')) THEN waar := waar || 'gewicht per fust ontbreekt'::text; END IF;
|
|
35229
|
+
|
|
35230
|
+
product_id := r.product_id; "actieUrl" := c_url;
|
|
35231
|
+
IF cardinality(blok) > 0 THEN
|
|
35232
|
+
oordeel := 'onmogelijk'; tekst := format('%s past niet op %s: %s.', r.product, naam, array_to_string(blok, '; '));
|
|
35233
|
+
ELSIF cardinality(mist) > 0 THEN
|
|
35234
|
+
oordeel := 'mist_gegevens'; tekst := r.uitleg;
|
|
35235
|
+
ELSIF cardinality(waar) > 0 THEN
|
|
35236
|
+
oordeel := 'waarschuwing'; tekst := format('%s: %s.', r.product, array_to_string(waar, '; '));
|
|
35237
|
+
ELSE
|
|
35238
|
+
oordeel := 'compleet'; tekst := format('%s: gegevens voor karindeling compleet.', r.product);
|
|
35239
|
+
END IF;
|
|
35240
|
+
RETURN NEXT;
|
|
35241
|
+
END LOOP;
|
|
35242
|
+
END $$;
|
|
35243
|
+
|
|
35244
|
+
GRANT EXECUTE ON FUNCTION public.logistics_kar_blokkade(text, numeric, numeric) TO authenticated, service_role;
|
|
35245
|
+
GRANT EXECUTE ON FUNCTION public.logistics_max_fusten_per_laag(numeric, numeric, numeric, numeric) TO authenticated, service_role;
|
|
35246
|
+
GRANT EXECUTE ON FUNCTION public.logistics_order_te_hoge_karren(uuid[]) TO authenticated, service_role;
|
|
35247
|
+
GRANT EXECUTE ON FUNCTION public.logistics_cart_te_hoog(uuid) TO authenticated, service_role;
|
|
35248
|
+
GRANT EXECUTE ON FUNCTION public.logistics_product_oordeel_many(uuid[]) TO authenticated, service_role;
|
|
35249
|
+
|
|
35250
|
+
CREATE OR REPLACE FUNCTION public.logistics_install_check_201b()
|
|
35251
|
+
RETURNS TABLE(punt text, ok boolean, uitleg text) LANGUAGE sql STABLE SECURITY DEFINER SET search_path TO 'public' AS $$
|
|
35252
|
+
SELECT 'kar_te_hoog-regel', to_regprocedure('public.logistics_kar_blokkade(text,numeric,numeric)') IS NOT NULL, 'Blokkade per kar (teHoog/blokkade)'
|
|
35253
|
+
UNION ALL SELECT 'guard pakbonfunctie', position('logistics_acc_guard' IN pg_get_functiondef('public.logistics_cart_slip_plan(uuid)'::regprocedure)) > 0, 'logistics_cart_slip_plan controleert de aanroeper'
|
|
35254
|
+
UNION ALL SELECT 'guard pakbonkopie', to_regprocedure('public.logistics_cart_slip_plan_v200(uuid)') IS NULL OR position('logistics_acc_guard' IN pg_get_functiondef('public.logistics_cart_slip_plan_v200(uuid)'::regprocedure)) > 0, 'logistics_cart_slip_plan_v200 controleert de aanroeper'
|
|
35255
|
+
UNION ALL SELECT 'guard kar-aanvulling', position('logistics_acc_guard' IN pg_get_functiondef('public.logistics_cart_platen_verrijk(jsonb)'::regprocedure)) > 0, 'logistics_cart_platen_verrijk controleert de aanroeper'
|
|
35256
|
+
UNION ALL SELECT 'productoordeel', to_regprocedure('public.logistics_product_oordeel_many(uuid[])') IS NOT NULL, 'Oordeel per product voor het orderformulier'
|
|
35257
|
+
$$;
|
|
35258
|
+
GRANT EXECUTE ON FUNCTION public.logistics_install_check_201b() TO authenticated, service_role;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BeladingIndelingCard.d.ts","sourceRoot":"","sources":["../../src/components/BeladingIndelingCard.tsx"],"names":[],"mappings":"AAsCA,wBAAgB,oBAAoB,
|
|
1
|
+
{"version":3,"file":"BeladingIndelingCard.d.ts","sourceRoot":"","sources":["../../src/components/BeladingIndelingCard.tsx"],"names":[],"mappings":"AAsCA,wBAAgB,oBAAoB,gCA0JnC"}
|
|
@@ -13,7 +13,7 @@ import { previewLoadCarrierPlan, replanOrderLoadCarriers } from "../lib/loadCarr
|
|
|
13
13
|
import { ladingdragerNaam } from "../lib/loadCarrierTypes.js";
|
|
14
14
|
import { useFloricodeLadingdragers } from "../hooks/useFloricodeLadingdragers.js";
|
|
15
15
|
import { layerUtilizationPct } from "../lib/layerUtilization.js";
|
|
16
|
-
import { ladingdragerPlatenTekst } from "../lib/extraPlaten.js";
|
|
16
|
+
import { karBlokkade, ladingdragerPlatenTekst } from "../lib/extraPlaten.js";
|
|
17
17
|
const db = () => supabase;
|
|
18
18
|
export function BeladingIndelingCard() {
|
|
19
19
|
const floricodeNamen = useFloricodeLadingdragers();
|
|
@@ -79,5 +79,5 @@ export function BeladingIndelingCard() {
|
|
|
79
79
|
}
|
|
80
80
|
};
|
|
81
81
|
const safeCarts = Array.isArray(carts) ? carts : [];
|
|
82
|
-
return (_jsxs(Card, { children: [_jsxs(CardHeader, { children: [_jsxs(CardTitle, { className: "flex items-center gap-2", children: [_jsx(Boxes, { className: "h-5 w-5 text-primary" }), "Beladingindeling"] }), _jsx(CardDescription, { children: "De Engine deelt de orderregels van de hele buffer in over ladingdragers. Bekijk eerst het resultaat en leg het daarna vast." }), _jsxs("div", { className: "flex flex-wrap items-end gap-2 pt-2", children: [_jsxs("div", { className: "space-y-1.5", children: [_jsx(Label, { htmlFor: "indeling-order", children: "Ordernummer" }), _jsx(Input, { id: "indeling-order", value: orderNummer, onChange: (e) => setOrderNummer(e.target.value), placeholder: "SO-12345", className: "w-56" })] }), _jsxs(Button, { onClick: () => void bereken(), disabled: bezig || !orderNummer.trim(), children: [_jsx(Search, { className: "mr-2 h-4 w-4" }), "Berekenen"] }), _jsx(Button, { variant: "outline", onClick: () => void vastleggen(), disabled: bezig || !orderId, children: "Vastleggen" })] })] }), _jsxs(CardContent, { className: "space-y-4", children: [warnings.length > 0 && (_jsx("ul", { className: "space-y-1 rounded-lg border border-dashed p-3 text-sm text-muted-foreground", children: warnings.map((w, i) => (_jsx("li", { children: w }, i))) })), safeCarts.length === 0 ? (_jsx("p", { className: "text-sm text-muted-foreground", children: "Nog geen indeling berekend." })) : (safeCarts.map((cart) => (_jsxs("div", { className: "space-y-2 rounded-lg border p-3", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsxs("span", { className: "font-medium", children: ["Ladingdrager ", cart.cart_number] }), _jsx(Badge, { variant: "secondary", children: ladingdragerNaam(cart.carrier_type, floricodeNamen) }), _jsxs(Badge, { variant: cart.is_complete ? "default" : "outline", children: [cart.occupancy_pct, "% vol"] }), _jsxs("span", { className: "text-xs text-muted-foreground", children: [ladingdragerPlatenTekst(ladingdragerNaam(cart.carrier_type, floricodeNamen), cart.total_layers_used, cart.occupancy_pct ?? layerUtilizationPct(cart.total_layers_used, cart.max_layers)), cart.weight_kg ? ` · ${cart.weight_kg} kg` : ""] })] }), _jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { children: "Partij" }), _jsx(TableHead, { className: "text-right", children: "Fusten" }), _jsx(TableHead, { className: "text-right", children: "Planten" }), _jsx(TableHead, { className: "text-right", children: "Aandeel" })] }) }), _jsx(TableBody, { children: (Array.isArray(cart.items) ? cart.items : []).map((item, idx) => (_jsxs(TableRow, { children: [_jsx(TableCell, { className: "font-medium", children: item.product_name }), _jsx(TableCell, { className: "text-right", children: item.crates }), _jsx(TableCell, { className: "text-right", children: item.plants }), _jsxs(TableCell, { className: "text-right", children: [Math.round(item.layer_share_pct * 10) / 10, "%"] })] }, `${item.order_item_id}-${idx}`))) })] })] }, cart.cart_number))))] })] }));
|
|
82
|
+
return (_jsxs(Card, { children: [_jsxs(CardHeader, { children: [_jsxs(CardTitle, { className: "flex items-center gap-2", children: [_jsx(Boxes, { className: "h-5 w-5 text-primary" }), "Beladingindeling"] }), _jsx(CardDescription, { children: "De Engine deelt de orderregels van de hele buffer in over ladingdragers. Bekijk eerst het resultaat en leg het daarna vast." }), _jsxs("div", { className: "flex flex-wrap items-end gap-2 pt-2", children: [_jsxs("div", { className: "space-y-1.5", children: [_jsx(Label, { htmlFor: "indeling-order", children: "Ordernummer" }), _jsx(Input, { id: "indeling-order", value: orderNummer, onChange: (e) => setOrderNummer(e.target.value), placeholder: "SO-12345", className: "w-56" })] }), _jsxs(Button, { onClick: () => void bereken(), disabled: bezig || !orderNummer.trim(), children: [_jsx(Search, { className: "mr-2 h-4 w-4" }), "Berekenen"] }), _jsx(Button, { variant: "outline", onClick: () => void vastleggen(), disabled: bezig || !orderId, children: "Vastleggen" })] })] }), _jsxs(CardContent, { className: "space-y-4", children: [warnings.length > 0 && (_jsx("ul", { className: "space-y-1 rounded-lg border border-dashed p-3 text-sm text-muted-foreground", children: warnings.map((w, i) => (_jsx("li", { children: w }, i))) })), safeCarts.length === 0 ? (_jsx("p", { className: "text-sm text-muted-foreground", children: "Nog geen indeling berekend." })) : (safeCarts.map((cart) => (_jsxs("div", { className: "space-y-2 rounded-lg border p-3", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsxs("span", { className: "font-medium", children: ["Ladingdrager ", cart.cart_number] }), _jsx(Badge, { variant: "secondary", children: ladingdragerNaam(cart.carrier_type, floricodeNamen) }), _jsxs(Badge, { variant: cart.is_complete ? "default" : "outline", children: [cart.occupancy_pct, "% vol"] }), _jsxs("span", { className: "text-xs text-muted-foreground", children: [ladingdragerPlatenTekst(ladingdragerNaam(cart.carrier_type, floricodeNamen), cart.total_layers_used, cart.occupancy_pct ?? layerUtilizationPct(cart.total_layers_used, cart.max_layers)), cart.weight_kg ? ` · ${cart.weight_kg} kg` : ""] })] }), karBlokkade(cart.cart_number, cart.total_layers_used, cart.max_layers).blokkade && (_jsx("p", { className: "rounded-md border border-destructive/40 bg-destructive/10 p-2 text-sm text-destructive", children: karBlokkade(cart.cart_number, cart.total_layers_used, cart.max_layers).blokkade.tekst })), _jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { children: "Partij" }), _jsx(TableHead, { className: "text-right", children: "Fusten" }), _jsx(TableHead, { className: "text-right", children: "Planten" }), _jsx(TableHead, { className: "text-right", children: "Aandeel" })] }) }), _jsx(TableBody, { children: (Array.isArray(cart.items) ? cart.items : []).map((item, idx) => (_jsxs(TableRow, { children: [_jsx(TableCell, { className: "font-medium", children: item.product_name }), _jsx(TableCell, { className: "text-right", children: item.crates }), _jsx(TableCell, { className: "text-right", children: item.plants }), _jsxs(TableCell, { className: "text-right", children: [Math.round(item.layer_share_pct * 10) / 10, "%"] })] }, `${item.order_item_id}-${idx}`))) })] })] }, cart.cart_number))))] })] }));
|
|
83
83
|
}
|