@flowselections/logistiek-engine 1.0.189 → 1.0.191
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 +1273 -0
- package/dist-lib/components/BeladingIndelingCard.d.ts.map +1 -1
- package/dist-lib/components/BeladingIndelingCard.js +4 -2
- package/dist-lib/components/GeblokkeerdeOrdersCard.d.ts.map +1 -1
- package/dist-lib/components/GeblokkeerdeOrdersCard.js +43 -4
- package/dist-lib/components/LadingdragersPage.d.ts.map +1 -1
- package/dist-lib/components/LadingdragersPage.js +4 -1
- package/dist-lib/components/OrdersZonderKarCard.d.ts +2 -0
- package/dist-lib/components/OrdersZonderKarCard.d.ts.map +1 -0
- package/dist-lib/components/OrdersZonderKarCard.js +36 -0
- package/dist-lib/components/RegiePage.d.ts.map +1 -1
- package/dist-lib/components/RegiePage.js +2 -1
- package/dist-lib/components/RuleEnginePage.js +8 -3
- package/dist-lib/hooks/useFloricodeLadingdragers.d.ts +2 -0
- package/dist-lib/hooks/useFloricodeLadingdragers.d.ts.map +1 -0
- package/dist-lib/hooks/useFloricodeLadingdragers.js +25 -0
- package/dist-lib/hooks/useGeblokkeerdeOrders.d.ts +1 -0
- package/dist-lib/hooks/useGeblokkeerdeOrders.d.ts.map +1 -1
- package/dist-lib/hooks/useGeblokkeerdeOrders.js +38 -1
- package/dist-lib/hooks/useRecalcJob.d.ts +1 -0
- package/dist-lib/hooks/useRecalcJob.d.ts.map +1 -1
- package/dist-lib/hooks/useRecalcJob.js +11 -1
- package/dist-lib/integrations/supabase/types.d.ts +407 -1
- package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
- package/dist-lib/lib/beladingRegels.d.ts +18 -0
- package/dist-lib/lib/beladingRegels.d.ts.map +1 -1
- package/dist-lib/lib/beladingRegels.js +28 -0
- package/dist-lib/lib/handlers/logistics-tick.server.d.ts.map +1 -1
- package/dist-lib/lib/handlers/logistics-tick.server.js +14 -0
- package/dist-lib/lib/loadCarrierPlanner.d.ts +3 -0
- package/dist-lib/lib/loadCarrierPlanner.d.ts.map +1 -1
- package/dist-lib/lib/loadCarrierPlanner.js +18 -13
- package/dist-lib/lib/loadCarrierPlanning.server.d.ts +16 -0
- package/dist-lib/lib/loadCarrierPlanning.server.d.ts.map +1 -1
- package/dist-lib/lib/loadCarrierPlanning.server.js +394 -24
- package/dist-lib/lib/loadCarrierTypes.d.ts +7 -0
- package/dist-lib/lib/loadCarrierTypes.d.ts.map +1 -1
- package/dist-lib/lib/loadCarrierTypes.js +17 -0
- package/dist-lib/lib/logisticsRecalcJob.server.d.ts +1 -0
- package/dist-lib/lib/logisticsRecalcJob.server.d.ts.map +1 -1
- package/dist-lib/lib/logisticsRecalcJob.server.js +3 -1
- package/dist-lib/lib/logisticsRecalculate.functions.js +2 -2
- package/dist-lib/lib/moduleVersion.d.ts +1 -1
- package/dist-lib/lib/moduleVersion.js +1 -1
- package/dist-lib/lib/planQueue.server.d.ts.map +1 -1
- package/dist-lib/lib/planQueue.server.js +6 -1
- package/dist-lib/lib/ruleEngineSchema.d.ts.map +1 -1
- package/dist-lib/lib/ruleEngineSchema.js +9 -0
- package/dist-lib/lib/scheduleTasks.server.d.ts +18 -0
- package/dist-lib/lib/scheduleTasks.server.d.ts.map +1 -0
- package/dist-lib/lib/scheduleTasks.server.js +65 -0
- package/package.json +1 -1
|
@@ -30364,3 +30364,1276 @@ END $function$;
|
|
|
30364
30364
|
REVOKE ALL ON FUNCTION public.logistics_plan_fail_log(text, uuid, text, text) FROM PUBLIC, anon, authenticated;
|
|
30365
30365
|
GRANT EXECUTE ON FUNCTION public.logistics_plan_fail_log(text, uuid, text, text) TO service_role;
|
|
30366
30366
|
COMMENT ON FUNCTION public.logistics_plan_fail_log(text, uuid, text, text) IS 'Logistiek Engine: legt een mislukte aanmelding voor (her)planning vast in het logboek en zet de order alsnog in de planwachtrij.';
|
|
30367
|
+
|
|
30368
|
+
-- ===========================================================================
|
|
30369
|
+
-- 1.0.190: dubbelzinnige overloads, verzendbrief zonder terugval, controle.
|
|
30370
|
+
-- Herhaalbaar; verwijdert niets.
|
|
30371
|
+
-- ===========================================================================
|
|
30372
|
+
CREATE OR REPLACE FUNCTION public.logistics_scan_delays()
|
|
30373
|
+
RETURNS integer
|
|
30374
|
+
LANGUAGE plpgsql
|
|
30375
|
+
SECURITY DEFINER
|
|
30376
|
+
SET search_path TO 'public'
|
|
30377
|
+
AS $function$
|
|
30378
|
+
DECLARE
|
|
30379
|
+
rec record;
|
|
30380
|
+
cnt integer := 0;
|
|
30381
|
+
BEGIN
|
|
30382
|
+
PERFORM public.logistics_acc_guard('ingelogd', 'logistics_scan_delays');
|
|
30383
|
+
FOR rec IN
|
|
30384
|
+
SELECT o.id
|
|
30385
|
+
FROM public.orders o
|
|
30386
|
+
WHERE o.deleted_at IS NULL
|
|
30387
|
+
AND o.delivery_date IS NOT NULL
|
|
30388
|
+
AND NULLIF(o.delivery_date::text, '')::date < current_date
|
|
30389
|
+
AND COALESCE(o.status::text, '') NOT IN ('delivered', 'cancelled', 'Geleverd', 'Geannuleerd')
|
|
30390
|
+
AND NOT EXISTS (
|
|
30391
|
+
SELECT 1 FROM public.logistics_delay_alerts d
|
|
30392
|
+
WHERE d.order_id = o.id AND d.status = 'open')
|
|
30393
|
+
LIMIT 200
|
|
30394
|
+
LOOP
|
|
30395
|
+
PERFORM public.logistics_report_delay(p_order_id => rec.id, p_reason => 'Geplande leverdatum is verstreken', p_expected_date => NULL::date, p_force_customer_email => false);
|
|
30396
|
+
cnt := cnt + 1;
|
|
30397
|
+
END LOOP;
|
|
30398
|
+
RETURN cnt;
|
|
30399
|
+
END;
|
|
30400
|
+
$function$;
|
|
30401
|
+
|
|
30402
|
+
CREATE OR REPLACE FUNCTION public.logistics_document_type_map(_document_code text)
|
|
30403
|
+
RETURNS doc_layout_type[]
|
|
30404
|
+
LANGUAGE sql
|
|
30405
|
+
IMMUTABLE
|
|
30406
|
+
SET search_path TO 'public'
|
|
30407
|
+
AS $function$
|
|
30408
|
+
WITH c AS (SELECT lower(btrim(coalesce(_document_code, ''))) AS code)
|
|
30409
|
+
SELECT CASE
|
|
30410
|
+
WHEN c.code = '' THEN ARRAY[]::public.doc_layout_type[]
|
|
30411
|
+
WHEN c.code LIKE '%pakbon%' THEN ARRAY['pakbon']::public.doc_layout_type[]
|
|
30412
|
+
WHEN c.code LIKE '%afleverbrief%' OR c.code LIKE '%afleverbon%'
|
|
30413
|
+
THEN ARRAY['afleverbrief','transportdocument']::public.doc_layout_type[]
|
|
30414
|
+
WHEN c.code LIKE '%veilingbrief%'
|
|
30415
|
+
THEN ARRAY['veilingbrief','transportdocument']::public.doc_layout_type[]
|
|
30416
|
+
WHEN c.code LIKE '%veilingbon%'
|
|
30417
|
+
THEN ARRAY['veilingbon','veilingbrief','transportdocument']::public.doc_layout_type[]
|
|
30418
|
+
WHEN c.code LIKE '%verzendbrief%' OR c.code LIKE '%transportbrief%'
|
|
30419
|
+
THEN ARRAY['afleverbrief','transportdocument']::public.doc_layout_type[]
|
|
30420
|
+
WHEN c.code LIKE '%verzendlabel%'
|
|
30421
|
+
THEN ARRAY['verzendlabel','transportdocument']::public.doc_layout_type[]
|
|
30422
|
+
WHEN c.code LIKE '%productiebon%'
|
|
30423
|
+
THEN ARRAY['productiebon']::public.doc_layout_type[]
|
|
30424
|
+
WHEN c.code LIKE '%productielijst%'
|
|
30425
|
+
THEN ARRAY['magazijnlijst','productiebon']::public.doc_layout_type[]
|
|
30426
|
+
WHEN c.code LIKE '%controlelijst%' OR c.code LIKE '%controlebrief%'
|
|
30427
|
+
THEN ARRAY['controlelijst']::public.doc_layout_type[]
|
|
30428
|
+
WHEN c.code LIKE '%magazijnlijst%'
|
|
30429
|
+
THEN ARRAY['magazijnlijst']::public.doc_layout_type[]
|
|
30430
|
+
WHEN c.code LIKE '%sticker%'
|
|
30431
|
+
THEN ARRAY['sticker','etiket']::public.doc_layout_type[]
|
|
30432
|
+
WHEN c.code LIKE '%etiket%'
|
|
30433
|
+
THEN ARRAY['etiket','sticker']::public.doc_layout_type[]
|
|
30434
|
+
WHEN c.code LIKE '%productlabel%'
|
|
30435
|
+
THEN ARRAY['productlabel','etiket']::public.doc_layout_type[]
|
|
30436
|
+
WHEN c.code LIKE '%barcode%'
|
|
30437
|
+
THEN ARRAY['barcode_label','etiket']::public.doc_layout_type[]
|
|
30438
|
+
WHEN c.code LIKE '%label%'
|
|
30439
|
+
THEN ARRAY['etiket']::public.doc_layout_type[]
|
|
30440
|
+
WHEN c.code LIKE '%factuur%' THEN ARRAY['factuur']::public.doc_layout_type[]
|
|
30441
|
+
WHEN c.code LIKE '%offerte%' THEN ARRAY['offerte']::public.doc_layout_type[]
|
|
30442
|
+
WHEN c.code LIKE '%orderbevestiging%' THEN ARRAY['orderbevestiging']::public.doc_layout_type[]
|
|
30443
|
+
WHEN c.code LIKE '%retourbon%' THEN ARRAY['vrij']::public.doc_layout_type[]
|
|
30444
|
+
ELSE ARRAY[]::public.doc_layout_type[]
|
|
30445
|
+
END
|
|
30446
|
+
FROM c;
|
|
30447
|
+
$function$;
|
|
30448
|
+
|
|
30449
|
+
CREATE OR REPLACE FUNCTION public.logistics_shipping_note_job_for_slip(_slip_job_id uuid)
|
|
30450
|
+
RETURNS uuid
|
|
30451
|
+
LANGUAGE plpgsql
|
|
30452
|
+
SECURITY DEFINER
|
|
30453
|
+
SET search_path TO 'public'
|
|
30454
|
+
AS $function$
|
|
30455
|
+
DECLARE
|
|
30456
|
+
slip public.print_jobs;
|
|
30457
|
+
layout_meta jsonb;
|
|
30458
|
+
v_layout uuid;
|
|
30459
|
+
v_doc_type text;
|
|
30460
|
+
v_layout_type text;
|
|
30461
|
+
v_key text;
|
|
30462
|
+
v_bron boolean;
|
|
30463
|
+
v_order text;
|
|
30464
|
+
v_res jsonb;
|
|
30465
|
+
BEGIN
|
|
30466
|
+
PERFORM public.logistics_acc_guard('ingelogd', 'logistics_shipping_note_job_for_slip');
|
|
30467
|
+
SELECT * INTO slip FROM public.print_jobs WHERE id = _slip_job_id;
|
|
30468
|
+
IF NOT FOUND THEN RETURN NULL; END IF;
|
|
30469
|
+
v_order := coalesce(slip.payload->>'ordernummer', slip.payload->>'order_number');
|
|
30470
|
+
|
|
30471
|
+
v_bron := public.logistics_payload_is_floriday(slip.payload);
|
|
30472
|
+
IF v_bron IS TRUE THEN
|
|
30473
|
+
INSERT INTO public.logistics_events(source_module, event_type, status, processed_at, payload)
|
|
30474
|
+
VALUES ('logistiek-engine', 'shippingnote.blocked', 'verwerkt', now(), jsonb_build_object(
|
|
30475
|
+
'slipJobId', _slip_job_id, 'orderNumber', v_order,
|
|
30476
|
+
'reason', 'floriday_order_geen_eigen_verzendbrief'));
|
|
30477
|
+
RETURN NULL;
|
|
30478
|
+
END IF;
|
|
30479
|
+
|
|
30480
|
+
v_key := 'verzendbrief-bij-pakbon:' || _slip_job_id::text;
|
|
30481
|
+
|
|
30482
|
+
-- 1.0.190: geen terugval meer. Herkomst onbekend of geen layout = fail closed met reden.
|
|
30483
|
+
IF v_bron IS NULL THEN
|
|
30484
|
+
BEGIN
|
|
30485
|
+
INSERT INTO public.logistics_trace_events(phase, step, status, reason, order_ref, slip_ref)
|
|
30486
|
+
VALUES ('verzendbrief', 'Verzendbrief wacht: herkomst onbekend', 'geblokkeerd',
|
|
30487
|
+
'HERKOMST_ONBEKEND: de herkomst van de order kon niet worden bepaald; er wordt geen verzendbrief gegokt.',
|
|
30488
|
+
coalesce(v_order, slip.payload->>'order_id'), _slip_job_id::text);
|
|
30489
|
+
EXCEPTION WHEN OTHERS THEN NULL;
|
|
30490
|
+
END;
|
|
30491
|
+
RETURN NULL;
|
|
30492
|
+
END IF;
|
|
30493
|
+
|
|
30494
|
+
layout_meta := public.logistics_document_for_order(
|
|
30495
|
+
public.logistics_try_uuid(coalesce(slip.payload->>'order_id', slip.payload->>'orderId')), 'verzendbrief');
|
|
30496
|
+
v_layout := public.logistics_try_uuid(layout_meta->>'layout_id');
|
|
30497
|
+
SELECT l.documenttype::text INTO v_layout_type FROM public.document_layouts l WHERE l.id = v_layout AND l.actief IS TRUE;
|
|
30498
|
+
IF NOT coalesce((layout_meta->>'ok')::boolean, false) OR v_layout_type IS NULL THEN
|
|
30499
|
+
BEGIN
|
|
30500
|
+
INSERT INTO public.logistics_trace_events(phase, step, status, reason, order_ref, slip_ref)
|
|
30501
|
+
VALUES ('verzendbrief', 'Verzendbrief wacht: geen layout', 'geblokkeerd',
|
|
30502
|
+
'GEEN_LAYOUT_VERZENDBRIEF: ' || coalesce(nullif(layout_meta->>'reden', ''), 'Er is geen actieve layout voor de verzendbrief van deze order.'),
|
|
30503
|
+
coalesce(v_order, slip.payload->>'order_id'), _slip_job_id::text);
|
|
30504
|
+
EXCEPTION WHEN OTHERS THEN NULL;
|
|
30505
|
+
END;
|
|
30506
|
+
RETURN NULL;
|
|
30507
|
+
END IF;
|
|
30508
|
+
v_doc_type := v_layout_type;
|
|
30509
|
+
|
|
30510
|
+
v_res := public.logistics_print_via_layout(
|
|
30511
|
+
v_doc_type, coalesce(slip.documentnaam, 'Verzendbrief') || ' · verzendbrief', NULL, NULL,
|
|
30512
|
+
v_layout, nullif(slip.payload->>'print_plek_id', '')::uuid, 'eigen', coalesce(slip.is_herprint, false),
|
|
30513
|
+
(coalesce(slip.payload, '{}'::jsonb) - 'cartId' - 'pick_station' - 'printPlekId' - 'print_place_id' - 'printPlaceId')
|
|
30514
|
+
|| jsonb_build_object(
|
|
30515
|
+
'ordernummer', v_order,
|
|
30516
|
+
'document_type_code', 'verzendbrief', 'documentType', 'verzendbrief', 'layout_id', v_layout,
|
|
30517
|
+
'trigger', 'with_packing_slip', 'is_floriday', false,
|
|
30518
|
+
'layout_keuze', layout_meta->>'keuze',
|
|
30519
|
+
'packing_slip_job_id', _slip_job_id, 'print_batch_key', 'kar-print:' || _slip_job_id::text),
|
|
30520
|
+
v_key);
|
|
30521
|
+
|
|
30522
|
+
IF NOT coalesce((v_res->>'ok')::boolean, false) THEN
|
|
30523
|
+
BEGIN
|
|
30524
|
+
INSERT INTO public.logistics_trace_events(phase, step, status, reason, order_ref, slip_ref)
|
|
30525
|
+
VALUES ('verzendbrief', 'Verzendbrief bij pakbon niet geprint', 'mislukt', v_res->>'error',
|
|
30526
|
+
coalesce(v_order, slip.payload->>'order_id'), _slip_job_id::text);
|
|
30527
|
+
EXCEPTION WHEN OTHERS THEN NULL;
|
|
30528
|
+
END;
|
|
30529
|
+
RETURN NULL;
|
|
30530
|
+
END IF;
|
|
30531
|
+
RETURN (v_res->>'job_id')::uuid;
|
|
30532
|
+
END;
|
|
30533
|
+
$function$;
|
|
30534
|
+
|
|
30535
|
+
CREATE OR REPLACE FUNCTION public.logistics_overload_check()
|
|
30536
|
+
RETURNS TABLE(onderdeel text, ok boolean, toelichting text)
|
|
30537
|
+
LANGUAGE plpgsql STABLE SECURITY DEFINER SET search_path TO 'public'
|
|
30538
|
+
AS $function$
|
|
30539
|
+
DECLARE r record; v_n integer := 0;
|
|
30540
|
+
BEGIN
|
|
30541
|
+
PERFORM public.logistics_acc_guard('ingelogd', 'logistics_overload_check');
|
|
30542
|
+
-- Dubbelzinnig: een kortere overload waarvan de aanroep ook op een langere past
|
|
30543
|
+
-- (alle extra parameters van de langere hebben een standaardwaarde).
|
|
30544
|
+
FOR r IN
|
|
30545
|
+
SELECT a.proname,
|
|
30546
|
+
pg_get_function_identity_arguments(a.oid) AS kort,
|
|
30547
|
+
pg_get_function_identity_arguments(b.oid) AS lang
|
|
30548
|
+
FROM pg_proc a
|
|
30549
|
+
JOIN pg_proc b ON b.proname = a.proname AND b.pronamespace = a.pronamespace AND b.oid <> a.oid
|
|
30550
|
+
WHERE a.pronamespace = 'public'::regnamespace
|
|
30551
|
+
AND a.proname LIKE 'logistics\_%'
|
|
30552
|
+
AND b.pronargs > a.pronargs
|
|
30553
|
+
AND b.pronargs - b.pronargdefaults <= a.pronargs
|
|
30554
|
+
AND (a.pronargs = 0 OR (b.proargtypes::oid[])[0:a.pronargs - 1] = (a.proargtypes::oid[])[0:a.pronargs - 1])
|
|
30555
|
+
LOOP
|
|
30556
|
+
v_n := v_n + 1;
|
|
30557
|
+
onderdeel := 'overload:' || r.proname;
|
|
30558
|
+
ok := false;
|
|
30559
|
+
toelichting := format('Dubbelzinnig: (%s) en (%s). Aanroepen gebeuren met benoemde argumenten; Shell-objecten worden niet verwijderd.', r.kort, r.lang);
|
|
30560
|
+
RETURN NEXT;
|
|
30561
|
+
END LOOP;
|
|
30562
|
+
IF v_n = 0 THEN
|
|
30563
|
+
onderdeel := 'overloads'; ok := true; toelichting := 'Geen dubbelzinnige overloads'; RETURN NEXT;
|
|
30564
|
+
END IF;
|
|
30565
|
+
END $function$;
|
|
30566
|
+
REVOKE ALL ON FUNCTION public.logistics_overload_check() FROM PUBLIC, anon;
|
|
30567
|
+
GRANT EXECUTE ON FUNCTION public.logistics_overload_check() TO authenticated, service_role;
|
|
30568
|
+
|
|
30569
|
+
DO $ic190$
|
|
30570
|
+
DECLARE v_def text;
|
|
30571
|
+
BEGIN
|
|
30572
|
+
v_def := pg_get_functiondef('public.logistics_install_check()'::regprocedure);
|
|
30573
|
+
IF v_def NOT LIKE '%logistics_overload_check()%' THEN
|
|
30574
|
+
v_def := regexp_replace(v_def, 'END \$function\$\s*$', E' RETURN QUERY SELECT * FROM public.logistics_overload_check();\nEND $function$');
|
|
30575
|
+
EXECUTE v_def;
|
|
30576
|
+
END IF;
|
|
30577
|
+
END $ic190$;
|
|
30578
|
+
-- 1.0.190 deel 2: verzendbriefstatus, barcode-pad, stickerreden, stickermaten, beladingbevestiging. Herhaalbaar; verwijdert niets.
|
|
30579
|
+
CREATE OR REPLACE FUNCTION public.logistics_shipping_note_status(p_slip_job_id uuid DEFAULT NULL::uuid, p_cart_id uuid DEFAULT NULL::uuid)
|
|
30580
|
+
RETURNS jsonb
|
|
30581
|
+
LANGUAGE plpgsql
|
|
30582
|
+
STABLE SECURITY DEFINER
|
|
30583
|
+
SET search_path TO 'public'
|
|
30584
|
+
AS $function$
|
|
30585
|
+
DECLARE
|
|
30586
|
+
v_note public.logistics_slip_shipping_note;
|
|
30587
|
+
v_slip public.print_jobs;
|
|
30588
|
+
v_job public.print_jobs;
|
|
30589
|
+
v_state text; v_message text; v_cart uuid;
|
|
30590
|
+
v_vrijgave boolean := false; v_scan_ok boolean;
|
|
30591
|
+
BEGIN
|
|
30592
|
+
IF p_slip_job_id IS NULL AND p_cart_id IS NULL THEN
|
|
30593
|
+
RETURN jsonb_build_object('status','mislukt','message',
|
|
30594
|
+
'De pakbon of kar ontbreekt; de verzendbriefstatus kan niet worden opgehaald.','attempts',0,'nextAttemptAt',NULL);
|
|
30595
|
+
END IF;
|
|
30596
|
+
IF p_slip_job_id IS NOT NULL THEN
|
|
30597
|
+
SELECT * INTO v_slip FROM public.print_jobs WHERE id = p_slip_job_id;
|
|
30598
|
+
ELSE
|
|
30599
|
+
SELECT * INTO v_slip FROM public.print_jobs p
|
|
30600
|
+
WHERE lower(coalesce(p.payload->>'document_type_code', p.payload->>'documentType', p.documenttype::text, '')) LIKE '%pakbon%'
|
|
30601
|
+
AND nullif(coalesce(p.payload->>'cart_id', p.payload->'cart'->>'cartId'), '') = p_cart_id::text
|
|
30602
|
+
ORDER BY p.created_at DESC LIMIT 1;
|
|
30603
|
+
END IF;
|
|
30604
|
+
IF v_slip.id IS NULL THEN
|
|
30605
|
+
RETURN jsonb_build_object('status','mislukt','message','De bijbehorende pakbon is niet gevonden.',
|
|
30606
|
+
'attempts',0,'nextAttemptAt',NULL,'cartId',p_cart_id);
|
|
30607
|
+
END IF;
|
|
30608
|
+
BEGIN
|
|
30609
|
+
v_cart := coalesce(nullif(v_slip.payload->>'cart_id','')::uuid, nullif(v_slip.payload->'cart'->>'cartId','')::uuid, p_cart_id);
|
|
30610
|
+
EXCEPTION WHEN invalid_text_representation THEN v_cart := p_cart_id;
|
|
30611
|
+
END;
|
|
30612
|
+
SELECT * INTO v_note FROM public.logistics_slip_shipping_note s
|
|
30613
|
+
WHERE s.slip_job_id = v_slip.id ORDER BY s.created_at DESC LIMIT 1;
|
|
30614
|
+
|
|
30615
|
+
IF v_note.id IS NULL THEN
|
|
30616
|
+
SELECT * INTO v_job FROM public.print_jobs p
|
|
30617
|
+
WHERE p.idempotentie_sleutel = 'verzendbrief-bij-pakbon:' || v_slip.id::text
|
|
30618
|
+
OR (p.payload->>'packing_slip_job_id' = v_slip.id::text
|
|
30619
|
+
AND lower(coalesce(p.payload->>'document_type_code', p.documenttype::text, '')) ~ '(verzend|aflever)')
|
|
30620
|
+
ORDER BY p.created_at DESC LIMIT 1;
|
|
30621
|
+
IF v_job.id IS NULL THEN
|
|
30622
|
+
IF v_slip.created_at < now() - interval '15 minutes' THEN
|
|
30623
|
+
v_state := 'mislukt';
|
|
30624
|
+
v_message := 'Er is geen verzendbrief bij deze pakbon aangemaakt. Controleer de printwachtrij of print de pakbon opnieuw.';
|
|
30625
|
+
ELSE
|
|
30626
|
+
v_state := 'klaar_voor_print'; v_message := 'Verzendbrief wordt klaargezet.';
|
|
30627
|
+
END IF;
|
|
30628
|
+
ELSIF v_job.status = 'voltooid' THEN
|
|
30629
|
+
v_state := 'geprint'; v_message := 'Pakbon en verzendbrief zijn naar de printer gestuurd.';
|
|
30630
|
+
ELSIF v_job.status IN ('wachtend','bezig') THEN
|
|
30631
|
+
v_state := 'klaar_voor_print'; v_message := 'Verzendbrief wordt klaargezet.';
|
|
30632
|
+
ELSE
|
|
30633
|
+
v_state := 'mislukt'; v_message := 'De verzendbrief kon niet worden geprint. Controleer de printwachtrij.';
|
|
30634
|
+
END IF;
|
|
30635
|
+
RETURN jsonb_build_object('status',v_state,'message',v_message,
|
|
30636
|
+
'attempts',coalesce(v_job.pogingen,0),'nextAttemptAt',v_job.volgende_poging,
|
|
30637
|
+
'slipJobId',v_slip.id,'shippingNoteJobId',v_job.id,'cartId',v_cart,
|
|
30638
|
+
'orderNumber',coalesce(v_slip.payload->>'order_number', v_slip.payload->'data'->>'orderNumber'));
|
|
30639
|
+
END IF;
|
|
30640
|
+
|
|
30641
|
+
IF v_note.print_job_id IS NOT NULL THEN
|
|
30642
|
+
SELECT * INTO v_job FROM public.print_jobs WHERE id = v_note.print_job_id;
|
|
30643
|
+
END IF;
|
|
30644
|
+
v_scan_ok := EXISTS (SELECT 1 FROM public.logistics_control_scan cs
|
|
30645
|
+
WHERE cs.cart_id = coalesce(v_note.cart_id, v_cart) AND cs.approved_at IS NOT NULL);
|
|
30646
|
+
|
|
30647
|
+
IF v_note.status = 'mislukt' THEN
|
|
30648
|
+
v_state := 'mislukt'; v_message := 'De officiële Floriday-afleverbrief kon niet worden klaargezet. Controleer de verzendbriefwachtrij.';
|
|
30649
|
+
ELSIF v_note.status = 'wacht_op_vrijgave'
|
|
30650
|
+
OR (v_note.status = 'wachtend' AND v_note.retry_class IN ('wacht_op_vrijgave','wacht_op_kenmerk'))
|
|
30651
|
+
OR (v_note.status = 'wachtend' AND v_note.retry_class IS NULL AND coalesce(v_note.attempts,0) = 0 AND NOT v_scan_ok) THEN
|
|
30652
|
+
v_state := 'wacht_op_floriday'; v_vrijgave := true;
|
|
30653
|
+
-- 1.0.190: echte wachtreden uit de wachtrij, geen vaste tekst meer.
|
|
30654
|
+
v_message := 'Pakbon geprint — verzendbrief wacht: ' || coalesce(nullif(btrim(v_note.error), ''),
|
|
30655
|
+
CASE WHEN NOT v_scan_ok THEN 'op een goedgekeurde controlescan.'
|
|
30656
|
+
ELSE 'tot Afhandeling de belading naar Floriday heeft gestuurd (na het aanmeldmoment van het transport).' END);
|
|
30657
|
+
ELSIF v_note.status = 'wachtend' AND v_note.retry_class = 'nog_niet_beschikbaar' THEN
|
|
30658
|
+
v_state := 'wacht_op_floriday'; v_message := 'Pakbon geprint — wachten op officiële Floriday-afleverbrief.';
|
|
30659
|
+
ELSIF v_note.status = 'wachtend' AND v_note.retry_class = 'tijdelijk' AND coalesce(v_note.attempts,0) > 0
|
|
30660
|
+
AND coalesce(v_note.error,'') ~* '(5\d\d|429|time-?out|netwerk|verbinding|niet bereikbaar)' THEN
|
|
30661
|
+
v_state := 'klaar_voor_print'; v_message := 'Floriday is tijdelijk niet bereikbaar. De verzendbrief wordt automatisch opnieuw geprobeerd.';
|
|
30662
|
+
ELSIF v_note.status = 'wachtend' AND v_note.retry_class = 'limiet_bereikt' THEN
|
|
30663
|
+
v_state := 'mislukt'; v_message := 'Het maximale aantal pogingen voor de verzendbrief is bereikt. Controleer de verzendbriefwachtrij.';
|
|
30664
|
+
ELSIF v_note.status = 'wachtend' THEN
|
|
30665
|
+
v_state := 'klaar_voor_print'; v_message := 'Verzendbrief wordt klaargezet.';
|
|
30666
|
+
ELSIF v_job.status = 'voltooid' THEN
|
|
30667
|
+
v_state := 'geprint'; v_message := 'Pakbon en verzendbrief zijn naar de printer gestuurd.';
|
|
30668
|
+
ELSIF v_job.status IN ('mislukt','geannuleerd') THEN
|
|
30669
|
+
v_state := 'mislukt'; v_message := 'De verzendbrief kon niet worden geprint. Controleer de printwachtrij.';
|
|
30670
|
+
ELSE
|
|
30671
|
+
v_state := 'klaar_voor_print'; v_message := 'De officiële Floriday-afleverbrief wordt naar dezelfde printer gestuurd.';
|
|
30672
|
+
END IF;
|
|
30673
|
+
|
|
30674
|
+
RETURN jsonb_build_object('status',v_state,'message',v_message,
|
|
30675
|
+
'attempts',coalesce(v_note.attempts,0),'nextAttemptAt',v_note.next_attempt_at,
|
|
30676
|
+
'slipJobId',v_slip.id,'shippingNoteJobId',v_note.print_job_id,
|
|
30677
|
+
'cartId',coalesce(v_note.cart_id, v_cart),'orderNumber',v_note.order_number,
|
|
30678
|
+
'wachtOpVrijgave', v_vrijgave,
|
|
30679
|
+
'wachtReden', CASE WHEN v_vrijgave THEN coalesce(nullif(btrim(v_note.error), ''), v_message) END,
|
|
30680
|
+
'wachtCode', CASE WHEN v_vrijgave THEN coalesce(v_note.retry_class, v_note.status) END,
|
|
30681
|
+
-- Verwacht tijdstip: volgt zodra Afhandeling het aanmeldmoment levert (koppelprompt).
|
|
30682
|
+
'verwachtOm', NULL::timestamptz);
|
|
30683
|
+
END;
|
|
30684
|
+
$function$;
|
|
30685
|
+
|
|
30686
|
+
CREATE OR REPLACE FUNCTION public.logistics_shipping_note_from_barcode(p_barcode text, p_allow_reprint boolean DEFAULT false, p_station_id uuid DEFAULT NULL::uuid)
|
|
30687
|
+
RETURNS jsonb
|
|
30688
|
+
LANGUAGE plpgsql
|
|
30689
|
+
SECURITY DEFINER
|
|
30690
|
+
SET search_path TO 'public'
|
|
30691
|
+
AS $function$
|
|
30692
|
+
DECLARE
|
|
30693
|
+
v_look jsonb;
|
|
30694
|
+
v_cart jsonb;
|
|
30695
|
+
v_cart_id uuid;
|
|
30696
|
+
v_order_id uuid;
|
|
30697
|
+
v_order_number text;
|
|
30698
|
+
v_layout jsonb;
|
|
30699
|
+
v_layout_id uuid;
|
|
30700
|
+
v_doc_type text;
|
|
30701
|
+
v_plek uuid;
|
|
30702
|
+
v_herprint boolean;
|
|
30703
|
+
v_volgnr integer;
|
|
30704
|
+
v_res jsonb;
|
|
30705
|
+
BEGIN
|
|
30706
|
+
v_look := public.logistics_barcode_lookup(p_barcode);
|
|
30707
|
+
IF NOT COALESCE((v_look->>'ok')::boolean, false) THEN
|
|
30708
|
+
RETURN v_look;
|
|
30709
|
+
END IF;
|
|
30710
|
+
|
|
30711
|
+
v_cart := v_look->'cart';
|
|
30712
|
+
v_cart_id := nullif(v_cart->>'cartId', '')::uuid;
|
|
30713
|
+
v_order_id := nullif(v_look->>'orderId', '')::uuid;
|
|
30714
|
+
v_order_number := v_look->'order'->>'orderNumber';
|
|
30715
|
+
|
|
30716
|
+
IF COALESCE((v_look->>'alreadyPrinted')::boolean, false) AND NOT COALESCE(p_allow_reprint, false) THEN
|
|
30717
|
+
RETURN v_look || jsonb_build_object(
|
|
30718
|
+
'ok', false,
|
|
30719
|
+
'errorCode', 'al_geprint',
|
|
30720
|
+
'error', 'Voor deze kar is al een verzendbrief gemaakt. Kies bewust voor opnieuw printen als dat de bedoeling is.');
|
|
30721
|
+
END IF;
|
|
30722
|
+
|
|
30723
|
+
-- 1.0.190: layout via de centrale documentkeuze per order; geen terugval op 'verzendlabel'.
|
|
30724
|
+
v_layout := public.logistics_document_for_order(v_order_id, 'verzendbrief');
|
|
30725
|
+
v_layout_id := public.logistics_try_uuid(v_layout->>'layout_id');
|
|
30726
|
+
SELECT l.documenttype::text INTO v_doc_type FROM public.document_layouts l WHERE l.id = v_layout_id AND l.actief IS TRUE;
|
|
30727
|
+
IF NOT coalesce((v_layout->>'ok')::boolean, false) OR v_doc_type IS NULL THEN
|
|
30728
|
+
RETURN v_look || jsonb_build_object(
|
|
30729
|
+
'ok', false,
|
|
30730
|
+
'errorCode', 'GEEN_LAYOUT_VERZENDBRIEF',
|
|
30731
|
+
'error', coalesce(nullif(v_layout->>'reden', ''), 'Er is geen actieve layout voor de verzendbrief van deze order.'),
|
|
30732
|
+
'printJobId', NULL, 'layoutId', NULL, 'documentType', NULL, 'printPlekId', NULL);
|
|
30733
|
+
END IF;
|
|
30734
|
+
|
|
30735
|
+
PERFORM public.logistics_ensure_cart_barcode(v_cart_id);
|
|
30736
|
+
|
|
30737
|
+
IF p_station_id IS NOT NULL THEN
|
|
30738
|
+
SELECT k.print_plek_id INTO v_plek FROM public.print_plek_koppelingen k
|
|
30739
|
+
WHERE k.bron = 'pickstation' AND k.bron_id = p_station_id::text
|
|
30740
|
+
ORDER BY k.created_at LIMIT 1;
|
|
30741
|
+
END IF;
|
|
30742
|
+
|
|
30743
|
+
v_herprint := COALESCE(p_allow_reprint, false) AND COALESCE((v_look->>'alreadyPrinted')::boolean, false);
|
|
30744
|
+
SELECT count(*) INTO v_volgnr FROM public.logistics_shipping_note_scan WHERE cart_id = v_cart_id;
|
|
30745
|
+
|
|
30746
|
+
v_res := public.logistics_print_via_layout(
|
|
30747
|
+
v_doc_type, 'Verzendbrief ' || coalesce(v_order_number, ''), NULL, NULL,
|
|
30748
|
+
v_layout_id, v_plek, NULL, v_herprint,
|
|
30749
|
+
jsonb_build_object(
|
|
30750
|
+
'source', 'pakbon_scan',
|
|
30751
|
+
'barcode', v_look->>'barcode',
|
|
30752
|
+
'order_id', v_order_id,
|
|
30753
|
+
'ordernummer', v_order_number,
|
|
30754
|
+
'order_number', v_order_number,
|
|
30755
|
+
'customer_name', v_look->'order'->>'customerName',
|
|
30756
|
+
'cart_id', v_cart_id,
|
|
30757
|
+
'cart_index', v_cart->>'cartIndex',
|
|
30758
|
+
'cart_total', v_cart->>'cartTotal',
|
|
30759
|
+
'cart_label', v_cart->>'cartLabel',
|
|
30760
|
+
'print_plek_id', v_plek,
|
|
30761
|
+
'station_id', p_station_id,
|
|
30762
|
+
'layout_keuze', v_layout->>'keuze',
|
|
30763
|
+
'data', v_cart),
|
|
30764
|
+
'verzendbrief-scan:' || coalesce(v_cart_id::text, v_look->>'barcode') || ':' || v_volgnr::text);
|
|
30765
|
+
|
|
30766
|
+
IF NOT coalesce((v_res->>'ok')::boolean, false) THEN
|
|
30767
|
+
RETURN v_look || jsonb_build_object(
|
|
30768
|
+
'ok', false,
|
|
30769
|
+
'errorCode', lower(v_res->>'error_code'),
|
|
30770
|
+
'error', v_res->>'error',
|
|
30771
|
+
'printJobId', NULL, 'layoutId', v_layout_id, 'documentType', v_doc_type, 'printPlekId', v_plek);
|
|
30772
|
+
END IF;
|
|
30773
|
+
|
|
30774
|
+
IF NOT coalesce((v_res->>'bestaand')::boolean, false) THEN
|
|
30775
|
+
INSERT INTO public.logistics_shipping_note_scan(cart_id, order_id, barcode, print_job_id, is_reprint)
|
|
30776
|
+
VALUES (v_cart_id, v_order_id, v_look->>'barcode', (v_res->>'job_id')::uuid,
|
|
30777
|
+
COALESCE((v_look->>'alreadyPrinted')::boolean, false));
|
|
30778
|
+
END IF;
|
|
30779
|
+
|
|
30780
|
+
RETURN v_look || jsonb_build_object(
|
|
30781
|
+
'ok', true,
|
|
30782
|
+
'printJobId', (v_res->>'job_id')::uuid,
|
|
30783
|
+
'layoutId', v_layout_id,
|
|
30784
|
+
'documentType', v_doc_type,
|
|
30785
|
+
'printPlekId', v_plek,
|
|
30786
|
+
'warning', NULL);
|
|
30787
|
+
END;
|
|
30788
|
+
$function$;
|
|
30789
|
+
|
|
30790
|
+
CREATE OR REPLACE FUNCTION public.logistics_cart_sticker_state(p_cart_id uuid)
|
|
30791
|
+
RETURNS jsonb
|
|
30792
|
+
LANGUAGE sql
|
|
30793
|
+
STABLE SECURITY DEFINER
|
|
30794
|
+
SET search_path TO 'public'
|
|
30795
|
+
AS $function$
|
|
30796
|
+
WITH s AS (SELECT * FROM public.logistics_sticker_prints WHERE cart_id = p_cart_id),
|
|
30797
|
+
vraag AS (
|
|
30798
|
+
SELECT count(*) FILTER (WHERE coalesce(oi.needs_sticker, false)) AS met_sticker, count(*) AS regels
|
|
30799
|
+
FROM public.order_cart_items ci
|
|
30800
|
+
LEFT JOIN public.order_items oi ON oi.id::text = split_part(ci.order_item_id::text, '::', 1)
|
|
30801
|
+
WHERE ci.cart_id = p_cart_id
|
|
30802
|
+
),
|
|
30803
|
+
t AS (
|
|
30804
|
+
SELECT
|
|
30805
|
+
count(*) FILTER (WHERE status <> 'niet_printen') AS totaal,
|
|
30806
|
+
count(*) FILTER (WHERE status IN ('in_printwachtrij','afgemeld')) AS in_wachtrij,
|
|
30807
|
+
count(*) FILTER (WHERE status IN ('wacht_op_moment','klaar')) AS wachtend,
|
|
30808
|
+
count(*) FILTER (WHERE status IN ('geblokkeerd','mislukt')) AS geblokkeerd,
|
|
30809
|
+
count(*) FILTER (WHERE status = 'niet_printen') AS niet_printen,
|
|
30810
|
+
(array_agg(reden ORDER BY soort, bron_id) FILTER (WHERE status = 'niet_printen' AND coalesce(btrim(reden), '') <> ''))[1] AS niet_reden,
|
|
30811
|
+
coalesce(jsonb_agg(jsonb_build_object('soort', soort, 'stickerId', bron_id, 'kopieen', kopieen,
|
|
30812
|
+
'status', status, 'reden', reden, 'printJobId', print_job_id, 'breedteMm', breedte_mm, 'hoogteMm', hoogte_mm)
|
|
30813
|
+
ORDER BY soort, bron_id), '[]'::jsonb) AS regels
|
|
30814
|
+
FROM s
|
|
30815
|
+
)
|
|
30816
|
+
SELECT jsonb_build_object(
|
|
30817
|
+
'cartId', p_cart_id,
|
|
30818
|
+
'totaal', t.totaal,
|
|
30819
|
+
'inPrintwachtrij', t.in_wachtrij,
|
|
30820
|
+
'wachtend', t.wachtend,
|
|
30821
|
+
'geblokkeerd', t.geblokkeerd,
|
|
30822
|
+
'regels', t.regels,
|
|
30823
|
+
-- 1.0.190: waarom deze kar geen stickers heeft, in gewone taal.
|
|
30824
|
+
'stickersNodig', t.totaal > 0,
|
|
30825
|
+
'geenStickersReden', CASE
|
|
30826
|
+
WHEN t.totaal > 0 THEN NULL
|
|
30827
|
+
WHEN t.niet_printen > 0 THEN coalesce(t.niet_reden, 'De stickers van deze kar hoeven niet geprint te worden.')
|
|
30828
|
+
WHEN v.regels = 0 THEN 'Deze kar heeft geen regels.'
|
|
30829
|
+
WHEN v.met_sticker = 0 THEN 'Geen regel op deze kar vraagt om een sticker.'
|
|
30830
|
+
ELSE 'Er vraagt een regel om een sticker, maar die is nog niet klaargezet. De stickerronde pakt dit automatisch op.'
|
|
30831
|
+
END)
|
|
30832
|
+
FROM t, vraag v;
|
|
30833
|
+
$function$;
|
|
30834
|
+
|
|
30835
|
+
-- Layouts 1.69.11: benodigde stickermaten per dag en printplek.
|
|
30836
|
+
CREATE OR REPLACE FUNCTION public.logistics_sticker_maten_per_dag(p_van date, p_tot date)
|
|
30837
|
+
RETURNS TABLE(dag date, print_plek_id text, breedte_mm numeric, hoogte_mm numeric, aantal integer)
|
|
30838
|
+
LANGUAGE plpgsql STABLE SECURITY DEFINER SET search_path TO 'public'
|
|
30839
|
+
AS $function$
|
|
30840
|
+
BEGIN
|
|
30841
|
+
PERFORM public.logistics_acc_guard('ingelogd', 'logistics_sticker_maten_per_dag');
|
|
30842
|
+
IF p_van IS NULL OR p_tot IS NULL OR p_tot < p_van THEN
|
|
30843
|
+
RAISE EXCEPTION 'Geef een geldige periode op (van t/m tot).' USING ERRCODE = '22023';
|
|
30844
|
+
END IF;
|
|
30845
|
+
RETURN QUERY
|
|
30846
|
+
SELECT coalesce(c.departure_at::date, o.delivery_date) AS dag,
|
|
30847
|
+
s.print_plek_id,
|
|
30848
|
+
s.breedte_mm, -- leeg = maat onbekend (apart geteld)
|
|
30849
|
+
s.hoogte_mm,
|
|
30850
|
+
sum(greatest(coalesce(s.kopieen, 1), 1))::integer AS aantal
|
|
30851
|
+
FROM public.logistics_sticker_prints s
|
|
30852
|
+
LEFT JOIN public.order_carts c ON c.id = s.cart_id
|
|
30853
|
+
LEFT JOIN public.orders o ON o.id = s.order_id
|
|
30854
|
+
WHERE s.status NOT IN ('niet_printen', 'afgemeld')
|
|
30855
|
+
AND coalesce(c.departure_at::date, o.delivery_date) BETWEEN p_van AND p_tot
|
|
30856
|
+
GROUP BY 1, 2, 3, 4
|
|
30857
|
+
ORDER BY 1, 2, 3, 4;
|
|
30858
|
+
END $function$;
|
|
30859
|
+
REVOKE ALL ON FUNCTION public.logistics_sticker_maten_per_dag(date, date) FROM PUBLIC, anon;
|
|
30860
|
+
GRANT EXECUTE ON FUNCTION public.logistics_sticker_maten_per_dag(date, date) TO authenticated, service_role;
|
|
30861
|
+
|
|
30862
|
+
-- C: wat wacht op bevestiging van de belading (per order en verpakking).
|
|
30863
|
+
CREATE OR REPLACE FUNCTION public.logistics_belading_wacht(p_order_id uuid DEFAULT NULL)
|
|
30864
|
+
RETURNS TABLE(order_id uuid, order_ref text, product text, sleutel text, redenen text[], sinds timestamptz)
|
|
30865
|
+
LANGUAGE plpgsql STABLE SECURITY DEFINER SET search_path TO 'public'
|
|
30866
|
+
AS $function$
|
|
30867
|
+
BEGIN
|
|
30868
|
+
PERFORM public.logistics_acc_guard('ingelogd', 'logistics_belading_wacht');
|
|
30869
|
+
RETURN QUERY
|
|
30870
|
+
SELECT DISTINCT ON (x.oid, x.sleutel)
|
|
30871
|
+
x.oid, o.order_number::text, x.product, x.sleutel, x.redenen, x.sinds
|
|
30872
|
+
FROM (
|
|
30873
|
+
SELECT public.logistics_try_uuid(c->>'order_id') AS oid,
|
|
30874
|
+
c->>'product' AS product,
|
|
30875
|
+
c->>'sleutel' AS sleutel,
|
|
30876
|
+
ARRAY(SELECT jsonb_array_elements_text(coalesce(c->'redenen', '[]'::jsonb))) AS redenen,
|
|
30877
|
+
e.created_at AS sinds
|
|
30878
|
+
FROM public.logistics_events e,
|
|
30879
|
+
LATERAL jsonb_path_query(e.payload, '$.**.bevestigen[*]') c
|
|
30880
|
+
WHERE e.event_type = 'plan.blocked'
|
|
30881
|
+
AND e.created_at > now() - interval '14 days'
|
|
30882
|
+
) x
|
|
30883
|
+
JOIN public.orders o ON o.id = x.oid
|
|
30884
|
+
WHERE x.oid IS NOT NULL AND coalesce(x.sleutel, '') <> ''
|
|
30885
|
+
AND (p_order_id IS NULL OR x.oid = p_order_id)
|
|
30886
|
+
AND EXISTS (SELECT 1 FROM public.logistics_plan_queue q
|
|
30887
|
+
WHERE q.order_id = x.oid AND q.status = 'geblokkeerd'
|
|
30888
|
+
AND q.reason_code = 'belading_onwaarschijnlijk')
|
|
30889
|
+
AND NOT EXISTS (SELECT 1 FROM public.logistics_belading_bevestigingen b
|
|
30890
|
+
WHERE b.order_id = x.oid AND b.verpakking_sleutel = x.sleutel)
|
|
30891
|
+
ORDER BY x.oid, x.sleutel, x.sinds DESC;
|
|
30892
|
+
END $function$;
|
|
30893
|
+
REVOKE ALL ON FUNCTION public.logistics_belading_wacht(uuid) FROM PUBLIC, anon;
|
|
30894
|
+
GRANT EXECUTE ON FUNCTION public.logistics_belading_wacht(uuid) TO authenticated, service_role;
|
|
30895
|
+
|
|
30896
|
+
CREATE OR REPLACE FUNCTION public.logistics_check_190()
|
|
30897
|
+
RETURNS TABLE(onderdeel text, ok boolean, toelichting text)
|
|
30898
|
+
LANGUAGE sql STABLE SECURITY DEFINER SET search_path TO 'public'
|
|
30899
|
+
AS $function$
|
|
30900
|
+
SELECT 'functie:' || f, to_regprocedure(f) IS NOT NULL,
|
|
30901
|
+
CASE WHEN to_regprocedure(f) IS NOT NULL THEN 'aanwezig' ELSE 'ontbreekt (schemasync controleren)' END
|
|
30902
|
+
FROM unnest(ARRAY[
|
|
30903
|
+
'public.logistics_sticker_maten_per_dag(date,date)',
|
|
30904
|
+
'public.logistics_belading_wacht(uuid)',
|
|
30905
|
+
'public.logistics_overload_check()',
|
|
30906
|
+
'public.logistics_cart_sticker_state(uuid)']) f;
|
|
30907
|
+
$function$;
|
|
30908
|
+
REVOKE ALL ON FUNCTION public.logistics_check_190() FROM PUBLIC, anon;
|
|
30909
|
+
GRANT EXECUTE ON FUNCTION public.logistics_check_190() TO authenticated, service_role;
|
|
30910
|
+
|
|
30911
|
+
DO $ic190b$
|
|
30912
|
+
DECLARE v_def text;
|
|
30913
|
+
BEGIN
|
|
30914
|
+
v_def := pg_get_functiondef('public.logistics_install_check()'::regprocedure);
|
|
30915
|
+
IF v_def NOT LIKE '%logistics_check_190()%' THEN
|
|
30916
|
+
v_def := regexp_replace(v_def, 'END \$function\$\s*$', E' RETURN QUERY SELECT * FROM public.logistics_check_190();\nEND $function$');
|
|
30917
|
+
EXECUTE v_def;
|
|
30918
|
+
END IF;
|
|
30919
|
+
END $ic190b$;
|
|
30920
|
+
|
|
30921
|
+
-- ===== 1.0.190 deel 3 =====
|
|
30922
|
+
-- Logistiek Engine 1.0.190 deel 3: plausibiliteit-waarschuwingen, lagen, opnieuw combineren,
|
|
30923
|
+
-- correctiefuncties, pickstation-voorbeeld, aanmeldmoment. Herhaalbaar; geen datacorrecties.
|
|
30924
|
+
|
|
30925
|
+
ALTER TABLE public.order_cart_items ADD COLUMN IF NOT EXISTS lagen numeric;
|
|
30926
|
+
COMMENT ON COLUMN public.order_cart_items.layer_fraction IS 'Vulling van één laag (0–1).';
|
|
30927
|
+
COMMENT ON COLUMN public.order_cart_items.lagen IS 'Aantal lagen = fusten ÷ fusten per laag (mag groter zijn dan 1).';
|
|
30928
|
+
|
|
30929
|
+
CREATE TABLE IF NOT EXISTS public.logistics_belading_waarschuwingen (
|
|
30930
|
+
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
30931
|
+
order_id uuid NOT NULL,
|
|
30932
|
+
sleutel text NOT NULL,
|
|
30933
|
+
tekst text NOT NULL,
|
|
30934
|
+
created_at timestamptz NOT NULL DEFAULT now(),
|
|
30935
|
+
updated_at timestamptz NOT NULL DEFAULT now(),
|
|
30936
|
+
UNIQUE (order_id, sleutel)
|
|
30937
|
+
);
|
|
30938
|
+
GRANT SELECT ON public.logistics_belading_waarschuwingen TO authenticated;
|
|
30939
|
+
GRANT ALL ON public.logistics_belading_waarschuwingen TO service_role;
|
|
30940
|
+
ALTER TABLE public.logistics_belading_waarschuwingen ENABLE ROW LEVEL SECURITY;
|
|
30941
|
+
DROP POLICY IF EXISTS "Ingelogd mag beladingwaarschuwingen lezen" ON public.logistics_belading_waarschuwingen;
|
|
30942
|
+
CREATE POLICY "Ingelogd mag beladingwaarschuwingen lezen" ON public.logistics_belading_waarschuwingen
|
|
30943
|
+
FOR SELECT TO authenticated USING (true);
|
|
30944
|
+
|
|
30945
|
+
CREATE OR REPLACE FUNCTION public.logistics_belading_waarschuwingen_zet(p_order_ids uuid[], p_items jsonb)
|
|
30946
|
+
RETURNS integer LANGUAGE plpgsql SECURITY DEFINER SET search_path TO 'public' AS $$
|
|
30947
|
+
DECLARE v_n integer := 0;
|
|
30948
|
+
BEGIN
|
|
30949
|
+
PERFORM public.logistics_acc_guard('server', 'logistics_belading_waarschuwingen_zet');
|
|
30950
|
+
IF p_order_ids IS NULL OR array_length(p_order_ids, 1) IS NULL THEN RETURN 0; END IF;
|
|
30951
|
+
DELETE FROM public.logistics_belading_waarschuwingen w
|
|
30952
|
+
WHERE w.order_id = ANY(p_order_ids)
|
|
30953
|
+
AND NOT EXISTS (SELECT 1 FROM jsonb_array_elements(coalesce(p_items, '[]'::jsonb)) i
|
|
30954
|
+
WHERE (i.value->>'order_id')::uuid = w.order_id AND i.value->>'sleutel' = w.sleutel);
|
|
30955
|
+
INSERT INTO public.logistics_belading_waarschuwingen (order_id, sleutel, tekst)
|
|
30956
|
+
SELECT DISTINCT ON ((i.value->>'order_id')::uuid, i.value->>'sleutel')
|
|
30957
|
+
(i.value->>'order_id')::uuid, i.value->>'sleutel', left(i.value->>'tekst', 500)
|
|
30958
|
+
FROM jsonb_array_elements(coalesce(p_items, '[]'::jsonb)) i
|
|
30959
|
+
WHERE (i.value->>'order_id') IS NOT NULL AND (i.value->>'order_id')::uuid = ANY(p_order_ids)
|
|
30960
|
+
ON CONFLICT (order_id, sleutel) DO UPDATE SET tekst = EXCLUDED.tekst, updated_at = now()
|
|
30961
|
+
WHERE public.logistics_belading_waarschuwingen.tekst IS DISTINCT FROM EXCLUDED.tekst;
|
|
30962
|
+
GET DIAGNOSTICS v_n = ROW_COUNT;
|
|
30963
|
+
RETURN v_n;
|
|
30964
|
+
END $$;
|
|
30965
|
+
REVOKE ALL ON FUNCTION public.logistics_belading_waarschuwingen_zet(uuid[], jsonb) FROM PUBLIC, anon, authenticated;
|
|
30966
|
+
GRANT EXECUTE ON FUNCTION public.logistics_belading_waarschuwingen_zet(uuid[], jsonb) TO service_role;
|
|
30967
|
+
|
|
30968
|
+
CREATE OR REPLACE FUNCTION public.logistics_apply_cart_swap(p_delete_ids uuid[], p_new_carts jsonb)
|
|
30969
|
+
RETURNS jsonb LANGUAGE plpgsql SECURITY DEFINER SET search_path TO 'public'
|
|
30970
|
+
AS $function$
|
|
30971
|
+
DECLARE
|
|
30972
|
+
v_del uuid[] := COALESCE(p_delete_ids, ARRAY[]::uuid[]);
|
|
30973
|
+
v_cart jsonb; v_row jsonb; v_id uuid; v_map jsonb := '[]'::jsonb;
|
|
30974
|
+
v_jobs int := 0; v_carts int := 0; v_items int := 0; v_locked int;
|
|
30975
|
+
BEGIN
|
|
30976
|
+
PERFORM public.logistics_acc_guard('server', 'logistics_apply_cart_swap');
|
|
30977
|
+
IF jsonb_typeof(COALESCE(p_new_carts, '[]'::jsonb)) <> 'array' THEN
|
|
30978
|
+
RAISE EXCEPTION 'Nieuwe karren ontbreken of zijn ongeldig.';
|
|
30979
|
+
END IF;
|
|
30980
|
+
IF EXISTS (
|
|
30981
|
+
SELECT 1 FROM jsonb_array_elements(COALESCE(p_new_carts, '[]'::jsonb)) c,
|
|
30982
|
+
jsonb_array_elements(COALESCE(c.value->'items', '[]'::jsonb)) i
|
|
30983
|
+
WHERE coalesce((i.value->>'plants_per_crate')::numeric, 0) <= 0
|
|
30984
|
+
OR coalesce((i.value->>'crates')::numeric, 0) < 0) THEN
|
|
30985
|
+
RAISE EXCEPTION 'Karwissel geweigerd: een karregel mist planten per fust (belading_onvolledig).';
|
|
30986
|
+
END IF;
|
|
30987
|
+
IF EXISTS (SELECT 1 FROM jsonb_array_elements(COALESCE(p_new_carts, '[]'::jsonb)) c
|
|
30988
|
+
WHERE nullif(btrim(coalesce(c.value->'row'->>'carrier_type', '')), '') IS NULL) THEN
|
|
30989
|
+
RAISE EXCEPTION 'Karwissel geweigerd: een kar heeft geen ladingdrager (Ladingdrager onbekend). Er is niets gewijzigd.';
|
|
30990
|
+
END IF;
|
|
30991
|
+
IF EXISTS (
|
|
30992
|
+
SELECT 1 FROM jsonb_array_elements(COALESCE(p_new_carts, '[]'::jsonb)) c,
|
|
30993
|
+
jsonb_array_elements(COALESCE(c.value->'items', '[]'::jsonb)) i
|
|
30994
|
+
WHERE coalesce((i.value->>'crates_per_layer')::numeric, 0) <= 0
|
|
30995
|
+
OR coalesce((i.value->>'layers_per_cart')::numeric, 0) <= 0) THEN
|
|
30996
|
+
RAISE EXCEPTION 'Karwissel geweigerd: een karregel mist fusten per laag of lagen (belading_onvolledig). Er is niets gewijzigd.';
|
|
30997
|
+
END IF;
|
|
30998
|
+
|
|
30999
|
+
IF array_length(v_del, 1) IS NOT NULL THEN
|
|
31000
|
+
PERFORM 1 FROM public.order_carts WHERE id = ANY(v_del) ORDER BY id FOR UPDATE;
|
|
31001
|
+
SELECT count(*) INTO v_locked FROM public.order_carts WHERE id = ANY(v_del) AND COALESCE(pickup_locked, false);
|
|
31002
|
+
IF v_locked > 0 THEN
|
|
31003
|
+
RAISE EXCEPTION 'Karwissel geweigerd: % kar(ren) zijn al vastgezet (opgehaald). Er is niets gewijzigd.', v_locked;
|
|
31004
|
+
END IF;
|
|
31005
|
+
UPDATE public.print_jobs
|
|
31006
|
+
SET status = 'geannuleerd'::print_job_status,
|
|
31007
|
+
foutmelding = 'Vervallen: kar verwijderd bij herindeling',
|
|
31008
|
+
updated_at = now()
|
|
31009
|
+
WHERE status IN ('wachtend'::print_job_status, 'bezig'::print_job_status)
|
|
31010
|
+
AND (payload->>'cart_id') = ANY(SELECT id::text FROM unnest(v_del) AS id);
|
|
31011
|
+
GET DIAGNOSTICS v_jobs = ROW_COUNT;
|
|
31012
|
+
DELETE FROM public.pakbon_barcode_inhoud WHERE cart_id = ANY(SELECT id::text FROM unnest(v_del) AS id);
|
|
31013
|
+
DELETE FROM public.order_cart_layer_positions WHERE cart_id = ANY(v_del);
|
|
31014
|
+
DELETE FROM public.order_cart_items WHERE cart_id = ANY(v_del);
|
|
31015
|
+
DELETE FROM public.order_carts WHERE id = ANY(v_del);
|
|
31016
|
+
GET DIAGNOSTICS v_carts = ROW_COUNT;
|
|
31017
|
+
END IF;
|
|
31018
|
+
|
|
31019
|
+
FOR v_cart IN SELECT value FROM jsonb_array_elements(COALESCE(p_new_carts, '[]'::jsonb)) LOOP
|
|
31020
|
+
v_row := v_cart->'row';
|
|
31021
|
+
v_id := gen_random_uuid();
|
|
31022
|
+
INSERT INTO public.order_carts (id, order_id, buffer_id, cart_number, total_carts, is_auto, is_complete, source,
|
|
31023
|
+
occupancy_pct, total_layers_used, carrier_type, carrier_type_id, max_layers, weight_kg, algorithm_version, layout_method)
|
|
31024
|
+
SELECT v_id, r.order_id, r.buffer_id, r.cart_number, r.total_carts, COALESCE(r.is_auto, true), COALESCE(r.is_complete, false),
|
|
31025
|
+
COALESCE(r.source, 'auto'), COALESCE(r.occupancy_pct, 0), COALESCE(r.total_layers_used, 0),
|
|
31026
|
+
r.carrier_type, r.carrier_type_id, r.max_layers, r.weight_kg, COALESCE(r.algorithm_version, 1), r.layout_method
|
|
31027
|
+
FROM jsonb_to_record(v_row) AS r(order_id uuid, buffer_id uuid, cart_number integer, total_carts integer, is_auto boolean,
|
|
31028
|
+
is_complete boolean, source text, occupancy_pct numeric, total_layers_used numeric, carrier_type text, carrier_type_id uuid,
|
|
31029
|
+
max_layers integer, weight_kg numeric, algorithm_version integer, layout_method text);
|
|
31030
|
+
|
|
31031
|
+
INSERT INTO public.order_cart_items (cart_id, order_item_id, product_id, product_name, crates, boxes, weight_kg,
|
|
31032
|
+
plants_per_crate, crates_per_layer, layers_per_cart, layer_fraction, lagen, layer_share_pct, layer_index, crates_in_layer, components)
|
|
31033
|
+
SELECT v_id, i.order_item_id, i.product_id, i.product_name, i.crates, i.boxes, i.weight_kg,
|
|
31034
|
+
i.plants_per_crate, i.crates_per_layer, i.layers_per_cart,
|
|
31035
|
+
least(1, greatest(0, COALESCE(i.layer_fraction, 0))),
|
|
31036
|
+
COALESCE(i.lagen, round(i.crates::numeric / nullif(i.crates_per_layer, 0), 3)),
|
|
31037
|
+
COALESCE(i.layer_share_pct, 0), i.layer_index, i.crates_in_layer,
|
|
31038
|
+
CASE WHEN jsonb_typeof(i.components) = 'array' THEN i.components END
|
|
31039
|
+
FROM jsonb_to_recordset(COALESCE(v_cart->'items', '[]'::jsonb)) AS i(order_item_id uuid, product_id uuid, product_name text,
|
|
31040
|
+
crates integer, boxes integer, weight_kg numeric, plants_per_crate integer, crates_per_layer integer, layers_per_cart integer,
|
|
31041
|
+
layer_fraction numeric, lagen numeric, layer_share_pct numeric, layer_index integer, crates_in_layer integer, components jsonb);
|
|
31042
|
+
v_items := v_items + jsonb_array_length(COALESCE(v_cart->'items', '[]'::jsonb));
|
|
31043
|
+
v_map := v_map || jsonb_build_object('planIndex', (v_cart->>'planIndex')::integer, 'id', v_id);
|
|
31044
|
+
END LOOP;
|
|
31045
|
+
|
|
31046
|
+
RETURN jsonb_build_object('karren', v_map, 'verwijderd', v_carts, 'printopdrachtenGeannuleerd', v_jobs, 'regels', v_items);
|
|
31047
|
+
END;
|
|
31048
|
+
$function$;
|
|
31049
|
+
|
|
31050
|
+
CREATE OR REPLACE FUNCTION public.logistics_rebuffer_aan()
|
|
31051
|
+
RETURNS boolean LANGUAGE plpgsql STABLE SECURITY DEFINER SET search_path TO 'public' AS $$
|
|
31052
|
+
DECLARE v_settings jsonb;
|
|
31053
|
+
BEGIN
|
|
31054
|
+
PERFORM public.logistics_acc_guard('ingelogd', 'logistics_rebuffer_aan');
|
|
31055
|
+
SELECT settings INTO v_settings FROM public.logistics_rule_configs
|
|
31056
|
+
WHERE is_active ORDER BY updated_at DESC LIMIT 1;
|
|
31057
|
+
RETURN public.logistics_rule_bool(coalesce(v_settings, '{}'::jsonb), 'order_verwerking', 'rebuffer_on_change', true);
|
|
31058
|
+
END $$;
|
|
31059
|
+
REVOKE ALL ON FUNCTION public.logistics_rebuffer_aan() FROM PUBLIC, anon;
|
|
31060
|
+
GRANT EXECUTE ON FUNCTION public.logistics_rebuffer_aan() TO authenticated, service_role;
|
|
31061
|
+
|
|
31062
|
+
CREATE OR REPLACE FUNCTION public.logistics_ensure_rebuffer_setting()
|
|
31063
|
+
RETURNS integer LANGUAGE plpgsql SECURITY DEFINER SET search_path TO 'public' AS $$
|
|
31064
|
+
DECLARE v_n integer := 0;
|
|
31065
|
+
BEGIN
|
|
31066
|
+
PERFORM public.logistics_acc_guard('ingelogd', 'logistics_ensure_rebuffer_setting');
|
|
31067
|
+
UPDATE public.logistics_rule_configs c
|
|
31068
|
+
SET settings = jsonb_set(coalesce(c.settings, '{}'::jsonb), '{order_verwerking}',
|
|
31069
|
+
coalesce(c.settings->'order_verwerking', '{}'::jsonb)
|
|
31070
|
+
|| jsonb_build_object('fields', coalesce(c.settings->'order_verwerking'->'fields', '{}'::jsonb)
|
|
31071
|
+
|| jsonb_build_object('rebuffer_on_change', true)), true)
|
|
31072
|
+
WHERE c.id IS NOT NULL
|
|
31073
|
+
AND NOT coalesce(c.settings->'order_verwerking'->'fields' ? 'rebuffer_on_change', false)
|
|
31074
|
+
AND NOT coalesce(c.settings->'order_verwerking' ? 'rebuffer_on_change', false);
|
|
31075
|
+
GET DIAGNOSTICS v_n = ROW_COUNT;
|
|
31076
|
+
RETURN v_n;
|
|
31077
|
+
END $$;
|
|
31078
|
+
REVOKE ALL ON FUNCTION public.logistics_ensure_rebuffer_setting() FROM PUBLIC, anon;
|
|
31079
|
+
GRANT EXECUTE ON FUNCTION public.logistics_ensure_rebuffer_setting() TO authenticated, service_role;
|
|
31080
|
+
|
|
31081
|
+
CREATE OR REPLACE FUNCTION public.logistics_rebuffer_orders(p_days integer DEFAULT 30)
|
|
31082
|
+
RETURNS jsonb LANGUAGE plpgsql SECURITY DEFINER SET search_path TO 'public'
|
|
31083
|
+
AS $function$
|
|
31084
|
+
DECLARE
|
|
31085
|
+
v_mode text; v_checked int := 0; v_updated int := 0; v_would int := 0; v_vast int := 0;
|
|
31086
|
+
v_start timestamptz := clock_timestamp(); v_ms int; r record; v_after uuid; v_report jsonb;
|
|
31087
|
+
BEGIN
|
|
31088
|
+
PERFORM public.logistics_acc_guard('ingelogd', 'logistics_rebuffer_orders');
|
|
31089
|
+
v_mode := CASE WHEN public.logistics_rebuffer_aan() THEN 'apply' ELSE 'report' END;
|
|
31090
|
+
BEGIN
|
|
31091
|
+
CREATE TEMP TABLE _rb_sim ON COMMIT DROP AS SELECT * FROM public.logistics_rebuffer_simulate_open();
|
|
31092
|
+
EXCEPTION WHEN OTHERS THEN
|
|
31093
|
+
UPDATE public.logistics_rebuffer_state SET pending = false, last_run_at = now(),
|
|
31094
|
+
last_error = left(SQLERRM, 300), updated_at = now() WHERE id;
|
|
31095
|
+
RAISE;
|
|
31096
|
+
END;
|
|
31097
|
+
SELECT count(*), count(*) FILTER (WHERE changed),
|
|
31098
|
+
coalesce(jsonb_agg(jsonb_build_object('orderId', s.id, 'orderNumber', s.order_number, 'deliveryDate', s.delivery_date,
|
|
31099
|
+
'huidig', s.cur_label, 'nieuw', s.new_label, 'wijzigt', s.changed, 'reden', s.reason) ORDER BY s.delivery_date, s.order_number), '[]'::jsonb)
|
|
31100
|
+
INTO v_checked, v_would, v_report FROM _rb_sim s;
|
|
31101
|
+
IF v_mode = 'apply' THEN
|
|
31102
|
+
FOR r IN SELECT s.id, s.buffer_id, s.order_number FROM _rb_sim s WHERE s.changed LOOP
|
|
31103
|
+
IF coalesce(public.logistics_order_released(r.order_number), false)
|
|
31104
|
+
OR EXISTS (SELECT 1 FROM public.pick_queue q
|
|
31105
|
+
WHERE q.order_number = r.order_number AND q.cancelled_at IS NULL AND q.completed_at IS NULL)
|
|
31106
|
+
OR coalesce(public.logistics_buffer_frozen_order(r.id), false) THEN
|
|
31107
|
+
v_vast := v_vast + 1;
|
|
31108
|
+
CONTINUE;
|
|
31109
|
+
END IF;
|
|
31110
|
+
v_after := public.logistics_apply_buffer_decision(r.id);
|
|
31111
|
+
IF v_after IS DISTINCT FROM r.buffer_id THEN v_updated := v_updated + 1; END IF;
|
|
31112
|
+
END LOOP;
|
|
31113
|
+
END IF;
|
|
31114
|
+
DROP TABLE IF EXISTS _rb_sim;
|
|
31115
|
+
v_ms := greatest(0, (extract(epoch from (clock_timestamp() - v_start)) * 1000)::int);
|
|
31116
|
+
UPDATE public.logistics_rebuffer_state
|
|
31117
|
+
SET pending = false, last_run_at = now(), last_checked = v_checked, last_updated = v_updated,
|
|
31118
|
+
last_would_change = v_would, last_mode = v_mode, last_duration_ms = v_ms,
|
|
31119
|
+
last_error = NULL, last_report = v_report, updated_at = now()
|
|
31120
|
+
WHERE id;
|
|
31121
|
+
RETURN jsonb_build_object('checked', v_checked, 'updated', v_updated, 'durationMs', v_ms,
|
|
31122
|
+
'mode', v_mode, 'wouldChange', v_would, 'vastgezet', v_vast,
|
|
31123
|
+
'scope', 'leverdag_vanaf_vandaag_open');
|
|
31124
|
+
END $function$;
|
|
31125
|
+
|
|
31126
|
+
CREATE OR REPLACE FUNCTION public.trg_logistics_rule_config_rebuffer()
|
|
31127
|
+
RETURNS trigger LANGUAGE plpgsql SECURITY DEFINER SET search_path TO 'public'
|
|
31128
|
+
AS $function$
|
|
31129
|
+
BEGIN
|
|
31130
|
+
UPDATE public.logistics_rebuffer_state
|
|
31131
|
+
SET pending = true, requested_at = now(), updated_at = now()
|
|
31132
|
+
WHERE id;
|
|
31133
|
+
BEGIN
|
|
31134
|
+
PERFORM public.logistics_rebuffer_orders(30);
|
|
31135
|
+
EXCEPTION WHEN others THEN
|
|
31136
|
+
UPDATE public.logistics_rebuffer_state
|
|
31137
|
+
SET last_error = left('Opnieuw combineren mislukt: ' || SQLERRM, 300), updated_at = now()
|
|
31138
|
+
WHERE id;
|
|
31139
|
+
RAISE WARNING 'logistics_rebuffer_orders mislukt: %', SQLERRM;
|
|
31140
|
+
END;
|
|
31141
|
+
RETURN NEW;
|
|
31142
|
+
END;
|
|
31143
|
+
$function$;
|
|
31144
|
+
|
|
31145
|
+
CREATE TABLE IF NOT EXISTS public.logistics_correctie_log (
|
|
31146
|
+
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
31147
|
+
correctie text NOT NULL,
|
|
31148
|
+
object_id uuid,
|
|
31149
|
+
omschrijving text,
|
|
31150
|
+
oud jsonb,
|
|
31151
|
+
nieuw jsonb,
|
|
31152
|
+
created_at timestamptz NOT NULL DEFAULT now()
|
|
31153
|
+
);
|
|
31154
|
+
GRANT SELECT ON public.logistics_correctie_log TO authenticated;
|
|
31155
|
+
GRANT ALL ON public.logistics_correctie_log TO service_role;
|
|
31156
|
+
ALTER TABLE public.logistics_correctie_log ENABLE ROW LEVEL SECURITY;
|
|
31157
|
+
DROP POLICY IF EXISTS "Beheer mag correctielog lezen" ON public.logistics_correctie_log;
|
|
31158
|
+
CREATE POLICY "Beheer mag correctielog lezen" ON public.logistics_correctie_log
|
|
31159
|
+
FOR SELECT TO authenticated USING (public.logistics_is_beheer());
|
|
31160
|
+
|
|
31161
|
+
CREATE OR REPLACE FUNCTION public.logistics_correctie_buffers_geannuleerd(p_uitvoeren boolean DEFAULT false)
|
|
31162
|
+
RETURNS TABLE(order_id uuid, order_number text, reden text, buffer_id uuid, actieve_karren integer,
|
|
31163
|
+
andere_open_orders text, uitgevoerd boolean)
|
|
31164
|
+
LANGUAGE plpgsql SECURITY DEFINER SET search_path TO 'public' AS $$
|
|
31165
|
+
#variable_conflict use_column
|
|
31166
|
+
DECLARE r record;
|
|
31167
|
+
BEGIN
|
|
31168
|
+
PERFORM public.logistics_acc_guard('server', 'logistics_correctie_buffers_geannuleerd');
|
|
31169
|
+
FOR r IN
|
|
31170
|
+
SELECT o.id, o.order_number, o.buffer_id,
|
|
31171
|
+
CASE WHEN o.deleted_at IS NOT NULL THEN 'verwijderd' ELSE 'geannuleerd' END AS reden,
|
|
31172
|
+
(SELECT count(*)::int FROM public.order_carts c WHERE c.order_id = o.id AND c.archived_at IS NULL) AS karren,
|
|
31173
|
+
(SELECT string_agg(x.order_number, ' ' ORDER BY x.order_number) FROM public.orders x
|
|
31174
|
+
WHERE x.buffer_id = o.buffer_id AND x.id <> o.id AND x.deleted_at IS NULL AND x.cancelled_at IS NULL) AS anderen
|
|
31175
|
+
FROM public.orders o
|
|
31176
|
+
WHERE o.buffer_id IS NOT NULL AND (o.deleted_at IS NOT NULL OR o.cancelled_at IS NOT NULL)
|
|
31177
|
+
ORDER BY o.order_number
|
|
31178
|
+
LOOP
|
|
31179
|
+
order_id := r.id; order_number := r.order_number; reden := r.reden; buffer_id := r.buffer_id;
|
|
31180
|
+
actieve_karren := r.karren; andere_open_orders := r.anderen; uitgevoerd := false;
|
|
31181
|
+
IF p_uitvoeren AND r.karren = 0 THEN
|
|
31182
|
+
PERFORM set_config('logistics.buffer_writer', 'engine', true);
|
|
31183
|
+
UPDATE public.orders SET buffer_id = NULL WHERE id = r.id AND buffer_id = r.buffer_id;
|
|
31184
|
+
INSERT INTO public.logistics_correctie_log (correctie, object_id, omschrijving, oud, nieuw)
|
|
31185
|
+
VALUES ('buffers_geannuleerd', r.id, r.order_number || ' (' || r.reden || ') losgemaakt uit buffer',
|
|
31186
|
+
jsonb_build_object('buffer_id', r.buffer_id), jsonb_build_object('buffer_id', NULL));
|
|
31187
|
+
IF r.anderen IS NOT NULL THEN
|
|
31188
|
+
BEGIN
|
|
31189
|
+
PERFORM public.logistics_plan_enqueue(p_order_id => x.id, p_reason => 'correctie_buffers_geannuleerd')
|
|
31190
|
+
FROM public.orders x WHERE x.buffer_id = r.buffer_id AND x.deleted_at IS NULL AND x.cancelled_at IS NULL;
|
|
31191
|
+
EXCEPTION WHEN undefined_function THEN NULL;
|
|
31192
|
+
END;
|
|
31193
|
+
END IF;
|
|
31194
|
+
uitgevoerd := true;
|
|
31195
|
+
END IF;
|
|
31196
|
+
RETURN NEXT;
|
|
31197
|
+
END LOOP;
|
|
31198
|
+
END $$;
|
|
31199
|
+
REVOKE ALL ON FUNCTION public.logistics_correctie_buffers_geannuleerd(boolean) FROM PUBLIC, anon, authenticated;
|
|
31200
|
+
GRANT EXECUTE ON FUNCTION public.logistics_correctie_buffers_geannuleerd(boolean) TO service_role;
|
|
31201
|
+
|
|
31202
|
+
CREATE OR REPLACE FUNCTION public.logistics_correctie_laagaandeel(p_uitvoeren boolean DEFAULT false)
|
|
31203
|
+
RETURNS TABLE(item_id uuid, cart_id uuid, orders text, fusten integer, fusten_per_laag integer,
|
|
31204
|
+
huidig numeric, nieuw_layer_fraction numeric, nieuw_lagen numeric, uitgevoerd boolean)
|
|
31205
|
+
LANGUAGE plpgsql SECURITY DEFINER SET search_path TO 'public' AS $$
|
|
31206
|
+
DECLARE r record;
|
|
31207
|
+
BEGIN
|
|
31208
|
+
PERFORM public.logistics_acc_guard('server', 'logistics_correctie_laagaandeel');
|
|
31209
|
+
FOR r IN
|
|
31210
|
+
SELECT ci.id, ci.cart_id, ci.crates, ci.crates_per_layer, ci.layer_fraction, ci.lagen,
|
|
31211
|
+
least(1, round(ci.crates::numeric / ci.crates_per_layer, 3)) AS nf,
|
|
31212
|
+
round(ci.crates::numeric / ci.crates_per_layer, 3) AS nl,
|
|
31213
|
+
(SELECT string_agg(DISTINCT o.order_number, ' ') FROM public.order_cart_items x
|
|
31214
|
+
JOIN public.order_items oi ON oi.id = x.order_item_id JOIN public.orders o ON o.id = oi.order_id
|
|
31215
|
+
WHERE x.cart_id = ci.cart_id) AS ords
|
|
31216
|
+
FROM public.order_cart_items ci
|
|
31217
|
+
JOIN public.order_carts c ON c.id = ci.cart_id
|
|
31218
|
+
WHERE c.archived_at IS NULL AND NOT coalesce(c.pickup_locked, false)
|
|
31219
|
+
AND ci.crates_per_layer > 0 AND ci.layer_index IS NULL
|
|
31220
|
+
AND (ci.layer_fraction IS DISTINCT FROM least(1, round(ci.crates::numeric / ci.crates_per_layer, 3))
|
|
31221
|
+
OR ci.lagen IS DISTINCT FROM round(ci.crates::numeric / ci.crates_per_layer, 3))
|
|
31222
|
+
AND NOT EXISTS (SELECT 1 FROM public.print_jobs j WHERE j.status = 'voltooid' AND j.payload->>'cart_id' = c.id::text)
|
|
31223
|
+
AND NOT EXISTS (SELECT 1 FROM public.logistics_control_scan s WHERE s.cart_id = c.id AND s.status <> 'geannuleerd')
|
|
31224
|
+
ORDER BY ci.cart_id, ci.id
|
|
31225
|
+
LOOP
|
|
31226
|
+
item_id := r.id; cart_id := r.cart_id; orders := r.ords; fusten := r.crates; fusten_per_laag := r.crates_per_layer;
|
|
31227
|
+
huidig := r.layer_fraction; nieuw_layer_fraction := r.nf; nieuw_lagen := r.nl; uitgevoerd := false;
|
|
31228
|
+
IF p_uitvoeren THEN
|
|
31229
|
+
UPDATE public.order_cart_items SET layer_fraction = r.nf, lagen = r.nl WHERE id = r.id;
|
|
31230
|
+
INSERT INTO public.logistics_correctie_log (correctie, object_id, omschrijving, oud, nieuw)
|
|
31231
|
+
VALUES ('laagaandeel', r.id, 'karregel van kar ' || r.cart_id,
|
|
31232
|
+
jsonb_build_object('layer_fraction', r.layer_fraction, 'lagen', r.lagen),
|
|
31233
|
+
jsonb_build_object('layer_fraction', r.nf, 'lagen', r.nl));
|
|
31234
|
+
uitgevoerd := true;
|
|
31235
|
+
END IF;
|
|
31236
|
+
RETURN NEXT;
|
|
31237
|
+
END LOOP;
|
|
31238
|
+
END $$;
|
|
31239
|
+
REVOKE ALL ON FUNCTION public.logistics_correctie_laagaandeel(boolean) FROM PUBLIC, anon, authenticated;
|
|
31240
|
+
GRANT EXECUTE ON FUNCTION public.logistics_correctie_laagaandeel(boolean) TO service_role;
|
|
31241
|
+
|
|
31242
|
+
CREATE OR REPLACE FUNCTION public.logistics_pick_voorbeeld_layout(p_queue_ids uuid[])
|
|
31243
|
+
RETURNS TABLE(queue_id uuid, documentsoort text, layout_id uuid, layout_naam text, breedte_mm numeric,
|
|
31244
|
+
hoogte_mm numeric, ok boolean, reden text)
|
|
31245
|
+
LANGUAGE plpgsql STABLE SECURITY DEFINER SET search_path TO 'public' AS $$
|
|
31246
|
+
DECLARE r record; v_res jsonb;
|
|
31247
|
+
BEGIN
|
|
31248
|
+
PERFORM public.logistics_acc_guard('ingelogd', 'logistics_pick_voorbeeld_layout');
|
|
31249
|
+
FOR r IN
|
|
31250
|
+
SELECT q.id, t.code, (SELECT o.id FROM public.orders o WHERE o.order_number = q.order_number AND o.deleted_at IS NULL LIMIT 1) AS oid
|
|
31251
|
+
FROM public.pick_queue q LEFT JOIN public.pick_document_types t ON t.id = q.document_type_id
|
|
31252
|
+
WHERE q.id = ANY(coalesce(p_queue_ids, '{}'))
|
|
31253
|
+
LOOP
|
|
31254
|
+
queue_id := r.id; layout_id := NULL; layout_naam := NULL; breedte_mm := NULL; hoogte_mm := NULL;
|
|
31255
|
+
IF r.oid IS NULL OR r.code IS NULL THEN
|
|
31256
|
+
documentsoort := r.code; ok := false;
|
|
31257
|
+
reden := CASE WHEN r.code IS NULL THEN 'Documentsoort van deze regel is onbekend.' ELSE 'Order van deze regel is niet gevonden.' END;
|
|
31258
|
+
ELSE
|
|
31259
|
+
v_res := public.logistics_document_for_order(p_order_id => r.oid, p_documenttype => r.code);
|
|
31260
|
+
documentsoort := coalesce(v_res->>'documenttype', r.code);
|
|
31261
|
+
ok := coalesce((v_res->>'ok')::boolean, false);
|
|
31262
|
+
reden := v_res->>'reden';
|
|
31263
|
+
layout_id := nullif(v_res->>'layout_id', '')::uuid;
|
|
31264
|
+
breedte_mm := nullif(v_res->>'breedte_mm', '')::numeric;
|
|
31265
|
+
hoogte_mm := nullif(v_res->>'hoogte_mm', '')::numeric;
|
|
31266
|
+
IF layout_id IS NOT NULL THEN
|
|
31267
|
+
SELECT l.naam INTO layout_naam FROM public.document_layouts l WHERE l.id = layout_id;
|
|
31268
|
+
END IF;
|
|
31269
|
+
END IF;
|
|
31270
|
+
RETURN NEXT;
|
|
31271
|
+
END LOOP;
|
|
31272
|
+
END $$;
|
|
31273
|
+
REVOKE ALL ON FUNCTION public.logistics_pick_voorbeeld_layout(uuid[]) FROM PUBLIC, anon;
|
|
31274
|
+
GRANT EXECUTE ON FUNCTION public.logistics_pick_voorbeeld_layout(uuid[]) TO authenticated, service_role;
|
|
31275
|
+
|
|
31276
|
+
CREATE OR REPLACE FUNCTION public.logistics_aanmeldmoment(p_order_ids uuid[])
|
|
31277
|
+
RETURNS TABLE(order_id uuid, aanmeld_at timestamptz, bron text, tekst text)
|
|
31278
|
+
LANGUAGE plpgsql STABLE SECURITY DEFINER SET search_path TO 'public' AS $$
|
|
31279
|
+
DECLARE v_tz text; r record; v_fout text;
|
|
31280
|
+
BEGIN
|
|
31281
|
+
PERFORM public.logistics_acc_guard('ingelogd', 'logistics_aanmeldmoment');
|
|
31282
|
+
BEGIN v_tz := public.logistics_opening_timezone(); EXCEPTION WHEN others THEN v_tz := NULL; END;
|
|
31283
|
+
IF to_regprocedure('public.afh_signup_moment_for_orders(uuid[])') IS NOT NULL THEN
|
|
31284
|
+
BEGIN
|
|
31285
|
+
FOR r IN EXECUTE 'SELECT order_id, aanmeld_at, bron FROM public.afh_signup_moment_for_orders(p_order_ids => $1)'
|
|
31286
|
+
USING p_order_ids
|
|
31287
|
+
LOOP
|
|
31288
|
+
order_id := r.order_id; aanmeld_at := r.aanmeld_at; bron := r.bron;
|
|
31289
|
+
tekst := CASE WHEN r.aanmeld_at IS NOT NULL AND v_tz IS NOT NULL AND r.bron IN ('verstuurd', 'gepland')
|
|
31290
|
+
THEN 'Verzendbrief komt vrij na de transportaanmelding om ' || to_char(r.aanmeld_at AT TIME ZONE v_tz, 'HH24:MI')
|
|
31291
|
+
ELSE 'Tijdstip van de transportaanmelding is nog niet bekend' END;
|
|
31292
|
+
RETURN NEXT;
|
|
31293
|
+
END LOOP;
|
|
31294
|
+
RETURN;
|
|
31295
|
+
EXCEPTION WHEN insufficient_privilege THEN v_fout := '42501';
|
|
31296
|
+
WHEN others THEN v_fout := SQLSTATE;
|
|
31297
|
+
END;
|
|
31298
|
+
END IF;
|
|
31299
|
+
FOR r IN SELECT unnest(coalesce(p_order_ids, '{}')) AS id LOOP
|
|
31300
|
+
order_id := r.id; aanmeld_at := NULL; bron := coalesce('fout_' || v_fout, 'onbekend');
|
|
31301
|
+
tekst := 'Tijdstip van de transportaanmelding is nog niet bekend';
|
|
31302
|
+
RETURN NEXT;
|
|
31303
|
+
END LOOP;
|
|
31304
|
+
END $$;
|
|
31305
|
+
REVOKE ALL ON FUNCTION public.logistics_aanmeldmoment(uuid[]) FROM PUBLIC, anon;
|
|
31306
|
+
GRANT EXECUTE ON FUNCTION public.logistics_aanmeldmoment(uuid[]) TO authenticated, service_role;
|
|
31307
|
+
|
|
31308
|
+
CREATE OR REPLACE FUNCTION public.logistics_check_190()
|
|
31309
|
+
RETURNS TABLE(onderdeel text, ok boolean, toelichting text)
|
|
31310
|
+
LANGUAGE sql STABLE SECURITY DEFINER SET search_path TO 'public'
|
|
31311
|
+
AS $function$
|
|
31312
|
+
SELECT 'functie:' || f, to_regprocedure(f) IS NOT NULL,
|
|
31313
|
+
CASE WHEN to_regprocedure(f) IS NOT NULL THEN 'aanwezig' ELSE 'ontbreekt (schemasync controleren)' END
|
|
31314
|
+
FROM unnest(ARRAY[
|
|
31315
|
+
'public.logistics_sticker_maten_per_dag(date,date)',
|
|
31316
|
+
'public.logistics_belading_wacht(uuid)',
|
|
31317
|
+
'public.logistics_overload_check()',
|
|
31318
|
+
'public.logistics_cart_sticker_state(uuid)',
|
|
31319
|
+
'public.logistics_belading_waarschuwingen_zet(uuid[],jsonb)',
|
|
31320
|
+
'public.logistics_rebuffer_aan()',
|
|
31321
|
+
'public.logistics_ensure_rebuffer_setting()',
|
|
31322
|
+
'public.logistics_correctie_buffers_geannuleerd(boolean)',
|
|
31323
|
+
'public.logistics_correctie_laagaandeel(boolean)',
|
|
31324
|
+
'public.logistics_pick_voorbeeld_layout(uuid[])',
|
|
31325
|
+
'public.logistics_aanmeldmoment(uuid[])']) f
|
|
31326
|
+
UNION ALL
|
|
31327
|
+
SELECT 'kolom:order_cart_items.lagen',
|
|
31328
|
+
EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = 'public' AND table_name = 'order_cart_items' AND column_name = 'lagen'),
|
|
31329
|
+
'nieuw veld lagen'
|
|
31330
|
+
UNION ALL
|
|
31331
|
+
SELECT 'tabel:' || t, to_regclass('public.' || t) IS NOT NULL, 'nieuw in 1.0.190'
|
|
31332
|
+
FROM unnest(ARRAY['logistics_belading_waarschuwingen', 'logistics_correctie_log']) t
|
|
31333
|
+
UNION ALL
|
|
31334
|
+
SELECT 'afhandeling:afh_signup_moment_for_orders(uuid[])',
|
|
31335
|
+
to_regprocedure('public.afh_signup_moment_for_orders(uuid[])') IS NOT NULL,
|
|
31336
|
+
'optioneel: ontbreekt = tekst "nog niet bekend", niets geblokkeerd';
|
|
31337
|
+
$function$;CREATE OR REPLACE FUNCTION public.logistics_shipping_note_status(p_slip_job_id uuid DEFAULT NULL::uuid, p_cart_id uuid DEFAULT NULL::uuid)
|
|
31338
|
+
RETURNS jsonb
|
|
31339
|
+
LANGUAGE plpgsql
|
|
31340
|
+
STABLE SECURITY DEFINER
|
|
31341
|
+
SET search_path TO 'public'
|
|
31342
|
+
AS $function$
|
|
31343
|
+
DECLARE
|
|
31344
|
+
v_note public.logistics_slip_shipping_note;
|
|
31345
|
+
v_slip public.print_jobs;
|
|
31346
|
+
v_job public.print_jobs;
|
|
31347
|
+
v_state text; v_message text; v_cart uuid;
|
|
31348
|
+
v_vrijgave boolean := false; v_scan_ok boolean;
|
|
31349
|
+
v_aanmeld_at timestamptz; v_aanmeld_tekst text; v_order uuid;
|
|
31350
|
+
BEGIN
|
|
31351
|
+
IF p_slip_job_id IS NULL AND p_cart_id IS NULL THEN
|
|
31352
|
+
RETURN jsonb_build_object('status','mislukt','message',
|
|
31353
|
+
'De pakbon of kar ontbreekt; de verzendbriefstatus kan niet worden opgehaald.','attempts',0,'nextAttemptAt',NULL);
|
|
31354
|
+
END IF;
|
|
31355
|
+
IF p_slip_job_id IS NOT NULL THEN
|
|
31356
|
+
SELECT * INTO v_slip FROM public.print_jobs WHERE id = p_slip_job_id;
|
|
31357
|
+
ELSE
|
|
31358
|
+
SELECT * INTO v_slip FROM public.print_jobs p
|
|
31359
|
+
WHERE lower(coalesce(p.payload->>'document_type_code', p.payload->>'documentType', p.documenttype::text, '')) LIKE '%pakbon%'
|
|
31360
|
+
AND nullif(coalesce(p.payload->>'cart_id', p.payload->'cart'->>'cartId'), '') = p_cart_id::text
|
|
31361
|
+
ORDER BY p.created_at DESC LIMIT 1;
|
|
31362
|
+
END IF;
|
|
31363
|
+
IF v_slip.id IS NULL THEN
|
|
31364
|
+
RETURN jsonb_build_object('status','mislukt','message','De bijbehorende pakbon is niet gevonden.',
|
|
31365
|
+
'attempts',0,'nextAttemptAt',NULL,'cartId',p_cart_id);
|
|
31366
|
+
END IF;
|
|
31367
|
+
BEGIN
|
|
31368
|
+
v_cart := coalesce(nullif(v_slip.payload->>'cart_id','')::uuid, nullif(v_slip.payload->'cart'->>'cartId','')::uuid, p_cart_id);
|
|
31369
|
+
EXCEPTION WHEN invalid_text_representation THEN v_cart := p_cart_id;
|
|
31370
|
+
END;
|
|
31371
|
+
SELECT * INTO v_note FROM public.logistics_slip_shipping_note s
|
|
31372
|
+
WHERE s.slip_job_id = v_slip.id ORDER BY s.created_at DESC LIMIT 1;
|
|
31373
|
+
|
|
31374
|
+
IF v_note.id IS NULL THEN
|
|
31375
|
+
SELECT * INTO v_job FROM public.print_jobs p
|
|
31376
|
+
WHERE p.idempotentie_sleutel = 'verzendbrief-bij-pakbon:' || v_slip.id::text
|
|
31377
|
+
OR (p.payload->>'packing_slip_job_id' = v_slip.id::text
|
|
31378
|
+
AND lower(coalesce(p.payload->>'document_type_code', p.documenttype::text, '')) ~ '(verzend|aflever)')
|
|
31379
|
+
ORDER BY p.created_at DESC LIMIT 1;
|
|
31380
|
+
IF v_job.id IS NULL THEN
|
|
31381
|
+
IF v_slip.created_at < now() - interval '15 minutes' THEN
|
|
31382
|
+
v_state := 'mislukt';
|
|
31383
|
+
v_message := 'Er is geen verzendbrief bij deze pakbon aangemaakt. Controleer de printwachtrij of print de pakbon opnieuw.';
|
|
31384
|
+
ELSE
|
|
31385
|
+
v_state := 'klaar_voor_print'; v_message := 'Verzendbrief wordt klaargezet.';
|
|
31386
|
+
END IF;
|
|
31387
|
+
ELSIF v_job.status = 'voltooid' THEN
|
|
31388
|
+
v_state := 'geprint'; v_message := 'Pakbon en verzendbrief zijn naar de printer gestuurd.';
|
|
31389
|
+
ELSIF v_job.status IN ('wachtend','bezig') THEN
|
|
31390
|
+
v_state := 'klaar_voor_print'; v_message := 'Verzendbrief wordt klaargezet.';
|
|
31391
|
+
ELSE
|
|
31392
|
+
v_state := 'mislukt'; v_message := 'De verzendbrief kon niet worden geprint. Controleer de printwachtrij.';
|
|
31393
|
+
END IF;
|
|
31394
|
+
RETURN jsonb_build_object('status',v_state,'message',v_message,
|
|
31395
|
+
'attempts',coalesce(v_job.pogingen,0),'nextAttemptAt',v_job.volgende_poging,
|
|
31396
|
+
'slipJobId',v_slip.id,'shippingNoteJobId',v_job.id,'cartId',v_cart,
|
|
31397
|
+
'orderNumber',coalesce(v_slip.payload->>'order_number', v_slip.payload->'data'->>'orderNumber'));
|
|
31398
|
+
END IF;
|
|
31399
|
+
|
|
31400
|
+
IF v_note.print_job_id IS NOT NULL THEN
|
|
31401
|
+
SELECT * INTO v_job FROM public.print_jobs WHERE id = v_note.print_job_id;
|
|
31402
|
+
END IF;
|
|
31403
|
+
v_scan_ok := EXISTS (SELECT 1 FROM public.logistics_control_scan cs
|
|
31404
|
+
WHERE cs.cart_id = coalesce(v_note.cart_id, v_cart) AND cs.approved_at IS NOT NULL);
|
|
31405
|
+
|
|
31406
|
+
IF NOT v_scan_ok OR v_note.status IN ('wacht_op_vrijgave','wachtend') THEN
|
|
31407
|
+
BEGIN
|
|
31408
|
+
v_order := v_note.order_id;
|
|
31409
|
+
IF v_order IS NOT NULL THEN
|
|
31410
|
+
SELECT a.aanmeld_at, a.tekst INTO v_aanmeld_at, v_aanmeld_tekst
|
|
31411
|
+
FROM public.logistics_aanmeldmoment(p_order_ids => ARRAY[v_order]) a LIMIT 1;
|
|
31412
|
+
END IF;
|
|
31413
|
+
EXCEPTION WHEN others THEN v_aanmeld_at := NULL; v_aanmeld_tekst := NULL;
|
|
31414
|
+
END;
|
|
31415
|
+
END IF;
|
|
31416
|
+
|
|
31417
|
+
IF v_note.status = 'mislukt' THEN
|
|
31418
|
+
v_state := 'mislukt'; v_message := 'De officiële Floriday-afleverbrief kon niet worden klaargezet. Controleer de verzendbriefwachtrij.';
|
|
31419
|
+
ELSIF v_note.status = 'wacht_op_vrijgave'
|
|
31420
|
+
OR (v_note.status = 'wachtend' AND v_note.retry_class IN ('wacht_op_vrijgave','wacht_op_kenmerk'))
|
|
31421
|
+
OR (v_note.status = 'wachtend' AND v_note.retry_class IS NULL AND coalesce(v_note.attempts,0) = 0 AND NOT v_scan_ok) THEN
|
|
31422
|
+
v_state := 'wacht_op_floriday'; v_vrijgave := true;
|
|
31423
|
+
v_message := CASE WHEN v_scan_ok
|
|
31424
|
+
THEN 'Pakbon geprint — ' || coalesce(v_aanmeld_tekst, 'Tijdstip van de transportaanmelding is nog niet bekend') || '.'
|
|
31425
|
+
ELSE 'Pakbon geprint — verzendbrief wacht: ' || coalesce(nullif(btrim(v_note.error), ''), 'op een goedgekeurde controlescan.') END;
|
|
31426
|
+
ELSIF v_note.status = 'wachtend' AND v_note.retry_class = 'nog_niet_beschikbaar' THEN
|
|
31427
|
+
v_state := 'wacht_op_floriday'; v_message := 'Pakbon geprint — wachten op officiële Floriday-afleverbrief.';
|
|
31428
|
+
ELSIF v_note.status = 'wachtend' AND v_note.retry_class = 'tijdelijk' AND coalesce(v_note.attempts,0) > 0
|
|
31429
|
+
AND coalesce(v_note.error,'') ~* '(5\d\d|429|time-?out|netwerk|verbinding|niet bereikbaar)' THEN
|
|
31430
|
+
v_state := 'klaar_voor_print'; v_message := 'Floriday is tijdelijk niet bereikbaar. De verzendbrief wordt automatisch opnieuw geprobeerd.';
|
|
31431
|
+
ELSIF v_note.status = 'wachtend' AND v_note.retry_class = 'limiet_bereikt' THEN
|
|
31432
|
+
v_state := 'mislukt'; v_message := 'Het maximale aantal pogingen voor de verzendbrief is bereikt. Controleer de verzendbriefwachtrij.';
|
|
31433
|
+
ELSIF v_note.status = 'wachtend' THEN
|
|
31434
|
+
v_state := 'klaar_voor_print'; v_message := 'Verzendbrief wordt klaargezet.';
|
|
31435
|
+
ELSIF v_job.status = 'voltooid' THEN
|
|
31436
|
+
v_state := 'geprint'; v_message := 'Pakbon en verzendbrief zijn naar de printer gestuurd.';
|
|
31437
|
+
ELSIF v_job.status IN ('mislukt','geannuleerd') THEN
|
|
31438
|
+
v_state := 'mislukt'; v_message := 'De verzendbrief kon niet worden geprint. Controleer de printwachtrij.';
|
|
31439
|
+
ELSE
|
|
31440
|
+
v_state := 'klaar_voor_print'; v_message := 'De officiële Floriday-afleverbrief wordt naar dezelfde printer gestuurd.';
|
|
31441
|
+
END IF;
|
|
31442
|
+
|
|
31443
|
+
RETURN jsonb_build_object('status',v_state,'message',v_message,
|
|
31444
|
+
'attempts',coalesce(v_note.attempts,0),'nextAttemptAt',v_note.next_attempt_at,
|
|
31445
|
+
'slipJobId',v_slip.id,'shippingNoteJobId',v_note.print_job_id,
|
|
31446
|
+
'cartId',coalesce(v_note.cart_id, v_cart),'orderNumber',v_note.order_number,
|
|
31447
|
+
'wachtOpVrijgave', v_vrijgave,
|
|
31448
|
+
'wachtReden', CASE WHEN v_vrijgave THEN CASE WHEN v_scan_ok THEN v_message ELSE coalesce(nullif(btrim(v_note.error), ''), v_message) END END,
|
|
31449
|
+
'wachtCode', CASE WHEN v_vrijgave THEN coalesce(v_note.retry_class, v_note.status) END,
|
|
31450
|
+
'verwachtOm', CASE WHEN v_vrijgave THEN v_aanmeld_at END,
|
|
31451
|
+
'aanmeldTekst', CASE WHEN v_vrijgave AND v_scan_ok THEN coalesce(v_aanmeld_tekst, 'Tijdstip van de transportaanmelding is nog niet bekend') END);
|
|
31452
|
+
END;
|
|
31453
|
+
$function$;
|
|
31454
|
+
-- ===== 1.0.190 deel 4 =====
|
|
31455
|
+
-- Logistiek Engine 1.0.190 deel 4: rebuffer zonder 'volle kar = vast', bewaking open orders zonder kar, stickermaten-datum. Herhaalbaar; geen datacorrecties.
|
|
31456
|
+
CREATE OR REPLACE FUNCTION public.logistics_rebuffer_vastgezet(p_order_id uuid)
|
|
31457
|
+
RETURNS boolean LANGUAGE sql STABLE SECURITY DEFINER SET search_path TO 'public' AS $$
|
|
31458
|
+
SELECT public.logistics_acc_guard('server', 'logistics_rebuffer_vastgezet');
|
|
31459
|
+
SELECT EXISTS (SELECT 1 FROM public.orders o WHERE o.id = p_order_id AND o.plan_locked_at IS NOT NULL)
|
|
31460
|
+
OR EXISTS (SELECT 1 FROM public.orders o WHERE o.id = p_order_id
|
|
31461
|
+
AND coalesce(public.logistics_order_released(o.order_number), false))
|
|
31462
|
+
OR EXISTS (SELECT 1 FROM public.orders o JOIN public.pick_queue q ON q.order_number = o.order_number
|
|
31463
|
+
WHERE o.id = p_order_id AND q.cancelled_at IS NULL AND q.completed_at IS NULL)
|
|
31464
|
+
OR EXISTS (
|
|
31465
|
+
SELECT 1 FROM public.orders o
|
|
31466
|
+
JOIN logistics_active.order_carts oc
|
|
31467
|
+
ON oc.buffer_id = o.buffer_id AND (oc.order_id = o.id OR oc.order_id IS NULL)
|
|
31468
|
+
WHERE o.id = p_order_id
|
|
31469
|
+
AND (oc.pickup_locked IS TRUE
|
|
31470
|
+
OR public.logistics_cart_handled(COALESCE(o.order_number, o.id::text), oc.cart_number)
|
|
31471
|
+
OR public.logistics_cart_is_frozen(oc.id)));
|
|
31472
|
+
$$;
|
|
31473
|
+
REVOKE ALL ON FUNCTION public.logistics_rebuffer_vastgezet(uuid) FROM PUBLIC, anon, authenticated;
|
|
31474
|
+
GRANT EXECUTE ON FUNCTION public.logistics_rebuffer_vastgezet(uuid) TO service_role;
|
|
31475
|
+
|
|
31476
|
+
CREATE OR REPLACE FUNCTION public.logistics_rebuffer_orders(p_days integer DEFAULT 30)
|
|
31477
|
+
RETURNS jsonb LANGUAGE plpgsql SECURITY DEFINER SET search_path TO 'public'
|
|
31478
|
+
AS $function$
|
|
31479
|
+
DECLARE
|
|
31480
|
+
v_mode text; v_checked int := 0; v_updated int := 0; v_would int := 0; v_vast int := 0;
|
|
31481
|
+
v_start timestamptz := clock_timestamp(); v_ms int; r record; v_after uuid; v_report jsonb;
|
|
31482
|
+
BEGIN
|
|
31483
|
+
PERFORM public.logistics_acc_guard('ingelogd', 'logistics_rebuffer_orders');
|
|
31484
|
+
v_mode := CASE WHEN public.logistics_rebuffer_aan() THEN 'apply' ELSE 'report' END;
|
|
31485
|
+
BEGIN
|
|
31486
|
+
CREATE TEMP TABLE _rb_sim ON COMMIT DROP AS SELECT * FROM public.logistics_rebuffer_simulate_open();
|
|
31487
|
+
EXCEPTION WHEN OTHERS THEN
|
|
31488
|
+
UPDATE public.logistics_rebuffer_state SET pending = false, last_run_at = now(),
|
|
31489
|
+
last_error = left(SQLERRM, 300), updated_at = now() WHERE id;
|
|
31490
|
+
RAISE;
|
|
31491
|
+
END;
|
|
31492
|
+
SELECT count(*), count(*) FILTER (WHERE changed),
|
|
31493
|
+
coalesce(jsonb_agg(jsonb_build_object('orderId', s.id, 'orderNumber', s.order_number, 'deliveryDate', s.delivery_date,
|
|
31494
|
+
'huidig', s.cur_label, 'nieuw', s.new_label, 'wijzigt', s.changed, 'reden', s.reason) ORDER BY s.delivery_date, s.order_number), '[]'::jsonb)
|
|
31495
|
+
INTO v_checked, v_would, v_report FROM _rb_sim s;
|
|
31496
|
+
IF v_mode = 'apply' THEN
|
|
31497
|
+
FOR r IN SELECT s.id, s.buffer_id, s.order_number FROM _rb_sim s WHERE s.changed LOOP
|
|
31498
|
+
IF public.logistics_rebuffer_vastgezet(r.id) THEN
|
|
31499
|
+
v_vast := v_vast + 1;
|
|
31500
|
+
CONTINUE;
|
|
31501
|
+
END IF;
|
|
31502
|
+
v_after := public.logistics_apply_buffer_decision(r.id);
|
|
31503
|
+
IF v_after IS DISTINCT FROM r.buffer_id THEN v_updated := v_updated + 1; END IF;
|
|
31504
|
+
END LOOP;
|
|
31505
|
+
END IF;
|
|
31506
|
+
DROP TABLE IF EXISTS _rb_sim;
|
|
31507
|
+
v_ms := greatest(0, (extract(epoch from (clock_timestamp() - v_start)) * 1000)::int);
|
|
31508
|
+
UPDATE public.logistics_rebuffer_state
|
|
31509
|
+
SET pending = false, last_run_at = now(), last_checked = v_checked, last_updated = v_updated,
|
|
31510
|
+
last_would_change = v_would, last_mode = v_mode, last_duration_ms = v_ms,
|
|
31511
|
+
last_error = CASE WHEN v_vast > 0 THEN v_vast || ' order(s) niet verplaatst: vrijgegeven, in de pickrij of geprinte/gescande kar.' END,
|
|
31512
|
+
last_report = v_report, updated_at = now()
|
|
31513
|
+
WHERE id;
|
|
31514
|
+
RETURN jsonb_build_object('checked', v_checked, 'updated', v_updated, 'durationMs', v_ms,
|
|
31515
|
+
'mode', v_mode, 'wouldChange', v_would, 'vastgezet', v_vast,
|
|
31516
|
+
'scope', 'leverdag_vanaf_vandaag_open');
|
|
31517
|
+
END $function$;CREATE TABLE IF NOT EXISTS public.logistics_zonder_kar_pogingen (
|
|
31518
|
+
order_id uuid PRIMARY KEY,
|
|
31519
|
+
order_number text,
|
|
31520
|
+
pogingen integer NOT NULL DEFAULT 0,
|
|
31521
|
+
laatste_poging_at timestamptz,
|
|
31522
|
+
gemeld_at timestamptz,
|
|
31523
|
+
opgelost_at timestamptz,
|
|
31524
|
+
created_at timestamptz NOT NULL DEFAULT now(),
|
|
31525
|
+
updated_at timestamptz NOT NULL DEFAULT now()
|
|
31526
|
+
);
|
|
31527
|
+
GRANT SELECT ON public.logistics_zonder_kar_pogingen TO authenticated;
|
|
31528
|
+
GRANT ALL ON public.logistics_zonder_kar_pogingen TO service_role;
|
|
31529
|
+
ALTER TABLE public.logistics_zonder_kar_pogingen ENABLE ROW LEVEL SECURITY;
|
|
31530
|
+
DROP POLICY IF EXISTS "Ingelogd mag zonder-kar-pogingen lezen" ON public.logistics_zonder_kar_pogingen;
|
|
31531
|
+
CREATE POLICY "Ingelogd mag zonder-kar-pogingen lezen" ON public.logistics_zonder_kar_pogingen
|
|
31532
|
+
FOR SELECT TO authenticated USING (true);
|
|
31533
|
+
|
|
31534
|
+
CREATE OR REPLACE FUNCTION public.logistics_open_orders_zonder_kar()
|
|
31535
|
+
RETURNS TABLE(order_id uuid, order_number text, delivery_date text, laatste_reden text)
|
|
31536
|
+
LANGUAGE sql STABLE SECURITY DEFINER SET search_path TO 'public' AS $$
|
|
31537
|
+
SELECT public.logistics_acc_guard('ingelogd', 'logistics_open_orders_zonder_kar');
|
|
31538
|
+
WITH lq AS (
|
|
31539
|
+
SELECT DISTINCT ON (q.order_id) q.order_id, q.status, q.reason_code, q.error
|
|
31540
|
+
FROM public.logistics_plan_queue q ORDER BY q.order_id, q.updated_at DESC
|
|
31541
|
+
)
|
|
31542
|
+
SELECT o.id, o.order_number, o.delivery_date, lq.error
|
|
31543
|
+
FROM public.orders o
|
|
31544
|
+
LEFT JOIN lq ON lq.order_id = o.id
|
|
31545
|
+
WHERE o.deleted_at IS NULL AND o.cancelled_at IS NULL
|
|
31546
|
+
AND lower(coalesce(o.status,'')) NOT IN ('cancelled','geannuleerd','deleted','verwijderd','afgehandeld','completed','delivered','geleverd')
|
|
31547
|
+
AND o.delivery_date ~ '^\d{4}-\d{2}-\d{2}' AND left(o.delivery_date,10)::date >= public.logistics_local_today()
|
|
31548
|
+
AND EXISTS (SELECT 1 FROM public.order_items oi WHERE oi.order_id = o.id)
|
|
31549
|
+
AND NOT coalesce(public.logistics_order_buffer_excluded(o.id), false)
|
|
31550
|
+
AND NOT (coalesce(lq.status,'') IN ('geblokkeerd','pending','running'))
|
|
31551
|
+
AND NOT (coalesce(lq.reason_code,'') IN ('belading_onwaarschijnlijk','belading_onvolledig','belading_onbekend','artikel_kiezen','ladingdrager_onbekend'))
|
|
31552
|
+
AND NOT EXISTS (SELECT 1 FROM logistics_active.order_carts c WHERE c.order_id = o.id)
|
|
31553
|
+
AND NOT EXISTS (SELECT 1 FROM public.order_items oi
|
|
31554
|
+
JOIN logistics_active.order_cart_items ci ON ci.order_item_id = oi.id
|
|
31555
|
+
JOIN logistics_active.order_carts c ON c.id = ci.cart_id
|
|
31556
|
+
WHERE oi.order_id = o.id);
|
|
31557
|
+
$$;
|
|
31558
|
+
REVOKE ALL ON FUNCTION public.logistics_open_orders_zonder_kar() FROM PUBLIC, anon;
|
|
31559
|
+
GRANT EXECUTE ON FUNCTION public.logistics_open_orders_zonder_kar() TO authenticated, service_role;
|
|
31560
|
+
|
|
31561
|
+
CREATE OR REPLACE FUNCTION public.logistics_bewaak_zonder_kar()
|
|
31562
|
+
RETURNS jsonb LANGUAGE plpgsql SECURITY DEFINER SET search_path TO 'public' AS $$
|
|
31563
|
+
DECLARE r record; v_nieuw int := 0; v_gemeld int := 0; v_p int;
|
|
31564
|
+
BEGIN
|
|
31565
|
+
PERFORM public.logistics_acc_guard('server', 'logistics_bewaak_zonder_kar');
|
|
31566
|
+
-- Opgelost: weer een kar of niet meer in de lijst.
|
|
31567
|
+
UPDATE public.logistics_zonder_kar_pogingen p SET opgelost_at = now(), updated_at = now()
|
|
31568
|
+
WHERE p.opgelost_at IS NULL
|
|
31569
|
+
AND NOT EXISTS (SELECT 1 FROM public.logistics_open_orders_zonder_kar() z WHERE z.order_id = p.order_id);
|
|
31570
|
+
FOR r IN SELECT * FROM public.logistics_open_orders_zonder_kar() LOOP
|
|
31571
|
+
INSERT INTO public.logistics_zonder_kar_pogingen(order_id, order_number) VALUES (r.order_id, r.order_number)
|
|
31572
|
+
ON CONFLICT (order_id) DO UPDATE SET opgelost_at = NULL,
|
|
31573
|
+
pogingen = CASE WHEN logistics_zonder_kar_pogingen.opgelost_at IS NOT NULL THEN 0 ELSE logistics_zonder_kar_pogingen.pogingen END,
|
|
31574
|
+
gemeld_at = CASE WHEN logistics_zonder_kar_pogingen.opgelost_at IS NOT NULL THEN NULL ELSE logistics_zonder_kar_pogingen.gemeld_at END
|
|
31575
|
+
RETURNING pogingen INTO v_p;
|
|
31576
|
+
IF v_p < 3 THEN
|
|
31577
|
+
-- Niet vaker dan eens per 10 minuten.
|
|
31578
|
+
IF EXISTS (SELECT 1 FROM public.logistics_zonder_kar_pogingen p WHERE p.order_id = r.order_id
|
|
31579
|
+
AND (p.laatste_poging_at IS NULL OR p.laatste_poging_at < now() - interval '10 minutes')) THEN
|
|
31580
|
+
PERFORM public.logistics_plan_enqueue(p_order_id => r.order_id, p_reason => 'bewaking: open order zonder kar');
|
|
31581
|
+
UPDATE public.logistics_zonder_kar_pogingen SET pogingen = pogingen + 1, laatste_poging_at = now(), updated_at = now()
|
|
31582
|
+
WHERE order_id = r.order_id;
|
|
31583
|
+
v_nieuw := v_nieuw + 1;
|
|
31584
|
+
END IF;
|
|
31585
|
+
ELSE
|
|
31586
|
+
UPDATE public.logistics_zonder_kar_pogingen SET gemeld_at = coalesce(gemeld_at, now()), updated_at = now()
|
|
31587
|
+
WHERE order_id = r.order_id;
|
|
31588
|
+
v_gemeld := v_gemeld + 1;
|
|
31589
|
+
END IF;
|
|
31590
|
+
END LOOP;
|
|
31591
|
+
RETURN jsonb_build_object('opnieuwIngepland', v_nieuw, 'gemeld', v_gemeld);
|
|
31592
|
+
END $$;
|
|
31593
|
+
REVOKE ALL ON FUNCTION public.logistics_bewaak_zonder_kar() FROM PUBLIC, anon, authenticated;
|
|
31594
|
+
GRANT EXECUTE ON FUNCTION public.logistics_bewaak_zonder_kar() TO service_role;CREATE OR REPLACE FUNCTION public.logistics_open_orders_zonder_kar()
|
|
31595
|
+
RETURNS TABLE(order_id uuid, order_number text, delivery_date text, laatste_reden text)
|
|
31596
|
+
LANGUAGE sql STABLE SECURITY DEFINER SET search_path TO 'public' AS $$
|
|
31597
|
+
SELECT public.logistics_acc_guard('ingelogd', 'logistics_open_orders_zonder_kar');
|
|
31598
|
+
WITH lq AS (
|
|
31599
|
+
SELECT DISTINCT ON (q.order_id) q.order_id, q.status, q.reason_code, q.error
|
|
31600
|
+
FROM public.logistics_plan_queue q ORDER BY q.order_id, q.updated_at DESC
|
|
31601
|
+
)
|
|
31602
|
+
SELECT o.id, o.order_number, o.delivery_date, lq.error
|
|
31603
|
+
FROM public.orders o
|
|
31604
|
+
LEFT JOIN lq ON lq.order_id = o.id
|
|
31605
|
+
WHERE o.deleted_at IS NULL AND o.cancelled_at IS NULL
|
|
31606
|
+
AND lower(coalesce(o.status,'')) NOT IN ('cancelled','geannuleerd','deleted','verwijderd','afgehandeld','completed','delivered','geleverd')
|
|
31607
|
+
AND o.delivery_date ~ '^\d{4}-\d{2}-\d{2}' AND left(o.delivery_date,10)::date >= public.logistics_local_today()
|
|
31608
|
+
AND EXISTS (SELECT 1 FROM public.order_items oi WHERE oi.order_id = o.id)
|
|
31609
|
+
AND NOT coalesce(public.logistics_order_buffer_excluded(o.id), false)
|
|
31610
|
+
AND NOT (coalesce(lq.status,'') IN ('geblokkeerd','pending','running'))
|
|
31611
|
+
AND NOT (coalesce(lq.status,'') = 'gearchiveerd'
|
|
31612
|
+
AND coalesce(lq.reason_code,'') IN ('belading_onwaarschijnlijk','belading_onvolledig','belading_onbekend','artikel_kiezen','ladingdrager_onbekend'))
|
|
31613
|
+
AND NOT EXISTS (SELECT 1 FROM logistics_active.order_carts c WHERE c.order_id = o.id)
|
|
31614
|
+
AND NOT EXISTS (SELECT 1 FROM public.order_items oi
|
|
31615
|
+
JOIN logistics_active.order_cart_items ci ON ci.order_item_id = oi.id
|
|
31616
|
+
JOIN logistics_active.order_carts c ON c.id = ci.cart_id
|
|
31617
|
+
WHERE oi.order_id = o.id);
|
|
31618
|
+
$$;CREATE OR REPLACE FUNCTION public.logistics_sticker_maten_per_dag(p_van date, p_tot date)
|
|
31619
|
+
RETURNS TABLE(dag date, print_plek_id text, breedte_mm numeric, hoogte_mm numeric, aantal integer)
|
|
31620
|
+
LANGUAGE plpgsql STABLE SECURITY DEFINER SET search_path TO 'public'
|
|
31621
|
+
AS $function$
|
|
31622
|
+
BEGIN
|
|
31623
|
+
PERFORM public.logistics_acc_guard('ingelogd', 'logistics_sticker_maten_per_dag');
|
|
31624
|
+
IF p_van IS NULL OR p_tot IS NULL OR p_tot < p_van THEN
|
|
31625
|
+
RAISE EXCEPTION 'Geef een geldige periode op (van t/m tot).' USING ERRCODE = '22023';
|
|
31626
|
+
END IF;
|
|
31627
|
+
RETURN QUERY
|
|
31628
|
+
WITH b AS (
|
|
31629
|
+
SELECT coalesce(c.departure_at::date,
|
|
31630
|
+
CASE WHEN o.delivery_date ~ '^\d{4}-\d{2}-\d{2}' THEN left(o.delivery_date, 10)::date END) AS d,
|
|
31631
|
+
s.print_plek_id, s.breedte_mm, s.hoogte_mm, s.kopieen
|
|
31632
|
+
FROM public.logistics_sticker_prints s
|
|
31633
|
+
LEFT JOIN public.order_carts c ON c.id = s.cart_id
|
|
31634
|
+
LEFT JOIN public.orders o ON o.id = s.order_id
|
|
31635
|
+
WHERE s.status NOT IN ('niet_printen', 'afgemeld'))
|
|
31636
|
+
SELECT b.d, b.print_plek_id, b.breedte_mm, b.hoogte_mm, sum(greatest(coalesce(b.kopieen, 1), 1))::integer
|
|
31637
|
+
FROM b WHERE b.d BETWEEN p_van AND p_tot
|
|
31638
|
+
GROUP BY 1, 2, 3, 4 ORDER BY 1, 2, 3, 4;
|
|
31639
|
+
END $function$;
|