@flowselections/logistiek-engine 1.0.180 → 1.0.183

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.
@@ -30373,3 +30373,304 @@ BEGIN
30373
30373
  END; $function$;
30374
30374
  REVOKE ALL ON FUNCTION public.logistics_own_traysticker_sync(integer) FROM PUBLIC, anon, authenticated;
30375
30375
  GRANT EXECUTE ON FUNCTION public.logistics_own_traysticker_sync(integer) TO service_role;
30376
+
30377
+ -- ============================================================================
30378
+ -- 1.0.182 Floriday-klantstickers: aantal uit vaste bronvolgorde, stickers ook
30379
+ -- zonder stickercache (additionalServices), bron vastgelegd, etiketstatus.
30380
+ -- Idempotent. Geen omgevingswaarden. Raakt geen functies van andere modules.
30381
+ -- ============================================================================
30382
+
30383
+ ALTER TABLE public.logistics_sticker_prints ADD COLUMN IF NOT EXISTS kopieen_bron text;
30384
+
30385
+ CREATE OR REPLACE FUNCTION public.logistics_sticker_order_counts(p_order uuid, p_trade_item text)
30386
+ RETURNS jsonb
30387
+ LANGUAGE sql
30388
+ STABLE SECURITY DEFINER
30389
+ SET search_path TO 'public'
30390
+ AS $function$
30391
+ WITH ci AS (
30392
+ SELECT ci.* FROM logistics_active.order_cart_items ci
30393
+ JOIN public.order_items oi ON oi.id = ci.order_item_id
30394
+ WHERE oi.order_id = p_order AND coalesce(ci.crates,0) > 0
30395
+ AND (p_trade_item IS NULL OR EXISTS (SELECT 1 FROM public.products p WHERE p.id = ci.product_id AND p.floriday_trade_item_id = p_trade_item))
30396
+ )
30397
+ SELECT jsonb_build_object(
30398
+ 'fusten', (SELECT nullif(sum(crates),0)::int FROM ci),
30399
+ 'planten', CASE WHEN EXISTS (SELECT 1 FROM ci WHERE coalesce(plants_per_crate,0) <= 0) THEN NULL
30400
+ ELSE (SELECT nullif(sum(crates * plants_per_crate),0)::int FROM ci) END,
30401
+ 'lagen', CASE WHEN EXISTS (SELECT 1 FROM ci WHERE layer_index IS NULL) THEN NULL
30402
+ ELSE (SELECT nullif(count(DISTINCT (cart_id, layer_index)),0)::int FROM ci) END,
30403
+ 'karren', (SELECT nullif(count(DISTINCT cart_id),0)::int FROM ci));
30404
+ $function$;
30405
+ REVOKE ALL ON FUNCTION public.logistics_sticker_order_counts(uuid, text) FROM PUBLIC, anon, authenticated;
30406
+ GRANT EXECUTE ON FUNCTION public.logistics_sticker_order_counts(uuid, text) TO service_role;
30407
+
30408
+ -- Bronvolgorde: numberOfCopies -> additionalServices.numberOfUnits (LABEL, zelfde stickerId) -> afgeleid uit soort.
30409
+ CREATE OR REPLACE FUNCTION public.logistics_sticker_copies(p_order uuid, p_sticker text, p_type text, p_fi jsonb, p_trade_item text)
30410
+ RETURNS jsonb
30411
+ LANGUAGE plpgsql
30412
+ STABLE SECURITY DEFINER
30413
+ SET search_path TO 'public'
30414
+ AS $function$
30415
+ DECLARE v int; c jsonb; t text := upper(coalesce(p_type,''));
30416
+ BEGIN
30417
+ IF (p_fi->>'numberOfCopies') ~ '^\d+$' AND (p_fi->>'numberOfCopies')::int > 0 THEN
30418
+ RETURN jsonb_build_object('aantal', (p_fi->>'numberOfCopies')::int, 'bron', 'numberOfCopies');
30419
+ END IF;
30420
+ IF p_order IS NOT NULL AND p_sticker IS NOT NULL THEN
30421
+ SELECT sum((e->>'numberOfUnits')::int)::int INTO v
30422
+ FROM public.orders o
30423
+ JOIN public.floriday_salesorders_cache s ON s.floriday_id = o.floriday_sales_order_id AND NOT coalesce(s.is_deleted,false)
30424
+ CROSS JOIN LATERAL jsonb_array_elements(CASE WHEN jsonb_typeof(s.data->'additionalServices') = 'array' THEN s.data->'additionalServices' ELSE '[]'::jsonb END) e
30425
+ WHERE o.id = p_order AND upper(coalesce(e->>'unit','')) = 'LABEL' AND e->>'stickerId' = p_sticker
30426
+ AND (e->>'numberOfUnits') ~ '^\d+$';
30427
+ IF coalesce(v,0) > 0 THEN RETURN jsonb_build_object('aantal', v, 'bron', 'numberOfUnits'); END IF;
30428
+ END IF;
30429
+ IF t = 'DOCUMENT' THEN RETURN jsonb_build_object('aantal', 1, 'bron', 'afgeleid:DOCUMENT'); END IF;
30430
+ IF p_order IS NOT NULL AND t IN ('PACKAGE','PRODUCT','POT','SLEEVE','LAYER','LOAD_CARRIER') THEN
30431
+ c := public.logistics_sticker_order_counts(p_order, p_trade_item);
30432
+ v := nullif(c->>CASE t WHEN 'PACKAGE' THEN 'fusten' WHEN 'LAYER' THEN 'lagen' WHEN 'LOAD_CARRIER' THEN 'karren' ELSE 'planten' END, '')::int;
30433
+ IF coalesce(v,0) > 0 THEN RETURN jsonb_build_object('aantal', v, 'bron', 'afgeleid:' || t); END IF;
30434
+ END IF;
30435
+ RETURN jsonb_build_object('aantal', NULL, 'bron', NULL);
30436
+ END; $function$;
30437
+ REVOKE ALL ON FUNCTION public.logistics_sticker_copies(uuid, text, text, jsonb, text) FROM PUBLIC, anon, authenticated;
30438
+ GRANT EXECUTE ON FUNCTION public.logistics_sticker_copies(uuid, text, text, jsonb, text) TO service_role;
30439
+
30440
+ CREATE OR REPLACE FUNCTION public.logistics_sticker_sync(p_limit integer DEFAULT 200)
30441
+ RETURNS jsonb
30442
+ LANGUAGE plpgsql
30443
+ SECURITY DEFINER
30444
+ SET search_path TO 'public'
30445
+ AS $function$
30446
+ DECLARE
30447
+ s jsonb; v_rel text; v_station text; v_tray text;
30448
+ st record; o record; d jsonb; fi jsonb; v_n int; v_order uuid; v_onr text;
30449
+ v_copies int; v_size numeric[]; v_type text; v_should text; v_keep uuid[]; v_del text; v_fo text; v_cart uuid;
30450
+ v_crates_tot int; v_pieces_tot int; r record; v_rows int; v_cp jsonb; v_bron text; v_ncarts int; sv record;
30451
+ n_st int := 0; n_tray int := 0;
30452
+ BEGIN
30453
+ SELECT settings INTO s FROM public.logistics_rule_configs
30454
+ ORDER BY is_active DESC, (status = 'actief') DESC, updated_at DESC LIMIT 1;
30455
+ v_rel := public.logistics_rule_text(s, 'pickstations', 'sticker_release', 'with_pick_release');
30456
+ v_station := CASE WHEN v_rel = 'with_packing_slip'
30457
+ THEN public.logistics_rule_text(s, 'pickstations', 'packing_slip_station', '')
30458
+ ELSE public.logistics_rule_text(s, 'pickstations', 'sticker_station', '') END;
30459
+ v_tray := public.logistics_rule_text(s, 'pickstations', 'traysticker_release', 'uit');
30460
+
30461
+ FOR st IN SELECT * FROM public.floriday_customer_stickers_cache ORDER BY updated_at DESC LIMIT greatest(p_limit,1) LOOP
30462
+ n_st := n_st + 1;
30463
+ d := coalesce(st.data, '{}'::jsonb);
30464
+ fi := coalesce(d->'stickerFileInformation', '{}'::jsonb);
30465
+ v_size := CASE WHEN st.sticker_width_mm IS NOT NULL AND st.sticker_height_mm IS NOT NULL
30466
+ THEN ARRAY[st.sticker_width_mm::numeric, st.sticker_height_mm::numeric]
30467
+ ELSE public.logistics_sticker_parse_size(d->'stickerSize') END;
30468
+ v_type := upper(nullif(btrim(coalesce(st.sticker_type, d->>'stickerType', '')), ''));
30469
+ v_should := lower(coalesce(st.should_be_printed::text, fi->>'shouldBePrinted', ''));
30470
+
30471
+ -- Koppeling volgens Suppliers API 2026v1: alleen via salesOrderId.
30472
+ SELECT count(*), (array_agg(id))[1], (array_agg(order_number))[1] INTO v_n, v_order, v_onr
30473
+ FROM public.orders WHERE deleted_at IS NULL
30474
+ AND nullif(btrim(coalesce(st.sales_order_id, d->>'salesOrderId', '')), '') IS NOT NULL
30475
+ AND floriday_sales_order_id = coalesce(st.sales_order_id, d->>'salesOrderId');
30476
+
30477
+ IF coalesce(st.is_deleted,false) OR lower(coalesce(d->>'isDeleted','')) = 'true' THEN
30478
+ UPDATE public.logistics_sticker_prints SET status = 'niet_printen', reden = 'De koper heeft deze sticker verwijderd.'
30479
+ WHERE soort = 'sticker' AND bron_id = st.floriday_id AND status NOT IN ('in_printwachtrij','afgemeld');
30480
+ CONTINUE;
30481
+ END IF;
30482
+ IF lower(coalesce(d->>'isHandled','')) = 'true' THEN
30483
+ UPDATE public.logistics_sticker_prints SET status = 'afgemeld', reden = 'Al afgemeld in Floriday.', handled_at = coalesce(handled_at, now())
30484
+ WHERE soort = 'sticker' AND bron_id = st.floriday_id AND status <> 'afgemeld';
30485
+ CONTINUE;
30486
+ END IF;
30487
+ IF v_n = 1 AND public.logistics_sales_order_external_fulfilled(v_order, coalesce(st.sales_order_id, d->>'salesOrderId')) THEN
30488
+ UPDATE public.logistics_sticker_prints SET status = 'niet_printen', reden = 'Afgehandeld in Floriday; door ons niet geprint en niet afgemeld.'
30489
+ WHERE soort = 'sticker' AND bron_id = st.floriday_id AND status NOT IN ('in_printwachtrij','afgemeld');
30490
+ PERFORM public.logistics_sticker_upsert('sticker', st.floriday_id, v_order, v_onr, NULL, 0, v_size, NULL, NULL, 'niet_printen', 'Afgehandeld in Floriday; door ons niet geprint en niet afgemeld.');
30491
+ CONTINUE;
30492
+ END IF;
30493
+ IF v_n <> 1 THEN
30494
+ PERFORM public.logistics_sticker_upsert('sticker', st.floriday_id, NULL, NULL, NULL, 0, v_size, NULL, NULL, 'geblokkeerd',
30495
+ format('Geen eenduidige order bij Floriday-verkooporder %s (%s gevonden).', coalesce(st.sales_order_id, d->>'salesOrderId', 'onbekend'), v_n));
30496
+ UPDATE public.logistics_sticker_prints SET sticker_type = v_type WHERE soort='sticker' AND bron_id = st.floriday_id;
30497
+ CONTINUE;
30498
+ END IF;
30499
+ IF v_should = 'false' THEN
30500
+ -- Geen printopdracht; zichtbaar als 'niet_printen'. Wordt ook niet afgemeld in Floriday.
30501
+ UPDATE public.logistics_sticker_prints SET status = 'niet_printen',
30502
+ reden = 'Floriday: deze sticker hoeft niet geprint te worden (shouldBePrinted = false).'
30503
+ WHERE soort = 'sticker' AND bron_id = st.floriday_id AND cart_id IS NOT NULL AND status NOT IN ('in_printwachtrij','afgemeld');
30504
+ PERFORM public.logistics_sticker_upsert('sticker', st.floriday_id, v_order, v_onr, NULL, 0, v_size, NULL, NULL, 'niet_printen',
30505
+ 'Floriday: deze sticker hoeft niet geprint te worden (shouldBePrinted = false).');
30506
+ UPDATE public.logistics_sticker_prints SET sticker_type = v_type WHERE soort='sticker' AND bron_id = st.floriday_id;
30507
+ CONTINUE;
30508
+ END IF;
30509
+ IF lower(coalesce(fi->>'deliverSeparately','')) = 'true' THEN
30510
+ PERFORM public.logistics_sticker_upsert('sticker', st.floriday_id, v_order, v_onr, NULL, 0, v_size, NULL, NULL, 'geblokkeerd',
30511
+ 'Sticker moet apart worden geleverd (deliverSeparately); kies handmatig hoe die mee gaat.');
30512
+ UPDATE public.logistics_sticker_prints SET sticker_type = v_type WHERE soort='sticker' AND bron_id = st.floriday_id;
30513
+ CONTINUE;
30514
+ END IF;
30515
+ IF nullif(btrim(coalesce(d->>'salesOrderCorrectionRequestId','')),'') IS NOT NULL THEN
30516
+ PERFORM public.logistics_sticker_upsert('sticker', st.floriday_id, v_order, v_onr, NULL, 0, v_size, NULL, NULL, 'wacht_op_moment',
30517
+ 'Wacht tot de correctie op de verkooporder is verwerkt.');
30518
+ CONTINUE;
30519
+ END IF;
30520
+ -- 1.0.182: aantal uit numberOfCopies -> additionalServices.numberOfUnits (LABEL) -> afgeleid uit stickersoort.
30521
+ v_cp := public.logistics_sticker_copies(v_order, st.floriday_id, v_type, fi, nullif(btrim(coalesce(st.trade_item_id,'')),''));
30522
+ v_copies := nullif(v_cp->>'aantal','')::int; v_bron := v_cp->>'bron';
30523
+ IF coalesce(v_copies,0) <= 0 THEN
30524
+ PERFORM public.logistics_sticker_upsert('sticker', st.floriday_id, v_order, v_onr, NULL, 0, v_size, NULL, NULL, 'geblokkeerd',
30525
+ 'Aantal stickers onbekend: geen numberOfCopies, geen numberOfUnits (LABEL) en niet af te leiden uit de stickersoort; niet geprint.');
30526
+ UPDATE public.logistics_sticker_prints SET sticker_type = v_type, kopieen_bron = NULL WHERE soort='sticker' AND bron_id = st.floriday_id;
30527
+ CONTINUE;
30528
+ END IF;
30529
+ IF v_type = 'DOCUMENT' THEN
30530
+ SELECT oc.id INTO v_cart FROM logistics_active.order_carts oc WHERE oc.order_id = v_order ORDER BY oc.cart_number, oc.created_at LIMIT 1;
30531
+ IF v_cart IS NULL THEN
30532
+ PERFORM public.logistics_sticker_upsert('sticker', st.floriday_id, v_order, v_onr, NULL, v_copies, v_size, NULL, NULL, 'wacht_op_moment',
30533
+ 'Wacht tot de order op een kar staat.');
30534
+ ELSE
30535
+ PERFORM public.logistics_sticker_upsert('sticker', st.floriday_id, v_order, v_onr, v_cart, v_copies, v_size,
30536
+ public.logistics_sticker_cart_plek(v_cart, v_station), NULL,
30537
+ CASE WHEN public.logistics_cart_slip_queued(v_cart) THEN 'klaar' ELSE 'wacht_op_moment' END,
30538
+ CASE WHEN public.logistics_cart_slip_queued(v_cart) THEN NULL ELSE 'Wacht op de pakbon van deze kar.' END);
30539
+ UPDATE public.logistics_sticker_prints SET status = 'niet_printen', reden = 'Vervangen door regels per kar.'
30540
+ WHERE soort = 'sticker' AND bron_id = st.floriday_id AND (cart_id IS NULL OR cart_id <> v_cart)
30541
+ AND status NOT IN ('in_printwachtrij','afgemeld','niet_printen');
30542
+ END IF;
30543
+ UPDATE public.logistics_sticker_prints SET sticker_type = v_type, kopieen_bron = v_bron WHERE soort='sticker' AND bron_id = st.floriday_id;
30544
+ CONTINUE;
30545
+ END IF;
30546
+ IF v_type IS NULL OR v_type NOT IN ('POT','PRODUCT','SLEEVE','PACKAGE','LAYER','LOAD_CARRIER') THEN
30547
+ PERFORM public.logistics_sticker_upsert('sticker', st.floriday_id, v_order, v_onr, NULL, v_copies, v_size, NULL, NULL, 'geblokkeerd',
30548
+ format('Onbekende of niet-ondersteunde stickersoort (%s); kies handmatig hoe deze sticker mee gaat.', coalesce(v_type,'leeg')));
30549
+ UPDATE public.logistics_sticker_prints SET sticker_type = v_type WHERE soort='sticker' AND bron_id = st.floriday_id;
30550
+ CONTINUE;
30551
+ END IF;
30552
+
30553
+ v_keep := ARRAY[]::uuid[]; v_rows := 0; v_crates_tot := 0; v_pieces_tot := 0;
30554
+ FOR r IN SELECT * FROM public.logistics_sticker_distribution(v_order, v_type, v_copies, nullif(btrim(coalesce(st.trade_item_id,'')),'')) LOOP
30555
+ v_rows := v_rows + 1; v_keep := v_keep || r.cart_id;
30556
+ v_crates_tot := v_crates_tot + r.crates; v_pieces_tot := v_pieces_tot + r.pieces;
30557
+ PERFORM public.logistics_sticker_upsert('sticker', st.floriday_id, v_order, v_onr, r.cart_id, r.kopieen, v_size,
30558
+ public.logistics_sticker_cart_plek(r.cart_id, v_station), NULL,
30559
+ CASE WHEN public.logistics_cart_slip_queued(r.cart_id) THEN 'klaar' ELSE 'wacht_op_moment' END,
30560
+ CASE WHEN public.logistics_cart_slip_queued(r.cart_id) THEN NULL ELSE 'Wacht op de pakbon van deze kar.' END);
30561
+ END LOOP;
30562
+ IF v_rows = 0 THEN
30563
+ PERFORM public.logistics_sticker_upsert('sticker', st.floriday_id, v_order, v_onr, NULL, v_copies, v_size, NULL, NULL, 'geblokkeerd',
30564
+ 'De verdeling over karren sluit niet: de order staat (nog) niet op een kar, of de karren hebben geen fusten/stuks van dit artikel.');
30565
+ UPDATE public.logistics_sticker_prints SET sticker_type = v_type WHERE soort='sticker' AND bron_id = st.floriday_id;
30566
+ CONTINUE;
30567
+ END IF;
30568
+ UPDATE public.logistics_sticker_prints SET sticker_type = v_type, kopieen_bron = v_bron WHERE soort='sticker' AND bron_id = st.floriday_id;
30569
+ IF (v_type IN ('PACKAGE','LAYER') AND v_crates_tot <> v_copies) OR (v_type IN ('POT','PRODUCT','SLEEVE') AND v_pieces_tot <> v_copies) THEN
30570
+ BEGIN
30571
+ INSERT INTO public.logistics_trace_events(phase, step, status, reason, order_ref)
30572
+ SELECT 'stickers', 'Aantal klantstickers wijkt af', 'waarschuwing',
30573
+ format('Floriday vraagt %s (%s); op de karren: %s fusten / %s stuks. Het aantal van Floriday wordt geprint.',
30574
+ v_copies, v_type, v_crates_tot, v_pieces_tot), v_onr
30575
+ WHERE NOT EXISTS (SELECT 1 FROM public.logistics_trace_events e WHERE e.order_ref = v_onr AND e.step = 'Aantal klantstickers wijkt af'
30576
+ AND e.created_at > now() - interval '1 day');
30577
+ EXCEPTION WHEN OTHERS THEN NULL; END;
30578
+ END IF;
30579
+ UPDATE public.logistics_sticker_prints SET status = 'niet_printen',
30580
+ reden = CASE WHEN cart_id IS NULL THEN 'Vervangen door regels per kar.' ELSE 'Kar hoort niet meer bij deze sticker.' END
30581
+ WHERE soort = 'sticker' AND bron_id = st.floriday_id
30582
+ AND (cart_id IS NULL OR NOT (cart_id = ANY (v_keep)))
30583
+ AND status NOT IN ('in_printwachtrij','afgemeld','niet_printen');
30584
+ END LOOP;
30585
+
30586
+ -- 1.0.182: stickers die alleen in additionalServices van de verkooporder staan (stickercache leeg of nog niet bijgewerkt).
30587
+ FOR sv IN
30588
+ SELECT o.id AS order_id, o.order_number, e->>'stickerId' AS sticker_id, sum((e->>'numberOfUnits')::int)::int AS units
30589
+ FROM public.orders o
30590
+ JOIN public.floriday_salesorders_cache s ON s.floriday_id = o.floriday_sales_order_id AND NOT coalesce(s.is_deleted,false)
30591
+ CROSS JOIN LATERAL jsonb_array_elements(CASE WHEN jsonb_typeof(s.data->'additionalServices') = 'array' THEN s.data->'additionalServices' ELSE '[]'::jsonb END) e
30592
+ WHERE o.deleted_at IS NULL AND o.delivery_date >= current_date - 2
30593
+ AND upper(coalesce(e->>'unit','')) = 'LABEL' AND (e->>'numberOfUnits') ~ '^\d+$'
30594
+ AND nullif(btrim(coalesce(e->>'stickerId','')),'') IS NOT NULL
30595
+ AND NOT EXISTS (SELECT 1 FROM public.floriday_customer_stickers_cache c WHERE c.floriday_id = e->>'stickerId')
30596
+ AND NOT public.logistics_sales_order_external_fulfilled(o.id, o.floriday_sales_order_id)
30597
+ GROUP BY o.id, o.order_number, e->>'stickerId'
30598
+ LIMIT greatest(p_limit,1)
30599
+ LOOP
30600
+ IF coalesce(sv.units,0) <= 0 THEN CONTINUE; END IF;
30601
+ SELECT count(*), (array_agg(oc.id ORDER BY oc.cart_number, oc.created_at))[1] INTO v_ncarts, v_cart
30602
+ FROM logistics_active.order_carts oc WHERE oc.order_id = sv.order_id;
30603
+ IF v_ncarts = 1 THEN
30604
+ PERFORM public.logistics_sticker_upsert('sticker', sv.sticker_id, sv.order_id, sv.order_number, v_cart, sv.units, NULL,
30605
+ public.logistics_sticker_cart_plek(v_cart, v_station), NULL,
30606
+ CASE WHEN public.logistics_cart_slip_queued(v_cart) THEN 'klaar' ELSE 'wacht_op_moment' END,
30607
+ CASE WHEN public.logistics_cart_slip_queued(v_cart) THEN NULL ELSE 'Wacht op de pakbon van deze kar.' END);
30608
+ UPDATE public.logistics_sticker_prints SET status = 'niet_printen', reden = 'Vervangen door regels per kar.'
30609
+ WHERE soort = 'sticker' AND bron_id = sv.sticker_id AND (cart_id IS NULL OR cart_id <> v_cart)
30610
+ AND status NOT IN ('in_printwachtrij','afgemeld','niet_printen');
30611
+ ELSE
30612
+ PERFORM public.logistics_sticker_upsert('sticker', sv.sticker_id, sv.order_id, sv.order_number, NULL, sv.units, NULL, NULL, NULL, 'wacht_op_moment',
30613
+ CASE WHEN v_ncarts = 0 THEN 'Wacht tot de order op een kar staat.'
30614
+ ELSE format('Stickersoort nog onbekend; wacht op de stickergegevens van Floriday om %s stickers over %s karren te verdelen.', sv.units, v_ncarts) END);
30615
+ END IF;
30616
+ UPDATE public.logistics_sticker_prints SET kopieen_bron = 'numberOfUnits' WHERE soort='sticker' AND bron_id = sv.sticker_id
30617
+ AND status NOT IN ('in_printwachtrij','afgemeld');
30618
+ n_st := n_st + 1;
30619
+ END LOOP;
30620
+
30621
+ IF v_tray <> 'uit' THEN
30622
+ FOR o IN
30623
+ SELECT o.id, o.order_number, nullif(btrim(o.floriday_sales_order_id),'') AS sales
30624
+ FROM public.orders o
30625
+ WHERE o.deleted_at IS NULL AND nullif(btrim(o.floriday_sales_order_id),'') IS NOT NULL
30626
+ AND o.delivery_date >= current_date - 2
30627
+ AND EXISTS (SELECT 1 FROM logistics_active.order_carts oc WHERE oc.order_id = o.id)
30628
+ ORDER BY o.delivery_date LIMIT greatest(p_limit,1)
30629
+ LOOP
30630
+ SELECT d2.floriday_id INTO v_del FROM public.floriday_delivery_orders_cache d2
30631
+ WHERE d2.data @> jsonb_build_object('fulfillmentRequests', jsonb_build_array(jsonb_build_object('salesOrderId', o.sales)))
30632
+ ORDER BY d2.fetched_at DESC LIMIT 1;
30633
+ IF v_del IS NULL THEN CONTINUE; END IF;
30634
+ IF EXISTS (SELECT 1 FROM public.logistics_sticker_prints WHERE soort = 'traysticker' AND bron_id = v_del) THEN
30635
+ v_fo := public.logistics_afhandeling_fulfillment_order(o.id)->>'fulfillmentOrderId';
30636
+ IF v_fo IS NOT NULL THEN
30637
+ UPDATE public.logistics_sticker_prints SET fulfillment_order_id = v_fo
30638
+ WHERE soort = 'traysticker' AND bron_id = v_del AND fulfillment_order_id IS DISTINCT FROM v_fo
30639
+ AND status NOT IN ('in_printwachtrij','afgemeld');
30640
+ END IF;
30641
+ CONTINUE;
30642
+ END IF;
30643
+ SELECT oc.id INTO v_cart FROM logistics_active.order_carts oc WHERE oc.order_id = o.id ORDER BY oc.cart_number, oc.created_at LIMIT 1;
30644
+ v_fo := public.logistics_afhandeling_fulfillment_order(o.id)->>'fulfillmentOrderId';
30645
+ PERFORM public.logistics_sticker_upsert('traysticker', v_del, o.id, o.order_number, v_cart, 1, NULL,
30646
+ public.logistics_sticker_cart_plek(v_cart, v_station), v_fo, 'wacht_op_moment',
30647
+ 'Wacht op geaccepteerde belading in Floriday.');
30648
+ n_tray := n_tray + 1;
30649
+ END LOOP;
30650
+ END IF;
30651
+
30652
+ RETURN jsonb_build_object('stickers', n_st, 'trayNieuw', n_tray, 'release', v_rel, 'trayRelease', v_tray,
30653
+ 'klantstickerMoment', 'with_packing_slip');
30654
+ END; $function$;
30655
+
30656
+
30657
+ -- Stand voor de melding "Stel bij je labelprinter in welk etiket erin zit" (alleen lezen).
30658
+ CREATE OR REPLACE FUNCTION public.logistics_sticker_etiket_status()
30659
+ RETURNS jsonb
30660
+ LANGUAGE sql
30661
+ STABLE SECURITY DEFINER
30662
+ SET search_path TO 'public'
30663
+ AS $function$
30664
+ SELECT jsonb_build_object(
30665
+ 'wachtend', (SELECT count(*) FROM public.logistics_sticker_prints
30666
+ WHERE status IN ('klaar','geblokkeerd') AND reden LIKE 'Stel bij je labelprinter in welk etiket erin zit%'),
30667
+ 'maat_onbekend', (SELECT count(*) FROM public.logistics_sticker_prints
30668
+ WHERE status IN ('klaar','geblokkeerd') AND reden LIKE 'Maat onbekend%'),
30669
+ 'labelprinters_zonder_etiket', (SELECT count(*) FROM public.printers p
30670
+ WHERE coalesce(p.actief, true) AND p.soort::text = 'labelprinter'
30671
+ AND coalesce(nullif(btrim(p.geladen_etiket::text), ''), 'null') IN ('null', '{}', '""')),
30672
+ 'printer_pad', (SELECT nullif(btrim(trim(both '"' from a.value::text)), '') FROM public.app_settings a
30673
+ WHERE a.key = 'layouts_printer_settings_path' LIMIT 1));
30674
+ $function$;
30675
+ REVOKE ALL ON FUNCTION public.logistics_sticker_etiket_status() FROM PUBLIC, anon;
30676
+ GRANT EXECUTE ON FUNCTION public.logistics_sticker_etiket_status() TO authenticated, service_role;
@@ -1 +1 @@
1
- {"version":3,"file":"BeheerPage.d.ts","sourceRoot":"","sources":["../../src/components/BeheerPage.tsx"],"names":[],"mappings":"AA2JA,wBAAgB,UAAU,gCAgBzB"}
1
+ {"version":3,"file":"BeheerPage.d.ts","sourceRoot":"","sources":["../../src/components/BeheerPage.tsx"],"names":[],"mappings":"AA4JA,wBAAgB,UAAU,gCAiBzB"}
@@ -16,6 +16,7 @@ import { AutomatischVersturenCard } from "./AutomatischVersturenCard.js";
16
16
  import { OpruimlijstKarrenCard } from "./OpruimlijstKarrenCard.js";
