@flowselections/logistiek-engine 1.0.243 → 1.0.244
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 +1777 -0
- package/dist-lib/integrations/supabase/types.d.ts +333 -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/modulePrompts.d.ts.map +1 -1
- package/dist-lib/lib/modulePrompts.js +57 -0
- package/dist-lib/lib/moduleVersion.d.ts +1 -1
- package/dist-lib/lib/moduleVersion.js +1 -1
- package/dist-lib/lib/ruleEngineSchema.d.ts.map +1 -1
- package/dist-lib/lib/ruleEngineSchema.js +32 -1
- package/package.json +5 -8
|
@@ -53027,3 +53027,1780 @@ BEGIN
|
|
|
53027
53027
|
END IF;
|
|
53028
53028
|
RETURN v_res;
|
|
53029
53029
|
END $function$;
|
|
53030
|
+
|
|
53031
|
+
|
|
53032
|
+
-- ===== Correctie Golf 1 (E1-C) (20261111081722) =====
|
|
53033
|
+
-- ===== Correctie Golf 1 (E1-C) =====
|
|
53034
|
+
|
|
53035
|
+
-- 4. Uitgang naar buiten per transactie uit te zetten (proefstand).
|
|
53036
|
+
CREATE OR REPLACE FUNCTION public.logistics_uitgang_uit()
|
|
53037
|
+
RETURNS boolean LANGUAGE sql STABLE SET search_path TO 'public'
|
|
53038
|
+
AS $function$ SELECT COALESCE(current_setting('logistics.uitgang_uit', true), '') = 'aan' $function$;
|
|
53039
|
+
REVOKE ALL ON FUNCTION public.logistics_uitgang_uit() FROM PUBLIC, anon;
|
|
53040
|
+
GRANT EXECUTE ON FUNCTION public.logistics_uitgang_uit() TO authenticated, service_role;
|
|
53041
|
+
|
|
53042
|
+
-- 4/7/8/2. Bestaande functies gericht aanpassen (zelfde naam en parameters).
|
|
53043
|
+
DO $e1c$
|
|
53044
|
+
DECLARE
|
|
53045
|
+
r record; d text; v_ret text; v_terug text;
|
|
53046
|
+
BEGIN
|
|
53047
|
+
-- Uitgang-uit-bewaking in alles wat mail, http of print naar buiten zet.
|
|
53048
|
+
FOR r IN SELECT p.oid::regprocedure AS sig, p.proname, format_type(p.prorettype, NULL) AS ret
|
|
53049
|
+
FROM pg_proc p
|
|
53050
|
+
WHERE p.pronamespace = 'public'::regnamespace
|
|
53051
|
+
AND p.proname IN ('logistics_queue_stock_email','logistics_queue_capacity_email','logistics_print_via_layout',
|
|
53052
|
+
'logistics_plan_kick','logistics_recalc_tick_kick')
|
|
53053
|
+
LOOP
|
|
53054
|
+
d := pg_get_functiondef(r.sig);
|
|
53055
|
+
CONTINUE WHEN position('logistics_uitgang_uit()' IN d) > 0;
|
|
53056
|
+
v_terug := CASE WHEN r.ret = 'void' THEN 'RETURN;'
|
|
53057
|
+
WHEN r.ret = 'jsonb' THEN 'RETURN jsonb_build_object(''ok'', false, ''error'', ''Proefstand: er gaat niets naar buiten.'');'
|
|
53058
|
+
WHEN r.ret = 'boolean' THEN 'RETURN false;'
|
|
53059
|
+
ELSE 'RETURN NULL;' END;
|
|
53060
|
+
d := regexp_replace(d, E'\\nBEGIN\\n', E'\nBEGIN\n IF public.logistics_uitgang_uit() THEN ' || v_terug || E' END IF;\n');
|
|
53061
|
+
EXECUTE d;
|
|
53062
|
+
END LOOP;
|
|
53063
|
+
|
|
53064
|
+
-- 7. Verzendbrief-trace: alleen geldige statuswaarden.
|
|
53065
|
+
IF to_regprocedure('public.logistics_shipping_note_job_for_slip(uuid)') IS NOT NULL THEN
|
|
53066
|
+
d := pg_get_functiondef('public.logistics_shipping_note_job_for_slip(uuid)'::regprocedure);
|
|
53067
|
+
IF position('''geblokkeerd'',' IN d) > 0 THEN
|
|
53068
|
+
d := replace(d, '''geblokkeerd'',', '''wachtend'',');
|
|
53069
|
+
EXECUTE d;
|
|
53070
|
+
END IF;
|
|
53071
|
+
END IF;
|
|
53072
|
+
|
|
53073
|
+
-- 8. Vooruit gekozen pickdag: deelkar direct printbaar, met vraag; blijft printbaar in latere rondes.
|
|
53074
|
+
IF to_regprocedure('public.logistics_cart_release_reason(uuid)') IS NOT NULL THEN
|
|
53075
|
+
d := pg_get_functiondef('public.logistics_cart_release_reason(uuid)'::regprocedure);
|
|
53076
|
+
IF position('vooruitGekozen' IN d) = 0 THEN
|
|
53077
|
+
d := replace(d, 'PERFORM public.logistics_acc_guard(''ingelogd'', ''logistics_cart_release_reason'');',
|
|
53078
|
+
'PERFORM public.logistics_acc_guard(''ingelogd'', ''logistics_cart_release_reason'');
|
|
53079
|
+
-- E1-C/8: bewust vooruit gekozen pickdag = de teamleider beslist; geen wachttijd voor aanvulling.
|
|
53080
|
+
IF NULLIF(current_setting(''logistics.forced_pick_day'', true), '''') IS NOT NULL
|
|
53081
|
+
OR EXISTS (SELECT 1 FROM public.pick_queue q WHERE q.reference = p_cart_id::text AND q.cancelled_at IS NULL
|
|
53082
|
+
AND nullif(q.payload->>''vooruitGekozen'', '''') IS NOT NULL) THEN
|
|
53083
|
+
RETURN NULL;
|
|
53084
|
+
END IF;');
|
|
53085
|
+
EXECUTE d;
|
|
53086
|
+
END IF;
|
|
53087
|
+
END IF;
|
|
53088
|
+
|
|
53089
|
+
IF to_regprocedure('public.logistics_sync_pick_queue_core()') IS NOT NULL THEN
|
|
53090
|
+
d := pg_get_functiondef('public.logistics_sync_pick_queue_core()'::regprocedure);
|
|
53091
|
+
IF position('vooruitGekozen' IN d) = 0 THEN
|
|
53092
|
+
d := replace(d, 'v_ok := v_forced IS NOT NULL OR public.logistics_cart_released(r_cart.cart_id);',
|
|
53093
|
+
'v_ok := v_forced IS NOT NULL OR public.logistics_cart_released(r_cart.cart_id)
|
|
53094
|
+
OR EXISTS (SELECT 1 FROM public.pick_queue q0 WHERE q0.reference = r_cart.cart_id::text AND q0.cancelled_at IS NULL
|
|
53095
|
+
AND nullif(q0.payload->>''vooruitGekozen'', '''') IS NOT NULL);');
|
|
53096
|
+
d := replace(d, '''printbaar'', v_ok, ''wachtTot'', v_wait, ''wachtReden'', v_reason)',
|
|
53097
|
+
'''printbaar'', v_ok, ''wachtTot'', v_wait, ''wachtReden'', v_reason,
|
|
53098
|
+
''vooruitGekozen'', COALESCE(v_forced::text, (SELECT nullif(q0.payload->>''vooruitGekozen'', '''') FROM public.pick_queue q0
|
|
53099
|
+
WHERE q0.reference = r_cart.cart_id::text AND q0.cancelled_at IS NULL
|
|
53100
|
+
AND nullif(q0.payload->>''vooruitGekozen'', '''') IS NOT NULL LIMIT 1)),
|
|
53101
|
+
''bevestigVraag'', CASE WHEN v_ok AND NOT public.logistics_cart_is_full(r_cart.cart_id)
|
|
53102
|
+
AND NOT public.logistics_cart_released(r_cart.cart_id)
|
|
53103
|
+
THEN ''Deze kar is nog niet vol. Toch nu printen?'' END)');
|
|
53104
|
+
END IF;
|
|
53105
|
+
-- 2. Snelle uitweg als er sinds de vorige ronde niets is veranderd.
|
|
53106
|
+
IF position('logistics_pickwachtrij_overslaan' IN d) = 0 THEN
|
|
53107
|
+
d := replace(d, 'PERFORM public.logistics_acc_guard(''ingelogd'', ''logistics_sync_pick_queue_core'');',
|
|
53108
|
+
'PERFORM public.logistics_acc_guard(''ingelogd'', ''logistics_sync_pick_queue_core'');
|
|
53109
|
+
-- E1-C/2: niets veranderd sinds de vorige ronde = direct klaar.
|
|
53110
|
+
IF NULLIF(current_setting(''logistics.forced_pick_day'', true), '''') IS NULL AND public.logistics_pickwachtrij_overslaan() THEN
|
|
53111
|
+
RETURN jsonb_build_object(''toegevoegd'', 0, ''afgesloten'', 0, ''kandidaten'', 0, ''guard'', ''niets veranderd sinds de vorige ronde'');
|
|
53112
|
+
END IF;');
|
|
53113
|
+
END IF;
|
|
53114
|
+
EXECUTE d;
|
|
53115
|
+
END IF;
|
|
53116
|
+
END $e1c$;
|
|
53117
|
+
|
|
53118
|
+
-- 2. Goedkope vingerafdruk van alles wat de pickwachtrij laat veranderen (ook tijd: venster open, wachttijd voorbij).
|
|
53119
|
+
CREATE OR REPLACE FUNCTION public.logistics_pickwachtrij_sig()
|
|
53120
|
+
RETURNS text LANGUAGE sql STABLE SECURITY DEFINER SET search_path TO 'public'
|
|
53121
|
+
AS $function$
|
|
53122
|
+
SELECT concat_ws('|',
|
|
53123
|
+
public.logistics_local_today()::text,
|
|
53124
|
+
(SELECT count(*) || ':' || count(*) FILTER (WHERE lt.planned_start <= now()) || ':' || COALESCE(max(lt.updated_at)::text, '')
|
|
53125
|
+
FROM public.logistics_tasks lt
|
|
53126
|
+
WHERE lt.task_type = 'pick.execute' AND lt.status NOT IN ('done','cancelled','superseded','skipped')),
|
|
53127
|
+
(SELECT count(*) || ':' || COALESCE(max(q.updated_at)::text, '') || ':'
|
|
53128
|
+
|| count(*) FILTER (WHERE (q.payload->>'printbaar') = 'false'
|
|
53129
|
+
AND nullif(q.payload->>'wachtTot', '') IS NOT NULL
|
|
53130
|
+
AND (q.payload->>'wachtTot')::timestamptz <= now())
|
|
53131
|
+
FROM public.pick_queue q WHERE q.cancelled_at IS NULL AND q.completed_at IS NULL),
|
|
53132
|
+
(SELECT COALESCE(max(c.updated_at)::text, '') FROM public.order_carts c),
|
|
53133
|
+
(SELECT COALESCE(max(o.updated_at)::text, '') FROM public.orders o));
|
|
53134
|
+
$function$;
|
|
53135
|
+
REVOKE ALL ON FUNCTION public.logistics_pickwachtrij_sig() FROM PUBLIC, anon, authenticated;
|
|
53136
|
+
GRANT EXECUTE ON FUNCTION public.logistics_pickwachtrij_sig() TO service_role;
|
|
53137
|
+
|
|
53138
|
+
CREATE OR REPLACE FUNCTION public.logistics_pickwachtrij_overslaan()
|
|
53139
|
+
RETURNS boolean LANGUAGE plpgsql SECURITY DEFINER SET search_path TO 'public'
|
|
53140
|
+
AS $function$
|
|
53141
|
+
DECLARE v_sig text := public.logistics_pickwachtrij_sig(); v_oud text; v_wanneer timestamptz;
|
|
53142
|
+
BEGIN
|
|
53143
|
+
SELECT cursor, bijgewerkt_op INTO v_oud, v_wanneer FROM public.logistics_werker_stand WHERE werker = 'pickwachtrij-vingerafdruk';
|
|
53144
|
+
IF v_oud = v_sig AND v_wanneer > now() - interval '10 minutes' THEN RETURN true; END IF;
|
|
53145
|
+
INSERT INTO public.logistics_werker_stand AS s (werker, cursor, bijgewerkt_op) VALUES ('pickwachtrij-vingerafdruk', v_sig, now())
|
|
53146
|
+
ON CONFLICT (werker) DO UPDATE SET cursor = EXCLUDED.cursor, bijgewerkt_op = now();
|
|
53147
|
+
RETURN false;
|
|
53148
|
+
END $function$;
|
|
53149
|
+
REVOKE ALL ON FUNCTION public.logistics_pickwachtrij_overslaan() FROM PUBLIC, anon, authenticated;
|
|
53150
|
+
GRANT EXECUTE ON FUNCTION public.logistics_pickwachtrij_overslaan() TO service_role;
|
|
53151
|
+
|
|
53152
|
+
-- 4. Voorraadmail: niet meer direct in de trigger, maar door de werker na commit (en minstens 2 minuten later).
|
|
53153
|
+
CREATE OR REPLACE FUNCTION public.logistics_stock_alert_email_trigger()
|
|
53154
|
+
RETURNS trigger LANGUAGE plpgsql SECURITY DEFINER SET search_path TO 'public'
|
|
53155
|
+
AS $function$
|
|
53156
|
+
BEGIN
|
|
53157
|
+
-- E1-C/4: de melding blijft 'pending'; logistics_stock_email_klaarzetten zet hem pas na commit in de outbox.
|
|
53158
|
+
RETURN NULL;
|
|
53159
|
+
END; $function$;
|
|
53160
|
+
|
|
53161
|
+
CREATE OR REPLACE FUNCTION public.logistics_stock_email_klaarzetten(p_max integer DEFAULT 50)
|
|
53162
|
+
RETURNS jsonb LANGUAGE plpgsql SECURITY DEFINER SET search_path TO 'public'
|
|
53163
|
+
AS $function$
|
|
53164
|
+
DECLARE r record; v_gezet int := 0; v_vervallen int := 0; v_fouten int := 0;
|
|
53165
|
+
BEGIN
|
|
53166
|
+
PERFORM public.logistics_acc_guard('server', 'logistics_stock_email_klaarzetten');
|
|
53167
|
+
IF public.logistics_uitgang_uit() THEN RETURN jsonb_build_object('overgeslagen', 'proefstand'); END IF;
|
|
53168
|
+
FOR r IN
|
|
53169
|
+
SELECT a.id, a.order_id, o.id AS o_id, o.deleted_at, o.cancelled_at, a.status
|
|
53170
|
+
FROM public.logistics_stock_alerts a LEFT JOIN public.orders o ON o.id = a.order_id
|
|
53171
|
+
WHERE a.email_status = 'pending' AND a.email_enabled AND a.created_at <= now() - interval '2 minutes'
|
|
53172
|
+
ORDER BY a.created_at LIMIT greatest(COALESCE(p_max, 50), 1)
|
|
53173
|
+
LOOP
|
|
53174
|
+
BEGIN
|
|
53175
|
+
IF r.status <> 'open' OR (r.order_id IS NOT NULL AND (r.o_id IS NULL OR r.deleted_at IS NOT NULL OR r.cancelled_at IS NOT NULL)) THEN
|
|
53176
|
+
UPDATE public.logistics_stock_alerts SET email_status = 'skipped',
|
|
53177
|
+
email_error = 'Niet verstuurd: de order bestaat niet meer of de melding is al afgehandeld.' WHERE id = r.id;
|
|
53178
|
+
v_vervallen := v_vervallen + 1;
|
|
53179
|
+
ELSE
|
|
53180
|
+
PERFORM public.logistics_queue_stock_email(r.id);
|
|
53181
|
+
v_gezet := v_gezet + 1;
|
|
53182
|
+
END IF;
|
|
53183
|
+
EXCEPTION WHEN OTHERS THEN
|
|
53184
|
+
v_fouten := v_fouten + 1;
|
|
53185
|
+
PERFORM public.logistics_melding('logistics_stock_email_klaarzetten', r.order_id,
|
|
53186
|
+
'Voorraadmail kon niet worden klaargezet; de volgende ronde probeert het opnieuw', SQLERRM);
|
|
53187
|
+
END;
|
|
53188
|
+
END LOOP;
|
|
53189
|
+
RETURN jsonb_build_object('klaargezet', v_gezet, 'vervallen', v_vervallen, 'fouten', v_fouten);
|
|
53190
|
+
END $function$;
|
|
53191
|
+
REVOKE ALL ON FUNCTION public.logistics_stock_email_klaarzetten(integer) FROM PUBLIC, anon, authenticated;
|
|
53192
|
+
GRANT EXECUTE ON FUNCTION public.logistics_stock_email_klaarzetten(integer) TO service_role;
|
|
53193
|
+
|
|
53194
|
+
-- 1. Ophaalmoment: elke vervoerderswissel bepaalt het moment opnieuw (ook binnen een Engine-schrijfactie).
|
|
53195
|
+
CREATE OR REPLACE FUNCTION public.logistics_orders_pickup_moment()
|
|
53196
|
+
RETURNS trigger LANGUAGE plpgsql SECURITY DEFINER SET search_path TO 'public' AS $$
|
|
53197
|
+
DECLARE n jsonb; v_basis jsonb;
|
|
53198
|
+
BEGIN
|
|
53199
|
+
IF TG_OP = 'UPDATE' AND NEW.carrier_id IS DISTINCT FROM OLD.carrier_id THEN
|
|
53200
|
+
-- E1-C/1: andere vervoerder = nieuw moment, tenzij de planning is vastgezet.
|
|
53201
|
+
IF NEW.plan_locked_at IS NOT NULL THEN RETURN NEW; END IF;
|
|
53202
|
+
BEGIN
|
|
53203
|
+
v_basis := COALESCE(NEW.custom_fields, '{}'::jsonb) - 'pickup_moment' - 'pickup_moment_key' - 'pickup_moment_bron' - 'pickup_moment_reden';
|
|
53204
|
+
n := public.logistics_pickup_cf_nieuw(v_basis, NEW.carrier_id, NEW.delivery_date::text,
|
|
53205
|
+
NEW.deleted_at, NEW.cancelled_at, NEW.plan_locked_at, NULL);
|
|
53206
|
+
IF n IS NOT NULL THEN NEW.custom_fields := n; END IF;
|
|
53207
|
+
EXCEPTION WHEN OTHERS THEN
|
|
53208
|
+
RAISE WARNING 'logistiek-engine: ophaalmoment na vervoerderswissel mislukt voor order %: %', NEW.id, SQLERRM;
|
|
53209
|
+
PERFORM public.logistics_melding('logistics_orders_pickup_moment', NEW.id,
|
|
53210
|
+
'Ophaalmoment kon na een vervoerderswissel niet opnieuw worden bepaald', SQLERRM);
|
|
53211
|
+
END;
|
|
53212
|
+
RETURN NEW;
|
|
53213
|
+
END IF;
|
|
53214
|
+
IF COALESCE(current_setting('logistics.pickup_writer', true), '') = 'engine' THEN RETURN NEW; END IF;
|
|
53215
|
+
IF TG_OP = 'UPDATE'
|
|
53216
|
+
AND NEW.delivery_date IS NOT DISTINCT FROM OLD.delivery_date
|
|
53217
|
+
AND (NEW.custom_fields->>'floriday_latest_delivery_at') IS NOT DISTINCT FROM (OLD.custom_fields->>'floriday_latest_delivery_at')
|
|
53218
|
+
AND NEW.deleted_at IS NOT DISTINCT FROM OLD.deleted_at
|
|
53219
|
+
AND (nullif(btrim(COALESCE(NEW.custom_fields->>'pickup_moment', '')), '') IS NOT NULL
|
|
53220
|
+
OR (NEW.custom_fields->'pickup_moment_reden') IS NOT NULL) THEN
|
|
53221
|
+
RETURN NEW;
|
|
53222
|
+
END IF;
|
|
53223
|
+
BEGIN
|
|
53224
|
+
n := public.logistics_pickup_cf_nieuw(NEW.custom_fields, NEW.carrier_id, NEW.delivery_date::text,
|
|
53225
|
+
NEW.deleted_at, NEW.cancelled_at, NEW.plan_locked_at, NULL);
|
|
53226
|
+
IF n IS NOT NULL THEN NEW.custom_fields := n; END IF;
|
|
53227
|
+
EXCEPTION WHEN OTHERS THEN
|
|
53228
|
+
RAISE WARNING 'logistiek-engine: ophaalmoment bepalen mislukt voor order %: %', NEW.id, SQLERRM;
|
|
53229
|
+
BEGIN
|
|
53230
|
+
PERFORM public._logistics_merge_melding('ophaalmoment_fout', NEW.id, NULL, NULL, 'ophaalmoment bepalen: ' || SQLERRM);
|
|
53231
|
+
EXCEPTION WHEN OTHERS THEN RAISE WARNING 'logistiek-engine: melding ophaalmoment mislukt: %', SQLERRM; END;
|
|
53232
|
+
END;
|
|
53233
|
+
RETURN NEW;
|
|
53234
|
+
END $$;
|
|
53235
|
+
|
|
53236
|
+
-- 1. Open orders waarvan het ophaalmoment niet past bij de huidige vervoerder.
|
|
53237
|
+
CREATE OR REPLACE FUNCTION public.logistics_pickup_afwijkend(p_voorbeelden integer DEFAULT 20)
|
|
53238
|
+
RETURNS jsonb LANGUAGE plpgsql STABLE SECURITY DEFINER SET search_path TO 'public'
|
|
53239
|
+
AS $function$
|
|
53240
|
+
DECLARE v_regel jsonb := public.logistics_pickup_regel(); v jsonb;
|
|
53241
|
+
BEGIN
|
|
53242
|
+
PERFORM public.logistics_acc_guard('ingelogd', 'logistics_pickup_afwijkend');
|
|
53243
|
+
WITH o AS (
|
|
53244
|
+
SELECT o.id, o.order_number, o.carrier_id, public.logistics_order_date(o.delivery_date::text) AS dag,
|
|
53245
|
+
o.custom_fields->>'pickup_moment' AS huidig,
|
|
53246
|
+
public.logistics_pickup_cf_nieuw(
|
|
53247
|
+
o.custom_fields - 'pickup_moment' - 'pickup_moment_key' - 'pickup_moment_bron' - 'pickup_moment_reden',
|
|
53248
|
+
o.carrier_id, o.delivery_date::text, o.deleted_at, o.cancelled_at, o.plan_locked_at, v_regel) AS n
|
|
53249
|
+
FROM public.orders o
|
|
53250
|
+
WHERE o.deleted_at IS NULL AND o.cancelled_at IS NULL AND o.plan_locked_at IS NULL AND o.carrier_id IS NOT NULL
|
|
53251
|
+
AND coalesce(o.delivery_date, '') ~ '^\d{4}-\d{2}-\d{2}'
|
|
53252
|
+
AND public.logistics_order_date(o.delivery_date::text) >= public.logistics_local_today()
|
|
53253
|
+
AND COALESCE(o.custom_fields->>'pickup_moment_bron', '') <> 'handmatig'
|
|
53254
|
+
AND NOT public.logistics_order_is_backfill(o.id)
|
|
53255
|
+
), a AS (
|
|
53256
|
+
SELECT o.*, o.n->>'pickup_moment' AS verwacht
|
|
53257
|
+
FROM o
|
|
53258
|
+
WHERE o.n IS NOT NULL
|
|
53259
|
+
AND public.logistics_pickup_ts(o.huidig, o.dag) IS DISTINCT FROM public.logistics_pickup_ts(o.n->>'pickup_moment', o.dag)
|
|
53260
|
+
)
|
|
53261
|
+
SELECT jsonb_build_object('aantal', (SELECT count(*) FROM a),
|
|
53262
|
+
'voorbeelden', COALESCE((SELECT jsonb_agg(jsonb_build_object('order', order_number, 'orderId', id, 'huidig', huidig, 'verwacht', verwacht))
|
|
53263
|
+
FROM (SELECT * FROM a ORDER BY dag, order_number LIMIT greatest(COALESCE(p_voorbeelden, 20), 0)) x), '[]'::jsonb))
|
|
53264
|
+
INTO v;
|
|
53265
|
+
RETURN v;
|
|
53266
|
+
END $function$;
|
|
53267
|
+
REVOKE ALL ON FUNCTION public.logistics_pickup_afwijkend(integer) FROM PUBLIC, anon;
|
|
53268
|
+
GRANT EXECUTE ON FUNCTION public.logistics_pickup_afwijkend(integer) TO authenticated, service_role;
|
|
53269
|
+
|
|
53270
|
+
CREATE OR REPLACE FUNCTION public.logistics_pickup_herstel_voorbeeld()
|
|
53271
|
+
RETURNS jsonb LANGUAGE sql STABLE SECURITY DEFINER SET search_path TO 'public'
|
|
53272
|
+
AS $function$ SELECT public.logistics_pickup_afwijkend(500) $function$;
|
|
53273
|
+
REVOKE ALL ON FUNCTION public.logistics_pickup_herstel_voorbeeld() FROM PUBLIC, anon;
|
|
53274
|
+
GRANT EXECUTE ON FUNCTION public.logistics_pickup_herstel_voorbeeld() TO authenticated, service_role;
|
|
53275
|
+
|
|
53276
|
+
CREATE OR REPLACE FUNCTION public.logistics_pickup_herstel_uitvoeren(p_max integer DEFAULT 500)
|
|
53277
|
+
RETURNS jsonb LANGUAGE plpgsql SECURITY DEFINER SET search_path TO 'public'
|
|
53278
|
+
AS $function$
|
|
53279
|
+
DECLARE r jsonb; v_cf jsonb; o record; v_n jsonb; v_hersteld int := 0; v_fouten jsonb := '[]'::jsonb;
|
|
53280
|
+
BEGIN
|
|
53281
|
+
PERFORM public.logistics_acc_guard('server', 'logistics_pickup_herstel_uitvoeren');
|
|
53282
|
+
FOR r IN SELECT jsonb_array_elements(public.logistics_pickup_afwijkend(greatest(COALESCE(p_max, 500), 1))->'voorbeelden') LOOP
|
|
53283
|
+
BEGIN
|
|
53284
|
+
SELECT * INTO o FROM public.orders WHERE id = (r->>'orderId')::uuid FOR UPDATE;
|
|
53285
|
+
v_cf := COALESCE(o.custom_fields, '{}'::jsonb) - 'pickup_moment' - 'pickup_moment_key' - 'pickup_moment_bron' - 'pickup_moment_reden';
|
|
53286
|
+
v_n := public.logistics_pickup_cf_nieuw(v_cf, o.carrier_id, o.delivery_date::text, o.deleted_at, o.cancelled_at, o.plan_locked_at, NULL);
|
|
53287
|
+
IF v_n IS NOT NULL THEN
|
|
53288
|
+
UPDATE public.orders SET custom_fields = v_n, updated_at = now() WHERE id = o.id;
|
|
53289
|
+
INSERT INTO public.logistics_trace_events(phase, step, status, reason, order_ref, actor)
|
|
53290
|
+
VALUES ('ophaalmoment', 'Ophaalmoment hersteld', 'geslaagd',
|
|
53291
|
+
'Was ' || COALESCE(r->>'huidig', 'leeg') || ', nu ' || COALESCE(v_n->>'pickup_moment', 'leeg') || ' (past bij de huidige vervoerder).',
|
|
53292
|
+
o.order_number, 'beheer');
|
|
53293
|
+
v_hersteld := v_hersteld + 1;
|
|
53294
|
+
END IF;
|
|
53295
|
+
EXCEPTION WHEN OTHERS THEN
|
|
53296
|
+
v_fouten := v_fouten || jsonb_build_array(jsonb_build_object('order', r->>'order', 'fout', SQLERRM));
|
|
53297
|
+
PERFORM public.logistics_melding('logistics_pickup_herstel_uitvoeren', (r->>'orderId')::uuid, 'Ophaalmoment herstellen mislukt', SQLERRM);
|
|
53298
|
+
END;
|
|
53299
|
+
END LOOP;
|
|
53300
|
+
RETURN jsonb_build_object('hersteld', v_hersteld, 'fouten', v_fouten, 'nogAfwijkend', (public.logistics_pickup_afwijkend(0)->>'aantal')::int);
|
|
53301
|
+
END $function$;
|
|
53302
|
+
REVOKE ALL ON FUNCTION public.logistics_pickup_herstel_uitvoeren(integer) FROM PUBLIC, anon, authenticated;
|
|
53303
|
+
GRANT EXECUTE ON FUNCTION public.logistics_pickup_herstel_uitvoeren(integer) TO service_role;
|
|
53304
|
+
|
|
53305
|
+
-- A. "Al aangemeld" alleen voor de vervoerder waarbij de order echt is aangemeld.
|
|
53306
|
+
CREATE OR REPLACE FUNCTION public.logistics_carrier_frozen_reason(p_order_id uuid)
|
|
53307
|
+
RETURNS text
|
|
53308
|
+
LANGUAGE plpgsql
|
|
53309
|
+
STABLE SECURITY DEFINER
|
|
53310
|
+
SET search_path TO 'public'
|
|
53311
|
+
AS $function$
|
|
53312
|
+
DECLARE v_locked timestamptz; v_carrier uuid; v_hit boolean;
|
|
53313
|
+
BEGIN
|
|
53314
|
+
PERFORM public.logistics_acc_guard('server', 'logistics_carrier_frozen_reason');
|
|
53315
|
+
SELECT plan_locked_at, carrier_id INTO v_locked, v_carrier FROM public.orders WHERE id = p_order_id;
|
|
53316
|
+
IF v_locked IS NOT NULL THEN RETURN 'de planning is vastgezet'; END IF;
|
|
53317
|
+
BEGIN
|
|
53318
|
+
IF public.logistics_order_is_frozen(p_order_id) THEN RETURN 'het picken is begonnen of de order staat op een vastgezette kar'; END IF;
|
|
53319
|
+
EXCEPTION WHEN undefined_function OR undefined_table THEN NULL; END;
|
|
53320
|
+
BEGIN
|
|
53321
|
+
IF public.logistics_order_frozen(p_order_id) THEN RETURN 'de order is gepickt, gecontroleerd of afgemeld'; END IF;
|
|
53322
|
+
EXCEPTION WHEN undefined_function OR undefined_table THEN NULL; END;
|
|
53323
|
+
IF to_regprocedure('public.afh_order_aangemeld_bij(uuid,uuid)') IS NOT NULL THEN
|
|
53324
|
+
-- E1-C/A: Afhandeling weet bij welke vervoerder de order echt is aangemeld.
|
|
53325
|
+
IF v_carrier IS NULL THEN RETURN NULL; END IF;
|
|
53326
|
+
BEGIN
|
|
53327
|
+
EXECUTE 'SELECT public.afh_order_aangemeld_bij($1, $2)' INTO v_hit USING p_order_id, v_carrier;
|
|
53328
|
+
IF COALESCE(v_hit, false) THEN RETURN 'de order is al bij deze vervoerder aangemeld'; END IF;
|
|
53329
|
+
EXCEPTION WHEN OTHERS THEN
|
|
53330
|
+
PERFORM public.logistics_melding('logistics_carrier_frozen_reason', p_order_id, 'Controle op vervoerdersaanmelding mislukt; de order is niet als aangemeld gezien', SQLERRM);
|
|
53331
|
+
END;
|
|
53332
|
+
RETURN NULL;
|
|
53333
|
+
END IF;
|
|
53334
|
+
IF to_regclass('public.carrier_signup_dispatches') IS NOT NULL THEN
|
|
53335
|
+
BEGIN
|
|
53336
|
+
EXECUTE 'SELECT EXISTS (SELECT 1 FROM public.carrier_signup_dispatches d
|
|
53337
|
+
WHERE nullif(to_jsonb(d)->>''sent_at'', '''') IS NOT NULL
|
|
53338
|
+
AND coalesce(to_jsonb(d)->''order_ids'', ''[]''::jsonb) ? $1)'
|
|
53339
|
+
INTO v_hit USING p_order_id::text;
|
|
53340
|
+
IF v_hit THEN RETURN 'de order is al bij de vervoerder aangemeld'; END IF;
|
|
53341
|
+
EXCEPTION WHEN OTHERS THEN
|
|
53342
|
+
PERFORM public.logistics_melding('logistics_carrier_frozen_reason', NULL, 'Controle op vervoerdersaanmelding mislukt; de order is niet als aangemeld gezien', SQLERRM); END;
|
|
53343
|
+
END IF;
|
|
53344
|
+
RETURN NULL;
|
|
53345
|
+
END $function$;
|
|
53346
|
+
|
|
53347
|
+
-- 2. Werkers: gespreide start, en bezette gegevens overslaan in plaats van wachten.
|
|
53348
|
+
CREATE OR REPLACE FUNCTION public.logistics_werker_draai(p_werker text, p_stappen jsonb)
|
|
53349
|
+
RETURNS jsonb
|
|
53350
|
+
LANGUAGE plpgsql
|
|
53351
|
+
SECURITY DEFINER
|
|
53352
|
+
SET search_path TO 'public'
|
|
53353
|
+
AS $function$
|
|
53354
|
+
DECLARE
|
|
53355
|
+
v_start timestamptz;
|
|
53356
|
+
v_eind timestamptz;
|
|
53357
|
+
v_n integer := jsonb_array_length(p_stappen);
|
|
53358
|
+
v_begin integer := 0; v_i integer; s jsonb; v_t timestamptz; v_txt text;
|
|
53359
|
+
v_uit jsonb := '[]'::jsonb; v_fouten integer := 0; v_uitgesteld integer := 0; v_eerste_uitgesteld integer; v_bezet integer := 0;
|
|
53360
|
+
BEGIN
|
|
53361
|
+
PERFORM public.logistics_acc_guard('server', 'logistics_werker_draai');
|
|
53362
|
+
IF NOT pg_try_advisory_xact_lock(hashtext('logistics_werker:' || p_werker)) THEN
|
|
53363
|
+
RETURN jsonb_build_object('werker', p_werker, 'overgeslagen', 'vorige ronde van deze werker loopt nog');
|
|
53364
|
+
END IF;
|
|
53365
|
+
-- E1-C/2: vaste korte startverschuiving per werker (nooit meer dan 1 s), zodat ze niet tegelijk beginnen.
|
|
53366
|
+
PERFORM pg_sleep(CASE p_werker WHEN 'pickwachtrij' THEN 0.3 WHEN 'taken' THEN 0.6 WHEN 'onderhoud' THEN 0.9 ELSE 0 END);
|
|
53367
|
+
-- Bezette gegevens: na 1 s overslaan; de volgende ronde pakt het op.
|
|
53368
|
+
PERFORM set_config('lock_timeout', '1000', true);
|
|
53369
|
+
v_start := clock_timestamp();
|
|
53370
|
+
v_eind := clock_timestamp() + make_interval(secs => public.logistics_werker_budget_ms() / 1000.0);
|
|
53371
|
+
SELECT COALESCE(NULLIF(cursor, '')::integer, 0) INTO v_begin FROM public.logistics_werker_stand WHERE werker = p_werker;
|
|
53372
|
+
v_begin := COALESCE(v_begin, 0) % GREATEST(v_n, 1);
|
|
53373
|
+
FOR k IN 0 .. v_n - 1 LOOP
|
|
53374
|
+
v_i := (v_begin + k) % v_n;
|
|
53375
|
+
s := p_stappen -> v_i;
|
|
53376
|
+
IF clock_timestamp() >= v_eind THEN
|
|
53377
|
+
v_uitgesteld := v_uitgesteld + 1;
|
|
53378
|
+
v_eerste_uitgesteld := COALESCE(v_eerste_uitgesteld, v_i);
|
|
53379
|
+
v_uit := v_uit || jsonb_build_object('stap', s->>'naam', 'uitkomst', 'uitgesteld naar de volgende ronde');
|
|
53380
|
+
CONTINUE;
|
|
53381
|
+
END IF;
|
|
53382
|
+
IF (s ? 'functie') AND to_regprocedure(s->>'functie') IS NULL THEN
|
|
53383
|
+
v_uit := v_uit || jsonb_build_object('stap', s->>'naam', 'uitkomst', 'overgeslagen: onderdeel ontbreekt in deze omgeving');
|
|
53384
|
+
CONTINUE;
|
|
53385
|
+
END IF;
|
|
53386
|
+
v_t := clock_timestamp();
|
|
53387
|
+
BEGIN
|
|
53388
|
+
EXECUTE format('SELECT (%s)::text', s->>'sql') INTO v_txt;
|
|
53389
|
+
v_uit := v_uit || jsonb_build_object('stap', s->>'naam',
|
|
53390
|
+
'ms', (extract(epoch FROM clock_timestamp() - v_t) * 1000)::int, 'uitkomst', left(COALESCE(v_txt, ''), 300));
|
|
53391
|
+
EXCEPTION
|
|
53392
|
+
WHEN lock_not_available THEN
|
|
53393
|
+
v_bezet := v_bezet + 1;
|
|
53394
|
+
v_uit := v_uit || jsonb_build_object('stap', s->>'naam',
|
|
53395
|
+
'ms', (extract(epoch FROM clock_timestamp() - v_t) * 1000)::int, 'uitkomst', 'overgeslagen: gegevens even bezet, volgende ronde');
|
|
53396
|
+
WHEN OTHERS THEN
|
|
53397
|
+
v_fouten := v_fouten + 1;
|
|
53398
|
+
v_uit := v_uit || jsonb_build_object('stap', s->>'naam',
|
|
53399
|
+
'ms', (extract(epoch FROM clock_timestamp() - v_t) * 1000)::int, 'fout', left(SQLERRM, 300));
|
|
53400
|
+
PERFORM public.logistics_melding('werker ' || p_werker, NULL,
|
|
53401
|
+
'Stap "' || (s->>'naam') || '" mislukt; de volgende ronde probeert het opnieuw', SQLERRM);
|
|
53402
|
+
END;
|
|
53403
|
+
END LOOP;
|
|
53404
|
+
PERFORM set_config('lock_timeout', '0', true);
|
|
53405
|
+
|
|
53406
|
+
INSERT INTO public.logistics_werker_stand AS w (werker, laatste_start, laatste_eind, laatste_duur_ms, uitgesteld, fouten, stappen, cursor, bijgewerkt_op)
|
|
53407
|
+
VALUES (p_werker, v_start, clock_timestamp(), (extract(epoch FROM clock_timestamp() - v_start) * 1000)::int,
|
|
53408
|
+
v_uitgesteld, v_fouten, v_uit, COALESCE(v_eerste_uitgesteld, 0)::text, now())
|
|
53409
|
+
ON CONFLICT (werker) DO UPDATE
|
|
53410
|
+
SET laatste_start = EXCLUDED.laatste_start, laatste_eind = EXCLUDED.laatste_eind,
|
|
53411
|
+
laatste_duur_ms = EXCLUDED.laatste_duur_ms, uitgesteld = EXCLUDED.uitgesteld, fouten = EXCLUDED.fouten,
|
|
53412
|
+
stappen = EXCLUDED.stappen, cursor = EXCLUDED.cursor, bijgewerkt_op = now();
|
|
53413
|
+
|
|
53414
|
+
RETURN jsonb_build_object('werker', p_werker, 'duurMs', (extract(epoch FROM clock_timestamp() - v_start) * 1000)::int,
|
|
53415
|
+
'uitgesteld', v_uitgesteld, 'fouten', v_fouten, 'bezet', v_bezet, 'stappen', v_uit);
|
|
53416
|
+
END $function$;
|
|
53417
|
+
|
|
53418
|
+
-- 4/5. Pickwachtrij-werker zet voorraadmails klaar; planningswerker neemt de bufferbesluiten over van de oude taak.
|
|
53419
|
+
CREATE OR REPLACE FUNCTION public.logistics_werker_pickwachtrij()
|
|
53420
|
+
RETURNS jsonb
|
|
53421
|
+
LANGUAGE plpgsql
|
|
53422
|
+
SECURITY DEFINER
|
|
53423
|
+
SET search_path TO 'public'
|
|
53424
|
+
AS $function$
|
|
53425
|
+
BEGIN
|
|
53426
|
+
PERFORM public.logistics_acc_guard('server', 'logistics_werker_pickwachtrij');
|
|
53427
|
+
RETURN public.logistics_werker_draai('pickwachtrij', jsonb_build_array(
|
|
53428
|
+
jsonb_build_object('naam', 'Orders afronden', 'sql', 'public.logistics_order_finalize_run(25, 120)'),
|
|
53429
|
+
jsonb_build_object('naam', 'Pickwachtrij bijwerken', 'sql', 'public.logistics_sync_pick_queue()'),
|
|
53430
|
+
jsonb_build_object('naam', 'Laadtoestand', 'sql', 'public.logistics_refresh_plan_state_als_nodig()'),
|
|
53431
|
+
jsonb_build_object('naam', 'Verzendbrieven aanvullen', 'sql', 'public.logistics_ensure_shipping_notes(200)'),
|
|
53432
|
+
jsonb_build_object('naam', 'Verkeerde verzendbrieven herstellen', 'sql', 'public.logistics_fix_wrong_shipping_notes()'),
|
|
53433
|
+
jsonb_build_object('naam', 'Verzendbrieven vrijgeven', 'sql', 'public.logistics_shipping_note_release(NULL::text)'),
|
|
53434
|
+
jsonb_build_object('naam', 'Verzendbrieven na acceptatie', 'sql', 'public.logistics_shipping_note_ensure_accepted(NULL::uuid, 100)'),
|
|
53435
|
+
jsonb_build_object('naam', 'Voorraadmails klaarzetten', 'sql', 'public.logistics_stock_email_klaarzetten(50)'),
|
|
53436
|
+
jsonb_build_object('naam', 'Status voorraadmails', 'sql', 'public.logistics_sync_stock_alert_email_status(500)'),
|
|
53437
|
+
jsonb_build_object('naam', 'Printopdrachten zonder kar', 'sql', 'public.logistics_cleanup_orphan_print_jobs()'),
|
|
53438
|
+
jsonb_build_object('naam', 'Barcodeverzoeken', 'sql', 'public.logistics_fulfill_barcode_requests(100)')
|
|
53439
|
+
));
|
|
53440
|
+
END $function$;
|
|
53441
|
+
|
|
53442
|
+
CREATE OR REPLACE FUNCTION public.logistics_werker_planning()
|
|
53443
|
+
RETURNS jsonb
|
|
53444
|
+
LANGUAGE plpgsql
|
|
53445
|
+
SECURITY DEFINER
|
|
53446
|
+
SET search_path TO 'public'
|
|
53447
|
+
AS $function$
|
|
53448
|
+
BEGIN
|
|
53449
|
+
PERFORM public.logistics_acc_guard('server', 'logistics_werker_planning');
|
|
53450
|
+
RETURN public.logistics_werker_draai('planning', jsonb_build_array(
|
|
53451
|
+
jsonb_build_object('naam', 'Werkset bijwerken', 'sql', 'public.logistics_werkset_herstel(500)'),
|
|
53452
|
+
jsonb_build_object('naam', 'Ophaalmomenten verversen', 'sql', 'public.logistics_refresh_pickup_moments(4)'),
|
|
53453
|
+
jsonb_build_object('naam', 'Wees-taken afsluiten', 'sql', 'public.logistics_cancel_orphan_tasks()'),
|
|
53454
|
+
jsonb_build_object('naam', 'Ontbrekende orders aanmelden', 'sql', 'public.logistics_reconcile_missing_plan_queue()'),
|
|
53455
|
+
jsonb_build_object('naam', 'Vervoerders bepalen', 'sql', 'public.logistics_carrier_recalc_run(50)'),
|
|
53456
|
+
jsonb_build_object('naam', 'Herindelen na wijziging', 'sql', 'public.logistics_rebuffer_pending()'),
|
|
53457
|
+
jsonb_build_object('naam', 'Bufferbesluiten', 'sql', 'public.logistics_reapply_buffer_decisions()'),
|
|
53458
|
+
jsonb_build_object('naam', 'Dubbele bufferregels opruimen', 'sql', 'public.logistics_prune_buffer_member_queue()'),
|
|
53459
|
+
jsonb_build_object('naam', 'Afgehandelde buffers sluiten', 'sql', 'public.logistics_buffer_detach_late_orders(NULL::uuid)'),
|
|
53460
|
+
jsonb_build_object('naam', 'Orders zonder kar bewaken', 'sql', 'public.logistics_bewaak_zonder_kar()'),
|
|
53461
|
+
jsonb_build_object('naam', 'Planwachtrij opruimen', 'sql', 'public.logistics_plan_housekeeping()'),
|
|
53462
|
+
jsonb_build_object('naam', 'Karindeling onderhoud', 'sql', 'public.logistics_karindeling_onderhoud()')
|
|
53463
|
+
));
|
|
53464
|
+
END $function$;
|
|
53465
|
+
|
|
53466
|
+
-- 3. Logboek: technische regels niet zichtbaar; bekende gebeurtenissen in gewone taal.
|
|
53467
|
+
CREATE OR REPLACE FUNCTION public.logistics_logboek_tekst(p_tabel text, p_actie text)
|
|
53468
|
+
RETURNS text LANGUAGE sql IMMUTABLE SET search_path TO 'public'
|
|
53469
|
+
AS $function$
|
|
53470
|
+
SELECT CASE
|
|
53471
|
+
WHEN p_tabel = 'order_carts' AND p_actie = 'UPDATE' THEN 'Kar opnieuw ingedeeld'
|
|
53472
|
+
WHEN p_tabel = 'order_carts' AND p_actie = 'DELETE' THEN 'Kar vervallen'
|
|
53473
|
+
WHEN p_tabel = 'pick_queue' AND p_actie = 'INSERT' THEN 'Kar in de pickwachtrij gezet'
|
|
53474
|
+
WHEN p_tabel = 'pick_queue' AND p_actie = 'UPDATE' THEN 'Pickwachtrij bijgewerkt'
|
|
53475
|
+
WHEN p_tabel = 'logistics_plan_queue' AND p_actie = 'INSERT' THEN 'Indeling aangevraagd'
|
|
53476
|
+
ELSE NULL END
|
|
53477
|
+
$function$;
|
|
53478
|
+
REVOKE ALL ON FUNCTION public.logistics_logboek_tekst(text, text) FROM PUBLIC, anon;
|
|
53479
|
+
GRANT EXECUTE ON FUNCTION public.logistics_logboek_tekst(text, text) TO authenticated, service_role;
|
|
53480
|
+
|
|
53481
|
+
CREATE OR REPLACE FUNCTION public.logistics_logboek_doorsturen()
|
|
53482
|
+
RETURNS trigger
|
|
53483
|
+
LANGUAGE plpgsql
|
|
53484
|
+
SECURITY DEFINER
|
|
53485
|
+
SET search_path TO 'public'
|
|
53486
|
+
AS $function$
|
|
53487
|
+
DECLARE
|
|
53488
|
+
v_fout boolean := (NEW.status = 'mislukt' OR NEW.error IS NOT NULL);
|
|
53489
|
+
-- Vaste momenten en de markering 'ingehaald' altijd bewaren (Golf 1 / 8, M, 14).
|
|
53490
|
+
v_altijd boolean := (COALESCE(NEW.node_id, '') = 'stap:afmelden' OR COALESCE(NEW.decision, '') IN ('ingehaald', 'toch indelen'));
|
|
53491
|
+
v_order_id uuid; v_leverdag text; v_vervalt date; v_vorige record; v_tekst text;
|
|
53492
|
+
BEGIN
|
|
53493
|
+
-- E1-C/3: technische regels ("tabel INSERT/UPDATE") niet bewaren; bekende gebeurtenissen in gewone taal.
|
|
53494
|
+
IF NEW.step ~ '^[a-z_]+ (INSERT|UPDATE|DELETE)$' OR COALESCE(NEW.actor, '') LIKE 'database:%' THEN
|
|
53495
|
+
v_tekst := public.logistics_logboek_tekst(COALESCE(NEW.db_table, split_part(NEW.step, ' ', 1)),
|
|
53496
|
+
COALESCE(NEW.db_action, split_part(NEW.step, ' ', 2)));
|
|
53497
|
+
IF NEW.step ~ '^[a-z_]+ (INSERT|UPDATE|DELETE)$' THEN
|
|
53498
|
+
IF v_tekst IS NULL AND NOT v_fout THEN RETURN NULL; END IF;
|
|
53499
|
+
NEW.step := COALESCE(v_tekst, 'Wijziging kon niet worden opgeslagen');
|
|
53500
|
+
END IF;
|
|
53501
|
+
NEW.actor := 'systeem';
|
|
53502
|
+
END IF;
|
|
53503
|
+
IF NEW.order_ref IS NOT NULL THEN
|
|
53504
|
+
IF NOT v_fout AND NOT v_altijd THEN
|
|
53505
|
+
SELECT o.id, o.delivery_date::text INTO v_order_id, v_leverdag
|
|
53506
|
+
FROM public.orders o WHERE o.order_number = NEW.order_ref
|
|
53507
|
+
ORDER BY o.created_at DESC LIMIT 1;
|
|
53508
|
+
IF v_order_id IS NOT NULL AND (public.logistics_order_is_backfill(v_order_id)
|
|
53509
|
+
OR public.logistics_order_date(v_leverdag) < public.logistics_local_today() - 1) THEN
|
|
53510
|
+
RETURN NULL;
|
|
53511
|
+
END IF;
|
|
53512
|
+
END IF;
|
|
53513
|
+
IF NEW.actor = 'afgeleid' AND NEW.node_id IS NOT NULL THEN
|
|
53514
|
+
IF EXISTS (SELECT 1 FROM public.logistics_logboek_alles a
|
|
53515
|
+
WHERE a.order_ref = NEW.order_ref AND a.node_id = NEW.node_id AND a.actor = 'afgeleid') THEN
|
|
53516
|
+
RETURN NULL;
|
|
53517
|
+
END IF;
|
|
53518
|
+
ELSE
|
|
53519
|
+
SELECT a.status, a.decision, a.reason, a.error, a.db_old, a.db_new, a.output INTO v_vorige
|
|
53520
|
+
FROM public.logistics_logboek_alles a
|
|
53521
|
+
WHERE a.order_ref = NEW.order_ref AND a.step IS NOT DISTINCT FROM NEW.step
|
|
53522
|
+
ORDER BY a.created_at DESC LIMIT 1;
|
|
53523
|
+
IF FOUND
|
|
53524
|
+
AND v_vorige.status IS NOT DISTINCT FROM NEW.status
|
|
53525
|
+
AND v_vorige.decision IS NOT DISTINCT FROM NEW.decision
|
|
53526
|
+
AND v_vorige.reason IS NOT DISTINCT FROM NEW.reason
|
|
53527
|
+
AND v_vorige.error IS NOT DISTINCT FROM NEW.error
|
|
53528
|
+
AND v_vorige.db_old IS NOT DISTINCT FROM NEW.db_old
|
|
53529
|
+
AND v_vorige.db_new IS NOT DISTINCT FROM NEW.db_new
|
|
53530
|
+
AND v_vorige.output IS NOT DISTINCT FROM NEW.output THEN
|
|
53531
|
+
RETURN NULL;
|
|
53532
|
+
END IF;
|
|
53533
|
+
END IF;
|
|
53534
|
+
END IF;
|
|
53535
|
+
|
|
53536
|
+
v_vervalt := (COALESCE(NEW.created_at, now()) AT TIME ZONE public.logistics_tz())::date
|
|
53537
|
+
+ public.logistics_retention_days(CASE WHEN v_fout THEN 'trace_error_retention_days' ELSE 'trace_retention_days' END);
|
|
53538
|
+
PERFORM public.logistics_logboek_partitie(v_vervalt);
|
|
53539
|
+
|
|
53540
|
+
INSERT INTO public.logistics_logboek (
|
|
53541
|
+
id, trace_id, order_ref, order_source, cart_ref, slip_ref, phase, step, status, actor,
|
|
53542
|
+
input, output, reads, decision, reason, values_used, rule, db_table, db_record, db_action, db_old, db_new,
|
|
53543
|
+
external_system, external_action, external_ref, external_status, external_response,
|
|
53544
|
+
error, attempts, next_attempt_at, duration_ms, created_at, node_id, error_class, calc, next_step, vervalt_op)
|
|
53545
|
+
VALUES (
|
|
53546
|
+
COALESCE(NEW.id, gen_random_uuid()), NEW.trace_id, NEW.order_ref, NEW.order_source, NEW.cart_ref, NEW.slip_ref,
|
|
53547
|
+
NEW.phase, NEW.step, NEW.status, NEW.actor, NEW.input, NEW.output, NEW.reads, NEW.decision, NEW.reason,
|
|
53548
|
+
NEW.values_used, NEW.rule, NEW.db_table, NEW.db_record, NEW.db_action, NEW.db_old, NEW.db_new,
|
|
53549
|
+
NEW.external_system, NEW.external_action, NEW.external_ref, NEW.external_status, NEW.external_response,
|
|
53550
|
+
NEW.error, NEW.attempts, NEW.next_attempt_at, NEW.duration_ms, COALESCE(NEW.created_at, now()),
|
|
53551
|
+
NEW.node_id, NEW.error_class, NEW.calc, NEW.next_step, v_vervalt);
|
|
53552
|
+
RETURN NULL;
|
|
53553
|
+
EXCEPTION WHEN OTHERS THEN
|
|
53554
|
+
PERFORM public.logistics_melding('logistics_logboek_doorsturen', v_order_id,
|
|
53555
|
+
'Logboekregel kon niet worden bewaard; de verwerking ging door', SQLERRM);
|
|
53556
|
+
RETURN NULL;
|
|
53557
|
+
END $function$;
|
|
53558
|
+
|
|
53559
|
+
-- 9. Stand van de indeling per order.
|
|
53560
|
+
CREATE OR REPLACE FUNCTION public.logistics_order_indeling_stand(p_order_id uuid)
|
|
53561
|
+
RETURNS jsonb LANGUAGE plpgsql STABLE SECURITY DEFINER SET search_path TO 'public'
|
|
53562
|
+
AS $function$
|
|
53563
|
+
DECLARE
|
|
53564
|
+
v_regels int; v_karren int; v_open timestamptz; v_laatst record; v_fp record; v_bij timestamptz;
|
|
53565
|
+
v_verouderd jsonb; v_reden text; v_volgende timestamptz := date_trunc('minute', now()) + interval '1 minute';
|
|
53566
|
+
BEGIN
|
|
53567
|
+
PERFORM public.logistics_acc_guard('ingelogd', 'logistics_order_indeling_stand');
|
|
53568
|
+
SELECT count(*) INTO v_regels FROM logistics_active.order_items oi WHERE oi.order_id = p_order_id;
|
|
53569
|
+
IF COALESCE(v_regels, 0) = 0 THEN
|
|
53570
|
+
RETURN jsonb_build_object('stand', 'geen_regels', 'bezig_sinds', NULL, 'bijgewerkt_op', NULL, 'verwacht_klaar', NULL,
|
|
53571
|
+
'tekst', 'Deze order heeft nog geen regels om in te delen.');
|
|
53572
|
+
END IF;
|
|
53573
|
+
SELECT min(q.enqueued_at) INTO v_open FROM public.logistics_plan_queue q
|
|
53574
|
+
WHERE q.order_id = p_order_id AND q.status NOT IN ('done','geblokkeerd','opgelost','gearchiveerd');
|
|
53575
|
+
SELECT q.status, q.error, q.reason_code, q.updated_at INTO v_laatst FROM public.logistics_plan_queue q
|
|
53576
|
+
WHERE q.order_id = p_order_id ORDER BY q.updated_at DESC NULLS LAST LIMIT 1;
|
|
53577
|
+
SELECT f.uitkomst, f.bijgewerkt_op INTO v_fp FROM public.logistics_plan_vingerafdruk f WHERE f.order_id = p_order_id;
|
|
53578
|
+
SELECT count(DISTINCT c.id), max(c.updated_at) INTO v_karren, v_bij FROM logistics_active.order_carts c
|
|
53579
|
+
WHERE c.order_id = p_order_id
|
|
53580
|
+
OR EXISTS (SELECT 1 FROM logistics_active.order_cart_items ci JOIN public.order_items oi ON oi.id = ci.order_item_id
|
|
53581
|
+
WHERE ci.cart_id = c.id AND oi.order_id = p_order_id);
|
|
53582
|
+
v_bij := GREATEST(v_bij, CASE WHEN v_fp.uitkomst IS DISTINCT FROM 'geblokkeerd' THEN v_fp.bijgewerkt_op END);
|
|
53583
|
+
|
|
53584
|
+
IF v_open IS NOT NULL THEN
|
|
53585
|
+
RETURN jsonb_build_object('stand', 'bezig', 'bezig_sinds', v_open, 'bijgewerkt_op', v_bij, 'verwacht_klaar', v_volgende,
|
|
53586
|
+
'tekst', 'Indeling wordt gemaakt');
|
|
53587
|
+
END IF;
|
|
53588
|
+
IF v_laatst.status = 'geblokkeerd' THEN
|
|
53589
|
+
v_reden := CASE WHEN v_laatst.error IS NULL OR v_laatst.error ~* '(error|sqlstate|function|relation|column|syntax|violates|null value|timeout|exception|[a-z]+_[a-z_]+\()'
|
|
53590
|
+
THEN 'De indeling kon niet worden gemaakt. Probeer het later opnieuw of neem contact op met de beheerder.'
|
|
53591
|
+
ELSE v_laatst.error END;
|
|
53592
|
+
RETURN jsonb_build_object('stand', 'niet_gelukt', 'bezig_sinds', NULL, 'bijgewerkt_op', v_bij, 'verwacht_klaar', NULL,
|
|
53593
|
+
'tekst', v_reden);
|
|
53594
|
+
END IF;
|
|
53595
|
+
v_verouderd := public.logistics_order_indeling_verouderd_core(p_order_id);
|
|
53596
|
+
IF COALESCE((v_verouderd->>'verouderd')::boolean, false) OR COALESCE(v_karren, 0) = 0 THEN
|
|
53597
|
+
RETURN jsonb_build_object('stand', 'bezig', 'bezig_sinds', COALESCE(v_laatst.updated_at, now()), 'bijgewerkt_op', v_bij,
|
|
53598
|
+
'verwacht_klaar', v_volgende, 'tekst', 'Indeling wordt gemaakt');
|
|
53599
|
+
END IF;
|
|
53600
|
+
RETURN jsonb_build_object('stand', 'klaar', 'bezig_sinds', NULL, 'bijgewerkt_op', v_bij, 'verwacht_klaar', NULL,
|
|
53601
|
+
'tekst', 'Indeling is bijgewerkt' || COALESCE(' om ' || public.logistics_local_hhmi(v_bij), ''));
|
|
53602
|
+
END $function$;
|
|
53603
|
+
REVOKE ALL ON FUNCTION public.logistics_order_indeling_stand(uuid) FROM PUBLIC, anon;
|
|
53604
|
+
GRANT EXECUTE ON FUNCTION public.logistics_order_indeling_stand(uuid) TO authenticated, service_role;
|
|
53605
|
+
|
|
53606
|
+
-- 10. Kar-tekening.
|
|
53607
|
+
CREATE OR REPLACE FUNCTION public.logistics_xml(p text)
|
|
53608
|
+
RETURNS text LANGUAGE sql IMMUTABLE SET search_path TO 'public'
|
|
53609
|
+
AS $function$
|
|
53610
|
+
SELECT replace(replace(replace(replace(replace(COALESCE(p, ''), '&', '&'), '<', '<'), '>', '>'), '"', '"'), '''', ''')
|
|
53611
|
+
$function$;
|
|
53612
|
+
REVOKE ALL ON FUNCTION public.logistics_xml(text) FROM PUBLIC, anon;
|
|
53613
|
+
GRANT EXECUTE ON FUNCTION public.logistics_xml(text) TO authenticated, service_role;
|
|
53614
|
+
|
|
53615
|
+
-- Klantkleuren: instelling in de werkwijze (alleen namen van themakleuren), anders veilige standaardlijst.
|
|
53616
|
+
CREATE OR REPLACE FUNCTION public.logistics_klantkleuren()
|
|
53617
|
+
RETURNS text[] LANGUAGE sql STABLE SECURITY DEFINER SET search_path TO 'public'
|
|
53618
|
+
AS $function$
|
|
53619
|
+
WITH bron AS (
|
|
53620
|
+
SELECT NULLIF(COALESCE(rc.settings->'karrenindeling'->'fields'->>'klantkleuren', rc.settings->'karrenindeling'->>'klantkleuren'), '') AS v
|
|
53621
|
+
FROM public.logistics_rule_configs rc
|
|
53622
|
+
LEFT JOIN public.logistics_strategies s ON s.id = rc.strategy_id
|
|
53623
|
+
ORDER BY (rc.is_active AND coalesce(s.is_active, false)) DESC, rc.is_active DESC, rc.updated_at DESC NULLS LAST
|
|
53624
|
+
LIMIT 1),
|
|
53625
|
+
k AS (
|
|
53626
|
+
SELECT array_agg('var(' || t || ')' ORDER BY n) AS l
|
|
53627
|
+
FROM bron, regexp_split_to_table(bron.v, '\s*,\s*') WITH ORDINALITY AS x(t, n)
|
|
53628
|
+
WHERE t ~ '^--[a-z0-9-]{1,40}$')
|
|
53629
|
+
SELECT CASE WHEN COALESCE(cardinality(k.l), 0) > 0 THEN k.l
|
|
53630
|
+
ELSE ARRAY['var(--chart-1)','var(--chart-2)','var(--chart-3)','var(--chart-4)','var(--chart-5)'] END
|
|
53631
|
+
FROM k
|
|
53632
|
+
$function$;
|
|
53633
|
+
REVOKE ALL ON FUNCTION public.logistics_klantkleuren() FROM PUBLIC, anon;
|
|
53634
|
+
GRANT EXECUTE ON FUNCTION public.logistics_klantkleuren() TO authenticated, service_role;
|
|
53635
|
+
|
|
53636
|
+
CREATE OR REPLACE FUNCTION public.logistics_kar_tekening(p_order_id uuid, p_opties jsonb DEFAULT '{}'::jsonb)
|
|
53637
|
+
RETURNS jsonb LANGUAGE plpgsql STABLE SECURITY DEFINER SET search_path TO 'public'
|
|
53638
|
+
AS $function$
|
|
53639
|
+
DECLARE
|
|
53640
|
+
v_stand jsonb; v_opt jsonb := COALESCE(p_opties, '{}'::jsonb); v_pakbon boolean;
|
|
53641
|
+
v_kleuren text[] := public.logistics_klantkleuren();
|
|
53642
|
+
v_order record; v_fase text; v_fl jsonb; v_fl_bron boolean;
|
|
53643
|
+
c record; v_totaal int; v_nr int := 0; v_soort text; v_woord text; v_max int;
|
|
53644
|
+
v_letters jsonb; v_lagen jsonb; v_laag jsonb; v_blokken jsonb; it record; v_cur int; v_span int; i int;
|
|
53645
|
+
v_svg text; v_y numeric; v_h numeric; v_x numeric; v_w numeric; v_som numeric; b jsonb; v_kleur text; v_tekst text;
|
|
53646
|
+
v_status text; v_waarsch jsonb; v_gedeeld int; v_dragers jsonb := '[]'::jsonb; v_rij text := ''; v_label text;
|
|
53647
|
+
v_fusten int; v_n int; v_m int; v_cellen int; v_fl_kar text;
|
|
53648
|
+
BEGIN
|
|
53649
|
+
PERFORM public.logistics_acc_guard('ingelogd', 'logistics_kar_tekening');
|
|
53650
|
+
v_pakbon := COALESCE(v_opt->>'vorm', '') = 'pakbon';
|
|
53651
|
+
v_stand := public.logistics_order_indeling_stand(p_order_id);
|
|
53652
|
+
SELECT o.id, o.order_number, o.status, o.order_source, o.floriday_sales_order_id INTO v_order FROM public.orders o WHERE o.id = p_order_id;
|
|
53653
|
+
v_fase := public.logistics_status_fase(v_order.status);
|
|
53654
|
+
v_fl_bron := v_order.floriday_sales_order_id IS NOT NULL OR COALESCE(v_order.order_source, '') ILIKE '%floriday%';
|
|
53655
|
+
IF v_fl_bron THEN v_fl := public.logistics_floriday_gelijk(p_order_id); END IF;
|
|
53656
|
+
|
|
53657
|
+
SELECT count(*) INTO v_totaal FROM (
|
|
53658
|
+
SELECT DISTINCT c2.id FROM logistics_active.order_carts c2
|
|
53659
|
+
WHERE c2.order_id = p_order_id
|
|
53660
|
+
OR EXISTS (SELECT 1 FROM logistics_active.order_cart_items ci JOIN public.order_items oi ON oi.id = ci.order_item_id
|
|
53661
|
+
WHERE ci.cart_id = c2.id AND oi.order_id = p_order_id)) x;
|
|
53662
|
+
|
|
53663
|
+
FOR c IN
|
|
53664
|
+
SELECT DISTINCT ON (c2.cart_number, c2.id) c2.id, c2.cart_number, c2.occupancy_pct, c2.total_layers_used, c2.max_layers, c2.weight_kg,
|
|
53665
|
+
COALESCE(t.capacity_model, 'layered') AS soort, COALESCE(t.name, c2.carrier_type, 'Ladingdrager') AS naam,
|
|
53666
|
+
t.layers_per_carrier, t.max_crates_per_layer, t.max_weight_kg
|
|
53667
|
+
FROM logistics_active.order_carts c2
|
|
53668
|
+
LEFT JOIN public.load_carrier_types t ON t.id = c2.carrier_type_id
|
|
53669
|
+
WHERE c2.order_id = p_order_id
|
|
53670
|
+
OR EXISTS (SELECT 1 FROM logistics_active.order_cart_items ci JOIN public.order_items oi ON oi.id = ci.order_item_id
|
|
53671
|
+
WHERE ci.cart_id = c2.id AND oi.order_id = p_order_id)
|
|
53672
|
+
ORDER BY c2.cart_number, c2.id
|
|
53673
|
+
LOOP
|
|
53674
|
+
v_nr := v_nr + 1;
|
|
53675
|
+
CONTINUE WHEN (v_opt ? 'drager_id') AND (v_opt->>'drager_id') <> c.id::text;
|
|
53676
|
+
v_soort := CASE WHEN c.soort IN ('layered','stacked','single','none') THEN c.soort ELSE 'layered' END;
|
|
53677
|
+
v_woord := CASE v_soort WHEN 'layered' THEN 'Kar' WHEN 'none' THEN 'Colli' ELSE 'Pallet' END;
|
|
53678
|
+
v_max := GREATEST(1, COALESCE(c.max_layers, c.layers_per_carrier, CEIL(COALESCE(c.total_layers_used, 1))::int, 1));
|
|
53679
|
+
|
|
53680
|
+
-- Klantletters per order op deze drager: eigen order eerst (A).
|
|
53681
|
+
SELECT COALESCE(jsonb_object_agg(order_id::text, chr((64 + rn)::int)), '{}'::jsonb) INTO v_letters FROM (
|
|
53682
|
+
SELECT oi.order_id, row_number() OVER (ORDER BY (oi.order_id = p_order_id) DESC, min(o2.order_number)) AS rn
|
|
53683
|
+
FROM public.order_cart_items ci JOIN public.order_items oi ON oi.id = ci.order_item_id
|
|
53684
|
+
JOIN public.orders o2 ON o2.id = oi.order_id
|
|
53685
|
+
WHERE ci.cart_id = c.id GROUP BY oi.order_id) l;
|
|
53686
|
+
SELECT count(DISTINCT oi.order_id) FILTER (WHERE oi.order_id <> p_order_id) INTO v_gedeeld
|
|
53687
|
+
FROM public.order_cart_items ci JOIN public.order_items oi ON oi.id = ci.order_item_id WHERE ci.cart_id = c.id;
|
|
53688
|
+
|
|
53689
|
+
-- Lagen opbouwen: met laagnummer per regel, anders opeenvolgend volgens het aantal lagen per regel.
|
|
53690
|
+
v_lagen := '[]'::jsonb;
|
|
53691
|
+
FOR i IN 1 .. v_max LOOP
|
|
53692
|
+
v_lagen := v_lagen || jsonb_build_array(jsonb_build_object('index', i, 'hoogte_aandeel', round(1.0 / v_max, 4), 'blokken', '[]'::jsonb));
|
|
53693
|
+
END LOOP;
|
|
53694
|
+
v_cur := 1;
|
|
53695
|
+
FOR it IN
|
|
53696
|
+
SELECT ci.id, ci.layer_index, COALESCE(ci.crates_in_layer, ci.crates, ci.boxes, 0) AS fusten, ci.lagen, ci.layer_fraction,
|
|
53697
|
+
COALESCE(ci.product_name, oi.product_name, 'Product') AS product, oi.order_id
|
|
53698
|
+
FROM public.order_cart_items ci LEFT JOIN public.order_items oi ON oi.id = ci.order_item_id
|
|
53699
|
+
WHERE ci.cart_id = c.id ORDER BY ci.layer_index NULLS LAST, ci.created_at, ci.id
|
|
53700
|
+
LOOP
|
|
53701
|
+
IF it.layer_index IS NOT NULL THEN v_cur := it.layer_index; v_span := 1;
|
|
53702
|
+
ELSE v_span := GREATEST(1, CEIL(COALESCE(it.lagen, it.layer_fraction, 1))::int); END IF;
|
|
53703
|
+
FOR i IN v_cur .. v_cur + v_span - 1 LOOP
|
|
53704
|
+
IF i > jsonb_array_length(v_lagen) THEN
|
|
53705
|
+
v_lagen := v_lagen || jsonb_build_array(jsonb_build_object('index', i, 'hoogte_aandeel', 0, 'blokken', '[]'::jsonb));
|
|
53706
|
+
END IF;
|
|
53707
|
+
b := CASE WHEN it.order_id = p_order_id
|
|
53708
|
+
THEN jsonb_build_object('klantletter', v_letters->>it.order_id::text, 'product', it.product,
|
|
53709
|
+
'fusten', CEIL(it.fusten::numeric / v_span)::int, 'eigen_order', true)
|
|
53710
|
+
ELSE jsonb_build_object('klantletter', NULL, 'product', NULL,
|
|
53711
|
+
'fusten', CEIL(it.fusten::numeric / v_span)::int, 'eigen_order', false) END;
|
|
53712
|
+
v_lagen := jsonb_set(v_lagen, ARRAY[(i - 1)::text, 'blokken'], (v_lagen->(i - 1)->'blokken') || jsonb_build_array(b));
|
|
53713
|
+
END LOOP;
|
|
53714
|
+
IF it.layer_index IS NULL THEN v_cur := v_cur + v_span; END IF;
|
|
53715
|
+
END LOOP;
|
|
53716
|
+
-- Gelijke hoogtes als er meer lagen zijn ontstaan dan het maximum.
|
|
53717
|
+
IF jsonb_array_length(v_lagen) > v_max THEN
|
|
53718
|
+
SELECT jsonb_agg(jsonb_set(l, '{hoogte_aandeel}', to_jsonb(round(1.0 / jsonb_array_length(v_lagen), 4))) ORDER BY n) INTO v_lagen
|
|
53719
|
+
FROM jsonb_array_elements(v_lagen) WITH ORDINALITY x(l, n);
|
|
53720
|
+
END IF;
|
|
53721
|
+
SELECT COALESCE(sum((bb->>'fusten')::int), 0) INTO v_fusten
|
|
53722
|
+
FROM jsonb_array_elements(v_lagen) l, jsonb_array_elements(l->'blokken') bb;
|
|
53723
|
+
|
|
53724
|
+
-- Status uit bestaande pick- en controlegegevens.
|
|
53725
|
+
v_status := CASE
|
|
53726
|
+
WHEN v_fase IN ('verzonden','gereed_voor_facturatie','gefactureerd','afgehandeld_floriday') THEN 'afgemeld'
|
|
53727
|
+
WHEN public.logistics_cart_scanned(c.id) THEN 'gecontroleerd'
|
|
53728
|
+
WHEN EXISTS (SELECT 1 FROM public.pick_queue q WHERE q.reference = c.id::text AND q.cancelled_at IS NULL AND q.completed_at IS NOT NULL) THEN 'gepickt'
|
|
53729
|
+
WHEN EXISTS (SELECT 1 FROM public.pick_queue q WHERE q.reference = c.id::text AND q.cancelled_at IS NULL AND q.printed_at IS NOT NULL) THEN 'geprint'
|
|
53730
|
+
WHEN EXISTS (SELECT 1 FROM public.pick_queue q WHERE q.reference = c.id::text AND q.cancelled_at IS NULL AND (q.payload->>'printbaar') = 'true') THEN 'vrijgegeven'
|
|
53731
|
+
ELSE 'open' END;
|
|
53732
|
+
|
|
53733
|
+
v_waarsch := '[]'::jsonb;
|
|
53734
|
+
IF c.max_weight_kg IS NOT NULL AND c.weight_kg IS NOT NULL AND c.weight_kg > c.max_weight_kg THEN
|
|
53735
|
+
v_waarsch := v_waarsch || to_jsonb(v_woord || ' is te zwaar: ' || round(c.weight_kg) || ' kg, maximaal ' || round(c.max_weight_kg) || ' kg');
|
|
53736
|
+
END IF;
|
|
53737
|
+
IF COALESCE(c.occupancy_pct, 0) > 100 THEN
|
|
53738
|
+
v_waarsch := v_waarsch || to_jsonb(v_woord || ' is voller dan toegestaan (' || round(c.occupancy_pct) || '%)');
|
|
53739
|
+
END IF;
|
|
53740
|
+
IF jsonb_array_length(v_lagen) > v_max THEN
|
|
53741
|
+
v_waarsch := v_waarsch || to_jsonb('Er zijn meer lagen gevuld dan de ' || lower(v_woord) || ' heeft');
|
|
53742
|
+
END IF;
|
|
53743
|
+
|
|
53744
|
+
v_fl_kar := NULL;
|
|
53745
|
+
IF v_fl_bron THEN
|
|
53746
|
+
SELECT COALESCE(e->>'uitkomst', e->>'stand') INTO v_fl_kar
|
|
53747
|
+
FROM jsonb_array_elements(CASE WHEN jsonb_typeof(v_fl->'karren') = 'array' THEN v_fl->'karren' ELSE '[]'::jsonb END) e
|
|
53748
|
+
WHERE COALESCE(e->>'cartId', e->>'kar_id', e->>'id') = c.id::text LIMIT 1;
|
|
53749
|
+
v_fl_kar := COALESCE(v_fl_kar, v_fl->>'uitkomst', v_fl->>'stand');
|
|
53750
|
+
END IF;
|
|
53751
|
+
|
|
53752
|
+
v_label := CASE WHEN v_soort = 'none' THEN 'Colli 1–' || GREATEST(v_fusten, 1)
|
|
53753
|
+
ELSE v_woord || ' ' || v_nr || ' van ' || v_totaal END;
|
|
53754
|
+
|
|
53755
|
+
-- SVG: alleen themakleuren, alle tekst ge-escaped, geen scripts of externe verwijzingen.
|
|
53756
|
+
v_svg := '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 260" role="img" aria-label="' || public.logistics_xml(v_label || ', ' || c.naam) || '">'
|
|
53757
|
+
|| '<title>' || public.logistics_xml(v_label || ' – ' || c.naam) || '</title>'
|
|
53758
|
+
|| '<rect x="1" y="1" width="198" height="258" rx="6" fill="var(--card)" stroke="var(--border)"/>'
|
|
53759
|
+
|| '<text x="10" y="18" font-size="12" fill="var(--foreground)">' || public.logistics_xml(v_label) || '</text>';
|
|
53760
|
+
IF v_soort = 'layered' THEN
|
|
53761
|
+
v_y := 240; v_h := 210.0 / GREATEST(jsonb_array_length(v_lagen), 1);
|
|
53762
|
+
FOR i IN 0 .. jsonb_array_length(v_lagen) - 1 LOOP
|
|
53763
|
+
v_laag := v_lagen->i; v_y := v_y - v_h;
|
|
53764
|
+
v_svg := v_svg || '<line x1="10" y1="' || round(v_y + v_h, 1) || '" x2="190" y2="' || round(v_y + v_h, 1) || '" stroke="var(--muted-foreground)" stroke-width="2"/>';
|
|
53765
|
+
SELECT COALESCE(sum((bb->>'fusten')::int), 0) INTO v_som FROM jsonb_array_elements(v_laag->'blokken') bb;
|
|
53766
|
+
v_som := GREATEST(v_som, COALESCE(c.max_crates_per_layer, 0), 1); v_x := 12;
|
|
53767
|
+
FOR b IN SELECT * FROM jsonb_array_elements(v_laag->'blokken') LOOP
|
|
53768
|
+
v_w := 176.0 * (b->>'fusten')::numeric / v_som;
|
|
53769
|
+
v_kleur := CASE WHEN (b->>'eigen_order')::boolean
|
|
53770
|
+
THEN v_kleuren[1 + ((ascii(COALESCE(b->>'klantletter', 'A')) - 65) % cardinality(v_kleuren))]
|
|
53771
|
+
ELSE 'var(--muted)' END;
|
|
53772
|
+
v_svg := v_svg || '<rect x="' || round(v_x, 1) || '" y="' || round(v_y + 2, 1) || '" width="' || round(GREATEST(v_w - 2, 1), 1)
|
|
53773
|
+
|| '" height="' || round(GREATEST(v_h - 4, 1), 1) || '" rx="2" fill="' || v_kleur || '" stroke="var(--border)"/>';
|
|
53774
|
+
IF NOT v_pakbon AND v_w >= 24 AND v_h >= 12 THEN
|
|
53775
|
+
v_tekst := CASE WHEN (b->>'eigen_order')::boolean THEN COALESCE(b->>'klantletter', '') || ' ' || (b->>'fusten') ELSE (b->>'fusten') END;
|
|
53776
|
+
v_svg := v_svg || '<text x="' || round(v_x + 3, 1) || '" y="' || round(v_y + v_h / 2 + 4, 1) || '" font-size="9" fill="var(--foreground)">'
|
|
53777
|
+
|| public.logistics_xml(v_tekst) || '</text>';
|
|
53778
|
+
END IF;
|
|
53779
|
+
v_x := v_x + v_w;
|
|
53780
|
+
END LOOP;
|
|
53781
|
+
END LOOP;
|
|
53782
|
+
ELSIF v_soort = 'stacked' THEN
|
|
53783
|
+
v_n := GREATEST(1, COALESCE(c.max_crates_per_layer, 4)); v_m := GREATEST(1, CEIL(v_fusten::numeric / v_n)::int);
|
|
53784
|
+
v_h := LEAST(30, 200.0 / v_m);
|
|
53785
|
+
FOR i IN 0 .. v_m - 1 LOOP
|
|
53786
|
+
FOR v_cur IN 0 .. LEAST(v_n, v_fusten - i * v_n) - 1 LOOP
|
|
53787
|
+
v_svg := v_svg || '<rect x="' || round(12 + v_cur * (176.0 / v_n), 1) || '" y="' || round(232 - (i + 1) * v_h, 1)
|
|
53788
|
+
|| '" width="' || round(176.0 / v_n - 2, 1) || '" height="' || round(v_h - 2, 1) || '" fill="' || v_kleuren[1] || '" stroke="var(--border)"/>';
|
|
53789
|
+
END LOOP;
|
|
53790
|
+
END LOOP;
|
|
53791
|
+
v_svg := v_svg || '<rect x="8" y="234" width="184" height="8" fill="var(--muted-foreground)"/>'
|
|
53792
|
+
|| '<text x="10" y="254" font-size="10" fill="var(--foreground)">' || public.logistics_xml(v_n || ' dozen per laag × ' || v_m || ' lagen') || '</text>';
|
|
53793
|
+
ELSIF v_soort = 'single' THEN
|
|
53794
|
+
SELECT string_agg(DISTINCT bb->>'product', ', ') INTO v_tekst
|
|
53795
|
+
FROM jsonb_array_elements(v_lagen) l, jsonb_array_elements(l->'blokken') bb WHERE (bb->>'eigen_order')::boolean;
|
|
53796
|
+
v_svg := v_svg || '<rect x="20" y="40" width="160" height="180" rx="4" fill="' || v_kleuren[1] || '" stroke="var(--border)"/>'
|
|
53797
|
+
|| '<text x="28" y="130" font-size="11" fill="var(--foreground)">' || public.logistics_xml(left(COALESCE(v_tekst, 'Product'), 28)) || '</text>'
|
|
53798
|
+
|| '<text x="28" y="148" font-size="11" fill="var(--foreground)">' || public.logistics_xml(v_fusten || ' stuks') || '</text>';
|
|
53799
|
+
ELSE
|
|
53800
|
+
v_cellen := LEAST(GREATEST(v_fusten, 1), 60);
|
|
53801
|
+
FOR i IN 0 .. v_cellen - 1 LOOP
|
|
53802
|
+
v_svg := v_svg || '<rect x="' || (12 + (i % 6) * 30) || '" y="' || (28 + (i / 6) * 22) || '" width="26" height="18" rx="2" fill="' || v_kleuren[1] || '" stroke="var(--border)"/>'
|
|
53803
|
+
|| '<text x="' || (16 + (i % 6) * 30) || '" y="' || (41 + (i / 6) * 22) || '" font-size="9" fill="var(--foreground)">' || (i + 1) || '</text>';
|
|
53804
|
+
END LOOP;
|
|
53805
|
+
END IF;
|
|
53806
|
+
IF v_gedeeld > 0 THEN
|
|
53807
|
+
v_svg := v_svg || '<text x="10" y="32" font-size="9" fill="var(--muted-foreground)">'
|
|
53808
|
+
|| public.logistics_xml('gedeeld met ' || v_gedeeld || CASE WHEN v_gedeeld = 1 THEN ' andere order' ELSE ' andere orders' END) || '</text>';
|
|
53809
|
+
END IF;
|
|
53810
|
+
v_svg := v_svg || '</svg>';
|
|
53811
|
+
v_rij := v_rij || '<g transform="translate(' || ((v_nr - 1) * 70) || ',0) scale(0.33)">' || substr(v_svg, position('>' IN v_svg) + 1, length(v_svg) - position('>' IN v_svg) - 6) || '</g>';
|
|
53812
|
+
|
|
53813
|
+
v_dragers := v_dragers || jsonb_build_array(jsonb_build_object(
|
|
53814
|
+
'id', c.id, 'nummer', v_nr, 'totaal', v_totaal, 'soort', v_soort, 'naam', c.naam,
|
|
53815
|
+
'vulgraad_pct', CASE WHEN c.occupancy_pct IS NOT NULL THEN round(c.occupancy_pct) END,
|
|
53816
|
+
'lagen_gebruikt', CASE WHEN c.total_layers_used IS NOT NULL THEN round(c.total_layers_used, 1) END,
|
|
53817
|
+
'lagen_max', CASE WHEN v_soort IN ('layered','stacked') THEN v_max END,
|
|
53818
|
+
'gewicht_kg', CASE WHEN c.weight_kg IS NOT NULL THEN round(c.weight_kg, 1) END,
|
|
53819
|
+
'status', v_status, 'floriday_gelijk', v_fl_kar, 'gedeeld_met', v_gedeeld,
|
|
53820
|
+
'waarschuwingen', v_waarsch, 'lagen', v_lagen, 'svg', v_svg));
|
|
53821
|
+
END LOOP;
|
|
53822
|
+
|
|
53823
|
+
RETURN jsonb_build_object(
|
|
53824
|
+
'stand', v_stand->'stand', 'bezig_sinds', v_stand->'bezig_sinds', 'bijgewerkt_op', v_stand->'bijgewerkt_op',
|
|
53825
|
+
'verwacht_klaar', v_stand->'verwacht_klaar', 'tekst', v_stand->'tekst',
|
|
53826
|
+
'svg_rij', CASE WHEN jsonb_array_length(v_dragers) = 0 THEN NULL
|
|
53827
|
+
ELSE '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ' || GREATEST(jsonb_array_length(v_dragers) * 70, 70)
|
|
53828
|
+
|| ' 90" role="img" aria-label="Overzicht van ' || jsonb_array_length(v_dragers) || ' ladingdragers"><title>Overzicht ladingdragers</title>'
|
|
53829
|
+
|| v_rij || '</svg>' END,
|
|
53830
|
+
'dragers', v_dragers);
|
|
53831
|
+
END $function$;
|
|
53832
|
+
REVOKE ALL ON FUNCTION public.logistics_kar_tekening(uuid, jsonb) FROM PUBLIC, anon;
|
|
53833
|
+
GRANT EXECUTE ON FUNCTION public.logistics_kar_tekening(uuid, jsonb) TO authenticated, service_role;
|
|
53834
|
+
|
|
53835
|
+
-- Installatiecontrole E1-C (alleen lezen).
|
|
53836
|
+
CREATE OR REPLACE FUNCTION public.logistics_install_check_e1c()
|
|
53837
|
+
RETURNS TABLE(onderdeel text, ok boolean, toelichting text)
|
|
53838
|
+
LANGUAGE plpgsql STABLE SECURITY DEFINER SET search_path TO 'public'
|
|
53839
|
+
AS $function$
|
|
53840
|
+
DECLARE f text; v_order uuid; v jsonb; v_n int;
|
|
53841
|
+
BEGIN
|
|
53842
|
+
FOREACH f IN ARRAY ARRAY['logistics_uitgang_uit()','logistics_pickup_afwijkend(integer)','logistics_pickup_herstel_voorbeeld()',
|
|
53843
|
+
'logistics_pickup_herstel_uitvoeren(integer)','logistics_stock_email_klaarzetten(integer)','logistics_pickwachtrij_overslaan()',
|
|
53844
|
+
'logistics_order_indeling_stand(uuid)','logistics_kar_tekening(uuid,jsonb)','logistics_klantkleuren()','logistics_xml(text)'] LOOP
|
|
53845
|
+
onderdeel := 'functie ' || f;
|
|
53846
|
+
ok := to_regprocedure('public.' || f) IS NOT NULL;
|
|
53847
|
+
toelichting := CASE WHEN ok THEN 'aanwezig' ELSE 'ontbreekt: werk de Engine bij via Supabase bijwerken' END;
|
|
53848
|
+
RETURN NEXT;
|
|
53849
|
+
END LOOP;
|
|
53850
|
+
IF to_regprocedure('public.logistics_pickup_afwijkend(integer)') IS NOT NULL THEN
|
|
53851
|
+
v_n := (public.logistics_pickup_afwijkend(0)->>'aantal')::int;
|
|
53852
|
+
onderdeel := 'ophaalmoment past bij vervoerder'; ok := v_n = 0;
|
|
53853
|
+
toelichting := CASE WHEN ok THEN 'alle open orders kloppen'
|
|
53854
|
+
ELSE v_n || ' open orders hebben een ophaalmoment van een andere vervoerder: draai het herstel (handmatige stap)' END;
|
|
53855
|
+
RETURN NEXT;
|
|
53856
|
+
END IF;
|
|
53857
|
+
SELECT c.order_id INTO v_order FROM logistics_active.order_carts c WHERE c.order_id IS NOT NULL ORDER BY c.updated_at DESC LIMIT 1;
|
|
53858
|
+
IF v_order IS NOT NULL AND to_regprocedure('public.logistics_kar_tekening(uuid,jsonb)') IS NOT NULL THEN
|
|
53859
|
+
v := public.logistics_kar_tekening(v_order, '{}'::jsonb);
|
|
53860
|
+
onderdeel := 'stand en kar-tekening';
|
|
53861
|
+
ok := (v->>'stand') IN ('bezig','klaar','niet_gelukt','geen_regels') AND jsonb_typeof(v->'dragers') = 'array';
|
|
53862
|
+
toelichting := CASE WHEN ok THEN 'geldige uitkomst voor een bestaande order' ELSE 'ongeldige uitkomst' END;
|
|
53863
|
+
RETURN NEXT;
|
|
53864
|
+
END IF;
|
|
53865
|
+
END $function$;
|
|
53866
|
+
REVOKE ALL ON FUNCTION public.logistics_install_check_e1c() FROM PUBLIC, anon;
|
|
53867
|
+
GRANT EXECUTE ON FUNCTION public.logistics_install_check_e1c() TO authenticated, service_role;
|
|
53868
|
+
|
|
53869
|
+
-- Golf 1-controle neemt E1-C mee.
|
|
53870
|
+
DO $e1c2$
|
|
53871
|
+
DECLARE d text;
|
|
53872
|
+
BEGIN
|
|
53873
|
+
d := pg_get_functiondef('public.logistics_install_check_golf1()'::regprocedure);
|
|
53874
|
+
IF position('logistics_install_check_e1c' IN d) = 0 THEN
|
|
53875
|
+
d := regexp_replace(d, E'\\nEND \\$function\\$\\s*$', E'\n RETURN QUERY SELECT * FROM public.logistics_install_check_e1c();\nEND $function$');
|
|
53876
|
+
EXECUTE d;
|
|
53877
|
+
END IF;
|
|
53878
|
+
END $e1c2$;
|
|
53879
|
+
|
|
53880
|
+
CREATE OR REPLACE FUNCTION public.logistics_schema_versie()
|
|
53881
|
+
RETURNS text LANGUAGE sql IMMUTABLE SET search_path TO 'public'
|
|
53882
|
+
AS $function$ SELECT '2026-11-10f'::text $function$;
|
|
53883
|
+
|
|
53884
|
+
-- ===== E1-C: interne hulpfuncties alleen voor server (20261111081737) =====
|
|
53885
|
+
REVOKE EXECUTE ON FUNCTION public.logistics_klantkleuren() FROM authenticated;
|
|
53886
|
+
REVOKE EXECUTE ON FUNCTION public.logistics_install_check_e1c() FROM authenticated;
|
|
53887
|
+
REVOKE EXECUTE ON FUNCTION public.logistics_pickup_herstel_voorbeeld() FROM authenticated;
|
|
53888
|
+
REVOKE EXECUTE ON FUNCTION public.logistics_pickup_afwijkend(integer) FROM authenticated;
|
|
53889
|
+
-- ===== E1-C: klantletter in kar-tekening (20261111081807) =====
|
|
53890
|
+
DO $fix$
|
|
53891
|
+
DECLARE d text;
|
|
53892
|
+
BEGIN
|
|
53893
|
+
d := pg_get_functiondef('public.logistics_kar_tekening(uuid,jsonb)'::regprocedure);
|
|
53894
|
+
IF position('chr(64 + rn)' IN d) > 0 THEN
|
|
53895
|
+
d := replace(d, 'chr(64 + rn)', 'chr((64 + rn)::int)');
|
|
53896
|
+
EXECUTE d;
|
|
53897
|
+
END IF;
|
|
53898
|
+
END $fix$;
|
|
53899
|
+
-- ===== E1-C: achterstand pickwachtrij ook overslaan zonder wijziging (20261111082613) =====
|
|
53900
|
+
CREATE OR REPLACE FUNCTION public.logistics_sync_pick_queue()
|
|
53901
|
+
RETURNS jsonb
|
|
53902
|
+
LANGUAGE plpgsql
|
|
53903
|
+
SECURITY DEFINER
|
|
53904
|
+
SET search_path TO 'public'
|
|
53905
|
+
AS $function$
|
|
53906
|
+
DECLARE v_core jsonb; v_back jsonb;
|
|
53907
|
+
BEGIN
|
|
53908
|
+
PERFORM public.logistics_acc_guard('ingelogd', 'logistics_sync_pick_queue');
|
|
53909
|
+
v_core := public.logistics_sync_pick_queue_core();
|
|
53910
|
+
-- E1-C/2: niets veranderd sinds de vorige ronde = ook de achterstand niet opnieuw doorrekenen.
|
|
53911
|
+
IF COALESCE(v_core->>'guard', '') = 'niets veranderd sinds de vorige ronde' THEN
|
|
53912
|
+
RETURN v_core || jsonb_build_object('achterstand', jsonb_build_object('overgeslagen', 'niets veranderd'));
|
|
53913
|
+
END IF;
|
|
53914
|
+
BEGIN
|
|
53915
|
+
v_back := public.logistics_sync_pick_queue_backlog();
|
|
53916
|
+
EXCEPTION WHEN others THEN
|
|
53917
|
+
v_back := jsonb_build_object('achterstand', 'fout', 'reden', SQLERRM);
|
|
53918
|
+
PERFORM public.logistics_melding('logistics_sync_pick_queue', NULL, 'Achterstand van de pickwachtrij kon niet worden bijgewerkt; de volgende ronde probeert het opnieuw', SQLERRM);
|
|
53919
|
+
END;
|
|
53920
|
+
RETURN COALESCE(v_core, '{}'::jsonb) || jsonb_build_object('achterstand', v_back);
|
|
53921
|
+
END $function$;
|
|
53922
|
+
-- ===== Vervolg E1-C (2026-11-10g): kar-tekening, afleveradressen, verzendmoment, laadtoestand =====
|
|
53923
|
+
-- ===== 2026-11-10g Vervolg E1-C =====
|
|
53924
|
+
|
|
53925
|
+
-- Punt 1: kar-tekening tekenen vanuit de dragergegevens (puur, geen tabellen).
|
|
53926
|
+
CREATE OR REPLACE FUNCTION public.logistics_kar_svg(p jsonb, p_klein boolean DEFAULT false)
|
|
53927
|
+
RETURNS text LANGUAGE plpgsql IMMUTABLE SET search_path TO 'public'
|
|
53928
|
+
AS $function$
|
|
53929
|
+
DECLARE
|
|
53930
|
+
v_soort text := coalesce(p->>'soort', 'layered');
|
|
53931
|
+
v_lagen jsonb := CASE WHEN jsonb_typeof(p->'lagen') = 'array' THEN p->'lagen' ELSE '[]'::jsonb END;
|
|
53932
|
+
v_kl jsonb := CASE WHEN jsonb_typeof(p->'kleuren') = 'array' AND jsonb_array_length(p->'kleuren') > 0 THEN p->'kleuren' ELSE '["#56B4E9"]'::jsonb END;
|
|
53933
|
+
v_fg text := 'var(--foreground, #1f2937)'; v_mut text := 'var(--muted-foreground, #6b7280)';
|
|
53934
|
+
v_bd text := 'var(--border, #d1d5db)'; v_card text := 'var(--card, #ffffff)'; v_red text := 'var(--destructive, #dc2626)';
|
|
53935
|
+
v_grijs text := 'var(--muted, #e5e7eb)'; v_prim text := 'var(--primary, #2563eb)';
|
|
53936
|
+
v_label text := coalesce(p->>'label', 'Ladingdrager'); v_naam text := coalesce(p->>'naam', '');
|
|
53937
|
+
v_pakbon boolean := coalesce((p->>'pakbon')::boolean, false);
|
|
53938
|
+
v_top numeric := 64; v_bot numeric; v_x0 numeric := 34; v_iw numeric := 166;
|
|
53939
|
+
v_n int; v_lh numeric; v_yb numeric; v_yt numeric; v_sh numeric; v_sw numeric;
|
|
53940
|
+
v_laag jsonb; b jsonb; i int; k int; v_P int; v_pos int; v_f int; v_s int; v_som int;
|
|
53941
|
+
v_kleur text; v_x numeric; v_bx numeric; v_titel text; v_body text := ''; v_desc text := '';
|
|
53942
|
+
v_pct numeric; v_g int; v_w jsonb; v_chars int; v_tekst text; v_eigen boolean; v_letter text; v_ly numeric; v_naam_ok boolean;
|
|
53943
|
+
BEGIN
|
|
53944
|
+
v_bot := CASE WHEN v_soort = 'layered' THEN 280 ELSE 282 END;
|
|
53945
|
+
-- Kop
|
|
53946
|
+
IF NOT p_klein THEN
|
|
53947
|
+
v_tekst := v_label || CASE WHEN v_naam <> '' THEN ' · ' || v_naam ELSE '' END;
|
|
53948
|
+
IF length(v_tekst) > 34 THEN v_tekst := left(v_tekst, 33) || '…'; END IF;
|
|
53949
|
+
v_body := v_body || '<text x="12" y="20" font-size="13" font-weight="600" fill="' || v_fg || '">' || public.logistics_xml(v_tekst) || '</text>';
|
|
53950
|
+
v_pct := (p->>'vulgraad_pct')::numeric;
|
|
53951
|
+
IF v_pct IS NOT NULL THEN
|
|
53952
|
+
v_body := v_body || '<rect x="12" y="28" width="166" height="8" rx="4" fill="' || v_grijs || '"/>'
|
|
53953
|
+
|| '<rect x="12" y="28" width="' || round(greatest(166 * least(v_pct, 100) / 100, 2), 1) || '" height="8" rx="4" fill="'
|
|
53954
|
+
|| CASE WHEN v_pct > 100 THEN v_red ELSE v_prim END || '"/>'
|
|
53955
|
+
|| '<text x="186" y="36" font-size="11" fill="' || v_fg || '">' || round(v_pct) || ' %</text>';
|
|
53956
|
+
END IF;
|
|
53957
|
+
v_g := coalesce((p->>'gedeeld_met')::int, 0);
|
|
53958
|
+
IF v_g > 0 THEN
|
|
53959
|
+
v_body := v_body || '<text x="12" y="52" font-size="10" fill="' || v_mut || '">'
|
|
53960
|
+
|| public.logistics_xml('gedeeld met ' || v_g || CASE WHEN v_g = 1 THEN ' andere order' ELSE ' andere orders' END) || '</text>';
|
|
53961
|
+
END IF;
|
|
53962
|
+
IF jsonb_typeof(p->'waarschuwingen') = 'array' AND jsonb_array_length(p->'waarschuwingen') > 0 THEN
|
|
53963
|
+
SELECT string_agg(x, '; ') INTO v_tekst FROM jsonb_array_elements_text(p->'waarschuwingen') x;
|
|
53964
|
+
v_body := v_body || '<g><title>' || public.logistics_xml(v_tekst) || '</title><circle cx="224" cy="16" r="9" fill="' || v_red || '"/>'
|
|
53965
|
+
|| '<text x="224" y="21" font-size="13" font-weight="700" text-anchor="middle" fill="' || v_card || '">!</text></g>';
|
|
53966
|
+
v_desc := v_desc || ' Let op: ' || v_tekst || '.';
|
|
53967
|
+
END IF;
|
|
53968
|
+
ELSE
|
|
53969
|
+
v_body := v_body || '<text x="120" y="50" font-size="52" font-weight="700" text-anchor="middle" fill="' || v_fg || '">'
|
|
53970
|
+
|| coalesce((p->>'nummer')::int, 1) || '</text>';
|
|
53971
|
+
END IF;
|
|
53972
|
+
|
|
53973
|
+
IF v_soort IN ('layered', 'stacked') THEN
|
|
53974
|
+
v_n := greatest(jsonb_array_length(v_lagen), 1);
|
|
53975
|
+
v_lh := (v_bot - v_top) / v_n;
|
|
53976
|
+
IF v_soort = 'layered' THEN
|
|
53977
|
+
v_body := v_body || '<rect x="26" y="' || (v_top - 8) || '" width="5" height="' || (v_bot - v_top + 10) || '" rx="2" fill="' || v_mut || '"/>'
|
|
53978
|
+
|| '<rect x="203" y="' || (v_top - 8) || '" width="5" height="' || (v_bot - v_top + 10) || '" rx="2" fill="' || v_mut || '"/>'
|
|
53979
|
+
|| '<rect x="22" y="' || v_bot || '" width="190" height="7" rx="2" fill="' || v_mut || '"/>'
|
|
53980
|
+
|| '<circle cx="42" cy="' || (v_bot + 19) || '" r="8" fill="' || v_card || '" stroke="' || v_mut || '" stroke-width="3"/>'
|
|
53981
|
+
|| '<circle cx="192" cy="' || (v_bot + 19) || '" r="8" fill="' || v_card || '" stroke="' || v_mut || '" stroke-width="3"/>'
|
|
53982
|
+
|| '<circle cx="42" cy="' || (v_bot + 19) || '" r="2" fill="' || v_mut || '"/><circle cx="192" cy="' || (v_bot + 19) || '" r="2" fill="' || v_mut || '"/>';
|
|
53983
|
+
ELSE
|
|
53984
|
+
v_body := v_body || '<rect x="24" y="' || v_bot || '" width="186" height="5" rx="1" fill="' || v_mut || '"/>'
|
|
53985
|
+
|| '<rect x="28" y="' || (v_bot + 5) || '" width="20" height="10" fill="' || v_mut || '"/>'
|
|
53986
|
+
|| '<rect x="107" y="' || (v_bot + 5) || '" width="20" height="10" fill="' || v_mut || '"/>'
|
|
53987
|
+
|| '<rect x="186" y="' || (v_bot + 5) || '" width="20" height="10" fill="' || v_mut || '"/>'
|
|
53988
|
+
|| '<rect x="24" y="' || (v_bot + 15) || '" width="186" height="4" rx="1" fill="' || v_mut || '"/>';
|
|
53989
|
+
END IF;
|
|
53990
|
+
FOR i IN 0 .. v_n - 1 LOOP
|
|
53991
|
+
v_laag := coalesce(v_lagen->i, '{}'::jsonb);
|
|
53992
|
+
v_yb := v_bot - i * v_lh; v_yt := v_yb - v_lh;
|
|
53993
|
+
IF v_soort = 'layered' AND i > 0 THEN
|
|
53994
|
+
v_body := v_body || '<rect x="29" y="' || round(v_yb - 1.5, 1) || '" width="176" height="3" rx="1" fill="' || v_mut || '"/>';
|
|
53995
|
+
END IF;
|
|
53996
|
+
v_P := least(greatest(coalesce((v_laag->>'plekken')::int, 1), 1), 40);
|
|
53997
|
+
v_sw := v_iw / v_P; v_sh := greatest(v_lh - 7, 2);
|
|
53998
|
+
v_pos := 0; v_som := 0;
|
|
53999
|
+
FOR b IN SELECT x FROM jsonb_array_elements(CASE WHEN jsonb_typeof(v_laag->'blokken') = 'array' THEN v_laag->'blokken' ELSE '[]'::jsonb END) x LOOP
|
|
54000
|
+
EXIT WHEN v_pos >= v_P;
|
|
54001
|
+
v_f := least(greatest(coalesce((b->>'fusten')::int, 0), 0), v_P - v_pos);
|
|
54002
|
+
v_s := least(greatest(v_f, round(coalesce((b->>'aandeel')::numeric, 0) * v_P)::int), v_P - v_pos);
|
|
54003
|
+
CONTINUE WHEN v_s = 0;
|
|
54004
|
+
v_eigen := coalesce((b->>'eigen_order')::boolean, false) OR (b->>'klantletter') IS NOT NULL;
|
|
54005
|
+
v_letter := b->>'klantletter';
|
|
54006
|
+
v_kleur := CASE WHEN v_eigen THEN v_kl->>(coalesce((b->>'kleur_index')::int, 0) % jsonb_array_length(v_kl)) ELSE v_grijs END;
|
|
54007
|
+
v_bx := v_x0 + v_pos * v_sw;
|
|
54008
|
+
IF v_pos > 0 THEN
|
|
54009
|
+
v_body := v_body || '<line x1="' || round(v_bx, 1) || '" y1="' || round(v_yt + 2, 1) || '" x2="' || round(v_bx, 1) || '" y2="' || round(v_yb - 3, 1)
|
|
54010
|
+
|| '" stroke="' || v_fg || '" stroke-width="0.8" opacity="0.6"/>';
|
|
54011
|
+
END IF;
|
|
54012
|
+
v_titel := concat_ws(' · ', CASE WHEN v_eigen THEN nullif(b->>'product', '') ELSE 'Andere order' END,
|
|
54013
|
+
CASE WHEN coalesce((b->>'eigen_order')::boolean, false) THEN nullif(b->>'klant', '') END,
|
|
54014
|
+
(b->>'fusten') || CASE WHEN (b->>'fusten') = '1' THEN ' fust' ELSE ' fusten' END,
|
|
54015
|
+
'laag ' || (i + 1) || ': ' || round(coalesce((b->>'aandeel')::numeric, 0) * 100) || ' % van de laag');
|
|
54016
|
+
v_body := v_body || '<g><title>' || public.logistics_xml(v_titel) || '</title>';
|
|
54017
|
+
v_naam_ok := NOT p_klein AND NOT v_pakbon AND v_eigen AND nullif(b->>'product', '') IS NOT NULL AND v_s * v_sw >= 56 AND v_sh >= 34;
|
|
54018
|
+
v_ly := CASE WHEN v_naam_ok THEN v_yt + 3 + 14 ELSE v_yt + 3 + v_sh / 2 + 4 END;
|
|
54019
|
+
FOR k IN 0 .. v_f - 1 LOOP
|
|
54020
|
+
v_x := v_bx + k * v_sw;
|
|
54021
|
+
v_body := v_body || '<rect x="' || round(v_x + 1.5, 1) || '" y="' || round(v_yt + 3, 1) || '" width="' || round(greatest(v_sw - 3, 1), 1)
|
|
54022
|
+
|| '" height="' || round(v_sh, 1) || '" rx="3" fill="' || v_kleur || '" stroke="' || v_fg || '" stroke-width="0.6"/>';
|
|
54023
|
+
IF NOT v_eigen THEN
|
|
54024
|
+
v_body := v_body || '<rect x="' || round(v_x + 1.5, 1) || '" y="' || round(v_yt + 3, 1) || '" width="' || round(greatest(v_sw - 3, 1), 1)
|
|
54025
|
+
|| '" height="' || round(v_sh, 1) || '" rx="3" fill="url(#lgx-arc)"/>';
|
|
54026
|
+
ELSIF NOT p_klein AND v_letter IS NOT NULL AND v_sw >= 12 AND v_sh >= 14 THEN
|
|
54027
|
+
v_body := v_body || '<text x="' || round(v_x + v_sw / 2, 1) || '" y="' || round(v_ly, 1) || '" font-size="11" font-weight="700" text-anchor="middle" fill="'
|
|
54028
|
+
|| v_fg || '" stroke="' || v_card || '" stroke-width="2.5" paint-order="stroke">' || public.logistics_xml(v_letter) || '</text>';
|
|
54029
|
+
END IF;
|
|
54030
|
+
END LOOP;
|
|
54031
|
+
FOR k IN v_f .. v_s - 1 LOOP
|
|
54032
|
+
v_x := v_bx + k * v_sw;
|
|
54033
|
+
v_body := v_body || '<rect x="' || round(v_x + 1.5, 1) || '" y="' || round(v_yt + 3, 1) || '" width="' || round(greatest(v_sw - 3, 1), 1)
|
|
54034
|
+
|| '" height="' || round(v_sh, 1) || '" rx="3" fill="none" stroke="' || v_bd || '" stroke-dasharray="3 2"/>';
|
|
54035
|
+
END LOOP;
|
|
54036
|
+
IF v_naam_ok THEN
|
|
54037
|
+
v_chars := floor((v_s * v_sw - 8) / 6)::int;
|
|
54038
|
+
v_tekst := b->>'product';
|
|
54039
|
+
IF length(v_tekst) > v_chars THEN v_tekst := left(v_tekst, greatest(v_chars - 1, 1)) || '…'; END IF;
|
|
54040
|
+
v_body := v_body || '<text x="' || round(v_bx + 4, 1) || '" y="' || round(v_yb - 8, 1) || '" font-size="10" fill="' || v_fg
|
|
54041
|
+
|| '" stroke="' || v_card || '" stroke-width="2.5" paint-order="stroke">' || public.logistics_xml(v_tekst) || '</text>';
|
|
54042
|
+
END IF;
|
|
54043
|
+
v_body := v_body || '</g>';
|
|
54044
|
+
v_pos := v_pos + v_s; v_som := v_som + v_f;
|
|
54045
|
+
END LOOP;
|
|
54046
|
+
FOR k IN v_pos .. v_P - 1 LOOP
|
|
54047
|
+
v_x := v_x0 + k * v_sw;
|
|
54048
|
+
v_body := v_body || '<rect x="' || round(v_x + 1.5, 1) || '" y="' || round(v_yt + 3, 1) || '" width="' || round(greatest(v_sw - 3, 1), 1)
|
|
54049
|
+
|| '" height="' || round(v_sh, 1) || '" rx="3" fill="none" stroke="' || v_bd || '" stroke-dasharray="3 2"/>';
|
|
54050
|
+
END LOOP;
|
|
54051
|
+
IF NOT p_klein THEN
|
|
54052
|
+
v_body := v_body || '<text x="15" y="' || round(v_yt + v_lh / 2 + 4, 1) || '" font-size="10" text-anchor="middle" fill="' || v_mut || '">' || (i + 1) || '</text>'
|
|
54053
|
+
|| '<text x="212" y="' || round(v_yt + v_lh / 2 + 4, 1) || '" font-size="10" fill="' || v_fg || '">' || v_som || '/' || v_P
|
|
54054
|
+
|| CASE WHEN v_som >= v_P THEN '<tspan fill="' || v_prim || '" font-weight="700"> ✓</tspan>' ELSE '' END || '</text>';
|
|
54055
|
+
END IF;
|
|
54056
|
+
IF nullif(v_laag->>'waarschuwing', '') IS NOT NULL THEN
|
|
54057
|
+
v_body := v_body || '<g><title>' || public.logistics_xml(v_laag->>'waarschuwing') || '</title><rect x="33" y="' || round(v_yt + 1, 1) || '" width="168" height="'
|
|
54058
|
+
|| round(greatest(v_lh - 2, 1), 1) || '" rx="3" fill="none" stroke="' || v_red || '" stroke-width="2"/>'
|
|
54059
|
+
|| CASE WHEN NOT p_klein THEN '<text x="232" y="' || round(v_yt + v_lh / 2 + 4, 1) || '" font-size="12" font-weight="700" fill="' || v_red || '">!</text>' ELSE '' END
|
|
54060
|
+
|| '</g>';
|
|
54061
|
+
END IF;
|
|
54062
|
+
v_desc := v_desc || ' Laag ' || (i + 1) || ': ' || v_som || ' van ' || v_P || ' plekken.';
|
|
54063
|
+
END LOOP;
|
|
54064
|
+
ELSIF v_soort = 'single' THEN
|
|
54065
|
+
SELECT x INTO b FROM jsonb_array_elements(v_lagen) l, jsonb_array_elements(coalesce(l->'blokken', '[]'::jsonb)) x
|
|
54066
|
+
WHERE coalesce((x->>'eigen_order')::boolean, false) LIMIT 1;
|
|
54067
|
+
SELECT coalesce(sum((x->>'fusten')::int), 0) INTO v_som FROM jsonb_array_elements(v_lagen) l, jsonb_array_elements(coalesce(l->'blokken', '[]'::jsonb)) x;
|
|
54068
|
+
v_kleur := CASE WHEN b IS NULL THEN v_grijs ELSE v_kl->>(coalesce((b->>'kleur_index')::int, 0) % jsonb_array_length(v_kl)) END;
|
|
54069
|
+
v_body := v_body || '<g><title>' || public.logistics_xml(concat_ws(' · ', coalesce(b->>'product', 'Product'), nullif(b->>'klant', ''), v_som || ' stuks')) || '</title>'
|
|
54070
|
+
|| '<rect x="46" y="96" width="148" height="160" rx="8" fill="' || v_kleur || '" stroke="' || v_fg || '" stroke-width="1"/>'
|
|
54071
|
+
|| '<line x1="46" y1="136" x2="194" y2="136" stroke="' || v_fg || '" stroke-width="0.8" opacity="0.5"/>'
|
|
54072
|
+
|| '<line x1="46" y1="216" x2="194" y2="216" stroke="' || v_fg || '" stroke-width="0.8" opacity="0.5"/>'
|
|
54073
|
+
|| '<rect x="40" y="88" width="160" height="12" rx="4" fill="' || v_kleur || '" stroke="' || v_fg || '" stroke-width="1"/>';
|
|
54074
|
+
IF NOT p_klein THEN
|
|
54075
|
+
v_tekst := coalesce(b->>'product', 'Product'); IF length(v_tekst) > 22 THEN v_tekst := left(v_tekst, 21) || '…'; END IF;
|
|
54076
|
+
v_body := v_body || '<circle cx="120" cy="168" r="18" fill="' || v_card || '" stroke="' || v_fg || '" stroke-width="1"/>'
|
|
54077
|
+
|| '<text x="120" y="176" font-size="20" font-weight="700" text-anchor="middle" fill="' || v_fg || '">' || public.logistics_xml(coalesce(b->>'klantletter', '')) || '</text>'
|
|
54078
|
+
|| CASE WHEN NOT v_pakbon THEN '<text x="120" y="122" font-size="12" text-anchor="middle" fill="' || v_fg || '" stroke="' || v_card || '" stroke-width="2.5" paint-order="stroke">' || public.logistics_xml(v_tekst) || '</text>' ELSE '' END
|
|
54079
|
+
|| '<text x="120" y="240" font-size="12" text-anchor="middle" fill="' || v_fg || '" stroke="' || v_card || '" stroke-width="2.5" paint-order="stroke">' || v_som || ' stuks</text>';
|
|
54080
|
+
END IF;
|
|
54081
|
+
v_body := v_body || '</g>';
|
|
54082
|
+
v_desc := v_desc || ' Eén colli met ' || v_som || ' stuks.';
|
|
54083
|
+
ELSE
|
|
54084
|
+
v_pos := 0;
|
|
54085
|
+
FOR b IN SELECT x FROM jsonb_array_elements(v_lagen) l, jsonb_array_elements(coalesce(l->'blokken', '[]'::jsonb)) x LOOP
|
|
54086
|
+
v_eigen := coalesce((b->>'eigen_order')::boolean, false) OR (b->>'klantletter') IS NOT NULL;
|
|
54087
|
+
v_kleur := CASE WHEN v_eigen THEN v_kl->>(coalesce((b->>'kleur_index')::int, 0) % jsonb_array_length(v_kl)) ELSE v_grijs END;
|
|
54088
|
+
v_f := greatest(coalesce((b->>'fusten')::int, 0), 0);
|
|
54089
|
+
v_body := v_body || '<g><title>' || public.logistics_xml(concat_ws(' · ', CASE WHEN v_eigen THEN b->>'product' ELSE 'Andere order' END, v_f || ' colli')) || '</title>';
|
|
54090
|
+
FOR k IN 0 .. v_f - 1 LOOP
|
|
54091
|
+
EXIT WHEN v_pos >= 48;
|
|
54092
|
+
v_body := v_body || '<rect x="' || (14 + (v_pos % 6) * 36) || '" y="' || (66 + (v_pos / 6) * 27) || '' || '" width="32" height="23" rx="3" fill="' || v_kleur
|
|
54093
|
+
|| '" stroke="' || v_fg || '" stroke-width="0.6"/>'
|
|
54094
|
+
|| CASE WHEN NOT v_eigen THEN '<rect x="' || (14 + (v_pos % 6) * 36) || '" y="' || (66 + (v_pos / 6) * 27) || '" width="32" height="23" rx="3" fill="url(#lgx-arc)"/>' ELSE '' END
|
|
54095
|
+
|| CASE WHEN NOT p_klein THEN '<text x="' || (30 + (v_pos % 6) * 36) || '" y="' || (82 + (v_pos / 6) * 27) || '" font-size="10" text-anchor="middle" fill="' || v_fg
|
|
54096
|
+
|| '" stroke="' || v_card || '" stroke-width="2.5" paint-order="stroke">' || (v_pos + 1) || '</text>' ELSE '' END;
|
|
54097
|
+
v_pos := v_pos + 1;
|
|
54098
|
+
END LOOP;
|
|
54099
|
+
v_body := v_body || '</g>';
|
|
54100
|
+
END LOOP;
|
|
54101
|
+
v_desc := v_desc || ' ' || v_pos || ' colli.';
|
|
54102
|
+
END IF;
|
|
54103
|
+
|
|
54104
|
+
RETURN '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 240 320" role="img" aria-label="' || public.logistics_xml(v_label || ', ' || v_naam) || '">'
|
|
54105
|
+
|| '<title>' || public.logistics_xml(v_label || CASE WHEN v_naam <> '' THEN ' · ' || v_naam ELSE '' END) || '</title>'
|
|
54106
|
+
|| '<desc>' || public.logistics_xml(btrim(v_desc)) || '</desc>'
|
|
54107
|
+
|| '<defs><pattern id="lgx-arc" width="6" height="6" patternUnits="userSpaceOnUse" patternTransform="rotate(45)"><line x1="0" y1="0" x2="0" y2="6" stroke="' || v_mut || '" stroke-width="1.5" opacity="0.6"/></pattern></defs>'
|
|
54108
|
+
|| '<rect x="1" y="1" width="238" height="318" rx="10" fill="' || v_card || '" stroke="' || v_bd || '"/>'
|
|
54109
|
+
|| v_body || '</svg>';
|
|
54110
|
+
END $function$;
|
|
54111
|
+
REVOKE ALL ON FUNCTION public.logistics_kar_svg(jsonb, boolean) FROM PUBLIC, anon;
|
|
54112
|
+
GRANT EXECUTE ON FUNCTION public.logistics_kar_svg(jsonb, boolean) TO authenticated, service_role;
|
|
54113
|
+
|
|
54114
|
+
CREATE OR REPLACE FUNCTION public.logistics_kar_tekening(p_order_id uuid, p_opties jsonb DEFAULT '{}'::jsonb)
|
|
54115
|
+
RETURNS jsonb LANGUAGE plpgsql STABLE SECURITY DEFINER SET search_path TO 'public'
|
|
54116
|
+
AS $function$
|
|
54117
|
+
DECLARE
|
|
54118
|
+
v_stand jsonb; v_opt jsonb := COALESCE(p_opties, '{}'::jsonb); v_pakbon boolean; v_alle boolean;
|
|
54119
|
+
v_kleuren text[] := public.logistics_klantkleuren(); v_kl jsonb := '[]'::jsonb;
|
|
54120
|
+
v_fb text[] := ARRAY['#E69F00','#56B4E9','#009E73','#CC79A7','#0072B2','#D55E00','#F0E442'];
|
|
54121
|
+
v_order record; v_fase text; v_fl jsonb; v_fl_bron boolean;
|
|
54122
|
+
c record; v_totaal int; v_nr int := 0; v_soort text; v_woord text; v_max int;
|
|
54123
|
+
v_letters jsonb; v_lagen jsonb; it record; v_cur int; v_span int; i int; j int;
|
|
54124
|
+
v_frac numeric; v_vrij numeric; v_aandeel numeric; v_rest int; v_f int; b jsonb; v_eigen boolean; v_letter text;
|
|
54125
|
+
v_status text; v_waarsch jsonb; v_gedeeld int; v_dragers jsonb := '[]'::jsonb; v_rij text := ''; v_label text;
|
|
54126
|
+
v_fusten int; v_fl_kar text; v_P int; v_prevP int; v_sumA numeric; v_sumF int; v_d jsonb; v_svg text; v_klein text;
|
|
54127
|
+
BEGIN
|
|
54128
|
+
PERFORM public.logistics_acc_guard('ingelogd', 'logistics_kar_tekening');
|
|
54129
|
+
v_pakbon := COALESCE(v_opt->>'vorm', '') = 'pakbon';
|
|
54130
|
+
v_alle := COALESCE((v_opt->>'alle_orders')::boolean, false) AND NOT v_pakbon;
|
|
54131
|
+
FOR i IN 1 .. cardinality(v_kleuren) LOOP
|
|
54132
|
+
v_kl := v_kl || to_jsonb(CASE WHEN v_kleuren[i] ~ '^var\(--[a-z0-9-]+\)$'
|
|
54133
|
+
THEN left(v_kleuren[i], length(v_kleuren[i]) - 1) || ', ' || v_fb[1 + ((i - 1) % 7)] || ')'
|
|
54134
|
+
ELSE v_kleuren[i] END);
|
|
54135
|
+
END LOOP;
|
|
54136
|
+
v_stand := public.logistics_order_indeling_stand(p_order_id);
|
|
54137
|
+
SELECT o.id, o.order_number, o.status, o.order_source, o.floriday_sales_order_id, o.customer_name INTO v_order FROM public.orders o WHERE o.id = p_order_id;
|
|
54138
|
+
v_fase := public.logistics_status_fase(v_order.status);
|
|
54139
|
+
v_fl_bron := v_order.floriday_sales_order_id IS NOT NULL OR COALESCE(v_order.order_source, '') ILIKE '%floriday%';
|
|
54140
|
+
IF v_fl_bron THEN v_fl := public.logistics_floriday_gelijk(p_order_id); END IF;
|
|
54141
|
+
|
|
54142
|
+
SELECT count(*) INTO v_totaal FROM (
|
|
54143
|
+
SELECT DISTINCT c2.id FROM logistics_active.order_carts c2
|
|
54144
|
+
WHERE c2.order_id = p_order_id
|
|
54145
|
+
OR EXISTS (SELECT 1 FROM logistics_active.order_cart_items ci JOIN public.order_items oi ON oi.id = ci.order_item_id
|
|
54146
|
+
WHERE ci.cart_id = c2.id AND oi.order_id = p_order_id)) x;
|
|
54147
|
+
|
|
54148
|
+
FOR c IN
|
|
54149
|
+
SELECT DISTINCT ON (c2.cart_number, c2.id) c2.id, c2.cart_number, c2.occupancy_pct, c2.total_layers_used, c2.max_layers, c2.weight_kg,
|
|
54150
|
+
COALESCE(t.capacity_model, 'layered') AS soort, COALESCE(t.name, c2.carrier_type, 'Ladingdrager') AS naam,
|
|
54151
|
+
t.layers_per_carrier, t.max_crates_per_layer, t.max_weight_kg
|
|
54152
|
+
FROM logistics_active.order_carts c2
|
|
54153
|
+
LEFT JOIN public.load_carrier_types t ON t.id = c2.carrier_type_id
|
|
54154
|
+
WHERE c2.order_id = p_order_id
|
|
54155
|
+
OR EXISTS (SELECT 1 FROM logistics_active.order_cart_items ci JOIN public.order_items oi ON oi.id = ci.order_item_id
|
|
54156
|
+
WHERE ci.cart_id = c2.id AND oi.order_id = p_order_id)
|
|
54157
|
+
ORDER BY c2.cart_number, c2.id
|
|
54158
|
+
LOOP
|
|
54159
|
+
v_nr := v_nr + 1;
|
|
54160
|
+
CONTINUE WHEN (v_opt ? 'drager_id') AND (v_opt->>'drager_id') <> c.id::text;
|
|
54161
|
+
v_soort := CASE WHEN c.soort IN ('layered','stacked','single','none') THEN c.soort ELSE 'layered' END;
|
|
54162
|
+
v_woord := CASE v_soort WHEN 'layered' THEN 'Kar' WHEN 'none' THEN 'Colli' WHEN 'single' THEN 'Colli' ELSE 'Pallet' END;
|
|
54163
|
+
v_max := GREATEST(1, COALESCE(c.max_layers, c.layers_per_carrier, CEIL(COALESCE(c.total_layers_used, 1))::int, 1));
|
|
54164
|
+
|
|
54165
|
+
SELECT COALESCE(jsonb_object_agg(order_id::text, chr((64 + rn)::int)), '{}'::jsonb) INTO v_letters FROM (
|
|
54166
|
+
SELECT oi.order_id, row_number() OVER (ORDER BY (oi.order_id = p_order_id) DESC, min(o2.order_number)) AS rn
|
|
54167
|
+
FROM public.order_cart_items ci JOIN public.order_items oi ON oi.id = ci.order_item_id
|
|
54168
|
+
JOIN public.orders o2 ON o2.id = oi.order_id
|
|
54169
|
+
WHERE ci.cart_id = c.id GROUP BY oi.order_id) l;
|
|
54170
|
+
SELECT count(DISTINCT oi.order_id) FILTER (WHERE oi.order_id <> p_order_id) INTO v_gedeeld
|
|
54171
|
+
FROM public.order_cart_items ci JOIN public.order_items oi ON oi.id = ci.order_item_id WHERE ci.cart_id = c.id;
|
|
54172
|
+
|
|
54173
|
+
-- Lagen: deel-lagen van verschillende regels delen één laag zolang ze passen.
|
|
54174
|
+
v_lagen := '[]'::jsonb;
|
|
54175
|
+
FOR i IN 1 .. v_max LOOP
|
|
54176
|
+
v_lagen := v_lagen || jsonb_build_array(jsonb_build_object('index', i, 'hoogte_aandeel', round(1.0 / v_max, 4), 'blokken', '[]'::jsonb));
|
|
54177
|
+
END LOOP;
|
|
54178
|
+
v_cur := 1; v_vrij := 1.0;
|
|
54179
|
+
FOR it IN
|
|
54180
|
+
SELECT ci.id, ci.layer_index, COALESCE(ci.crates_in_layer, ci.crates, ci.boxes, 0) AS fusten, ci.lagen, ci.layer_fraction,
|
|
54181
|
+
COALESCE(ci.product_name, oi.product_name, 'Product') AS product, oi.order_id
|
|
54182
|
+
FROM public.order_cart_items ci LEFT JOIN public.order_items oi ON oi.id = ci.order_item_id
|
|
54183
|
+
WHERE ci.cart_id = c.id ORDER BY ci.layer_index NULLS LAST, ci.created_at, ci.id
|
|
54184
|
+
LOOP
|
|
54185
|
+
v_frac := GREATEST(COALESCE(it.lagen, it.layer_fraction, 1), 0.01);
|
|
54186
|
+
v_eigen := it.order_id = p_order_id;
|
|
54187
|
+
v_letter := v_letters->>it.order_id::text;
|
|
54188
|
+
IF it.layer_index IS NOT NULL THEN
|
|
54189
|
+
v_cur := it.layer_index; v_span := 1;
|
|
54190
|
+
ELSIF v_frac < 0.9999 THEN
|
|
54191
|
+
IF v_vrij + 0.0001 < v_frac THEN v_cur := v_cur + 1; v_vrij := 1.0; END IF;
|
|
54192
|
+
v_span := 1;
|
|
54193
|
+
ELSE
|
|
54194
|
+
IF v_vrij < 0.9999 THEN v_cur := v_cur + 1; v_vrij := 1.0; END IF;
|
|
54195
|
+
v_span := CEIL(v_frac - 0.0001)::int;
|
|
54196
|
+
END IF;
|
|
54197
|
+
v_rest := it.fusten;
|
|
54198
|
+
FOR j IN 1 .. v_span LOOP
|
|
54199
|
+
i := v_cur + j - 1;
|
|
54200
|
+
WHILE i > jsonb_array_length(v_lagen) LOOP
|
|
54201
|
+
v_lagen := v_lagen || jsonb_build_array(jsonb_build_object('index', jsonb_array_length(v_lagen) + 1, 'hoogte_aandeel', 0, 'blokken', '[]'::jsonb));
|
|
54202
|
+
END LOOP;
|
|
54203
|
+
v_aandeel := CASE WHEN it.layer_index IS NOT NULL THEN LEAST(v_frac, 1) ELSE LEAST(1, v_frac - (j - 1)) END;
|
|
54204
|
+
v_f := CASE WHEN j = v_span THEN v_rest ELSE ROUND(it.fusten * v_aandeel / v_frac)::int END;
|
|
54205
|
+
v_rest := v_rest - v_f;
|
|
54206
|
+
b := CASE WHEN v_eigen OR v_alle
|
|
54207
|
+
THEN jsonb_build_object('klantletter', v_letter, 'product', CASE WHEN v_pakbon THEN NULL ELSE it.product END,
|
|
54208
|
+
'klant', CASE WHEN v_eigen THEN v_order.customer_name END,
|
|
54209
|
+
'fusten', v_f, 'eigen_order', v_eigen, 'aandeel', round(v_aandeel, 4),
|
|
54210
|
+
'kleur_index', GREATEST(ascii(COALESCE(v_letter, 'A')) - 65, 0))
|
|
54211
|
+
ELSE jsonb_build_object('klantletter', NULL, 'product', NULL, 'fusten', v_f, 'eigen_order', false, 'aandeel', round(v_aandeel, 4)) END;
|
|
54212
|
+
v_lagen := jsonb_set(v_lagen, ARRAY[(i - 1)::text, 'blokken'], (v_lagen->(i - 1)->'blokken') || jsonb_build_array(b));
|
|
54213
|
+
END LOOP;
|
|
54214
|
+
IF it.layer_index IS NULL THEN
|
|
54215
|
+
IF v_span = 1 AND v_frac < 0.9999 THEN v_vrij := v_vrij - v_frac;
|
|
54216
|
+
ELSIF v_frac - (v_span - 1) < 0.9999 THEN v_cur := v_cur + v_span - 1; v_vrij := 1 - (v_frac - (v_span - 1));
|
|
54217
|
+
ELSE v_cur := v_cur + v_span; v_vrij := 1.0; END IF;
|
|
54218
|
+
END IF;
|
|
54219
|
+
END LOOP;
|
|
54220
|
+
IF jsonb_array_length(v_lagen) > v_max THEN
|
|
54221
|
+
SELECT jsonb_agg(jsonb_set(l, '{hoogte_aandeel}', to_jsonb(round(1.0 / jsonb_array_length(v_lagen), 4))) ORDER BY n) INTO v_lagen
|
|
54222
|
+
FROM jsonb_array_elements(v_lagen) WITH ORDINALITY x(l, n);
|
|
54223
|
+
END IF;
|
|
54224
|
+
|
|
54225
|
+
-- Plekken per laag uit de belading: fusten gedeeld door het deel van de laag dat ze innemen.
|
|
54226
|
+
v_prevP := c.max_crates_per_layer;
|
|
54227
|
+
FOR i IN 0 .. jsonb_array_length(v_lagen) - 1 LOOP
|
|
54228
|
+
SELECT max(CASE WHEN (bb->>'aandeel')::numeric > 0 THEN round((bb->>'fusten')::numeric / (bb->>'aandeel')::numeric) END),
|
|
54229
|
+
coalesce(sum((bb->>'aandeel')::numeric), 0), coalesce(sum((bb->>'fusten')::int), 0)
|
|
54230
|
+
INTO v_P, v_sumA, v_sumF
|
|
54231
|
+
FROM jsonb_array_elements(v_lagen->i->'blokken') bb;
|
|
54232
|
+
v_P := CASE WHEN v_sumF = 0 THEN COALESCE(v_prevP, c.max_crates_per_layer, 1) ELSE GREATEST(COALESCE(v_P, v_sumF), v_sumF, 1) END;
|
|
54233
|
+
v_P := LEAST(GREATEST(v_P, 1), 40);
|
|
54234
|
+
IF v_sumF > 0 THEN v_prevP := v_P; END IF;
|
|
54235
|
+
v_lagen := jsonb_set(v_lagen, ARRAY[i::text, 'plekken'], to_jsonb(v_P));
|
|
54236
|
+
IF i + 1 > v_max AND v_sumF > 0 THEN
|
|
54237
|
+
v_lagen := jsonb_set(v_lagen, ARRAY[i::text, 'waarschuwing'], to_jsonb('Te hoog: deze laag past niet meer op de ' || lower(v_woord)));
|
|
54238
|
+
ELSIF v_sumA > 1.001 THEN
|
|
54239
|
+
v_lagen := jsonb_set(v_lagen, ARRAY[i::text, 'waarschuwing'], to_jsonb('Te vol: deze laag is voor ' || round(v_sumA * 100) || ' % gevuld'));
|
|
54240
|
+
END IF;
|
|
54241
|
+
END LOOP;
|
|
54242
|
+
-- Lege plekken op lege lagen onderaan aanvullen met de maat van de eerste gevulde laag.
|
|
54243
|
+
SELECT (l->>'plekken')::int INTO v_P FROM jsonb_array_elements(v_lagen) l
|
|
54244
|
+
WHERE jsonb_array_length(l->'blokken') > 0 ORDER BY (l->>'index')::int LIMIT 1;
|
|
54245
|
+
IF v_P IS NOT NULL THEN
|
|
54246
|
+
FOR i IN 0 .. jsonb_array_length(v_lagen) - 1 LOOP
|
|
54247
|
+
EXIT WHEN jsonb_array_length(v_lagen->i->'blokken') > 0;
|
|
54248
|
+
v_lagen := jsonb_set(v_lagen, ARRAY[i::text, 'plekken'], to_jsonb(v_P));
|
|
54249
|
+
END LOOP;
|
|
54250
|
+
END IF;
|
|
54251
|
+
SELECT COALESCE(sum((bb->>'fusten')::int), 0) INTO v_fusten
|
|
54252
|
+
FROM jsonb_array_elements(v_lagen) l, jsonb_array_elements(l->'blokken') bb;
|
|
54253
|
+
|
|
54254
|
+
v_status := CASE
|
|
54255
|
+
WHEN v_fase IN ('verzonden','gereed_voor_facturatie','gefactureerd','afgehandeld_floriday') THEN 'afgemeld'
|
|
54256
|
+
WHEN public.logistics_cart_scanned(c.id) THEN 'gecontroleerd'
|
|
54257
|
+
WHEN EXISTS (SELECT 1 FROM public.pick_queue q WHERE q.reference = c.id::text AND q.cancelled_at IS NULL AND q.completed_at IS NOT NULL) THEN 'gepickt'
|
|
54258
|
+
WHEN EXISTS (SELECT 1 FROM public.pick_queue q WHERE q.reference = c.id::text AND q.cancelled_at IS NULL AND q.printed_at IS NOT NULL) THEN 'geprint'
|
|
54259
|
+
WHEN EXISTS (SELECT 1 FROM public.pick_queue q WHERE q.reference = c.id::text AND q.cancelled_at IS NULL AND (q.payload->>'printbaar') = 'true') THEN 'vrijgegeven'
|
|
54260
|
+
ELSE 'open' END;
|
|
54261
|
+
|
|
54262
|
+
v_waarsch := '[]'::jsonb;
|
|
54263
|
+
IF c.max_weight_kg IS NOT NULL AND c.weight_kg IS NOT NULL AND c.weight_kg > c.max_weight_kg THEN
|
|
54264
|
+
v_waarsch := v_waarsch || to_jsonb(v_woord || ' is te zwaar: ' || round(c.weight_kg) || ' kg, maximaal ' || round(c.max_weight_kg) || ' kg');
|
|
54265
|
+
END IF;
|
|
54266
|
+
IF COALESCE(c.occupancy_pct, 0) > 100 THEN
|
|
54267
|
+
v_waarsch := v_waarsch || to_jsonb(v_woord || ' is voller dan toegestaan (' || round(c.occupancy_pct) || '%)');
|
|
54268
|
+
END IF;
|
|
54269
|
+
IF jsonb_array_length(v_lagen) > v_max THEN
|
|
54270
|
+
v_waarsch := v_waarsch || to_jsonb('Er zijn meer lagen gevuld dan de ' || lower(v_woord) || ' heeft');
|
|
54271
|
+
END IF;
|
|
54272
|
+
|
|
54273
|
+
v_fl_kar := NULL;
|
|
54274
|
+
IF v_fl_bron THEN
|
|
54275
|
+
SELECT COALESCE(e->>'uitkomst', e->>'stand') INTO v_fl_kar
|
|
54276
|
+
FROM jsonb_array_elements(CASE WHEN jsonb_typeof(v_fl->'karren') = 'array' THEN v_fl->'karren' ELSE '[]'::jsonb END) e
|
|
54277
|
+
WHERE COALESCE(e->>'cartId', e->>'kar_id', e->>'id') = c.id::text LIMIT 1;
|
|
54278
|
+
v_fl_kar := COALESCE(v_fl_kar, v_fl->>'uitkomst', v_fl->>'stand');
|
|
54279
|
+
END IF;
|
|
54280
|
+
|
|
54281
|
+
v_label := CASE WHEN v_soort IN ('none','single') THEN v_woord || ' ' || v_nr || ' van ' || v_totaal
|
|
54282
|
+
ELSE v_woord || ' ' || v_nr || ' van ' || v_totaal END;
|
|
54283
|
+
v_d := jsonb_build_object('soort', v_soort, 'label', v_label, 'naam', c.naam, 'nummer', v_nr,
|
|
54284
|
+
'vulgraad_pct', CASE WHEN c.occupancy_pct IS NOT NULL THEN round(c.occupancy_pct) END,
|
|
54285
|
+
'gedeeld_met', v_gedeeld, 'kleuren', v_kl, 'waarschuwingen', v_waarsch, 'lagen', v_lagen, 'pakbon', v_pakbon);
|
|
54286
|
+
v_svg := public.logistics_kar_svg(v_d, false);
|
|
54287
|
+
v_klein := public.logistics_kar_svg(v_d, true);
|
|
54288
|
+
v_rij := v_rij || '<svg x="' || ((v_nr - 1) * 64) || '" y="0" width="60" height="80"' || substr(v_klein, length('<svg') + 1);
|
|
54289
|
+
|
|
54290
|
+
v_dragers := v_dragers || jsonb_build_array(jsonb_build_object(
|
|
54291
|
+
'id', c.id, 'nummer', v_nr, 'totaal', v_totaal, 'soort', v_soort, 'naam', c.naam,
|
|
54292
|
+
'vulgraad_pct', CASE WHEN c.occupancy_pct IS NOT NULL THEN round(c.occupancy_pct) END,
|
|
54293
|
+
'lagen_gebruikt', CASE WHEN c.total_layers_used IS NOT NULL THEN round(c.total_layers_used, 1) END,
|
|
54294
|
+
'lagen_max', CASE WHEN v_soort IN ('layered','stacked') THEN v_max END,
|
|
54295
|
+
'gewicht_kg', CASE WHEN c.weight_kg IS NOT NULL THEN round(c.weight_kg, 1) END,
|
|
54296
|
+
'status', v_status, 'floriday_gelijk', v_fl_kar, 'gedeeld_met', v_gedeeld,
|
|
54297
|
+
'waarschuwingen', v_waarsch, 'lagen', v_lagen, 'svg', v_svg));
|
|
54298
|
+
END LOOP;
|
|
54299
|
+
|
|
54300
|
+
RETURN jsonb_build_object(
|
|
54301
|
+
'stand', v_stand->'stand', 'bezig_sinds', v_stand->'bezig_sinds', 'bijgewerkt_op', v_stand->'bijgewerkt_op',
|
|
54302
|
+
'verwacht_klaar', v_stand->'verwacht_klaar', 'tekst', v_stand->'tekst',
|
|
54303
|
+
'svg_rij', CASE WHEN jsonb_array_length(v_dragers) = 0 THEN NULL
|
|
54304
|
+
ELSE '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ' || GREATEST(jsonb_array_length(v_dragers) * 64, 64)
|
|
54305
|
+
|| ' 80" role="img" aria-label="Overzicht van ' || jsonb_array_length(v_dragers)
|
|
54306
|
+
|| CASE WHEN jsonb_array_length(v_dragers) = 1 THEN ' ladingdrager' ELSE ' ladingdragers' END
|
|
54307
|
+
|| '"><title>Overzicht ladingdragers</title>' || v_rij || '</svg>' END,
|
|
54308
|
+
'dragers', v_dragers);
|
|
54309
|
+
END $function$;
|
|
54310
|
+
REVOKE ALL ON FUNCTION public.logistics_kar_tekening(uuid, jsonb) FROM PUBLIC, anon;
|
|
54311
|
+
GRANT EXECUTE ON FUNCTION public.logistics_kar_tekening(uuid, jsonb) TO authenticated, service_role;
|
|
54312
|
+
|
|
54313
|
+
-- Punt 2: afleveradressen per vervoerder (Afhandeling), ook zonder GLN. Ontbreekt de kolom: lege lijst.
|
|
54314
|
+
CREATE OR REPLACE FUNCTION public.logistics_carrier_adressen(p_carrier_id uuid)
|
|
54315
|
+
RETURNS jsonb LANGUAGE sql STABLE SECURITY DEFINER SET search_path TO 'public'
|
|
54316
|
+
AS $function$
|
|
54317
|
+
SELECT COALESCE((SELECT CASE WHEN jsonb_typeof(to_jsonb(ca)->'delivery_location_ids') = 'array'
|
|
54318
|
+
THEN to_jsonb(ca)->'delivery_location_ids' ELSE '[]'::jsonb END
|
|
54319
|
+
FROM public.carriers ca WHERE ca.id = p_carrier_id), '[]'::jsonb)
|
|
54320
|
+
$function$;
|
|
54321
|
+
REVOKE ALL ON FUNCTION public.logistics_carrier_adressen(uuid) FROM PUBLIC, anon, authenticated;
|
|
54322
|
+
GRANT EXECUTE ON FUNCTION public.logistics_carrier_adressen(uuid) TO service_role;
|
|
54323
|
+
|
|
54324
|
+
DO $mig$
|
|
54325
|
+
DECLARE v_def text; v_nieuw text; a1 text; a2 text;
|
|
54326
|
+
BEGIN
|
|
54327
|
+
v_def := pg_get_functiondef('public.logistics_resolve_carrier_core(jsonb,boolean)'::regprocedure);
|
|
54328
|
+
IF position('logistics_carrier_adressen' IN v_def) > 0 THEN RETURN; END IF;
|
|
54329
|
+
a1 := E' IF v_cust IS NOT NULL THEN\n SELECT to_jsonb(x) INTO cu FROM public.customers x WHERE x.id = v_cust;';
|
|
54330
|
+
a2 := E' v_skip := public.logistics_carrier_skip_reason(r.id);\n IF v_skip IS NULL AND NOT public.logistics_carrier_picks_up_for(r.id, v_date) THEN';
|
|
54331
|
+
IF position(a1 IN v_def) = 0 OR position(a2 IN v_def) = 0 THEN
|
|
54332
|
+
RAISE EXCEPTION 'logistics_resolve_carrier_core: verwachte tekst niet gevonden; aanpassing niet toegepast';
|
|
54333
|
+
END IF;
|
|
54334
|
+
v_nieuw := replace(v_def, a1, E' -- E1-C vervolg punt 2: vervoerders met dit afleveradres in hun lijst (ook zonder GLN).\n'
|
|
54335
|
+
|| E' IF l IS NOT NULL AND (l->>''id'') IS NOT NULL THEN\n'
|
|
54336
|
+
|| E' FOR r IN SELECT ca.id FROM public.carriers ca\n'
|
|
54337
|
+
|| E' WHERE public.logistics_carrier_adressen(ca.id) ? (l->>''id'')\n'
|
|
54338
|
+
|| E' ORDER BY ca.sort_order NULLS LAST, ca.id LOOP\n'
|
|
54339
|
+
|| E' v_skip := public.logistics_carrier_skip_reason(r.id);\n'
|
|
54340
|
+
|| E' IF v_skip IS NULL THEN\n'
|
|
54341
|
+
|| E' RETURN public.logistics_carrier_result(r.id, ''afleveradres'', ''Vervoerder rijdt naar dit afleveradres.'', v_skips);\n'
|
|
54342
|
+
|| E' END IF;\n'
|
|
54343
|
+
|| E' v_skips := v_skips || (''afleveradressen: '' || v_skip);\n'
|
|
54344
|
+
|| E' END LOOP;\n'
|
|
54345
|
+
|| E' END IF;\n\n' || a1);
|
|
54346
|
+
v_nieuw := replace(v_nieuw, a2, E' v_skip := public.logistics_carrier_skip_reason(r.id);\n'
|
|
54347
|
+
|| E' IF v_skip IS NULL AND jsonb_array_length(public.logistics_carrier_adressen(r.id)) > 0\n'
|
|
54348
|
+
|| E' AND NOT (public.logistics_carrier_adressen(r.id) ? coalesce(l->>''id'', '''')) THEN\n'
|
|
54349
|
+
|| E' v_skip := (SELECT name FROM public.carriers WHERE id = r.id) || '': rijdt niet naar dit afleveradres'';\n'
|
|
54350
|
+
|| E' END IF;\n'
|
|
54351
|
+
|| E' IF v_skip IS NULL AND NOT public.logistics_carrier_picks_up_for(r.id, v_date) THEN');
|
|
54352
|
+
EXECUTE v_nieuw;
|
|
54353
|
+
END $mig$;
|
|
54354
|
+
|
|
54355
|
+
-- Punt 3: sluitregel van buffer en karindeling volgens de werkwijze.
|
|
54356
|
+
CREATE OR REPLACE FUNCTION public.logistics_buffer_sluiten_modus()
|
|
54357
|
+
RETURNS text LANGUAGE sql STABLE SECURITY DEFINER SET search_path TO 'public'
|
|
54358
|
+
AS $function$
|
|
54359
|
+
SELECT COALESCE((
|
|
54360
|
+
SELECT CASE WHEN v IN ('verzendmoment', 'werkuren') THEN v ELSE 'werkuren' END FROM (
|
|
54361
|
+
SELECT public.logistics_rule_text(rc.settings, 'order_verwerking', 'buffer_sluiten', 'werkuren') AS v
|
|
54362
|
+
FROM public.logistics_rule_configs rc
|
|
54363
|
+
LEFT JOIN public.logistics_strategies s ON s.id = rc.strategy_id
|
|
54364
|
+
ORDER BY (rc.is_active AND coalesce(s.is_active, false)) DESC, rc.is_active DESC, rc.updated_at DESC NULLS LAST
|
|
54365
|
+
LIMIT 1) x), 'werkuren')
|
|
54366
|
+
$function$;
|
|
54367
|
+
REVOKE ALL ON FUNCTION public.logistics_buffer_sluiten_modus() FROM PUBLIC, anon;
|
|
54368
|
+
GRANT EXECUTE ON FUNCTION public.logistics_buffer_sluiten_modus() TO authenticated, service_role;
|
|
54369
|
+
|
|
54370
|
+
DO $mig$
|
|
54371
|
+
DECLARE v_def text; v_nieuw text; a0 text; a1 text; a2 text; a3 text; a4 text;
|
|
54372
|
+
BEGIN
|
|
54373
|
+
v_def := pg_get_functiondef('public.logistics_schedule_tasks(text)'::regprocedure);
|
|
54374
|
+
IF position('tmp_logistics_verzend' IN v_def) > 0 THEN RETURN; END IF;
|
|
54375
|
+
a0 := E'\n WITH pick AS (';
|
|
54376
|
+
a1 := E' SELECT id, pickup_at, new_rule, dep,\n CASE\n WHEN new_rule AND pickup_at IS NULL THEN NULL';
|
|
54377
|
+
a2 := E' CASE WHEN new_rule THEN\n CASE WHEN off_min <= dur_min THEN pickup_at';
|
|
54378
|
+
a3 := E' FROM work\n ), clamped AS (';
|
|
54379
|
+
a4 := E' || CASE WHEN c.shifted THEN';
|
|
54380
|
+
IF position(a0 IN v_def) = 0 OR position(a1 IN v_def) = 0 OR position(a2 IN v_def) = 0
|
|
54381
|
+
OR position(a3 IN v_def) = 0 OR position(a4 IN v_def) = 0 THEN
|
|
54382
|
+
RAISE EXCEPTION 'logistics_schedule_tasks: verwachte tekst niet gevonden; aanpassing niet toegepast';
|
|
54383
|
+
END IF;
|
|
54384
|
+
v_nieuw := replace(v_def, a0, E'\n -- E1-C vervolg punt 3: buffer en karindeling sluiten op het verzendmoment van de aanmelding (werkwijze).\n'
|
|
54385
|
+
|| E' CREATE TEMP TABLE IF NOT EXISTS tmp_logistics_verzend (task_id uuid PRIMARY KEY, verzend_at timestamptz) ON COMMIT DROP;\n'
|
|
54386
|
+
|| E' TRUNCATE tmp_logistics_verzend;\n'
|
|
54387
|
+
|| E' IF public.logistics_buffer_sluiten_modus() = ''verzendmoment'' AND EXISTS (SELECT 1 FROM tmp_logistics_dep) THEN\n'
|
|
54388
|
+
|| E' BEGIN\n'
|
|
54389
|
+
|| E' INSERT INTO tmp_logistics_verzend(task_id, verzend_at)\n'
|
|
54390
|
+
|| E' SELECT t.id, a.aanmeld_at\n'
|
|
54391
|
+
|| E' FROM public.logistics_aanmeldmoment(ARRAY(SELECT d.order_id FROM tmp_logistics_dep d)) a\n'
|
|
54392
|
+
|| E' JOIN tmp_logistics_dep d ON d.order_id = a.order_id\n'
|
|
54393
|
+
|| E' JOIN public.logistics_tasks t ON t.order_ref = d.ref AND t.task_type IN (''buffer.close'', ''load.compose'') AND t.status IN (''planned'', ''ready'')\n'
|
|
54394
|
+
|| E' WHERE a.aanmeld_at IS NOT NULL AND a.bron IN (''gepland'', ''verstuurd'')\n'
|
|
54395
|
+
|| E' ON CONFLICT (task_id) DO NOTHING;\n'
|
|
54396
|
+
|| E' EXCEPTION WHEN OTHERS THEN\n'
|
|
54397
|
+
|| E' PERFORM public.logistics_melding(''logistics_schedule_tasks'', NULL,\n'
|
|
54398
|
+
|| E' ''Verzendmoment van de aanmelding niet te lezen; buffer en karindeling volgen nu de werkuren'', SQLERRM);\n'
|
|
54399
|
+
|| E' END;\n'
|
|
54400
|
+
|| E' END IF;\n' || a0);
|
|
54401
|
+
v_nieuw := replace(v_nieuw, a1, E' SELECT id, pickup_at, new_rule, dep, vzz.verzend_at,\n CASE\n'
|
|
54402
|
+
|| E' WHEN vzz.verzend_at IS NOT NULL AND task_type = ''buffer.close'' THEN vzz.verzend_at\n'
|
|
54403
|
+
|| E' WHEN vzz.verzend_at IS NOT NULL AND task_type = ''load.compose'' THEN vzz.verzend_at - interval ''10 minutes''\n'
|
|
54404
|
+
|| E' WHEN new_rule AND pickup_at IS NULL THEN NULL');
|
|
54405
|
+
v_nieuw := replace(v_nieuw, a2, E' CASE WHEN vzz.verzend_at IS NOT NULL AND task_type IN (''buffer.close'', ''load.compose'') THEN vzz.verzend_at\n'
|
|
54406
|
+
|| E' WHEN new_rule THEN\n CASE WHEN off_min <= dur_min THEN pickup_at');
|
|
54407
|
+
v_nieuw := replace(v_nieuw, a3, E' FROM work\n LEFT JOIN tmp_logistics_verzend vzz ON vzz.task_id = work.id\n ), clamped AS (');
|
|
54408
|
+
v_nieuw := replace(v_nieuw, a4, E' || CASE WHEN c.task_type IN (''buffer.close'', ''load.compose'') THEN jsonb_build_object(\n'
|
|
54409
|
+
|| E' ''sluitRegel'', CASE WHEN c.verzend_at IS NOT NULL THEN ''verzendmoment'' ELSE ''werkuren'' END,\n'
|
|
54410
|
+
|| E' ''verzendAt'', c.verzend_at) ELSE ''{}''::jsonb END\n' || a4);
|
|
54411
|
+
EXECUTE v_nieuw;
|
|
54412
|
+
END $mig$;
|
|
54413
|
+
|
|
54414
|
+
-- Nieuwe werkwijze Standaard krijgt "Op het verzendmoment"; bestaande werkwijzen blijven ongewijzigd.
|
|
54415
|
+
DO $mig$
|
|
54416
|
+
DECLARE v_def text; v_nieuw text;
|
|
54417
|
+
BEGIN
|
|
54418
|
+
v_def := pg_get_functiondef('public.logistics_ensure_defaults()'::regprocedure);
|
|
54419
|
+
IF position('buffer_sluiten' IN v_def) > 0 THEN RETURN; END IF;
|
|
54420
|
+
IF (length(v_def) - length(replace(v_def, '''buffer_enabled'', true,', ''))) / length('''buffer_enabled'', true,') <> 1 THEN
|
|
54421
|
+
RAISE EXCEPTION 'logistics_ensure_defaults: verwachte tekst niet precies één keer gevonden';
|
|
54422
|
+
END IF;
|
|
54423
|
+
v_nieuw := replace(v_def, '''buffer_enabled'', true,', '''buffer_enabled'', true, ''buffer_sluiten'', ''verzendmoment'',');
|
|
54424
|
+
EXECUTE v_nieuw;
|
|
54425
|
+
END $mig$;
|
|
54426
|
+
|
|
54427
|
+
-- Punt 4: laadtoestand alleen bij een wijziging die hem raakt.
|
|
54428
|
+
CREATE OR REPLACE FUNCTION public.logistics_laadtoestand_sig()
|
|
54429
|
+
RETURNS text LANGUAGE sql STABLE SECURITY DEFINER SET search_path TO 'public'
|
|
54430
|
+
AS $function$
|
|
54431
|
+
SELECT md5(concat_ws('|',
|
|
54432
|
+
(SELECT string_agg(q.id::text, ',' ORDER BY q.id) FROM public.pick_queue q WHERE q.cancelled_at IS NULL AND q.completed_at IS NULL),
|
|
54433
|
+
(SELECT string_agg(pq.order_id::text || pq.status, ',' ORDER BY pq.order_id, pq.status) FROM public.logistics_plan_queue pq WHERE pq.status IN ('pending', 'running')),
|
|
54434
|
+
(SELECT count(*)::text || ':' || coalesce(max(c.updated_at)::text, '') FROM logistics_active.order_carts c),
|
|
54435
|
+
(SELECT string_agg(s || ':' || n, ',' ORDER BY s) FROM (SELECT t.status AS s, count(*) AS n FROM public.logistics_tasks t GROUP BY t.status) x),
|
|
54436
|
+
(SELECT count(*)::text FROM public.logistics_tasks t WHERE t.status IN ('planned', 'ready') AND t.planned_start <= now()),
|
|
54437
|
+
(SELECT count(*)::text || ':' || coalesce(max(r.created_at)::text, '') FROM public.logistics_pick_data_requests r WHERE r.finished_at IS NULL)))
|
|
54438
|
+
$function$;
|
|
54439
|
+
REVOKE ALL ON FUNCTION public.logistics_laadtoestand_sig() FROM PUBLIC, anon, authenticated;
|
|
54440
|
+
GRANT EXECUTE ON FUNCTION public.logistics_laadtoestand_sig() TO service_role;
|
|
54441
|
+
|
|
54442
|
+
CREATE OR REPLACE FUNCTION public.logistics_refresh_plan_state_als_nodig()
|
|
54443
|
+
RETURNS jsonb LANGUAGE plpgsql SECURITY DEFINER SET search_path TO 'public'
|
|
54444
|
+
AS $function$
|
|
54445
|
+
DECLARE v_sig text; v_prev text; v_at timestamptz; v_res jsonb; v_bezig boolean;
|
|
54446
|
+
BEGIN
|
|
54447
|
+
PERFORM public.logistics_acc_guard('server', 'logistics_refresh_plan_state_als_nodig');
|
|
54448
|
+
v_sig := public.logistics_laadtoestand_sig();
|
|
54449
|
+
SELECT value->>'sig', (value->>'at')::timestamptz INTO v_prev, v_at
|
|
54450
|
+
FROM public.logistics_tick_state WHERE key = 'refresh_plan_state';
|
|
54451
|
+
SELECT (value->>'cursor') IS NOT NULL INTO v_bezig FROM public.logistics_tick_state WHERE key = 'refresh_plan_state_cursor';
|
|
54452
|
+
-- Vangnet: hooguit elke 15 minuten toch een volledige ronde.
|
|
54453
|
+
IF NOT coalesce(v_bezig, false) AND v_at IS NOT NULL AND v_sig = v_prev AND v_at > now() - interval '15 minutes' THEN
|
|
54454
|
+
RETURN jsonb_build_object('overgeslagen', 'niets veranderd sinds de vorige ronde');
|
|
54455
|
+
END IF;
|
|
54456
|
+
v_res := public.logistics_refresh_plan_state(p_limit => 500);
|
|
54457
|
+
INSERT INTO public.logistics_tick_state(key, value, updated_at)
|
|
54458
|
+
VALUES ('refresh_plan_state', jsonb_build_object('sig', v_sig, 'at', now()), now())
|
|
54459
|
+
ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value, updated_at = now();
|
|
54460
|
+
RETURN v_res;
|
|
54461
|
+
END $function$;
|
|
54462
|
+
|
|
54463
|
+
CREATE OR REPLACE FUNCTION public.logistics_install_check_e1c()
|
|
54464
|
+
RETURNS TABLE(onderdeel text, ok boolean, toelichting text)
|
|
54465
|
+
LANGUAGE plpgsql STABLE SECURITY DEFINER SET search_path TO 'public'
|
|
54466
|
+
AS $function$
|
|
54467
|
+
DECLARE f text; v_order uuid; v jsonb; v_n int;
|
|
54468
|
+
BEGIN
|
|
54469
|
+
FOREACH f IN ARRAY ARRAY['logistics_uitgang_uit()','logistics_pickup_afwijkend(integer)','logistics_pickup_herstel_voorbeeld()',
|
|
54470
|
+
'logistics_pickup_herstel_uitvoeren(integer)','logistics_stock_email_klaarzetten(integer)','logistics_pickwachtrij_overslaan()',
|
|
54471
|
+
'logistics_order_indeling_stand(uuid)','logistics_kar_tekening(uuid,jsonb)','logistics_klantkleuren()','logistics_xml(text)',
|
|
54472
|
+
'logistics_kar_svg(jsonb,boolean)','logistics_carrier_adressen(uuid)','logistics_buffer_sluiten_modus()','logistics_laadtoestand_sig()'] LOOP
|
|
54473
|
+
onderdeel := 'functie ' || f;
|
|
54474
|
+
ok := to_regprocedure('public.' || f) IS NOT NULL;
|
|
54475
|
+
toelichting := CASE WHEN ok THEN 'aanwezig' ELSE 'ontbreekt: werk de Engine bij via Supabase bijwerken' END;
|
|
54476
|
+
RETURN NEXT;
|
|
54477
|
+
END LOOP;
|
|
54478
|
+
IF to_regprocedure('public.logistics_pickup_afwijkend(integer)') IS NOT NULL THEN
|
|
54479
|
+
v_n := (public.logistics_pickup_afwijkend(0)->>'aantal')::int;
|
|
54480
|
+
onderdeel := 'ophaalmoment past bij vervoerder'; ok := v_n = 0;
|
|
54481
|
+
toelichting := CASE WHEN ok THEN 'alle open orders kloppen'
|
|
54482
|
+
ELSE v_n || ' open orders hebben een ophaalmoment van een andere vervoerder: draai het herstel (handmatige stap)' END;
|
|
54483
|
+
RETURN NEXT;
|
|
54484
|
+
END IF;
|
|
54485
|
+
SELECT c.order_id INTO v_order FROM logistics_active.order_carts c WHERE c.order_id IS NOT NULL ORDER BY c.updated_at DESC LIMIT 1;
|
|
54486
|
+
IF v_order IS NOT NULL AND to_regprocedure('public.logistics_kar_tekening(uuid,jsonb)') IS NOT NULL THEN
|
|
54487
|
+
v := public.logistics_kar_tekening(v_order, '{}'::jsonb);
|
|
54488
|
+
onderdeel := 'stand en kar-tekening';
|
|
54489
|
+
ok := (v->>'stand') IN ('bezig','klaar','niet_gelukt','geen_regels') AND jsonb_typeof(v->'dragers') = 'array';
|
|
54490
|
+
toelichting := CASE WHEN ok THEN 'geldige uitkomst voor een bestaande order' ELSE 'ongeldige uitkomst' END;
|
|
54491
|
+
RETURN NEXT;
|
|
54492
|
+
END IF;
|
|
54493
|
+
END $function$;
|
|
54494
|
+
|
|
54495
|
+
CREATE OR REPLACE FUNCTION public.logistics_schema_versie()
|
|
54496
|
+
RETURNS text LANGUAGE sql IMMUTABLE SET search_path TO 'public'
|
|
54497
|
+
AS $function$ SELECT '2026-11-10g'::text $function$;
|
|
54498
|
+
REVOKE EXECUTE ON FUNCTION public.logistics_buffer_sluiten_modus() FROM authenticated;
|
|
54499
|
+
DO $mig$
|
|
54500
|
+
DECLARE v_def text; a text := 'CASE WHEN v_som >= v_P THEN';
|
|
54501
|
+
BEGIN
|
|
54502
|
+
v_def := pg_get_functiondef('public.logistics_kar_svg(jsonb,boolean)'::regprocedure);
|
|
54503
|
+
IF position('nullif(v_laag->>''waarschuwing'', '''') IS NULL AND v_som >= v_P' IN v_def) > 0 THEN RETURN; END IF;
|
|
54504
|
+
IF position(a IN v_def) = 0 THEN RAISE EXCEPTION 'logistics_kar_svg: verwachte tekst niet gevonden'; END IF;
|
|
54505
|
+
EXECUTE replace(v_def, a, 'CASE WHEN nullif(v_laag->>''waarschuwing'', '''') IS NULL AND v_som >= v_P THEN');
|
|
54506
|
+
END $mig$;
|
|
54507
|
+
|
|
54508
|
+
-- ===== Vervolg E1-E (2026-11-10h)
|
|
54509
|
+
-- E1-E (schemaversie 2026-11-10h): eigen ophaaltijd leidend; sluitregel omzetten geldt ook voor vandaag.
|
|
54510
|
+
CREATE OR REPLACE FUNCTION public.logistics_pickup_eigen_tijd(p_carrier_id uuid, p_cf jsonb)
|
|
54511
|
+
RETURNS jsonb LANGUAGE plpgsql STABLE SECURITY DEFINER SET search_path TO 'public' AS $$
|
|
54512
|
+
DECLARE v_soort text; v_pm text; v_bron text; v_geldig boolean;
|
|
54513
|
+
BEGIN
|
|
54514
|
+
IF p_carrier_id IS NULL THEN RETURN jsonb_build_object('eigen', false, 'soort', NULL); END IF;
|
|
54515
|
+
SELECT to_jsonb(c)->>'carrier_kind' INTO v_soort FROM public.carriers c WHERE c.id = p_carrier_id;
|
|
54516
|
+
IF COALESCE(v_soort, '') NOT IN ('klant_haalt_op', 'eigen_vervoer') THEN
|
|
54517
|
+
RETURN jsonb_build_object('eigen', false, 'soort', v_soort);
|
|
54518
|
+
END IF;
|
|
54519
|
+
v_pm := nullif(btrim(COALESCE(p_cf->>'pickup_moment', '')), '');
|
|
54520
|
+
v_bron := COALESCE(p_cf->>'pickup_moment_bron', '');
|
|
54521
|
+
v_geldig := v_pm ~ '^\d{4}-\d{2}-\d{2}\|\d{1,2}:\d{2}$';
|
|
54522
|
+
RETURN jsonb_build_object('eigen', v_geldig AND v_bron <> 'auto', 'soort', v_soort,
|
|
54523
|
+
'tijd', CASE WHEN v_geldig AND v_bron <> 'auto' THEN v_pm END);
|
|
54524
|
+
END $$;
|
|
54525
|
+
REVOKE ALL ON FUNCTION public.logistics_pickup_eigen_tijd(uuid, jsonb) FROM PUBLIC, anon, authenticated;
|
|
54526
|
+
GRANT EXECUTE ON FUNCTION public.logistics_pickup_eigen_tijd(uuid, jsonb) TO service_role;
|
|
54527
|
+
|
|
54528
|
+
-- Ophaalmoment-trigger: een eigen tijd blijft staan, ook bij aanmaken en vervoerderswissel.
|
|
54529
|
+
CREATE OR REPLACE FUNCTION public.logistics_orders_pickup_moment()
|
|
54530
|
+
RETURNS trigger LANGUAGE plpgsql SECURITY DEFINER SET search_path TO 'public' AS $$
|
|
54531
|
+
DECLARE n jsonb; v_basis jsonb; v_eigen jsonb;
|
|
54532
|
+
BEGIN
|
|
54533
|
+
BEGIN
|
|
54534
|
+
v_eigen := public.logistics_pickup_eigen_tijd(NEW.carrier_id, NEW.custom_fields);
|
|
54535
|
+
EXCEPTION WHEN OTHERS THEN
|
|
54536
|
+
v_eigen := '{"eigen":false}'::jsonb;
|
|
54537
|
+
PERFORM public.logistics_melding('logistics_orders_pickup_moment', NEW.id, 'Soort vervoerder kon niet worden gelezen', SQLERRM);
|
|
54538
|
+
END;
|
|
54539
|
+
-- E1-E/1: eigen tijd (klant haalt op / eigen vervoer met tijd) is leidend.
|
|
54540
|
+
IF COALESCE((v_eigen->>'eigen')::boolean, false) THEN
|
|
54541
|
+
IF COALESCE(NEW.custom_fields->>'pickup_moment_bron', '') <> 'eigen' OR (NEW.custom_fields ? 'pickup_moment_reden') THEN
|
|
54542
|
+
NEW.custom_fields := (NEW.custom_fields - 'pickup_moment_reden' - 'pickup_moment_key') || jsonb_build_object('pickup_moment_bron', 'eigen');
|
|
54543
|
+
END IF;
|
|
54544
|
+
RETURN NEW;
|
|
54545
|
+
END IF;
|
|
54546
|
+
IF TG_OP = 'UPDATE' AND NEW.carrier_id IS DISTINCT FROM OLD.carrier_id THEN
|
|
54547
|
+
IF NEW.plan_locked_at IS NOT NULL THEN RETURN NEW; END IF;
|
|
54548
|
+
BEGIN
|
|
54549
|
+
v_basis := COALESCE(NEW.custom_fields, '{}'::jsonb) - 'pickup_moment' - 'pickup_moment_key' - 'pickup_moment_bron' - 'pickup_moment_reden';
|
|
54550
|
+
n := public.logistics_pickup_cf_nieuw(v_basis, NEW.carrier_id, NEW.delivery_date::text,
|
|
54551
|
+
NEW.deleted_at, NEW.cancelled_at, NEW.plan_locked_at, NULL);
|
|
54552
|
+
IF n IS NOT NULL THEN NEW.custom_fields := n; END IF;
|
|
54553
|
+
EXCEPTION WHEN OTHERS THEN
|
|
54554
|
+
RAISE WARNING 'logistiek-engine: ophaalmoment na vervoerderswissel mislukt voor order %: %', NEW.id, SQLERRM;
|
|
54555
|
+
PERFORM public.logistics_melding('logistics_orders_pickup_moment', NEW.id,
|
|
54556
|
+
'Ophaalmoment kon na een vervoerderswissel niet opnieuw worden bepaald', SQLERRM);
|
|
54557
|
+
END;
|
|
54558
|
+
RETURN NEW;
|
|
54559
|
+
END IF;
|
|
54560
|
+
IF COALESCE(current_setting('logistics.pickup_writer', true), '') = 'engine' THEN RETURN NEW; END IF;
|
|
54561
|
+
IF TG_OP = 'UPDATE'
|
|
54562
|
+
AND NEW.delivery_date IS NOT DISTINCT FROM OLD.delivery_date
|
|
54563
|
+
AND (NEW.custom_fields->>'floriday_latest_delivery_at') IS NOT DISTINCT FROM (OLD.custom_fields->>'floriday_latest_delivery_at')
|
|
54564
|
+
AND NEW.deleted_at IS NOT DISTINCT FROM OLD.deleted_at
|
|
54565
|
+
AND (nullif(btrim(COALESCE(NEW.custom_fields->>'pickup_moment', '')), '') IS NOT NULL
|
|
54566
|
+
OR (NEW.custom_fields->'pickup_moment_reden') IS NOT NULL) THEN
|
|
54567
|
+
RETURN NEW;
|
|
54568
|
+
END IF;
|
|
54569
|
+
BEGIN
|
|
54570
|
+
n := public.logistics_pickup_cf_nieuw(NEW.custom_fields, NEW.carrier_id, NEW.delivery_date::text,
|
|
54571
|
+
NEW.deleted_at, NEW.cancelled_at, NEW.plan_locked_at, NULL);
|
|
54572
|
+
IF n IS NOT NULL THEN NEW.custom_fields := n; END IF;
|
|
54573
|
+
EXCEPTION WHEN OTHERS THEN
|
|
54574
|
+
RAISE WARNING 'logistiek-engine: ophaalmoment bepalen mislukt voor order %: %', NEW.id, SQLERRM;
|
|
54575
|
+
BEGIN
|
|
54576
|
+
PERFORM public._logistics_merge_melding('ophaalmoment_fout', NEW.id, NULL, NULL, 'ophaalmoment bepalen: ' || SQLERRM);
|
|
54577
|
+
EXCEPTION WHEN OTHERS THEN RAISE WARNING 'logistiek-engine: melding ophaalmoment mislukt: %', SQLERRM; END;
|
|
54578
|
+
END;
|
|
54579
|
+
RETURN NEW;
|
|
54580
|
+
END $$;
|
|
54581
|
+
|
|
54582
|
+
-- E1-E: gerichte aanpassingen op bestaande functies (idempotent: alleen als de nieuwe tekst er nog niet in staat).
|
|
54583
|
+
|
|
54584
|
+
DO $do$
|
|
54585
|
+
DECLARE d text := pg_get_functiondef('public.logistics_pickup_cf_nieuw(jsonb,uuid,text,timestamptz,timestamptz,timestamptz,jsonb)'::regprocedure); a text := $e1e0a$ BEGIN v_dag := public.logistics_order_date(p_delivery_date);$e1e0a$; b text := $e1e0b$ IF (public.logistics_pickup_eigen_tijd(p_carrier_id, cf)->>'soort') = 'klant_haalt_op' THEN
|
|
54586
|
+
n := (cf - 'pickup_moment' - 'pickup_moment_key')
|
|
54587
|
+
|| jsonb_build_object('pickup_moment_bron', 'eigen', 'pickup_moment_reden', 'Ophaaltijd van de klant ontbreekt; vul die in bij de order');
|
|
54588
|
+
IF n = cf THEN RETURN NULL; END IF;
|
|
54589
|
+
RETURN n;
|
|
54590
|
+
END IF;
|
|
54591
|
+
BEGIN v_dag := public.logistics_order_date(p_delivery_date);$e1e0b$;
|
|
54592
|
+
BEGIN
|
|
54593
|
+
IF strpos(d, b) > 0 THEN RETURN; END IF;
|
|
54594
|
+
IF strpos(d, a) = 0 THEN RAISE EXCEPTION 'E1-E: verwachte tekst niet gevonden in logistics_pickup_cf_nieuw(jsonb,uuid,text,timestamptz,timestamptz,timestamptz,jsonb)'; END IF;
|
|
54595
|
+
EXECUTE replace(d, a, b);
|
|
54596
|
+
END $do$;
|
|
54597
|
+
|
|
54598
|
+
DO $do$
|
|
54599
|
+
DECLARE d text := pg_get_functiondef('public.logistics_pickup_afwijkend(integer)'::regprocedure); a text := $e1e1a$ AND NOT public.logistics_order_is_backfill(o.id)$e1e1a$; b text := $e1e1b$ AND NOT public.logistics_order_is_backfill(o.id)
|
|
54600
|
+
AND NOT COALESCE((public.logistics_pickup_eigen_tijd(o.carrier_id, o.custom_fields)->>'eigen')::boolean, false)$e1e1b$;
|
|
54601
|
+
BEGIN
|
|
54602
|
+
IF strpos(d, b) > 0 THEN RETURN; END IF;
|
|
54603
|
+
IF strpos(d, a) = 0 THEN RAISE EXCEPTION 'E1-E: verwachte tekst niet gevonden in logistics_pickup_afwijkend(integer)'; END IF;
|
|
54604
|
+
EXECUTE replace(d, a, b);
|
|
54605
|
+
END $do$;
|
|
54606
|
+
|
|
54607
|
+
DO $do$
|
|
54608
|
+
DECLARE d text := pg_get_functiondef('public.logistics_pickup_herstel_uitvoeren(integer)'::regprocedure); a text := $e1e2a$ SELECT * INTO o FROM public.orders WHERE id = (r->>'orderId')::uuid FOR UPDATE;$e1e2a$; b text := $e1e2b$ SELECT * INTO o FROM public.orders WHERE id = (r->>'orderId')::uuid FOR UPDATE;
|
|
54609
|
+
CONTINUE WHEN COALESCE((public.logistics_pickup_eigen_tijd(o.carrier_id, o.custom_fields)->>'eigen')::boolean, false);$e1e2b$;
|
|
54610
|
+
BEGIN
|
|
54611
|
+
IF strpos(d, b) > 0 THEN RETURN; END IF;
|
|
54612
|
+
IF strpos(d, a) = 0 THEN RAISE EXCEPTION 'E1-E: verwachte tekst niet gevonden in logistics_pickup_herstel_uitvoeren(integer)'; END IF;
|
|
54613
|
+
EXECUTE replace(d, a, b);
|
|
54614
|
+
END $do$;
|
|
54615
|
+
|
|
54616
|
+
DO $do$
|
|
54617
|
+
DECLARE d text := pg_get_functiondef('public.logistics_install_check()'::regprocedure); a text := $e1e3a$'logistics_pickup_herbereken()'] LOOP$e1e3a$; b text := $e1e3b$'logistics_pickup_herbereken()', 'logistics_pickup_eigen_tijd(uuid,jsonb)'] LOOP$e1e3b$;
|
|
54618
|
+
BEGIN
|
|
54619
|
+
IF strpos(d, b) > 0 THEN RETURN; END IF;
|
|
54620
|
+
IF strpos(d, a) = 0 THEN RAISE EXCEPTION 'E1-E: verwachte tekst niet gevonden in logistics_install_check()'; END IF;
|
|
54621
|
+
EXECUTE replace(d, a, b);
|
|
54622
|
+
END $do$;
|
|
54623
|
+
|
|
54624
|
+
DO $do$
|
|
54625
|
+
DECLARE d text := pg_get_functiondef('public.logistics_schedule_tasks(text)'::regprocedure); a text := $e1e4a$ AND tt.planned_start >= c.raw_start AND tt.planned_start <= now()$e1e4a$; b text := $e1e4b$ AND tt.planned_start >= c.raw_start AND tt.planned_start <= now()
|
|
54626
|
+
AND NOT (c.task_type IN ('buffer.close', 'load.compose')
|
|
54627
|
+
AND COALESCE(tt.payload->>'sluitRegel', 'werkuren')
|
|
54628
|
+
IS DISTINCT FROM CASE WHEN c.verzend_at IS NOT NULL THEN 'verzendmoment' ELSE 'werkuren' END)$e1e4b$;
|
|
54629
|
+
BEGIN
|
|
54630
|
+
IF strpos(d, b) > 0 THEN RETURN; END IF;
|
|
54631
|
+
IF strpos(d, a) = 0 THEN RAISE EXCEPTION 'E1-E: verwachte tekst niet gevonden in logistics_schedule_tasks(text)'; END IF;
|
|
54632
|
+
EXECUTE replace(d, a, b);
|
|
54633
|
+
END $do$;
|
|
54634
|
+
|
|
54635
|
+
DO $do$
|
|
54636
|
+
DECLARE d text := pg_get_functiondef('public.logistics_schedule_tasks(text)'::regprocedure); a text := $e1e5a$ WHERE n.id = t.id
|
|
54637
|
+
AND (t.planned_start$e1e5a$; b text := $e1e5b$ WHERE n.id = t.id
|
|
54638
|
+
AND NOT (t.task_type IN ('buffer.close', 'load.compose')
|
|
54639
|
+
AND public._logistics_order_karren_vast((SELECT o.id FROM public.orders o WHERE o.order_number = t.order_ref AND o.deleted_at IS NULL LIMIT 1)))
|
|
54640
|
+
AND (t.planned_start$e1e5b$;
|
|
54641
|
+
BEGIN
|
|
54642
|
+
IF strpos(d, b) > 0 THEN RETURN; END IF;
|
|
54643
|
+
IF strpos(d, a) = 0 THEN RAISE EXCEPTION 'E1-E: verwachte tekst niet gevonden in logistics_schedule_tasks(text)'; END IF;
|
|
54644
|
+
EXECUTE replace(d, a, b);
|
|
54645
|
+
END $do$;
|
|
54646
|
+
|
|
54647
|
+
-- E1-E/A: eigen tijd gewijzigd in Verkoop -> taken opnieuw plannen, zolang er niets geprint of gepickt is.
|
|
54648
|
+
CREATE OR REPLACE FUNCTION public.logistics_eigen_tijd_herplan()
|
|
54649
|
+
RETURNS trigger LANGUAGE plpgsql SECURITY DEFINER SET search_path TO 'public' AS $$
|
|
54650
|
+
BEGIN
|
|
54651
|
+
IF NEW.order_number IS NULL OR NEW.deleted_at IS NOT NULL THEN RETURN NULL; END IF;
|
|
54652
|
+
IF (NEW.custom_fields->>'pickup_moment') IS NOT DISTINCT FROM (OLD.custom_fields->>'pickup_moment')
|
|
54653
|
+
AND NEW.carrier_id IS NOT DISTINCT FROM OLD.carrier_id THEN RETURN NULL; END IF;
|
|
54654
|
+
BEGIN
|
|
54655
|
+
IF public._logistics_order_karren_vast(NEW.id) THEN
|
|
54656
|
+
INSERT INTO public.logistics_trace_events(phase, step, status, reason, order_ref, actor)
|
|
54657
|
+
VALUES ('ophaalmoment', 'Ophaaltijd gewijzigd', 'overgeslagen',
|
|
54658
|
+
'Er is al een kar geprint of gepickt; de planning blijft zoals hij was.', NEW.order_number, 'engine');
|
|
54659
|
+
RETURN NULL;
|
|
54660
|
+
END IF;
|
|
54661
|
+
PERFORM public.logistics_schedule_tasks(NEW.order_number);
|
|
54662
|
+
INSERT INTO public.logistics_trace_events(phase, step, status, reason, order_ref, actor)
|
|
54663
|
+
VALUES ('ophaalmoment', 'Ophaaltijd gewijzigd', 'geslaagd',
|
|
54664
|
+
'Was ' || COALESCE(OLD.custom_fields->>'pickup_moment', 'leeg') || ', nu ' || COALESCE(NEW.custom_fields->>'pickup_moment', 'leeg') || '; taken opnieuw gepland.',
|
|
54665
|
+
NEW.order_number, 'engine');
|
|
54666
|
+
EXCEPTION WHEN OTHERS THEN
|
|
54667
|
+
RAISE WARNING 'logistiek-engine: herplannen na ophaaltijd mislukt voor order %: %', NEW.id, SQLERRM;
|
|
54668
|
+
PERFORM public.logistics_melding('logistics_eigen_tijd_herplan', NEW.id,
|
|
54669
|
+
'Taken konden na een gewijzigde ophaaltijd niet opnieuw worden gepland; de volgende ronde probeert het opnieuw', SQLERRM);
|
|
54670
|
+
END;
|
|
54671
|
+
RETURN NULL;
|
|
54672
|
+
END $$;
|
|
54673
|
+
REVOKE ALL ON FUNCTION public.logistics_eigen_tijd_herplan() FROM PUBLIC, anon, authenticated;
|
|
54674
|
+
DROP TRIGGER IF EXISTS trg_logistics_eigen_tijd_herplan ON public.orders;
|
|
54675
|
+
CREATE TRIGGER trg_logistics_eigen_tijd_herplan AFTER UPDATE OF custom_fields, carrier_id ON public.orders
|
|
54676
|
+
FOR EACH ROW EXECUTE FUNCTION public.logistics_eigen_tijd_herplan();
|
|
54677
|
+
|
|
54678
|
+
-- E1-E/2+B: wissel van sluitregel in de werkwijze -> alle open taken meteen opnieuw plannen (geen merge-run).
|
|
54679
|
+
CREATE OR REPLACE FUNCTION public.logistics_sluitregel_gewisseld()
|
|
54680
|
+
RETURNS trigger LANGUAGE plpgsql SECURITY DEFINER SET search_path TO 'public' AS $$
|
|
54681
|
+
BEGIN
|
|
54682
|
+
IF public.logistics_rule_text(NEW.settings, 'order_verwerking', 'buffer_sluiten', 'werkuren')
|
|
54683
|
+
IS NOT DISTINCT FROM public.logistics_rule_text(OLD.settings, 'order_verwerking', 'buffer_sluiten', 'werkuren') THEN
|
|
54684
|
+
RETURN NULL;
|
|
54685
|
+
END IF;
|
|
54686
|
+
BEGIN
|
|
54687
|
+
PERFORM public.logistics_schedule_tasks(NULL);
|
|
54688
|
+
EXCEPTION WHEN OTHERS THEN
|
|
54689
|
+
RAISE WARNING 'logistiek-engine: herplannen na wissel sluitregel mislukt: %', SQLERRM;
|
|
54690
|
+
PERFORM public.logistics_melding('logistics_sluitregel_gewisseld', NULL,
|
|
54691
|
+
'Na het wisselen van de sluitregel konden de taken niet meteen opnieuw worden gepland; de volgende ronde doet het alsnog', SQLERRM);
|
|
54692
|
+
END;
|
|
54693
|
+
RETURN NULL;
|
|
54694
|
+
END $$;
|
|
54695
|
+
REVOKE ALL ON FUNCTION public.logistics_sluitregel_gewisseld() FROM PUBLIC, anon, authenticated;
|
|
54696
|
+
DROP TRIGGER IF EXISTS trg_logistics_sluitregel_gewisseld ON public.logistics_rule_configs;
|
|
54697
|
+
CREATE TRIGGER trg_logistics_sluitregel_gewisseld AFTER UPDATE OF settings ON public.logistics_rule_configs
|
|
54698
|
+
FOR EACH ROW EXECUTE FUNCTION public.logistics_sluitregel_gewisseld();
|
|
54699
|
+
|
|
54700
|
+
CREATE OR REPLACE FUNCTION public.logistics_schema_versie()
|
|
54701
|
+
RETURNS text LANGUAGE sql IMMUTABLE SET search_path TO 'public'
|
|
54702
|
+
AS $function$ SELECT '2026-11-10h'::text $function$;
|
|
54703
|
+
|
|
54704
|
+
-- ===== Correctie E1-E (2026-11-10i)
|
|
54705
|
+
-- E1-E correctie (2026-11-10i): een aanmelding telt alleen als "verstuurd" bij de huidige vervoerder en zonder onjuist-markering.
|
|
54706
|
+
CREATE OR REPLACE FUNCTION public.logistics_aanmelding_geldig_at(p_order_id uuid, p_carrier_id uuid)
|
|
54707
|
+
RETURNS timestamptz LANGUAGE plpgsql STABLE SECURITY DEFINER SET search_path TO 'public' AS $$
|
|
54708
|
+
DECLARE v_ok boolean; v_sent timestamptz; v_onj boolean;
|
|
54709
|
+
BEGIN
|
|
54710
|
+
IF p_order_id IS NULL OR p_carrier_id IS NULL OR to_regclass('public.carrier_signup_dispatches') IS NULL THEN RETURN NULL; END IF;
|
|
54711
|
+
IF to_regprocedure('public.afh_order_aangemeld_bij(uuid,uuid)') IS NOT NULL THEN
|
|
54712
|
+
EXECUTE 'SELECT public.afh_order_aangemeld_bij($1, $2)' INTO v_ok USING p_order_id, p_carrier_id;
|
|
54713
|
+
IF NOT COALESCE(v_ok, false) THEN RETURN NULL; END IF;
|
|
54714
|
+
END IF;
|
|
54715
|
+
v_onj := EXISTS (SELECT 1 FROM information_schema.columns
|
|
54716
|
+
WHERE table_schema = 'public' AND table_name = 'carrier_signup_dispatches' AND column_name = 'onjuist_at');
|
|
54717
|
+
EXECUTE format('SELECT min(d.sent_at) FROM public.carrier_signup_dispatches d
|
|
54718
|
+
WHERE d.carrier_id = $2 AND d.sent_at IS NOT NULL
|
|
54719
|
+
AND $1 = ANY(COALESCE(d.order_ids, ''{}''::uuid[])) %s',
|
|
54720
|
+
CASE WHEN v_onj THEN 'AND d.onjuist_at IS NULL' ELSE '' END)
|
|
54721
|
+
INTO v_sent USING p_order_id, p_carrier_id;
|
|
54722
|
+
RETURN v_sent;
|
|
54723
|
+
END $$;
|
|
54724
|
+
REVOKE ALL ON FUNCTION public.logistics_aanmelding_geldig_at(uuid, uuid) FROM PUBLIC, anon, authenticated;
|
|
54725
|
+
GRANT EXECUTE ON FUNCTION public.logistics_aanmelding_geldig_at(uuid, uuid) TO service_role;
|
|
54726
|
+
|
|
54727
|
+
-- Gepland verzendmoment volgens de aanmeldgroepen van Afhandeling, bij voorkeur bij de huidige vervoerder.
|
|
54728
|
+
CREATE OR REPLACE FUNCTION public.logistics_aanmelding_gepland_at(p_order_id uuid, p_carrier_id uuid)
|
|
54729
|
+
RETURNS timestamptz LANGUAGE plpgsql STABLE SECURITY DEFINER SET search_path TO 'public' AS $$
|
|
54730
|
+
DECLARE v_cfg jsonb; v_lead int; v_dag date; v_groups jsonb; v_at timestamptz;
|
|
54731
|
+
BEGIN
|
|
54732
|
+
IF p_order_id IS NULL OR to_regprocedure('public.afh_signup_config()') IS NULL
|
|
54733
|
+
OR to_regprocedure('public.afh_signup_groups(date,date,text)') IS NULL THEN RETURN NULL; END IF;
|
|
54734
|
+
EXECUTE 'SELECT public.afh_signup_config()' INTO v_cfg;
|
|
54735
|
+
v_lead := nullif(v_cfg->>'lead_minutes', '')::int;
|
|
54736
|
+
SELECT public.logistics_order_date(o.delivery_date::text) INTO v_dag FROM public.orders o WHERE o.id = p_order_id;
|
|
54737
|
+
IF v_lead IS NULL OR v_dag IS NULL OR nullif(v_cfg->>'timezone', '') IS NULL THEN RETURN NULL; END IF;
|
|
54738
|
+
EXECUTE 'SELECT public.afh_signup_groups($1, $2, $3)->''groups''' INTO v_groups USING v_dag - 1, v_dag + 1, v_cfg->>'timezone';
|
|
54739
|
+
SELECT (g->>'signup_deadline')::timestamptz - make_interval(mins => v_lead) INTO v_at
|
|
54740
|
+
FROM jsonb_array_elements(COALESCE(v_groups, '[]'::jsonb)) g
|
|
54741
|
+
WHERE EXISTS (SELECT 1 FROM jsonb_array_elements(COALESCE(g->'orders', '[]'::jsonb)) e WHERE (e->>'orderId')::uuid = p_order_id)
|
|
54742
|
+
ORDER BY ((g->>'carrier_id')::uuid IS NOT DISTINCT FROM p_carrier_id) DESC, (g->>'signup_deadline')::timestamptz
|
|
54743
|
+
LIMIT 1;
|
|
54744
|
+
RETURN v_at;
|
|
54745
|
+
END $$;
|
|
54746
|
+
REVOKE ALL ON FUNCTION public.logistics_aanmelding_gepland_at(uuid, uuid) FROM PUBLIC, anon, authenticated;
|
|
54747
|
+
GRANT EXECUTE ON FUNCTION public.logistics_aanmelding_gepland_at(uuid, uuid) TO service_role;
|
|
54748
|
+
|
|
54749
|
+
CREATE OR REPLACE FUNCTION public.logistics_aanmeldmoment(p_order_ids uuid[])
|
|
54750
|
+
RETURNS TABLE(order_id uuid, aanmeld_at timestamp with time zone, bron text, tekst text)
|
|
54751
|
+
LANGUAGE plpgsql
|
|
54752
|
+
STABLE SECURITY DEFINER
|
|
54753
|
+
SET search_path TO 'public'
|
|
54754
|
+
AS $function$
|
|
54755
|
+
DECLARE v_tz text; r record; v_fout text; v_cid uuid; v_at timestamptz; v_bron text; v_sent timestamptz;
|
|
54756
|
+
BEGIN
|
|
54757
|
+
PERFORM public.logistics_acc_guard('ingelogd', 'logistics_aanmeldmoment');
|
|
54758
|
+
BEGIN v_tz := public.logistics_opening_timezone(); EXCEPTION WHEN others THEN v_tz := NULL; END;
|
|
54759
|
+
IF to_regprocedure('public.afh_signup_moment_for_orders(uuid[])') IS NOT NULL THEN
|
|
54760
|
+
BEGIN
|
|
54761
|
+
FOR r IN EXECUTE 'SELECT order_id, aanmeld_at, bron FROM public.afh_signup_moment_for_orders(p_order_ids => $1)'
|
|
54762
|
+
USING p_order_ids
|
|
54763
|
+
LOOP
|
|
54764
|
+
v_at := r.aanmeld_at; v_bron := r.bron;
|
|
54765
|
+
-- E1-E correctie: "verstuurd" alleen bij een geldige aanmelding voor de huidige vervoerder.
|
|
54766
|
+
IF v_bron = 'verstuurd' THEN
|
|
54767
|
+
SELECT o.carrier_id INTO v_cid FROM public.orders o WHERE o.id = r.order_id;
|
|
54768
|
+
v_sent := public.logistics_aanmelding_geldig_at(r.order_id, v_cid);
|
|
54769
|
+
IF v_sent IS NOT NULL THEN
|
|
54770
|
+
v_at := v_sent;
|
|
54771
|
+
ELSE
|
|
54772
|
+
v_at := public.logistics_aanmelding_gepland_at(r.order_id, v_cid);
|
|
54773
|
+
v_bron := CASE WHEN v_at IS NOT NULL THEN 'gepland' ELSE 'onbekend' END;
|
|
54774
|
+
END IF;
|
|
54775
|
+
END IF;
|
|
54776
|
+
order_id := r.order_id; aanmeld_at := v_at; bron := v_bron;
|
|
54777
|
+
tekst := CASE WHEN v_at IS NOT NULL AND v_tz IS NOT NULL AND v_bron IN ('verstuurd', 'gepland')
|
|
54778
|
+
THEN 'Verzendbrief komt vrij na de transportaanmelding om ' || to_char(v_at AT TIME ZONE v_tz, 'HH24:MI')
|
|
54779
|
+
ELSE 'Tijdstip van de transportaanmelding is nog niet bekend' END;
|
|
54780
|
+
RETURN NEXT;
|
|
54781
|
+
END LOOP;
|
|
54782
|
+
RETURN;
|
|
54783
|
+
EXCEPTION WHEN insufficient_privilege THEN v_fout := '42501';
|
|
54784
|
+
WHEN others THEN v_fout := SQLSTATE;
|
|
54785
|
+
END;
|
|
54786
|
+
END IF;
|
|
54787
|
+
FOR r IN SELECT unnest(coalesce(p_order_ids, '{}')) AS id LOOP
|
|
54788
|
+
order_id := r.id; aanmeld_at := NULL; bron := coalesce('fout_' || v_fout, 'onbekend');
|
|
54789
|
+
tekst := 'Tijdstip van de transportaanmelding is nog niet bekend';
|
|
54790
|
+
RETURN NEXT;
|
|
54791
|
+
END LOOP;
|
|
54792
|
+
END $function$;
|
|
54793
|
+
|
|
54794
|
+
DO $do$
|
|
54795
|
+
DECLARE d text := pg_get_functiondef('public.logistics_install_check()'::regprocedure);
|
|
54796
|
+
a text := $a$'logistics_pickup_eigen_tijd(uuid,jsonb)'] LOOP$a$;
|
|
54797
|
+
b text := $b$'logistics_pickup_eigen_tijd(uuid,jsonb)', 'logistics_aanmelding_geldig_at(uuid,uuid)', 'logistics_aanmelding_gepland_at(uuid,uuid)'] LOOP$b$;
|
|
54798
|
+
BEGIN
|
|
54799
|
+
IF strpos(d, b) > 0 THEN RETURN; END IF;
|
|
54800
|
+
IF strpos(d, a) = 0 THEN RAISE EXCEPTION 'E1-E correctie: verwachte tekst niet gevonden in logistics_install_check'; END IF;
|
|
54801
|
+
EXECUTE replace(d, a, b);
|
|
54802
|
+
END $do$;
|
|
54803
|
+
|
|
54804
|
+
CREATE OR REPLACE FUNCTION public.logistics_schema_versie()
|
|
54805
|
+
RETURNS text LANGUAGE sql IMMUTABLE SET search_path TO 'public'
|
|
54806
|
+
AS $function$ SELECT '2026-11-10i'::text $function$;
|