17
17
  import { HerindelingMeldingCard } from "./HerindelingMeldingCard.js";
18
18
  import { GeblokkeerdeStickersCard } from "./GeblokkeerdeStickersCard.js";
19
+ import { EtiketInstellenMelding } from "./EtiketInstellenMelding.js";
19
20
  import { BeladingVerstuurderCard } from "./BeladingVerstuurderCard.js";
20
21
  import { DocumentkeuzeCard } from "./DocumentkeuzeCard.js";
21
22
  import { MinuutrondeStartMelding } from "./MinuutrondeStartMelding.js";
@@ -77,5 +78,5 @@ function HerindelenBeheer() {
77
78
  : `${voor?.geprintOfGepickt ?? 0} order(s) die al geprint of gepickt zijn, blijven ongemoeid.` })] }), _jsxs(AlertDialogFooter, { children: [_jsx(AlertDialogCancel, { children: "Annuleren" }), _jsxs(AlertDialogAction, { onClick: () => void voerUit(), disabled: (voor?.aantal ?? 0) === 0, children: ["Ja, ", voor?.aantal ?? 0, " order(s) opnieuw indelen"] })] })] }) })] }));
78
79
  }
79
80
  export function BeheerPage() {
80
- return (_jsx(RolPoort, { rollen: ENGINE_TOEGANG.beheer, children: _jsxs("div", { className: "space-y-6 p-6", children: [_jsx(MinuutrondeStartMelding, {}), _jsx(HerindelenBeheer, {}), _jsx(HerindelingMeldingCard, {}), _jsx(GeblokkeerdeStickersCard, {}), _jsx(OudeOrderregelsCard, {}), _jsx(BeladingVerstuurderCard, {}), _jsx(DocumentkeuzeCard, {}), _jsx(AutomatischVersturenCard, {}), _jsx(OpruimlijstKarrenCard, {})] }) }));
81
+ return (_jsx(RolPoort, { rollen: ENGINE_TOEGANG.beheer, children: _jsxs("div", { className: "space-y-6 p-6", children: [_jsx(MinuutrondeStartMelding, {}), _jsx(HerindelenBeheer, {}), _jsx(HerindelingMeldingCard, {}), _jsx(EtiketInstellenMelding, {}), _jsx(GeblokkeerdeStickersCard, {}), _jsx(OudeOrderregelsCard, {}), _jsx(BeladingVerstuurderCard, {}), _jsx(DocumentkeuzeCard, {}), _jsx(AutomatischVersturenCard, {}), _jsx(OpruimlijstKarrenCard, {})] }) }));
81
82
  }
@@ -0,0 +1,10 @@
1
+ interface Stand {
2
+ wachtend: number;
3
+ maatOnbekend: number;
4
+ zonderEtiket: number;
5
+ pad: string | null;
6
+ }
7
+ export declare function leesEtiketStand(data: unknown): Stand | null;
8
+ export declare function EtiketInstellenMelding(): import("react").JSX.Element | null;
9
+ export {};
10
+ //# sourceMappingURL=EtiketInstellenMelding.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EtiketInstellenMelding.d.ts","sourceRoot":"","sources":["../../src/components/EtiketInstellenMelding.tsx"],"names":[],"mappings":"AAaA,UAAU,KAAK;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;CACpB;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,OAAO,GAAG,KAAK,GAAG,IAAI,CAM3D;AAED,wBAAgB,sBAAsB,uCAqDrC"}
@@ -0,0 +1,37 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ // ============================================================================
3
+ // src/components/EtiketInstellenMelding.tsx — melding als het geladen etiket ontbreekt
4
+ // ============================================================================
5
+ // Alleen lezen via logistics_sticker_etiket_status(). Het pad naar de
6
+ // printerinstellingen komt uit app_settings (layouts_printer_settings_path);
7
+ // ontbreekt het, dan geen knop (fail closed, geen verzonnen pad).
8
+ // GEEN <Header /> hier — die levert de shell.
9
+ // ============================================================================
10
+ import { useEffect, useState } from "react";
11
+ import { Button, Card, CardContent, CardDescription, CardHeader, CardTitle } from "@flowselections/core";
12
+ import { Printer } from "lucide-react";
13
+ import { supabase } from "../integrations/supabase/client.js";
14
+ export function leesEtiketStand(data) {
15
+ if (!data || typeof data !== "object")
16
+ return null;
17
+ const d = data;
18
+ const n = (v) => (typeof v === "number" ? v : Number(v) || 0);
19
+ const pad = typeof d["printer_pad"] === "string" && d["printer_pad"].startsWith("/") ? d["printer_pad"] : null;
20
+ return { wachtend: n(d["wachtend"]), maatOnbekend: n(d["maat_onbekend"]), zonderEtiket: n(d["labelprinters_zonder_etiket"]), pad };
21
+ }
22
+ export function EtiketInstellenMelding() {
23
+ const [stand, setStand] = useState(null);
24
+ useEffect(() => {
25
+ let actief = true;
26
+ void supabase.rpc("logistics_sticker_etiket_status").then(({ data, error }) => {
27
+ if (actief && !error)
28
+ setStand(leesEtiketStand(data));
29
+ });
30
+ return () => {
31
+ actief = false;
32
+ };
33
+ }, []);
34
+ if (!stand || (stand.wachtend === 0 && stand.zonderEtiket === 0 && stand.maatOnbekend === 0))
35
+ return null;
36
+ return (_jsxs("div", { className: "space-y-3", children: [(stand.wachtend > 0 || stand.zonderEtiket > 0) && (_jsxs(Card, { className: "border-destructive", children: [_jsxs(CardHeader, { children: [_jsxs(CardTitle, { className: "flex items-center gap-2 text-destructive", children: [_jsx(Printer, { className: "h-4 w-4" }), "Stel bij je labelprinter in welk etiket erin zit"] }), _jsxs(CardDescription, { children: [stand.wachtend > 0 ? `${stand.wachtend} sticker(s) wachten hierop. ` : "", stand.zonderEtiket > 0 ? `${stand.zonderEtiket} labelprinter(s) hebben geen geladen etiket. ` : "", "Zodra het etiket is ingesteld, worden de stickers vanzelf geprint."] })] }), _jsx(CardContent, { children: stand.pad ? (_jsx(Button, { size: "sm", variant: "outline", onClick: () => window.location.assign(stand.pad), children: "Naar de printerinstellingen" })) : (_jsx("p", { className: "text-xs text-muted-foreground", children: "Open de printerinstellingen in de module Layouts en printers." })) })] })), stand.maatOnbekend > 0 && (_jsx(Card, { children: _jsxs(CardHeader, { children: [_jsx(CardTitle, { children: "Maat onbekend" }), _jsxs(CardDescription, { children: [stand.maatOnbekend, " sticker(s) wachten: Floriday gaf geen maat en de pdf bevat er geen."] })] }) }))] }));
37
+ }
@@ -19,6 +19,7 @@ export { AfmetingenBronnenCard } from './components/AfmetingenBronnenCard.js';
19
19
  export { WerkwijzeHerindelingCard } from './components/WerkwijzeHerindelingCard.js';
20
20
  export { HerindelingMeldingCard } from './components/HerindelingMeldingCard.js';
21
21
  export { GeblokkeerdeStickersCard } from './components/GeblokkeerdeStickersCard.js';
22
+ export { EtiketInstellenMelding } from './components/EtiketInstellenMelding.js';
22
23
  export { WerktijdenCard } from './components/WerktijdenCard.js';
23
24
  export { useWerktijden } from './hooks/useWerktijden.js';
24
25
  export { FloridayPoortCard } from './components/FloridayPoortCard.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist-lib/index.js CHANGED
@@ -22,6 +22,7 @@ export { AfmetingenBronnenCard } from './components/AfmetingenBronnenCard.js';
22
22
  export { WerkwijzeHerindelingCard } from './components/WerkwijzeHerindelingCard.js';
23
23
  export { HerindelingMeldingCard } from './components/HerindelingMeldingCard.js';
24
24
  export { GeblokkeerdeStickersCard } from './components/GeblokkeerdeStickersCard.js';
25
+ export { EtiketInstellenMelding } from './components/EtiketInstellenMelding.js';
25
26
  export { WerktijdenCard } from './components/WerktijdenCard.js';
26
27
  export { useWerktijden } from './hooks/useWerktijden.js';
27
28
  export { FloridayPoortCard } from './components/FloridayPoortCard.js';
@@ -0,0 +1,4 @@
1
+ import type { SupabaseClient } from '@supabase/supabase-js';
2
+ import type { Database } from './types.js';
3
+ export declare const supabase: SupabaseClient<Database>;
4
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../../src/integrations/supabase/client.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AA4DxC,eAAO,MAAM,QAAQ,EAAE,cAAc,CAAC,QAAQ,CAK5C,CAAC"}
@@ -0,0 +1,55 @@
1
+ // This file is automatically generated. Do not edit it directly.
2
+ import { createClient } from '@supabase/supabase-js';
3
+ import { brokeredPreviewStorage } from './previewAuthStorage.js';
4
+ function isNewSupabaseApiKey(value) {
5
+ return value.startsWith('sb_publishable_') || value.startsWith('sb_secret_');
6
+ }
7
+ function createSupabaseFetch(supabaseKey) {
8
+ return (input, init) => {
9
+ const headers = new Headers(typeof Request !== 'undefined' && input instanceof Request ? input.headers : undefined);
10
+ if (init?.headers) {
11
+ new Headers(init.headers).forEach((value, key) => headers.set(key, value));
12
+ }
13
+ // New Supabase API keys are opaque strings, not bearer JWTs.
14
+ if (isNewSupabaseApiKey(supabaseKey) && headers.get('Authorization') === `Bearer ${supabaseKey}`) {
15
+ headers.delete('Authorization');
16
+ }
17
+ headers.set('apikey', supabaseKey);
18
+ return fetch(input, { ...init, headers });
19
+ };
20
+ }
21
+ function createSupabaseClient() {
22
+ // Use import.meta.env for client-side (Vite build-time replacement)
23
+ // Fall back to process.env for SSR (server-side rendering)
24
+ const SUPABASE_URL = import.meta.env['VITE_SUPABASE_URL'] || process.env['SUPABASE_URL'];
25
+ const SUPABASE_PUBLISHABLE_KEY = import.meta.env['VITE_SUPABASE_PUBLISHABLE_KEY'] || process.env['SUPABASE_PUBLISHABLE_KEY'];
26
+ if (!SUPABASE_URL || !SUPABASE_PUBLISHABLE_KEY) {
27
+ const missing = [
28
+ ...(!SUPABASE_URL ? ['SUPABASE_URL'] : []),
29
+ ...(!SUPABASE_PUBLISHABLE_KEY ? ['SUPABASE_PUBLISHABLE_KEY'] : []),
30
+ ];
31
+ const message = `Missing Supabase environment variable(s): ${missing.join(', ')}. Connect Supabase in Lovable Cloud.`;
32
+ console.error(`[Supabase] ${message}`);
33
+ throw new Error(message);
34
+ }
35
+ return createClient(SUPABASE_URL, SUPABASE_PUBLISHABLE_KEY, {
36
+ global: {
37
+ fetch: createSupabaseFetch(SUPABASE_PUBLISHABLE_KEY),
38
+ },
39
+ auth: {
40
+ storage: brokeredPreviewStorage(),
41
+ persistSession: true,
42
+ autoRefreshToken: true,
43
+ }
44
+ });
45
+ }
46
+ let _supabase;
47
+ // Import the supabase client like this:
48
+ // import { supabase } from "./client.js";
49
+ export const supabase = new Proxy({}, {
50
+ get(_, prop, receiver) {
51
+ if (!_supabase)
52
+ _supabase = createSupabaseClient();
53
+ return Reflect.get(_supabase, prop, receiver);
54
+ },
55
+ });
@@ -1 +1 @@
1
- {"version":3,"file":"modulePrompts.d.ts","sourceRoot":"","sources":["../../src/lib/modulePrompts.ts"],"names":[],"mappings":"AAWA;;;;GAIG;AACH,eAAO,MAAM,oBAAoB;;;;;;;;;;;;CAKvB,CAAC;AAEX,MAAM,MAAM,eAAe,GAAG,MAAM,OAAO,oBAAoB,CAAC;AAKhE,MAAM,WAAW,oBAAoB;IACnC,6DAA6D;IAC7D,MAAM,EAAE,MAAM,CAAC;IACf,mCAAmC;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,yDAAyD;IACzD,IAAI,EAAE,MAAM,CAAC;IACb,6CAA6C;IAC7C,OAAO,EAAE,MAAM,CAAC;IAChB,qEAAqE;IACrE,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,iFAAiF;IACjF,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,+BAA+B;IAC/B,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,eAAe,CAAC;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,oBAAoB,EAAE,CAAC;CACnC;AAosMD,eAAO,MAAM,cAAc,EAAE,YAAY,EAUL,CAAC;AAErC,gEAAgE;AAChE,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,YAAY,EACpB,QAAQ,EAAE,oBAAoB,GAC7B,MAAM,CAmBR;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CAE9D;AAED,oEAAoE;AACpE,wBAAgB,qBAAqB,IAAI,MAAM,CAiB9C;AAED,wBAAgB,oBAAoB,CAClC,SAAS,EAAE,eAAe,EAC1B,cAAc,CAAC,EAAE,MAAM,GACtB,MAAM,CAIR"}
1
+ {"version":3,"file":"modulePrompts.d.ts","sourceRoot":"","sources":["../../src/lib/modulePrompts.ts"],"names":[],"mappings":"AAWA;;;;GAIG;AACH,eAAO,MAAM,oBAAoB;;;;;;;;;;;;CAKvB,CAAC;AAEX,MAAM,MAAM,eAAe,GAAG,MAAM,OAAO,oBAAoB,CAAC;AAKhE,MAAM,WAAW,oBAAoB;IACnC,6DAA6D;IAC7D,MAAM,EAAE,MAAM,CAAC;IACf,mCAAmC;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,yDAAyD;IACzD,IAAI,EAAE,MAAM,CAAC;IACb,6CAA6C;IAC7C,OAAO,EAAE,MAAM,CAAC;IAChB,qEAAqE;IACrE,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,iFAAiF;IACjF,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,+BAA+B;IAC/B,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,eAAe,CAAC;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,oBAAoB,EAAE,CAAC;CACnC;AAiuMD,eAAO,MAAM,cAAc,EAAE,YAAY,EAUL,CAAC;AAErC,gEAAgE;AAChE,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,YAAY,EACpB,QAAQ,EAAE,oBAAoB,GAC7B,MAAM,CAmBR;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CAE9D;AAED,oEAAoE;AACpE,wBAAgB,qBAAqB,IAAI,MAAM,CAiB9C;AAED,wBAAgB,oBAAoB,CAClC,SAAS,EAAE,eAAe,EAC1B,cAAc,CAAC,EAAE,MAAM,GACtB,MAAM,CAIR"}
@@ -5351,6 +5351,35 @@ Zelf rekenen doe je niet.
5351
5351
  order: 9,
5352
5352
  summary: "Levert de documentlayouts. Kiest zelf geen layout en geen printer: de Engine bepaalt welke layout en welke printplek bij een document horen.",
5353
5353
  revisions: [
5354
+ {
5355
+ number: 14,
5356
+ title: "Geladen etiket instellen: pad en formaat",
5357
+ date: "2026-10-21",
5358
+ summary: "De Engine toont 'Stel bij je labelprinter in welk etiket erin zit' met een knop naar jullie printerinstelling.",
5359
+ body: `KOPPELPROMPT LAYOUTS EN PRINTERS – geladen etiket (Engine 1.0.182)
5360
+
5361
+ Situatie: bij alle printers is printers.geladen_etiket leeg, ook bij de
5362
+ labelprinter. De Engine print dan geen Floriday-klantstickers (nooit schalen,
5363
+ nooit gokken) en toont nu in Beheer:
5364
+ "Stel bij je labelprinter in welk etiket erin zit" met een knop.
5365
+
5366
+ Gevraagd:
5367
+ 1. Zet in app_settings de sleutel 'layouts_printer_settings_path' met het
5368
+ relatieve pad (binnen de Shell, met leading slash, zonder domein) naar de
5369
+ pagina waar je het geladen etiket van een printer instelt. Bijvoorbeeld het
5370
+ pad van jullie printeroverzicht. Geen volledige URL, geen domein. Zonder
5371
+ deze sleutel toont de Engine de melding zonder knop.
5372
+ 2. Maak op die pagina het veld "Geladen etiket" (breedte × hoogte in mm)
5373
+ duidelijk zichtbaar bij elke labelprinter, met dezelfde tekst als kop.
5374
+ 3. Bevestig dat geladen_etiketten(_werkplek_id, _print_plek_id) per printer
5375
+ breedte_mm en hoogte_mm teruggeeft zodra het etiket is ingesteld, en een
5376
+ lege lijst (geen fout) als geen enkele printer een etiket heeft.
5377
+ 4. Als een pickstation-scherm van jullie de printers toont: dezelfde melding
5378
+ tonen zolang een labelprinter geen geladen etiket heeft.
5379
+
5380
+ Zodra het etiket is ingesteld, pakt de Engine de wachtende stickers bij de
5381
+ volgende ronde vanzelf weer op.`,
5382
+ },
5354
5383
  {
5355
5384
  number: 13,
5356
5385
  title: "Correctie 1.69.2: eigen traysticker per fust",
@@ -1,2 +1,2 @@
1
- export declare const MODULE_VERSION = "1.0.180";
1
+ export declare const MODULE_VERSION = "1.0.183";
2
2
  //# sourceMappingURL=moduleVersion.d.ts.map
@@ -1,2 +1,2 @@
1
1
  // Automatisch gegenereerd door prebuild:lib; niet handmatig aanpassen.
2
- export const MODULE_VERSION = "1.0.180";
2
+ export const MODULE_VERSION = "1.0.183";
@@ -1 +1 @@
1
- {"version":3,"file":"stickerQueue.server.d.ts","sourceRoot":"","sources":["../../src/lib/stickerQueue.server.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAkB5D,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,UAAU,GAAG;IACX,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,SAAS,GAAG,aAAa,GAAG,eAAe,GAAG,mBAAmB,CAAC;IACzE,OAAO,EAAE,MAAM,CAAC;IAChB,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,qBAAqB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACvC;AAKD,6EAA6E;AAC7E,eAAO,MAAM,oBAAoB,sBAAsB,CAAC;AAExD,oDAAoD;AACpD,wBAAgB,aAAa,CAAC,KAAK,EAAE,GAAG,CAAC,OAAO,CAAC,GAAG,MAAM,CAEzD;AAaD,wBAAsB,eAAe,CAAC,EAAE,EAAE,cAAc,EAAE,IAAI,GAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAO,GAAG,OAAO,CAAC,UAAU,CAAC,CAuW5G;AAoGD,kFAAkF;AAClF,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAGzD"}
1
+ {"version":3,"file":"stickerQueue.server.d.ts","sourceRoot":"","sources":["../../src/lib/stickerQueue.server.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAuB5D,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,UAAU,GAAG;IACX,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,SAAS,GAAG,aAAa,GAAG,eAAe,GAAG,mBAAmB,CAAC;IACzE,OAAO,EAAE,MAAM,CAAC;IAChB,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,qBAAqB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACvC;AAKD,6EAA6E;AAC7E,eAAO,MAAM,oBAAoB,sBAAsB,CAAC;AAExD,oDAAoD;AACpD,wBAAgB,aAAa,CAAC,KAAK,EAAE,GAAG,CAAC,OAAO,CAAC,GAAG,MAAM,CAEzD;AAaD,wBAAsB,eAAe,CAAC,EAAE,EAAE,cAAc,EAAE,IAAI,GAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAO,GAAG,OAAO,CAAC,UAAU,CAAC,CAwa5G;AAoGD,kFAAkF;AAClF,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAGzD"}
@@ -18,7 +18,7 @@ import { trace, newTraceId } from "./trace.server.js";
18
18
  import { printViaLayoutGecontroleerd, floridayLayout, bucketUitLink } from "./layoutPrint.server.js";
19
19
  import { getFlorydayDocument, getFlorydayFulfillmentStatus } from "./floridayDocuments.server.js";
20
20
  import { controleerTrayPayload } from "./eigenTraysticker.js";
21
- import { controleerEtiket, parseGeladenEtiketten, pdfFormaat, stickerBackoffMinuten, stickerKopieen, waarschuwingTekst, } from "./stickerRules.js";
21
+ import { controleerEtiket, ETIKET_ONTBREEKT, MAAT_ONBEKEND, maatSleutel, pastOpEenPrinter, pastNergensTekst, parseGeladenEtiketten, pdfFormaat, stickerBackoffMinuten, stickerKopieen, waarschuwingTekst, } from "./stickerRules.js";
22
22
  const COLS = "id, soort, bron_id, order_id, order_number, cart_id, kopieen, breedte_mm, hoogte_mm, print_plek_id, fulfillment_order_id, status, attempts, sticker_type, print_job_id, herprint_reden, herprint_requested_at, eigen_sticker_id";
23
23
  /** Opslag van eigen stickers (Klanten-module, customer_product_stickers). */
24
24
  export const EIGEN_STICKER_BUCKET = "customer-stickers";
@@ -146,6 +146,58 @@ export async function runStickerQueue(sb, opts = {}) {
146
146
  await meld(r, f, c.etiket, c.ok);
147
147
  }
148
148
  }
149
+ // 3b. 1.0.182: vroeg waarschuwen bij binnenkomst: past de stickermaat op geen enkele printer?
150
+ // Eén melding per maat per order (bron_id = maat:BxH). Niet leesbaar → geen melding.
151
+ {
152
+ const { data: alle, error: alleFout } = await sb.rpc("geladen_etiketten", { _werkplek_id: null, _print_plek_id: null });
153
+ const lijstAlle = alleFout ? null : parseGeladenEtiketten(alle);
154
+ const { data } = await sb
155
+ .from("logistics_sticker_prints")
156
+ .select("order_id, breedte_mm, hoogte_mm")
157
+ .eq("soort", "sticker")
158
+ .in("status", ["wacht_op_moment", "klaar", "geblokkeerd"])
159
+ .not("breedte_mm", "is", null)
160
+ .not("order_id", "is", null)
161
+ .limit(500);
162
+ const gezien = new Set();
163
+ for (const r of (Array.isArray(data) ? data : [])) {
164
+ const f = { breedteMm: Number(r["breedte_mm"]), hoogteMm: Number(r["hoogte_mm"]) };
165
+ if (!(f.breedteMm > 0 && f.hoogteMm > 0))
166
+ continue;
167
+ const orderId = String(r["order_id"]);
168
+ const sleutel = maatSleutel(f);
169
+ if (gezien.has(orderId + sleutel))
170
+ continue;
171
+ gezien.add(orderId + sleutel);
172
+ const past = pastOpEenPrinter(f, lijstAlle);
173
+ if (past === null)
174
+ continue;
175
+ if (past) {
176
+ await sb
177
+ .from("logistics_order_sticker_warnings")
178
+ .update({ opgelost_op: nu() })
179
+ .eq("order_id", orderId)
180
+ .eq("soort", "sticker-maat")
181
+ .eq("bron_id", sleutel)
182
+ .is("opgelost_op", null);
183
+ continue;
184
+ }
185
+ const grootste = lijstAlle[0] ?? null;
186
+ await sb.from("logistics_order_sticker_warnings").upsert({
187
+ order_id: orderId,
188
+ soort: "sticker-maat",
189
+ bron_id: sleutel,
190
+ benodigd_breedte_mm: f.breedteMm,
191
+ benodigd_hoogte_mm: f.hoogteMm,
192
+ geladen_breedte_mm: grootste?.breedteMm ?? null,
193
+ geladen_hoogte_mm: grootste?.hoogteMm ?? null,
194
+ plek: "alle printers",
195
+ melding: pastNergensTekst(f, lijstAlle),
196
+ opgelost_op: null,
197
+ }, { onConflict: "order_id,soort,bron_id", ignoreDuplicates: false });
198
+ run.warnings++;
199
+ }
200
+ }
149
201
  // 4. Printen.
150
202
  const { data: klaar } = await sb
151
203
  .from("logistics_sticker_prints")
@@ -283,10 +335,18 @@ export async function runStickerQueue(sb, opts = {}) {
283
335
  f = null;
284
336
  }
285
337
  }
338
+ // 1.0.182: maat onbekend of geen geladen etiket = wachten met uitleg, niet stil weigeren.
339
+ if (!f) {
340
+ await later(MAAT_ONBEKEND);
341
+ continue;
342
+ }
286
343
  const lijst = await etiketten(r.print_plek_id);
344
+ if (lijst === null || lijst.length === 0) {
345
+ await later(ETIKET_ONTBREEKT);
346
+ continue;
347
+ }
287
348
  const controle = controleerEtiket(f, lijst, r.soort === "traysticker" ? "tray-sticker" : "sticker");
288
- if (f && lijst !== null)
289
- await meld(r, f, controle.etiket, controle.ok);
349
+ await meld(r, f, controle.etiket, controle.ok);
290
350
  if (!controle.ok) {
291
351
  await blokkeer(controle.reden);
292
352
  continue;
@@ -18,6 +18,38 @@ export declare function past(sticker: Formaat, etiket: Formaat, spelingMm?: numb
18
18
  * Rijen zonder bruikbaar formaat vallen weg. null = antwoord niet leesbaar.
19
19
  */
20
20
  export declare function parseGeladenEtiketten(data: unknown): GeladenEtiket[] | null;
21
+ /** 1.0.182: vaste meldingen; de sticker wacht (geen stille weigering). */
22
+ export declare const ETIKET_ONTBREEKT = "Stel bij je labelprinter in welk etiket erin zit. De sticker wacht tot het geladen etiket bekend is.";
23
+ export declare const MAAT_ONBEKEND = "Maat onbekend: Floriday gaf geen stickermaat en de pdf bevat er geen. De sticker wacht.";
24
+ export type StickerSoort = "PACKAGE" | "PRODUCT" | "POT" | "SLEEVE" | "LAYER" | "LOAD_CARRIER" | "DOCUMENT";
25
+ export interface OrderTellingen {
26
+ fusten: number | null;
27
+ planten: number | null;
28
+ lagen: number | null;
29
+ karren: number | null;
30
+ }
31
+ export type KopieenBron = "numberOfCopies" | "numberOfUnits" | `afgeleid:${StickerSoort}`;
32
+ /**
33
+ * 1.0.182 (gelijk aan logistics_sticker_copies): aantal klantstickers.
34
+ * numberOfCopies → additionalServices.numberOfUnits (unit LABEL, zelfde stickerId)
35
+ * → afgeleid uit stickersoort en order. null = onbekend → blokkeren.
36
+ */
37
+ export declare function kiesStickerKopieen(input: {
38
+ stickerId: string;
39
+ numberOfCopies?: unknown;
40
+ additionalServices?: unknown;
41
+ stickerType?: string | null;
42
+ order?: OrderTellingen | null;
43
+ }): {
44
+ aantal: number;
45
+ bron: KopieenBron;
46
+ } | null;
47
+ /** 1.0.182: past een maat op minstens één geladen etiket? null = niet te beoordelen. */
48
+ export declare function pastOpEenPrinter(sticker: Formaat, etiketten: GeladenEtiket[] | null): boolean | null;
49
+ /** Vroege melding als de maat op geen enkele printer past. */
50
+ export declare function pastNergensTekst(sticker: Formaat, etiketten: GeladenEtiket[]): string;
51
+ /** Sleutel voor één vroege waarschuwing per maat per order. */
52
+ export declare function maatSleutel(f: Formaat): string;
21
53
  export type EtiketControle = {
22
54
  ok: true;
23
55
  etiket: GeladenEtiket;
@@ -1 +1 @@
1
- {"version":3,"file":"stickerRules.d.ts","sourceRoot":"","sources":["../../src/lib/stickerRules.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,OAAO;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAID,kFAAkF;AAClF,wBAAgB,UAAU,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,GAAG,IAAI,CAY5D;AAED,wBAAgB,YAAY,CAAC,CAAC,EAAE,OAAO,GAAG,MAAM,CAG/C;AAED,yEAAyE;AACzE,wBAAgB,IAAI,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,SAAI,GAAG,OAAO,CAG9E;AAOD;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,OAAO,GAAG,aAAa,EAAE,GAAG,IAAI,CAsB3E;AAED,MAAM,MAAM,cAAc,GACtB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,aAAa,CAAA;CAAE,GACnC;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,aAAa,GAAG,IAAI,CAAA;CAAE,CAAC;AAE/D;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,OAAO,GAAG,IAAI,EACvB,SAAS,EAAE,aAAa,EAAE,GAAG,IAAI,EACjC,IAAI,SAAY,GACf,cAAc,CAwBhB;AAED,wEAAwE;AACxE,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,aAAa,GAAG,IAAI,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAItG;AAED,kEAAkE;AAClE,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAI9D;AAED,oHAAoH;AACpH,wBAAgB,cAAc,CAAC,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAGxD;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAC/B,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EACnC,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EACnC,UAAU,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GACpC,MAAM,GAAG,IAAI,CAOf;AAED,uHAAuH;AACvH,eAAO,MAAM,2BAA2B,+BAAgC,CAAC;AAEzE;;;GAGG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,cAAc,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAAC;IAAC,iBAAiB,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,IAAI,GAC9G,cAAc,GAAG,cAAc,GAAG,IAAI,CAQxC"}
1
+ {"version":3,"file":"stickerRules.d.ts","sourceRoot":"","sources":["../../src/lib/stickerRules.ts"],"names":[],"mappings":"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"}
@@ -66,6 +66,64 @@ export function parseGeladenEtiketten(data) {
66
66
  }
67
67
  return uit;
68
68
  }
69
+ /** 1.0.182: vaste meldingen; de sticker wacht (geen stille weigering). */
70
+ export const ETIKET_ONTBREEKT = "Stel bij je labelprinter in welk etiket erin zit. De sticker wacht tot het geladen etiket bekend is.";
71
+ export const MAAT_ONBEKEND = "Maat onbekend: Floriday gaf geen stickermaat en de pdf bevat er geen. De sticker wacht.";
72
+ function positief(v) {
73
+ const n = typeof v === "string" && /^\d+$/.test(v.trim()) ? Number(v) : v;
74
+ return typeof n === "number" && Number.isInteger(n) && n > 0 ? n : null;
75
+ }
76
+ /**
77
+ * 1.0.182 (gelijk aan logistics_sticker_copies): aantal klantstickers.
78
+ * numberOfCopies → additionalServices.numberOfUnits (unit LABEL, zelfde stickerId)
79
+ * → afgeleid uit stickersoort en order. null = onbekend → blokkeren.
80
+ */
81
+ export function kiesStickerKopieen(input) {
82
+ const a = positief(input.numberOfCopies);
83
+ if (a)
84
+ return { aantal: a, bron: "numberOfCopies" };
85
+ if (Array.isArray(input.additionalServices)) {
86
+ let som = 0;
87
+ for (const s of input.additionalServices) {
88
+ if (!s || typeof s !== "object")
89
+ continue;
90
+ const r = s;
91
+ if (String(r["unit"] ?? "").toUpperCase() !== "LABEL" || r["stickerId"] !== input.stickerId)
92
+ continue;
93
+ som += positief(r["numberOfUnits"]) ?? 0;
94
+ }
95
+ if (som > 0)
96
+ return { aantal: som, bron: "numberOfUnits" };
97
+ }
98
+ const t = String(input.stickerType ?? "").toUpperCase();
99
+ if (t === "DOCUMENT")
100
+ return { aantal: 1, bron: "afgeleid:DOCUMENT" };
101
+ const o = input.order;
102
+ if (!o)
103
+ return null;
104
+ const v = t === "PACKAGE" ? o.fusten
105
+ : t === "PRODUCT" || t === "POT" || t === "SLEEVE" ? o.planten
106
+ : t === "LAYER" ? o.lagen
107
+ : t === "LOAD_CARRIER" ? o.karren
108
+ : null;
109
+ const n = positief(v);
110
+ return n ? { aantal: n, bron: `afgeleid:${t}` } : null;
111
+ }
112
+ /** 1.0.182: past een maat op minstens één geladen etiket? null = niet te beoordelen. */
113
+ export function pastOpEenPrinter(sticker, etiketten) {
114
+ if (!etiketten || etiketten.length === 0)
115
+ return null;
116
+ return etiketten.some((e) => past(sticker, e));
117
+ }
118
+ /** Vroege melding als de maat op geen enkele printer past. */
119
+ export function pastNergensTekst(sticker, etiketten) {
120
+ const geladen = [...new Set(etiketten.map((e) => formaatTekst(e)))].join(", ");
121
+ return `Voor deze order is een sticker ${formaatTekst(sticker)} nodig; die past op geen enkele printer (geladen: ${geladen}).`;
122
+ }
123
+ /** Sleutel voor één vroege waarschuwing per maat per order. */
124
+ export function maatSleutel(f) {
125
+ return `maat:${f.breedteMm}x${f.hoogteMm}`;
126
+ }
69
127
  /**
70
128
  * Fail closed: geen leesbaar antwoord, geen labelprinter of één labelprinter
71
129
  * waarop de sticker niet past = niet printen.
@@ -74,15 +132,8 @@ export function controleerEtiket(sticker, etiketten, bron = "sticker") {
74
132
  if (!sticker) {
75
133
  return { ok: false, reden: `Formaat van de ${bron} is onbekend; niet geprint.`, etiket: null };
76
134
  }
77
- if (etiketten === null) {
78
- return {
79
- ok: false,
80
- reden: "Sticker niet geprint: het etiketformaat van de printer is onbekend. Stel het geladen etiket in bij Printen.",
81
- etiket: null,
82
- };
83
- }
84
- if (etiketten.length === 0) {
85
- return { ok: false, reden: "Op deze plek staat geen labelprinter met een geladen etiket; niet geprint.", etiket: null };
135
+ if (etiketten === null || etiketten.length === 0) {
136
+ return { ok: false, reden: ETIKET_ONTBREEKT, etiket: null };
86
137
  }
87
138
  for (const e of etiketten) {
88
139
  if (!past(sticker, e)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flowselections/logistiek-engine",
3
- "version": "1.0.180",
3
+ "version": "1.0.183",
4
4
  "flowselections": {
5
5
  "moduleId": "logistiek-engine",
6
6
  "pages": [