@flowselections/floriday-voorraad 1.0.99 → 1.0.103

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.
Files changed (33) hide show
  1. package/dist-lib/components/voorraad/FloridayConnectionNotice.d.ts +12 -0
  2. package/dist-lib/components/voorraad/FloridayConnectionNotice.d.ts.map +1 -0
  3. package/dist-lib/components/voorraad/FloridayConnectionNotice.js +16 -0
  4. package/dist-lib/components/voorraad/NieuwArtikelPage.d.ts.map +1 -1
  5. package/dist-lib/components/voorraad/NieuwArtikelPage.js +19 -10
  6. package/dist-lib/components/voorraad/StockBulkButtons.d.ts.map +1 -1
  7. package/dist-lib/components/voorraad/StockBulkButtons.js +2 -1
  8. package/dist-lib/components/voorraad/VoorraadPage.d.ts.map +1 -1
  9. package/dist-lib/components/voorraad/VoorraadPage.js +5 -3
  10. package/dist-lib/components/voorraad/VoorraadStockCard.d.ts.map +1 -1
  11. package/dist-lib/components/voorraad/VoorraadStockCard.js +5 -2
  12. package/dist-lib/components/voorraad/VoorraadTable.d.ts.map +1 -1
  13. package/dist-lib/components/voorraad/VoorraadTable.js +5 -1
  14. package/dist-lib/hooks/useFloridayConnection.d.ts +7 -1
  15. package/dist-lib/hooks/useFloridayConnection.d.ts.map +1 -1
  16. package/dist-lib/hooks/useFloridayConnection.js +51 -18
  17. package/dist-lib/hooks/useTradeItemStock.d.ts +1 -1
  18. package/dist-lib/hooks/useTradeItemStock.d.ts.map +1 -1
  19. package/dist-lib/hooks/useTradeItemStock.js +14 -6
  20. package/dist-lib/hooks/useVoorraadData.d.ts.map +1 -1
  21. package/dist-lib/hooks/useVoorraadData.js +12 -21
  22. package/dist-lib/integrations/supabase/types.d.ts +1092 -10
  23. package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
  24. package/dist-lib/lib/__tests__/stock-scope.test.d.ts +2 -0
  25. package/dist-lib/lib/__tests__/stock-scope.test.d.ts.map +1 -0
  26. package/dist-lib/lib/__tests__/stock-scope.test.js +44 -0
  27. package/dist-lib/lib/floriday-client.d.ts +2 -0
  28. package/dist-lib/lib/floriday-client.d.ts.map +1 -1
  29. package/dist-lib/lib/stock-scope.d.ts +25 -0
  30. package/dist-lib/lib/stock-scope.d.ts.map +1 -0
  31. package/dist-lib/lib/stock-scope.js +37 -0
  32. package/package.json +2 -2
  33. package/sql/floriday-voorraad-schema.sql +382 -176
@@ -1,4 +1,4 @@
1
- -- @flowselections/floriday-voorraad 1.0.98 – volledig schema (idempotent, geen gegevens).
1
+ -- @flowselections/floriday-voorraad 1.0.100 – volledig schema (idempotent, geen gegevens).
2
2
  -- Gegenereerd uit de database met scripts/generate-voorraad-schema.py. Niet met de hand wijzigen.
3
3
  -- Volgorde 20. Ontbrekende tabellen van andere modules worden overgeslagen met een NOTICE; draai opnieuw na die module.
4
4
  -- De statuscontrole van floriday_continuous_stock_queue is van Voorraad en wordt hier opnieuw gemaakt.
@@ -502,6 +502,16 @@ ALTER TABLE public.voorraad_buffer_state ADD COLUMN IF NOT EXISTS detached_order
502
502
  ALTER TABLE public.voorraad_buffer_state ADD COLUMN IF NOT EXISTS created_at timestamp with time zone DEFAULT now() NOT NULL;
503
503
  ALTER TABLE public.voorraad_buffer_state ADD COLUMN IF NOT EXISTS updated_at timestamp with time zone DEFAULT now() NOT NULL;
504
504
 
505
+ -- voorraad_correcties
506
+ CREATE TABLE IF NOT EXISTS public.voorraad_correcties ();
507
+ ALTER TABLE public.voorraad_correcties ADD COLUMN IF NOT EXISTS id uuid DEFAULT gen_random_uuid() NOT NULL;
508
+ ALTER TABLE public.voorraad_correcties ADD COLUMN IF NOT EXISTS correctie text NOT NULL;
509
+ ALTER TABLE public.voorraad_correcties ADD COLUMN IF NOT EXISTS uitgevoerd_op timestamp with time zone DEFAULT now() NOT NULL;
510
+ ALTER TABLE public.voorraad_correcties ADD COLUMN IF NOT EXISTS uitgevoerd_door text NOT NULL;
511
+ ALTER TABLE public.voorraad_correcties ADD COLUMN IF NOT EXISTS proef boolean NOT NULL;
512
+ ALTER TABLE public.voorraad_correcties ADD COLUMN IF NOT EXISTS aantallen jsonb DEFAULT '{}'::jsonb NOT NULL;
513
+ ALTER TABLE public.voorraad_correcties ADD COLUMN IF NOT EXISTS hash_ok boolean;
514
+
505
515
  -- voorraad_custom_fields
506
516
  CREATE TABLE IF NOT EXISTS public.voorraad_custom_fields ();
507
517
  ALTER TABLE public.voorraad_custom_fields ADD COLUMN IF NOT EXISTS id uuid DEFAULT gen_random_uuid() NOT NULL;
@@ -622,6 +632,7 @@ CREATE OR REPLACE FUNCTION public._floriday_queue_cleanup_batch(p_statuses text[
622
632
  AS $function$
623
633
  DECLARE v_n integer;
624
634
  BEGIN
635
+ PERFORM public.voorraad_vereis('server', '_floriday_queue_cleanup_batch');
625
636
  WITH victims AS (
626
637
  SELECT q.id FROM public.floriday_continuous_stock_queue q
627
638
  WHERE q.status = ANY(p_statuses)
@@ -646,8 +657,10 @@ GRANT EXECUTE ON FUNCTION _floriday_queue_cleanup_batch(text[],integer,boolean,i
646
657
  CREATE OR REPLACE FUNCTION public._migrate_rewrite_stock_key(_from_key text, _to_key text)
647
658
  RETURNS void
648
659
  LANGUAGE plpgsql
660
+ SET search_path TO 'public'
649
661
  AS $function$
650
- BEGIN
662
+ BEGIN
663
+ PERFORM public.voorraad_vereis('server', '_migrate_rewrite_stock_key');
651
664
  IF _from_key = _to_key THEN RETURN; END IF;
652
665
 
653
666
  -- Voor rijen die botsen: som fysiek/reserved/incoming in de target, kies max voor minimum
@@ -736,6 +749,7 @@ DECLARE
736
749
  v_src text := CASE WHEN v_norm = 'FO' THEN 'FLORIDAY_ORDER' ELSE 'ORDER_DELIVERY' END;
737
750
  v_ctx record; v_key text; r public.trade_item_stock%ROWTYPE; v_after numeric; v_res_after numeric;
738
751
  BEGIN
752
+ PERFORM public.voorraad_vereis('ingelogd', 'apply_stock_consumption');
739
753
  IF p_trade_item_key IS NULL OR COALESCE(p_qty,0) <= 0 THEN RETURN; END IF;
740
754
  SELECT * INTO v_ctx FROM public.get_active_floriday_context();
741
755
  v_key := public.ensure_trade_item_stock_row_key(p_trade_item_key);
@@ -800,6 +814,7 @@ DECLARE
800
814
  v_src text := public.normalize_order_source(p_source);
801
815
  v_found boolean := false;
802
816
  BEGIN
817
+ PERFORM public.voorraad_vereis('ingelogd', 'apply_stock_reservation');
803
818
  IF p_qty = 0 THEN RETURN; END IF;
804
819
  IF p_trade_item_key IS NULL THEN
805
820
  INSERT INTO public.stock_mutation_log(trade_item_key, order_id, order_item_id, order_source, mutation_type, qty_delta, reason, created_by)
@@ -840,6 +855,7 @@ CREATE OR REPLACE FUNCTION public.autolink_product_to_floriday(p_product_id uuid
840
855
  AS $function$
841
856
  DECLARE v_row RECORD; v_new_key text; v_old_key text; v_floriday_id text;
842
857
  BEGIN
858
+ PERFORM public.voorraad_vereis('ingelogd', 'autolink_product_to_floriday');
843
859
  SELECT id, product, barcode, trade_item_key, floriday_trade_item_id INTO v_row
844
860
  FROM public.products WHERE id = p_product_id;
845
861
  IF NOT FOUND THEN RETURN NULL; END IF;
@@ -902,6 +918,7 @@ DECLARE
902
918
  v_key text;
903
919
  n int := 0;
904
920
  BEGIN
921
+ PERFORM public.voorraad_vereis('server', 'backfill_open_order_reservations');
905
922
  FOR r IN
906
923
  SELECT oi.id AS oi_id, oi.order_id, oi.product_id, oi.quantity,
907
924
  COALESCE(o.order_source,'manual') AS src
@@ -937,6 +954,7 @@ DECLARE
937
954
  s public.floriday_inventory_settings%ROWTYPE;
938
955
  v_pack integer; v_calc jsonb; v_type text; v_avail integer := 0; v_raw integer; v_reason text;
939
956
  BEGIN
957
+ PERFORM public.voorraad_vereis('ingelogd', 'calculate_floriday_desired_stock');
940
958
  SELECT * INTO s FROM public.floriday_inventory_settings
941
959
  WHERE connection_id = p_connection_id AND product_id = p_product_id;
942
960
  stock_mode := COALESCE(s.stock_mode, 'FOLLOW_ERP');
@@ -993,6 +1011,7 @@ DECLARE
993
1011
  v_sum integer := 0; v_min integer; v_limit uuid; v_reason text; v_info text;
994
1012
  v_parts jsonb := '[]'; v_member uuid; v_qty numeric; v_possible integer; v_path uuid[];
995
1013
  BEGIN
1014
+ PERFORM public.voorraad_vereis('ingelogd', 'calculate_structure_available');
996
1015
  IF p_product_id = ANY(p_path) THEN
997
1016
  RETURN jsonb_build_object('type','UNKNOWN','available',0,'reason','CYCLE',
998
1017
  'path', to_jsonb(p_path || p_product_id));
@@ -1174,6 +1193,7 @@ CREATE OR REPLACE FUNCTION public.check_product_structure(p_kind text, p_parent
1174
1193
  AS $function$
1175
1194
  DECLARE v_head uuid; v_other uuid; v_path uuid[]; v_depth integer; v_max integer := public.product_structure_max_depth();
1176
1195
  BEGIN
1196
+ PERFORM public.voorraad_vereis('ingelogd', 'check_product_structure');
1177
1197
  ok := true; code := NULL; message := NULL;
1178
1198
  IF p_kind = 'group_member' THEN
1179
1199
  SELECT COALESCE(g.head_product_id, public.product_id_from_ref(g.head_trade_item_floriday_id)) INTO v_head
@@ -1267,6 +1287,7 @@ CREATE OR REPLACE FUNCTION public.claim_floriday_base_supply(p_worker text DEFAU
1267
1287
  AS $function$
1268
1288
  DECLARE l record;
1269
1289
  BEGIN
1290
+ PERFORM public.voorraad_vereis('ingelogd', 'claim_floriday_base_supply');
1270
1291
  SELECT * INTO l FROM public.inventory_recompute_limits WHERE inventory_recompute_limits.id;
1271
1292
  -- Time-out: blijven hangen aanmaken gaat terug naar wachtend
1272
1293
  UPDATE public.floriday_continuous_stock_queue q
@@ -1308,6 +1329,7 @@ CREATE OR REPLACE FUNCTION public.claim_floriday_stock_queue(p_limit integer DEF
1308
1329
  SET search_path TO 'public'
1309
1330
  AS $function$
1310
1331
  BEGIN
1332
+ PERFORM public.voorraad_vereis('ingelogd', 'claim_floriday_stock_queue');
1311
1333
  RETURN QUERY
1312
1334
  WITH candidates AS (
1313
1335
  SELECT q.id
@@ -1340,47 +1362,6 @@ GRANT EXECUTE ON FUNCTION claim_floriday_stock_queue(integer,text) TO service_ro
1340
1362
  REVOKE EXECUTE ON FUNCTION claim_floriday_stock_queue(integer,text) FROM anon;
1341
1363
  GRANT EXECUTE ON FUNCTION claim_floriday_stock_queue(integer,text) TO authenticated;
1342
1364
  GRANT EXECUTE ON FUNCTION claim_floriday_stock_queue(integer,text) TO service_role;
1343
- CREATE OR REPLACE FUNCTION public.claim_logistics_tasks(_owner_module text, _task_type text, _worker text, _limit integer DEFAULT 25, _stale_minutes integer DEFAULT 5)
1344
- RETURNS SETOF logistics_tasks
1345
- LANGUAGE plpgsql
1346
- SECURITY DEFINER
1347
- SET search_path TO 'public'
1348
- AS $function$
1349
- BEGIN
1350
- RETURN QUERY
1351
- WITH candidates AS (
1352
- SELECT t.id
1353
- FROM public.logistics_tasks t
1354
- WHERE t.owner_module = _owner_module
1355
- AND t.task_type = _task_type
1356
- AND (
1357
- (t.status IN ('planned', 'claimed')
1358
- AND (t.next_attempt_at IS NULL OR t.next_attempt_at <= now()))
1359
- OR (t.status = 'in_progress'
1360
- AND t.claimed_at IS NOT NULL
1361
- AND t.claimed_at < now() - make_interval(mins => _stale_minutes))
1362
- )
1363
- ORDER BY t.priority DESC, t.planned_start NULLS FIRST, t.created_at
1364
- LIMIT GREATEST(_limit, 0)
1365
- FOR UPDATE SKIP LOCKED
1366
- )
1367
- UPDATE public.logistics_tasks t
1368
- SET status = 'in_progress',
1369
- claimed_by = _worker,
1370
- claimed_at = now(),
1371
- started_at = COALESCE(t.started_at, now()),
1372
- updated_at = now()
1373
- FROM candidates c
1374
- WHERE t.id = c.id
1375
- RETURNING t.*;
1376
- END;
1377
- $function$
1378
- ;
1379
- REVOKE ALL ON FUNCTION claim_logistics_tasks(text,text,text,integer,integer) FROM PUBLIC;
1380
- REVOKE ALL ON FUNCTION claim_logistics_tasks(text,text,text,integer,integer) FROM anon;
1381
- REVOKE EXECUTE ON FUNCTION claim_logistics_tasks(text,text,text,integer,integer) FROM anon;
1382
- GRANT EXECUTE ON FUNCTION claim_logistics_tasks(text,text,text,integer,integer) TO authenticated;
1383
- GRANT EXECUTE ON FUNCTION claim_logistics_tasks(text,text,text,integer,integer) TO service_role;
1384
1365
  CREATE OR REPLACE FUNCTION public.cleanup_floriday_stock_queue()
1385
1366
  RETURNS void
1386
1367
  LANGUAGE sql
@@ -1407,6 +1388,7 @@ CREATE OR REPLACE FUNCTION public.cleanup_voorraad_module_history()
1407
1388
  SET search_path TO 'public'
1408
1389
  AS $function$
1409
1390
  BEGIN
1391
+ PERFORM public.voorraad_vereis('server', 'cleanup_voorraad_module_history');
1410
1392
  DELETE FROM public.logistics_tasks
1411
1393
  WHERE status IN ('done', 'skipped', 'cancelled', 'failed')
1412
1394
  AND COALESCE(completed_at, updated_at) < now() - interval '90 days';
@@ -1428,6 +1410,7 @@ CREATE OR REPLACE FUNCTION public.complete_floriday_base_supply(p_id uuid, p_kin
1428
1410
  AS $function$
1429
1411
  DECLARE l record; v_row public.floriday_continuous_stock_queue%ROWTYPE; v_steps int[]; v_wait int; v_since timestamptz;
1430
1412
  BEGIN
1413
+ PERFORM public.voorraad_vereis('ingelogd', 'complete_floriday_base_supply');
1431
1414
  SELECT * INTO v_row FROM public.floriday_continuous_stock_queue WHERE id = p_id;
1432
1415
  IF v_row.id IS NULL THEN RETURN 'missing'; END IF;
1433
1416
  SELECT * INTO l FROM public.inventory_recompute_limits WHERE inventory_recompute_limits.id;
@@ -1509,6 +1492,7 @@ DECLARE
1509
1492
  v_since timestamptz;
1510
1493
  v_min integer;
1511
1494
  BEGIN
1495
+ PERFORM public.voorraad_vereis('ingelogd', 'complete_floriday_stock_queue');
1512
1496
  SELECT * INTO v_row FROM public.floriday_continuous_stock_queue WHERE id = p_id;
1513
1497
  IF v_row.id IS NULL THEN RETURN; END IF;
1514
1498
  v_attempts := COALESCE(v_row.attempts, 0) + 1;
@@ -1611,6 +1595,7 @@ CREATE OR REPLACE FUNCTION public.complete_floriday_stock_queue_outcome(p_id uui
1611
1595
  AS $function$
1612
1596
  DECLARE v_row public.floriday_continuous_stock_queue%ROWTYPE; v_reason text; v_outcome text := p_outcome; v_err text := p_error;
1613
1597
  BEGIN
1598
+ PERFORM public.voorraad_vereis('ingelogd', 'complete_floriday_stock_queue_outcome');
1614
1599
  SELECT * INTO v_row FROM public.floriday_continuous_stock_queue WHERE id = p_id;
1615
1600
  IF v_row.id IS NULL THEN RETURN; END IF;
1616
1601
 
@@ -1756,7 +1741,8 @@ DECLARE
1756
1741
  v_component_product_id uuid;
1757
1742
  v_stock_row record;
1758
1743
  v_connection_id uuid := p_connection_id;
1759
- BEGIN
1744
+ BEGIN
1745
+ PERFORM public.voorraad_vereis('ingelogd', 'consume_bom_for_production');
1760
1746
  IF p_quantity IS NULL OR p_quantity <= 0 THEN
1761
1747
  RETURN jsonb_build_object('ok', true, 'consumed', '[]'::jsonb);
1762
1748
  END IF;
@@ -2011,7 +1997,8 @@ DECLARE
2011
1997
  v_stock_row record;
2012
1998
  v_connection_id uuid := p_connection_id;
2013
1999
  v_batch_id uuid := COALESCE(p_batch_id, gen_random_uuid());
2014
- BEGIN
2000
+ BEGIN
2001
+ PERFORM public.voorraad_vereis('ingelogd', 'consume_bom_for_production');
2015
2002
  IF p_quantity IS NULL OR p_quantity <= 0 THEN
2016
2003
  RETURN jsonb_build_object('ok', true, 'consumed', '[]'::jsonb, 'batch_id', v_batch_id);
2017
2004
  END IF;
@@ -2201,6 +2188,7 @@ CREATE OR REPLACE FUNCTION public.consume_order_item(p_order_id uuid, p_order_it
2201
2188
  AS $function$
2202
2189
  DECLARE a record; n integer := 0; v_ins integer; v_norm text := public.normalize_order_source(p_source);
2203
2190
  BEGIN
2191
+ PERFORM public.voorraad_vereis('ingelogd', 'consume_order_item');
2204
2192
  IF p_order_item_id IS NULL THEN RETURN 0; END IF;
2205
2193
  IF EXISTS (SELECT 1 FROM public.stock_mutation_log WHERE order_item_id = p_order_item_id AND mutation_type = 'consumption')
2206
2194
  AND NOT EXISTS (SELECT 1 FROM public.stock_mutation_log WHERE order_item_id = p_order_item_id AND mutation_type = 'consumption_done') THEN
@@ -2245,6 +2233,7 @@ CREATE OR REPLACE FUNCTION public.drain_inventory_recompute_queue()
2245
2233
  AS $function$
2246
2234
  DECLARE n integer := 0; v_batch int;
2247
2235
  BEGIN
2236
+ PERFORM public.voorraad_vereis('server', 'drain_inventory_recompute_queue');
2248
2237
  SELECT drain_batch INTO v_batch FROM public.inventory_recompute_limits WHERE id;
2249
2238
  PERFORM set_config('inventory.inline_overflow', '0', true);
2250
2239
  n := public.process_inventory_recompute_queue(COALESCE(v_batch, 500));
@@ -2372,6 +2361,7 @@ CREATE OR REPLACE FUNCTION public.enqueue_structure_recompute(p_product_ids uuid
2372
2361
  AS $function$
2373
2362
  DECLARE n integer := 0;
2374
2363
  BEGIN
2364
+ PERFORM public.voorraad_vereis('ingelogd', 'enqueue_structure_recompute');
2375
2365
  IF p_product_ids IS NULL OR cardinality(p_product_ids) = 0 THEN RETURN 0; END IF;
2376
2366
  INSERT INTO public.inventory_recompute_queue(product_id, reason)
2377
2367
  SELECT DISTINCT id, p_reason FROM (
@@ -2393,6 +2383,7 @@ CREATE OR REPLACE FUNCTION public.ensure_inventory_recompute_drain()
2393
2383
  SET search_path TO 'public'
2394
2384
  AS $function$
2395
2385
  BEGIN
2386
+ PERFORM public.voorraad_vereis('server', 'ensure_inventory_recompute_drain');
2396
2387
  IF NOT EXISTS (SELECT 1 FROM cron.job WHERE jobname = 'inventory-recompute-drain') THEN
2397
2388
  PERFORM cron.schedule('inventory-recompute-drain', '* * * * *', 'select public.drain_inventory_recompute_queue()');
2398
2389
  END IF;
@@ -2410,6 +2401,7 @@ CREATE OR REPLACE FUNCTION public.ensure_trade_item_stock_row(p_key text)
2410
2401
  SET search_path TO 'public'
2411
2402
  AS $function$
2412
2403
  BEGIN
2404
+ PERFORM public.voorraad_vereis('ingelogd', 'ensure_trade_item_stock_row');
2413
2405
  PERFORM public.ensure_trade_item_stock_row_key(p_key);
2414
2406
  END
2415
2407
  $function$
@@ -2430,6 +2422,7 @@ DECLARE
2430
2422
  v_product_id uuid;
2431
2423
  v_canonical_key text;
2432
2424
  BEGIN
2425
+ PERFORM public.voorraad_vereis('ingelogd', 'ensure_trade_item_stock_row_key');
2433
2426
  IF p_key IS NULL OR btrim(p_key) = '' THEN
2434
2427
  RETURN NULL;
2435
2428
  END IF;
@@ -2588,6 +2581,7 @@ CREATE OR REPLACE FUNCTION public.find_trade_item_key(p_name text, p_barcode tex
2588
2581
  AS $function$
2589
2582
  DECLARE v_key text; v_bar text; v_name text;
2590
2583
  BEGIN
2584
+ PERFORM public.voorraad_vereis('ingelogd', 'find_trade_item_key');
2591
2585
  v_bar := NULLIF(btrim(p_barcode), '');
2592
2586
  v_name := NULLIF(lower(btrim(p_name)), '');
2593
2587
 
@@ -2652,6 +2646,7 @@ DECLARE
2652
2646
  v_moved jsonb := '{}'::jsonb;
2653
2647
  v_count bigint;
2654
2648
  BEGIN
2649
+ PERFORM public.voorraad_vereis('ingelogd', 'floriday_adopt_connection');
2655
2650
  IF p_old IS NULL OR p_new IS NULL OR p_old = p_new THEN
2656
2651
  RETURN jsonb_build_object('ok', false, 'reason', 'ongeldige connecties');
2657
2652
  END IF;
@@ -2707,6 +2702,7 @@ DECLARE
2707
2702
  v_adopted jsonb := '[]'::jsonb;
2708
2703
  v_relink jsonb;
2709
2704
  BEGIN
2705
+ PERFORM public.voorraad_vereis('server', 'floriday_autoheal_connections');
2710
2706
  FOR env IN
2711
2707
  SELECT environment
2712
2708
  FROM public.floriday_connections
@@ -2749,6 +2745,7 @@ AS $function$
2749
2745
  DECLARE
2750
2746
  v_rows integer;
2751
2747
  BEGIN
2748
+ PERFORM public.voorraad_vereis('server', 'floriday_capture_stock_snapshot');
2752
2749
  INSERT INTO public.inventory_stock_snapshots (
2753
2750
  user_id, connection_id, snapshot_at, granularity,
2754
2751
  erp_available_quantity, floriday_desired_quantity, floriday_remote_quantity
@@ -2799,6 +2796,7 @@ DECLARE
2799
2796
  g jsonb; v_days int; v_total int; v_n int; v_batches int := 0;
2800
2797
  v_deleted jsonb := '{}'::jsonb; v_more boolean := false; v_statuses text[];
2801
2798
  BEGIN
2799
+ PERFORM public.voorraad_vereis('server', 'floriday_cleanup_stock_queue_v2');
2802
2800
  SELECT * INTO l FROM public.inventory_recompute_limits LIMIT 1;
2803
2801
  INSERT INTO public.floriday_queue_cleanup_runs DEFAULT VALUES RETURNING id INTO v_run;
2804
2802
  BEGIN
@@ -2839,6 +2837,7 @@ CREATE OR REPLACE FUNCTION public.floriday_compact_stock_snapshots()
2839
2837
  SET search_path TO 'public'
2840
2838
  AS $function$
2841
2839
  BEGIN
2840
+ PERFORM public.voorraad_vereis('server', 'floriday_compact_stock_snapshots');
2842
2841
  INSERT INTO public.inventory_stock_snapshots (
2843
2842
  user_id, connection_id, snapshot_at, granularity,
2844
2843
  erp_available_quantity, floriday_desired_quantity, floriday_remote_quantity
@@ -2955,7 +2954,8 @@ AS $function$
2955
2954
  DECLARE v_pid uuid; v_q int; v_av boolean; v_reason text; v_mode text; v_h int := public.floriday_publish_horizon_weeks(); v_w jsonb; v_d date; i int;
2956
2955
  v_tid text; v_price numeric; v_forms jsonb; v_y int; v_wk int; v_fl int;
2957
2956
  BEGIN
2958
- IF auth.uid() IS NULL OR NOT public.voorraad_can_write() THEN
2957
+ PERFORM public.voorraad_vereis('ingelogd', 'floriday_erp_leading_preview');
2958
+ IF NOT public.voorraad_is_server() AND (auth.uid() IS NULL OR NOT public.voorraad_can_write()) THEN
2959
2959
  RAISE EXCEPTION 'Je hebt geen rechten' USING ERRCODE = '42501'; END IF;
2960
2960
  IF p_stock_mode NOT IN ('FOLLOW_ERP','MAXIMUM','FIXED_REFILL','MANUAL') THEN RAISE EXCEPTION 'Kies een voorraadmodus'; END IF;
2961
2961
  FOREACH v_pid IN ARRAY p_product_ids LOOP
@@ -3024,6 +3024,7 @@ DECLARE
3024
3024
  r record;
3025
3025
  v_count integer := 0;
3026
3026
  BEGIN
3027
+ PERFORM public.voorraad_vereis('ingelogd', 'floriday_fill_publish_horizon');
3027
3028
  -- verlopen weken afsluiten
3028
3029
  UPDATE public.floriday_continuous_stock_queue
3029
3030
  SET status = 'superseded', updated_at = now()
@@ -3059,6 +3060,7 @@ DECLARE
3059
3060
  v_stock numeric; v_price numeric; v_res text; v_fs text; v_old_claims text;
3060
3061
  v_upd int := 0; v_same int := 0; v_skip int := 0; v_err int := 0;
3061
3062
  BEGIN
3063
+ PERFORM public.voorraad_vereis('ingelogd', 'floriday_follow_apply');
3062
3064
  IF NOT public.voorraad_hook_on('follow_floriday') THEN
3063
3065
  RETURN jsonb_build_object('waiting', true, 'reason', 'Volgt Floriday staat uit');
3064
3066
  END IF;
@@ -3138,6 +3140,7 @@ DECLARE
3138
3140
  v_fust_delta integer;
3139
3141
  v_owner uuid;
3140
3142
  BEGIN
3143
+ PERFORM public.voorraad_vereis('ingelogd', 'floriday_ingest_sales_orders');
3141
3144
  FOR r IN
3142
3145
  SELECT c.connection_id,
3143
3146
  c.floriday_id,
@@ -3342,7 +3345,8 @@ CREATE OR REPLACE FUNCTION public.floriday_make_erp_leading(p_connection_id uuid
3342
3345
  AS $function$
3343
3346
  DECLARE v_pid uuid; v_n int := 0; v_skipped jsonb := '[]'::jsonb; v_old text; v_block text; r record;
3344
3347
  BEGIN
3345
- IF auth.uid() IS NULL OR NOT public.voorraad_can_write() THEN
3348
+ PERFORM public.voorraad_vereis('ingelogd', 'floriday_make_erp_leading');
3349
+ IF NOT public.voorraad_is_server() AND (auth.uid() IS NULL OR NOT public.voorraad_can_write()) THEN
3346
3350
  RAISE EXCEPTION 'Je hebt geen rechten' USING ERRCODE = '42501'; END IF;
3347
3351
  IF p_stock_mode NOT IN ('FOLLOW_ERP','MAXIMUM','FIXED_REFILL','MANUAL') THEN
3348
3352
  RAISE EXCEPTION 'Kies een voorraadmodus'; END IF;
@@ -3528,6 +3532,7 @@ DECLARE
3528
3532
  v_horizon integer := public.floriday_publish_horizon_weeks();
3529
3533
  v_offset integer; v_date date; v_year integer; v_week integer; v_changed boolean;
3530
3534
  BEGIN
3535
+ PERFORM public.voorraad_vereis('ingelogd', 'floriday_recompute_inventory');
3531
3536
  SELECT * INTO s FROM public.floriday_inventory_settings
3532
3537
  WHERE connection_id = p_connection_id AND product_id = p_product_id;
3533
3538
  IF s.id IS NULL THEN RETURN; END IF;
@@ -3601,6 +3606,7 @@ DECLARE
3601
3606
  v_conn uuid; v_cache_count int; v_row RECORD; v_cf jsonb; v_cf_id text; v_match text;
3602
3607
  v_linked int := 0; v_repaired int := 0; v_cleared int := 0;
3603
3608
  BEGIN
3609
+ PERFORM public.voorraad_vereis('ingelogd', 'floriday_relink_products');
3604
3610
  v_conn := p_connection_id;
3605
3611
  IF v_conn IS NULL THEN
3606
3612
  SELECT c.id INTO v_conn FROM public.floriday_settings s
@@ -3668,6 +3674,7 @@ CREATE OR REPLACE FUNCTION public.floriday_requeue_dead_letters(p_connection_id
3668
3674
  AS $function$
3669
3675
  DECLARE v_count integer;
3670
3676
  BEGIN
3677
+ PERFORM public.voorraad_vereis('ingelogd', 'floriday_requeue_dead_letters');
3671
3678
  UPDATE public.floriday_continuous_stock_queue
3672
3679
  SET status = 'pending',
3673
3680
  attempts = 0,
@@ -3849,7 +3856,8 @@ DECLARE
3849
3856
  v_run uuid; v_it jsonb; v_tid text; v_pid uuid; v_created boolean; v_old_stock numeric; v_old_price numeric;
3850
3857
  v_old_lead boolean; v_old_forms jsonb; v_stock numeric; v_price numeric; v_n int := 0; v_c int := 0; v_name text; v_fs text;
3851
3858
  BEGIN
3852
- IF auth.uid() IS NULL OR NOT public.voorraad_can_write() THEN
3859
+ PERFORM public.voorraad_vereis('ingelogd', 'floriday_takeover_apply');
3860
+ IF NOT public.voorraad_is_server() AND (auth.uid() IS NULL OR NOT public.voorraad_can_write()) THEN
3853
3861
  RAISE EXCEPTION 'Je hebt geen rechten om voorraad over te nemen' USING ERRCODE = '42501';
3854
3862
  END IF;
3855
3863
  IF p_connection_id IS NULL OR NOT EXISTS (SELECT 1 FROM public.floriday_connections c WHERE c.id = p_connection_id) THEN
@@ -3927,6 +3935,7 @@ CREATE OR REPLACE FUNCTION public.floriday_takeover_fetch_state(p_connection_id
3927
3935
  AS $function$
3928
3936
  DECLARE r record; v_found boolean; v_cached boolean := false;
3929
3937
  BEGIN
3938
+ PERFORM public.voorraad_vereis('ingelogd', 'floriday_takeover_fetch_state');
3930
3939
  SELECT s.backfill_target_sequence, s.backfill_done_at, s.last_success_at INTO r
3931
3940
  FROM public.floriday_sync_state s WHERE s.connection_id = p_connection_id AND s.entity_type = p_entity LIMIT 1;
3932
3941
  v_found := FOUND;
@@ -3962,7 +3971,8 @@ AS $function$
3962
3971
  DECLARE v_r public.floriday_takeover_runs%ROWTYPE; v_i public.floriday_takeover_items%ROWTYPE;
3963
3972
  v_cur numeric; v_curp numeric; v_curlead boolean; v_done int := 0; v_skip int := 0; v_reason text;
3964
3973
  BEGIN
3965
- IF auth.uid() IS NULL OR NOT public.voorraad_can_write() THEN
3974
+ PERFORM public.voorraad_vereis('ingelogd', 'floriday_takeover_undo');
3975
+ IF NOT public.voorraad_is_server() AND (auth.uid() IS NULL OR NOT public.voorraad_can_write()) THEN
3966
3976
  RAISE EXCEPTION 'Je hebt geen rechten om een overname terug te draaien' USING ERRCODE = '42501';
3967
3977
  END IF;
3968
3978
  SELECT * INTO v_r FROM public.floriday_takeover_runs WHERE id = p_run_id FOR UPDATE;
@@ -4048,38 +4058,6 @@ END $function$
4048
4058
  REVOKE ALL ON FUNCTION fust_types_duplicate_guard() FROM PUBLIC;
4049
4059
  REVOKE ALL ON FUNCTION fust_types_duplicate_guard() FROM anon;
4050
4060
  GRANT EXECUTE ON FUNCTION fust_types_duplicate_guard() TO service_role;
4051
- CREATE OR REPLACE FUNCTION public.get_active_floriday_connection_public()
4052
- RETURNS TABLE(id uuid, label text, environment text, organization_name text, gln_code text, preferred_warehouse_id text, preferred_warehouse_name text, active_environment text)
4053
- LANGUAGE sql
4054
- STABLE SECURITY DEFINER
4055
- SET search_path TO 'public'
4056
- AS $function$
4057
- SELECT
4058
- c.id,
4059
- c.label,
4060
- c.environment,
4061
- c.organization_name,
4062
- c.gln_code,
4063
- c.preferred_warehouse_id,
4064
- c.preferred_warehouse_name,
4065
- s.active_environment
4066
- FROM public.floriday_settings s
4067
- LEFT JOIN LATERAL (
4068
- SELECT *
4069
- FROM public.floriday_connections fc
4070
- WHERE fc.is_active = true
4071
- AND fc.environment = s.active_environment
4072
- ORDER BY fc.updated_at DESC NULLS LAST, fc.created_at DESC NULLS LAST
4073
- LIMIT 1
4074
- ) c ON true
4075
- WHERE s.id = 1;
4076
- $function$
4077
- ;
4078
- REVOKE ALL ON FUNCTION get_active_floriday_connection_public() FROM PUBLIC;
4079
- REVOKE ALL ON FUNCTION get_active_floriday_connection_public() FROM anon;
4080
- REVOKE EXECUTE ON FUNCTION get_active_floriday_connection_public() FROM anon;
4081
- GRANT EXECUTE ON FUNCTION get_active_floriday_connection_public() TO authenticated;
4082
- GRANT EXECUTE ON FUNCTION get_active_floriday_connection_public() TO service_role;
4083
4061
  CREATE OR REPLACE FUNCTION public.get_active_floriday_context()
4084
4062
  RETURNS TABLE(connection_id uuid, user_id uuid)
4085
4063
  LANGUAGE sql
@@ -4174,7 +4152,8 @@ AS $function$
4174
4152
  DECLARE
4175
4153
  v_connection_id uuid; v_key text; v_product_key text; v_floriday_key text;
4176
4154
  BEGIN
4177
- IF auth.uid() IS NULL THEN RAISE EXCEPTION 'Niet ingelogd' USING ERRCODE = '42501'; END IF;
4155
+ PERFORM public.voorraad_vereis('ingelogd', 'get_product_stock');
4156
+ IF auth.uid() IS NULL AND NOT public.voorraad_is_server() THEN RAISE EXCEPTION 'Niet ingelogd' USING ERRCODE = '42501'; END IF;
4178
4157
  SELECT NULLIF(trim(COALESCE(p.floriday_trade_item_id, '')), ''), NULLIF(trim(COALESCE(p.trade_item_key, '')), '')
4179
4158
  INTO v_floriday_key, v_product_key FROM public.products p WHERE p.id = p_product_id;
4180
4159
  v_key := COALESCE(v_floriday_key, v_product_key, 'product:' || p_product_id::text);
@@ -4210,6 +4189,7 @@ CREATE OR REPLACE FUNCTION public.get_product_structure_totals(p_product_id uuid
4210
4189
  AS $function$
4211
4190
  DECLARE v_ctx record;
4212
4191
  BEGIN
4192
+ PERFORM public.voorraad_vereis('ingelogd', 'get_product_structure_totals');
4213
4193
  SELECT * INTO v_ctx FROM public.get_active_floriday_context();
4214
4194
  IF v_ctx.connection_id IS NULL THEN
4215
4195
  RETURN jsonb_build_object('product_id',p_product_id,'type',public.product_type(p_product_id),'reason','NO_ACTIVE_CONNECTION');
@@ -4247,6 +4227,7 @@ CREATE OR REPLACE FUNCTION public.inventory_inline_budget_ok()
4247
4227
  AS $function$
4248
4228
  DECLARE v_n int; v_t0 timestamptz; l record;
4249
4229
  BEGIN
4230
+ PERFORM public.voorraad_vereis('ingelogd', 'inventory_inline_budget_ok');
4250
4231
  SELECT max_parents_inline, max_ms_inline INTO l FROM public.inventory_recompute_limits WHERE id;
4251
4232
  v_n := COALESCE(NULLIF(current_setting('inventory.inline_n', true),'')::int, 0) + 1;
4252
4233
  v_t0 := COALESCE(NULLIF(current_setting('inventory.inline_t0', true),'')::timestamptz, clock_timestamp());
@@ -4271,7 +4252,8 @@ CREATE OR REPLACE FUNCTION public.merge_products(_canonical uuid, _loser uuid)
4271
4252
  SET search_path TO 'public'
4272
4253
  AS $function$
4273
4254
  DECLARE loser_row record;
4274
- BEGIN
4255
+ BEGIN
4256
+ PERFORM public.voorraad_vereis('ingelogd', 'merge_products');
4275
4257
  IF _canonical = _loser THEN RETURN; END IF;
4276
4258
  SELECT * INTO loser_row FROM public.products WHERE id = _loser;
4277
4259
  IF NOT FOUND THEN RETURN; END IF;
@@ -4306,95 +4288,6 @@ GRANT EXECUTE ON FUNCTION merge_products(uuid,uuid) TO service_role;
4306
4288
  REVOKE EXECUTE ON FUNCTION merge_products(uuid,uuid) FROM anon;
4307
4289
  GRANT EXECUTE ON FUNCTION merge_products(uuid,uuid) TO authenticated;
4308
4290
  GRANT EXECUTE ON FUNCTION merge_products(uuid,uuid) TO service_role;
4309
- CREATE OR REPLACE FUNCTION public.mirror_offer_order_to_orders()
4310
- RETURNS trigger
4311
- LANGUAGE plpgsql
4312
- SECURITY DEFINER
4313
- SET search_path TO 'public'
4314
- AS $function$
4315
- DECLARE
4316
- v_order_id uuid; v_item jsonb;
4317
- v_total_numeric numeric := COALESCE(NEW.total_amount, 0);
4318
- v_total_text text; v_product_id uuid;
4319
- v_pieces int; v_plants_per_crate int; v_crate_price numeric;
4320
- v_qty int; v_unit text; v_unit_price numeric;
4321
- BEGIN
4322
- IF EXISTS (SELECT 1 FROM public.orders WHERE (custom_fields->>'offer_order_id')::uuid = NEW.id) THEN
4323
- RETURN NEW;
4324
- END IF;
4325
- v_total_text := '€ ' || replace(replace(to_char(v_total_numeric,'FM999G999G990D00'),'.','X'),',','.');
4326
- v_total_text := replace(v_total_text,'X',',');
4327
- v_order_id := gen_random_uuid();
4328
-
4329
- INSERT INTO public.orders (id, order_number, customer_name, customer_id, order_date, delivery_date,
4330
- status, total, notes, custom_fields, order_source, created_at, updated_at)
4331
- VALUES (v_order_id, NEW.order_number, COALESCE(NEW.customer_name,'Onbekende klant'), NEW.customer_id,
4332
- to_char(COALESCE(NEW.created_at,now()),'YYYY-MM-DD'), to_char(COALESCE(NEW.created_at,now()),'YYYY-MM-DD'),
4333
- 'open', v_total_text, NEW.notes,
4334
- jsonb_build_object('source','offer','offer_order_id',NEW.id,'offer_campaign_id',NEW.campaign_id,
4335
- 'offer_recipient_id',NEW.recipient_id,'customer_email',NEW.customer_email,'customer_phone',NEW.customer_phone,
4336
- 'order_type','AO'),
4337
- 'offer', COALESCE(NEW.created_at,now()), now());
4338
-
4339
- IF jsonb_typeof(NEW.items)='array' THEN
4340
- FOR v_item IN SELECT * FROM jsonb_array_elements(NEW.items) LOOP
4341
- v_product_id := NULL;
4342
- BEGIN v_product_id := NULLIF(v_item->>'product_id','')::uuid;
4343
- EXCEPTION WHEN others THEN v_product_id := NULL; END;
4344
-
4345
- v_pieces := COALESCE((v_item->>'quantity')::int,0);
4346
- v_unit_price := COALESCE((v_item->>'unit_price')::numeric,0);
4347
- v_qty := v_pieces;
4348
- v_unit := COALESCE(NULLIF(v_item->>'unit',''),'stuks');
4349
-
4350
- -- Zoek plants_per_crate + tier prijs in offer_campaign_products
4351
- v_plants_per_crate := NULL; v_crate_price := NULL;
4352
- IF v_product_id IS NOT NULL AND NEW.campaign_id IS NOT NULL THEN
4353
- SELECT plants_per_crate INTO v_plants_per_crate
4354
- FROM public.offer_campaign_products
4355
- WHERE id = v_product_id AND campaign_id = NEW.campaign_id;
4356
-
4357
- IF v_plants_per_crate IS NOT NULL AND v_plants_per_crate > 0 THEN
4358
- v_qty := CEIL(v_pieces::numeric / v_plants_per_crate)::int;
4359
- v_unit := 'fust';
4360
- -- Beste tier: hoogste min_crates <= v_qty
4361
- SELECT price_per_crate INTO v_crate_price
4362
- FROM public.offer_campaign_product_tiers
4363
- WHERE campaign_product_id = v_product_id AND min_crates <= v_qty
4364
- ORDER BY min_crates DESC LIMIT 1;
4365
- IF v_crate_price IS NOT NULL THEN
4366
- v_unit_price := v_crate_price;
4367
- ELSE
4368
- v_unit_price := v_unit_price * v_plants_per_crate;
4369
- END IF;
4370
- END IF;
4371
- END IF;
4372
-
4373
- -- v_product_id verwijst naar offer_campaign_products.id, niet products.id
4374
- IF v_product_id IS NOT NULL AND NOT EXISTS (SELECT 1 FROM public.products WHERE id = v_product_id) THEN
4375
- v_product_id := NULL;
4376
- END IF;
4377
-
4378
- INSERT INTO public.order_items (order_id, product_id, product_name, quantity, unit, unit_price)
4379
- VALUES (v_order_id, v_product_id, COALESCE(v_item->>'product_name','Artikel'),
4380
- v_qty, v_unit, v_unit_price);
4381
- END LOOP;
4382
- END IF;
4383
- RETURN NEW;
4384
- EXCEPTION WHEN OTHERS THEN
4385
- RAISE WARNING 'mirror_offer_order_to_orders failed for %: %', NEW.id, SQLERRM;
4386
- RETURN NEW;
4387
- END;
4388
- $function$
4389
- ;
4390
- REVOKE ALL ON FUNCTION mirror_offer_order_to_orders() FROM PUBLIC;
4391
- REVOKE ALL ON FUNCTION mirror_offer_order_to_orders() FROM anon;
4392
- REVOKE EXECUTE ON FUNCTION mirror_offer_order_to_orders() FROM PUBLIC;
4393
- GRANT EXECUTE ON FUNCTION mirror_offer_order_to_orders() TO authenticated;
4394
- GRANT EXECUTE ON FUNCTION mirror_offer_order_to_orders() TO service_role;
4395
- REVOKE EXECUTE ON FUNCTION mirror_offer_order_to_orders() FROM anon;
4396
- GRANT EXECUTE ON FUNCTION mirror_offer_order_to_orders() TO authenticated;
4397
- GRANT EXECUTE ON FUNCTION mirror_offer_order_to_orders() TO service_role;
4398
4291
  CREATE OR REPLACE FUNCTION public.normalize_order_source(p_src text)
4399
4292
  RETURNS text
4400
4293
  LANGUAGE sql
@@ -4427,6 +4320,7 @@ CREATE OR REPLACE FUNCTION public.process_inventory_recompute_queue(p_limit inte
4427
4320
  AS $function$
4428
4321
  DECLARE r record; s record; n integer := 0;
4429
4322
  BEGIN
4323
+ PERFORM public.voorraad_vereis('server', 'process_inventory_recompute_queue');
4430
4324
  FOR r IN SELECT q.product_id FROM public.inventory_recompute_queue q
4431
4325
  ORDER BY q.enqueued_at LIMIT GREATEST(1, LEAST(p_limit, 1000)) FOR UPDATE SKIP LOCKED LOOP
4432
4326
  BEGIN
@@ -4463,7 +4357,8 @@ DECLARE
4463
4357
  v_connection_id uuid;
4464
4358
  v_result jsonb;
4465
4359
  v_batch_id uuid := gen_random_uuid();
4466
- BEGIN
4360
+ BEGIN
4361
+ PERFORM public.voorraad_vereis('ingelogd', 'produce_designed_product');
4467
4362
  IF v_uid IS NULL THEN RAISE EXCEPTION 'Niet ingelogd'; END IF;
4468
4363
  IF p_quantity IS NULL OR p_quantity <= 0 THEN RAISE EXCEPTION 'Aantal moet groter dan 0 zijn'; END IF;
4469
4364
 
@@ -4620,6 +4515,7 @@ CREATE OR REPLACE FUNCTION public.product_own_available(p_connection_id uuid, p_
4620
4515
  AS $function$
4621
4516
  DECLARE v_key text; v_p numeric; v_r numeric; v_hit boolean := false;
4622
4517
  BEGIN
4518
+ PERFORM public.voorraad_vereis('ingelogd', 'product_own_available');
4623
4519
  has_row := false; physical := 0; reserved := 0; available := 0;
4624
4520
  SELECT COALESCE(NULLIF(btrim(COALESCE(p.floriday_trade_item_id,'')),''), NULLIF(btrim(COALESCE(p.trade_item_key,'')),''))
4625
4521
  INTO v_key FROM public.products p WHERE p.id = p_product_id;
@@ -4643,6 +4539,7 @@ CREATE OR REPLACE FUNCTION public.product_packings_derive(p_product_id uuid)
4643
4539
  AS $function$
4644
4540
  DECLARE r record; v_nullable boolean;
4645
4541
  BEGIN
4542
+ PERFORM public.voorraad_vereis('ingelogd', 'product_packings_derive');
4646
4543
  SELECT * INTO r FROM public.product_packings WHERE product_id = p_product_id AND is_primary LIMIT 1;
4647
4544
  IF FOUND AND r.pieces_per_package > 0 AND r.packages_per_layer > 0 AND r.layers_per_load_carrier > 0 THEN
4648
4545
  UPDATE public.products SET
@@ -4676,6 +4573,7 @@ CREATE OR REPLACE FUNCTION public.product_packings_replace(p_product_id uuid, p_
4676
4573
  AS $function$
4677
4574
  DECLARE n integer;
4678
4575
  BEGIN
4576
+ PERFORM public.voorraad_vereis('ingelogd', 'product_packings_replace');
4679
4577
  IF p_source NOT IN ('floriday','eigen') THEN RAISE EXCEPTION 'Onbekende verpakkingsbron %', p_source; END IF;
4680
4578
  DELETE FROM public.product_packings WHERE product_id = p_product_id;
4681
4579
  INSERT INTO public.product_packings(product_id, source, floriday_packing_id, vbn_package_code, custom_package_id,
@@ -4706,6 +4604,7 @@ CREATE OR REPLACE FUNCTION public.product_packings_sync_from_floriday(p_connecti
4706
4604
  AS $function$
4707
4605
  DECLARE r record; v_list jsonb; n_done int := 0; n_missing int := 0;
4708
4606
  BEGIN
4607
+ PERFORM public.voorraad_vereis('ingelogd', 'product_packings_sync_from_floriday');
4709
4608
  FOR r IN
4710
4609
  SELECT p.id, c.data
4711
4610
  FROM public.products p
@@ -4775,6 +4674,7 @@ CREATE OR REPLACE FUNCTION public.product_set_depth_above(p_product_id uuid, p_p
4775
4674
  AS $function$
4776
4675
  DECLARE r record; d integer := 0; v integer;
4777
4676
  BEGIN
4677
+ PERFORM public.voorraad_vereis('ingelogd', 'product_set_depth_above');
4778
4678
  IF p_product_id = ANY(p_path) OR cardinality(p_path) > 10 THEN RETURN 99; END IF;
4779
4679
  FOR r IN SELECT DISTINCT COALESCE(b.parent_product_id, public.product_id_from_ref(b.parent_trade_item_floriday_id)) AS p
4780
4680
  FROM public.product_bom_items b
@@ -4798,6 +4698,7 @@ CREATE OR REPLACE FUNCTION public.product_set_depth_below(p_product_id uuid, p_p
4798
4698
  AS $function$
4799
4699
  DECLARE r record; d integer := 0; v integer;
4800
4700
  BEGIN
4701
+ PERFORM public.voorraad_vereis('ingelogd', 'product_set_depth_below');
4801
4702
  IF p_product_id = ANY(p_path) OR cardinality(p_path) > 10 THEN RETURN 99; END IF;
4802
4703
  IF NOT public.product_is_set(p_product_id) THEN RETURN 0; END IF;
4803
4704
  FOR r IN SELECT COALESCE(b.component_product_id, public.product_id_from_ref(b.component_trade_item_floriday_id)) AS c
@@ -4921,6 +4822,7 @@ CREATE OR REPLACE FUNCTION public.promote_trade_item_key(p_old text, p_new text)
4921
4822
  AS $function$
4922
4823
  DECLARE r RECORD; v_existing_id uuid;
4923
4824
  BEGIN
4825
+ PERFORM public.voorraad_vereis('ingelogd', 'promote_trade_item_key');
4924
4826
  IF p_old IS NULL OR p_new IS NULL OR p_old = p_new THEN RETURN; END IF;
4925
4827
  FOR r IN SELECT * FROM public.trade_item_stock WHERE trade_item_key = p_old FOR UPDATE LOOP
4926
4828
  SELECT id INTO v_existing_id FROM public.trade_item_stock
@@ -4964,6 +4866,7 @@ DECLARE
4964
4866
  v_struct jsonb := '[]'::jsonb; v_struct_qty numeric := 0;
4965
4867
  v_frozen_ids uuid[];
4966
4868
  BEGIN
4869
+ PERFORM public.voorraad_vereis('ingelogd', 'recompute_all_reserved_stock');
4967
4870
  IF current_setting('app.recomputing_reserved', true) = 'on' THEN RETURN; END IF;
4968
4871
  PERFORM set_config('app.recomputing_reserved', 'on', true);
4969
4872
 
@@ -5174,6 +5077,7 @@ DECLARE
5174
5077
  v_total integer;
5175
5078
  v_delivered integer;
5176
5079
  BEGIN
5080
+ PERFORM public.voorraad_vereis('ingelogd', 'recompute_order_status');
5177
5081
  SELECT o.status, o.order_number
5178
5082
  INTO v_current, v_order_number
5179
5083
  FROM public.orders o
@@ -5227,6 +5131,7 @@ CREATE OR REPLACE FUNCTION public.recompute_reserved_for_order(p_order_id uuid)
5227
5131
  AS $function$
5228
5132
  DECLARE k text;
5229
5133
  BEGIN
5134
+ PERFORM public.voorraad_vereis('ingelogd', 'recompute_reserved_for_order');
5230
5135
  FOR k IN
5231
5136
  SELECT DISTINCT p.trade_item_key
5232
5137
  FROM logistics_active.order_items oi
@@ -5258,6 +5163,7 @@ DECLARE
5258
5163
  v_pid uuid;
5259
5164
  v_leads boolean;
5260
5165
  BEGIN
5166
+ PERFORM public.voorraad_vereis('ingelogd', 'recompute_reserved_stock');
5261
5167
  IF p_trade_item_key IS NULL OR btrim(p_trade_item_key) = '' THEN RETURN; END IF;
5262
5168
  IF current_setting('app.recomputing_reserved', true) = 'on' THEN RETURN; END IF;
5263
5169
 
@@ -5354,6 +5260,7 @@ CREATE OR REPLACE FUNCTION public.requeue_skipped_stock_rows(p_status text, p_co
5354
5260
  AS $function$
5355
5261
  DECLARE r record; n integer := 0;
5356
5262
  BEGIN
5263
+ PERFORM public.voorraad_vereis('ingelogd', 'requeue_skipped_stock_rows');
5357
5264
  FOR r IN
5358
5265
  SELECT DISTINCT ON (trade_item_id) id, trade_item_id
5359
5266
  FROM public.floriday_continuous_stock_queue
@@ -5385,6 +5292,7 @@ DECLARE
5385
5292
  v_count integer := 0;
5386
5293
  v_n integer;
5387
5294
  BEGIN
5295
+ PERFORM public.voorraad_vereis('ingelogd', 'reset_stalled_floriday_stock_queue');
5388
5296
  -- Geclaimd maar nooit afgerond (worker gecrasht): terug in de wachtrij.
5389
5297
  UPDATE public.floriday_continuous_stock_queue
5390
5298
  SET status = 'pending',
@@ -5440,7 +5348,8 @@ DECLARE
5440
5348
  take integer;
5441
5349
  avail integer;
5442
5350
  fallback_key text := NULL;
5443
- BEGIN
5351
+ BEGIN
5352
+ PERFORM public.voorraad_vereis('ingelogd', 'resolve_group_allocation');
5444
5353
  FOR r IN
5445
5354
  SELECT public.resolve_group_member_stock_key(pgi.trade_item_id) AS stock_key,
5446
5355
  COALESCE(ts.physical_stock, 0)::integer AS physical_stock,
@@ -5538,6 +5447,7 @@ CREATE OR REPLACE FUNCTION public.resolve_product_id_for_item(p_product_name tex
5538
5447
  AS $function$
5539
5448
  DECLARE v_id uuid;
5540
5449
  BEGIN
5450
+ PERFORM public.voorraad_vereis('ingelogd', 'resolve_product_id_for_item');
5541
5451
  IF p_product_name IS NULL OR btrim(p_product_name) = '' THEN RETURN NULL; END IF;
5542
5452
  SELECT id INTO v_id FROM public.products
5543
5453
  WHERE lower(btrim(product)) = lower(btrim(p_product_name))
@@ -5599,7 +5509,8 @@ DECLARE
5599
5509
  v_uid uuid := COALESCE(p_user_id, auth.uid());
5600
5510
  v_reversed jsonb := '[]'::jsonb;
5601
5511
  v_batches_processed int := 0;
5602
- BEGIN
5512
+ BEGIN
5513
+ PERFORM public.voorraad_vereis('ingelogd', 'reverse_production');
5603
5514
  IF v_remaining <= 0 OR p_parent_key IS NULL THEN
5604
5515
  RETURN jsonb_build_object('ok', true, 'reversed', v_reversed);
5605
5516
  END IF;
@@ -5717,6 +5628,7 @@ CREATE OR REPLACE FUNCTION public.run_floriday_base_supply_process()
5717
5628
  SET search_path TO 'public'
5718
5629
  AS $function$
5719
5630
  BEGIN
5631
+ PERFORM public.voorraad_vereis('server', 'run_floriday_base_supply_process');
5720
5632
  IF NOT EXISTS (SELECT 1 FROM floriday_continuous_stock_queue q
5721
5633
  WHERE q.status IN ('needs_base_supply','creating_base_supply') LIMIT 1) THEN
5722
5634
  PERFORM cron.unschedule(j.jobid) FROM cron.job j WHERE j.jobname = 'floriday-base-supply-retry';
@@ -5743,6 +5655,7 @@ CREATE OR REPLACE FUNCTION public.run_floriday_continuous_stock_sync()
5743
5655
  SET search_path TO 'public'
5744
5656
  AS $function$
5745
5657
  BEGIN
5658
+ PERFORM public.voorraad_vereis('server', 'run_floriday_continuous_stock_sync');
5746
5659
  IF public.voorraad_hook_on('continuous_stock') AND EXISTS (
5747
5660
  SELECT 1 FROM floriday_continuous_stock_queue WHERE status = 'pending'
5748
5661
  AND created_at < now() - interval '5 minutes' LIMIT 1) THEN
@@ -5768,6 +5681,7 @@ CREATE OR REPLACE FUNCTION public.run_floriday_follow()
5768
5681
  SET search_path TO 'public'
5769
5682
  AS $function$
5770
5683
  BEGIN
5684
+ PERFORM public.voorraad_vereis('server', 'run_floriday_follow');
5771
5685
  IF NOT public.voorraad_hook_on('follow_floriday') THEN RETURN; END IF;
5772
5686
  IF NOT EXISTS (SELECT 1 FROM public.floriday_inventory_settings WHERE erp_leading = false LIMIT 1) THEN RETURN; END IF;
5773
5687
  PERFORM public.voorraad_hook_post('/api/public/hooks/floriday-follow', 55000);
@@ -5786,6 +5700,7 @@ DECLARE
5786
5700
  v_base_url text;
5787
5701
  v_apikey text;
5788
5702
  BEGIN
5703
+ PERFORM public.voorraad_vereis('server', 'run_floriday_stock_queue_process');
5789
5704
  SELECT value INTO v_base_url FROM public.module_runtime_config WHERE key = 'stock_queue_processor_base_url';
5790
5705
  SELECT value INTO v_apikey FROM public.module_runtime_config WHERE key = 'voorraad_hook_apikey';
5791
5706
 
@@ -5811,6 +5726,7 @@ CREATE OR REPLACE FUNCTION public.run_floriday_stock_reconcile()
5811
5726
  SET search_path TO 'public'
5812
5727
  AS $function$
5813
5728
  BEGIN
5729
+ PERFORM public.voorraad_vereis('server', 'run_floriday_stock_reconcile');
5814
5730
  IF NOT public.voorraad_hook_on('stock_reconcile') THEN RETURN; END IF;
5815
5731
  PERFORM public.voorraad_hook_post('/api/public/hooks/floriday-stock-reconcile', 55000);
5816
5732
  END $function$
@@ -5825,6 +5741,7 @@ CREATE OR REPLACE FUNCTION public.run_logistics_tasks_dispatch()
5825
5741
  SET search_path TO 'public'
5826
5742
  AS $function$
5827
5743
  BEGIN
5744
+ PERFORM public.voorraad_vereis('ingelogd', 'run_logistics_tasks_dispatch');
5828
5745
  IF NOT public.voorraad_hook_on('logistics_tasks') THEN RETURN; END IF;
5829
5746
  IF NOT EXISTS (SELECT 1 FROM logistics_tasks t WHERE t.owner_module = 'voorraad'
5830
5747
  AND ((t.status IN ('planned','claimed') AND (t.next_attempt_at IS NULL OR t.next_attempt_at <= now()))
@@ -5849,7 +5766,8 @@ CREATE OR REPLACE FUNCTION public.self_heal_floriday_links()
5849
5766
  SET search_path TO 'public'
5850
5767
  AS $function$
5851
5768
  DECLARE v_updated integer := 0; v_tmp integer;
5852
- BEGIN
5769
+ BEGIN
5770
+ PERFORM public.voorraad_vereis('server', 'self_heal_floriday_links');
5853
5771
  WITH upd AS (
5854
5772
  UPDATE public.products p
5855
5773
  SET floriday_trade_item_id = p.id::text,
@@ -5957,6 +5875,7 @@ AS $function$
5957
5875
  DECLARE
5958
5876
  v_user_id uuid := auth.uid(); v_connection_id uuid; v_key text; v_row_key text; v_product_key text; v_floriday_key text;
5959
5877
  BEGIN
5878
+ PERFORM public.voorraad_vereis('ingelogd', 'set_product_stock');
5960
5879
  IF v_user_id IS NULL THEN RAISE EXCEPTION 'Niet ingelogd' USING ERRCODE = '42501'; END IF;
5961
5880
  IF p_physical_stock IS NULL THEN RAISE EXCEPTION 'Vul een voorraadaantal in'; END IF;
5962
5881
  IF p_physical_stock < 0 OR p_minimum_stock < 0 OR p_incoming_stock < 0 THEN
@@ -6005,6 +5924,7 @@ AS $function$
6005
5924
  DECLARE
6006
5925
  v_user_id uuid := auth.uid(); v_connection_id uuid; v_key text; v_row_key text; v_product_key text; v_floriday_key text; v_source text;
6007
5926
  BEGIN
5927
+ PERFORM public.voorraad_vereis('ingelogd', 'set_product_stock');
6008
5928
  IF v_user_id IS NULL THEN RAISE EXCEPTION 'Niet ingelogd' USING ERRCODE = '42501'; END IF;
6009
5929
  IF p_physical_stock IS NULL THEN RAISE EXCEPTION 'Vul een voorraadaantal in'; END IF;
6010
5930
  IF p_physical_stock < 0 OR p_minimum_stock < 0 OR p_incoming_stock < 0 THEN
@@ -6080,6 +6000,7 @@ CREATE OR REPLACE FUNCTION public.stock_import_cleanup()
6080
6000
  AS $function$
6081
6001
  DECLARE v_days int; n int := 0;
6082
6002
  BEGIN
6003
+ PERFORM public.voorraad_vereis('ingelogd', 'stock_import_cleanup');
6083
6004
  SELECT import_retention_days INTO v_days FROM public.inventory_recompute_limits WHERE id;
6084
6005
  DELETE FROM public.stock_import_rows d
6085
6006
  WHERE d.id IN (SELECT ir.id FROM public.stock_import_rows ir JOIN public.stock_import_runs r ON r.id = ir.run_id
@@ -6108,6 +6029,7 @@ AS $function$
6108
6029
  DECLARE v_uid uuid := auth.uid(); v_ctx record; v_conn uuid; v_user uuid; l record; n int; v_run uuid; v_act record; v_dup record;
6109
6030
  v_cs int; v_ncs int; v_chunks int;
6110
6031
  BEGIN
6032
+ PERFORM public.voorraad_vereis('ingelogd', 'stock_import_create');
6111
6033
  IF v_uid IS NULL AND COALESCE(auth.role(),'') <> 'service_role' THEN RAISE EXCEPTION 'Niet ingelogd' USING ERRCODE = '42501'; END IF;
6112
6034
  IF NOT public.voorraad_can_write() THEN RETURN jsonb_build_object('ok', false, 'code', 'NO_WRITE_ROLE', 'message', 'Je hebt geen rechten om voorraad te importeren. Vraag een beheerder om een schrijfrol.'); END IF;
6113
6035
  SELECT * INTO v_ctx FROM public.get_active_floriday_context();
@@ -6188,6 +6110,7 @@ AS $function$
6188
6110
  DECLARE v_run record; v_chunk record; v_ctx record; r record; v_pid uuid; v_key text; s record; v_t0 timestamptz := clock_timestamp();
6189
6111
  np int; nm int; ni int; v_fid text; n_ok int := 0; n_err int := 0; v_done int;
6190
6112
  BEGIN
6113
+ PERFORM public.voorraad_vereis('ingelogd', 'stock_import_process_chunk');
6191
6114
  SELECT * INTO v_run FROM public.stock_import_runs WHERE id = p_run_id FOR UPDATE;
6192
6115
  IF v_run.id IS NULL THEN RAISE EXCEPTION 'Import niet gevonden'; END IF;
6193
6116
  IF NOT public._stock_import_caller(v_run.user_id) THEN RAISE EXCEPTION 'Geen toegang tot deze import' USING ERRCODE = '42501'; END IF;
@@ -6279,6 +6202,7 @@ CREATE OR REPLACE FUNCTION public.stock_import_stop(p_run_id uuid)
6279
6202
  AS $function$
6280
6203
  DECLARE v_run record;
6281
6204
  BEGIN
6205
+ PERFORM public.voorraad_vereis('ingelogd', 'stock_import_stop');
6282
6206
  SELECT * INTO v_run FROM public.stock_import_runs WHERE id = p_run_id FOR UPDATE;
6283
6207
  IF v_run.id IS NULL THEN RAISE EXCEPTION 'Import niet gevonden'; END IF;
6284
6208
  IF NOT public._stock_import_caller(v_run.user_id) THEN RAISE EXCEPTION 'Geen toegang tot deze import' USING ERRCODE = '42501'; END IF;
@@ -6298,6 +6222,7 @@ CREATE OR REPLACE FUNCTION public.stock_import_undo_chunk(p_run_id uuid, p_chunk
6298
6222
  AS $function$
6299
6223
  DECLARE v_run record; v_chunk record; r record; s record; tp int; tm int; ti int; v_note text; n_back int := 0; n_skip int := 0; v_left int;
6300
6224
  BEGIN
6225
+ PERFORM public.voorraad_vereis('ingelogd', 'stock_import_undo_chunk');
6301
6226
  SELECT * INTO v_run FROM public.stock_import_runs WHERE id = p_run_id FOR UPDATE;
6302
6227
  IF v_run.id IS NULL THEN RAISE EXCEPTION 'Import niet gevonden'; END IF;
6303
6228
  IF NOT public._stock_import_caller(v_run.user_id) THEN RAISE EXCEPTION 'Geen toegang tot deze import' USING ERRCODE = '42501'; END IF;
@@ -6384,6 +6309,7 @@ DECLARE
6384
6309
  v_row record;
6385
6310
  v_user uuid;
6386
6311
  BEGIN
6312
+ PERFORM public.voorraad_vereis('ingelogd', 'sync_order_item_fusten');
6387
6313
  SELECT oi.*, o.order_number INTO v_item
6388
6314
  FROM logistics_active.order_items oi
6389
6315
  LEFT JOIN public.orders o ON o.id = oi.order_id
@@ -7761,6 +7687,7 @@ CREATE OR REPLACE FUNCTION public.voorraad_alert_floriday_not_updated(p_key text
7761
7687
  AS $function$
7762
7688
  DECLARE v_code text; v_name text; v_nr text; v_tx bigint := -(txid_current() + 1125899906842624);
7763
7689
  BEGIN
7690
+ PERFORM public.voorraad_vereis('ingelogd', 'voorraad_alert_floriday_not_updated');
7764
7691
  SELECT p.article_number, p.product INTO v_code, v_name FROM public.products p
7765
7692
  WHERE lower(p.floriday_trade_item_id) = lower(p_key) OR p.trade_item_key = p_key OR 'product:' || p.id::text = p_key OR p.id::text = p_key
7766
7693
  LIMIT 1;
@@ -7808,6 +7735,7 @@ declare
7808
7735
  v_dead int := 0;
7809
7736
  v_sent int := 0;
7810
7737
  begin
7738
+ PERFORM public.voorraad_vereis('server', 'voorraad_cleanup_old_rows');
7811
7739
  delete from public.floriday_continuous_stock_queue
7812
7740
  where status = 'superseded'
7813
7741
  and created_at < now() - interval '2 days';
@@ -7842,6 +7770,7 @@ CREATE OR REPLACE FUNCTION public.voorraad_consume_external_fulfilled(p_order_id
7842
7770
  AS $function$
7843
7771
  DECLARE oi record; n integer := 0; v_norm text := public.normalize_order_source(COALESCE(p_source,'HO'));
7844
7772
  BEGIN
7773
+ PERFORM public.voorraad_vereis('ingelogd', 'voorraad_consume_external_fulfilled');
7845
7774
  IF p_order_id IS NULL OR COALESCE(array_length(p_ids,1),0) = 0 THEN RETURN 0; END IF;
7846
7775
  FOR oi IN SELECT id, source_line_key, quantity FROM logistics_active.order_items WHERE order_id = p_order_id LOOP
7847
7776
  IF oi.source_line_key IS NOT NULL AND oi.source_line_key = ANY(p_ids) THEN
@@ -7861,6 +7790,182 @@ END $function$
7861
7790
  REVOKE ALL ON FUNCTION voorraad_consume_external_fulfilled(uuid,text[],text) FROM PUBLIC;
7862
7791
  REVOKE ALL ON FUNCTION voorraad_consume_external_fulfilled(uuid,text[],text) FROM anon;
7863
7792
  GRANT EXECUTE ON FUNCTION voorraad_consume_external_fulfilled(uuid,text[],text) TO service_role;
7793
+ CREATE OR REPLACE FUNCTION public.voorraad_correctie_1098(p_uitvoeren boolean DEFAULT false)
7794
+ RETURNS jsonb
7795
+ LANGUAGE plpgsql
7796
+ SECURITY DEFINER
7797
+ SET search_path TO 'public'
7798
+ AS $function$
7799
+ DECLARE
7800
+ v_cache_n bigint; v_cache_h text; v_queue_n bigint; v_queue_h text;
7801
+ v_hash_ok boolean := NULL;
7802
+ v_ins int := 0; v_upd int := 0; v_art int := 0; v_meld int := 0; v_prod int := 0;
7803
+ v_ex_ins jsonb := '[]'; v_ex_upd jsonb := '[]'; v_ex_art jsonb := '[]'; v_ex_meld jsonb := '[]';
7804
+ v_door text; r record; e record; v_res jsonb;
7805
+ BEGIN
7806
+ IF NOT public.voorraad_is_server() THEN
7807
+ RAISE EXCEPTION 'Alleen de server mag deze correctie uitvoeren' USING ERRCODE = '42501';
7808
+ END IF;
7809
+ PERFORM set_config('stock.change_source', 'CORRECTIE_1098', true);
7810
+ v_door := COALESCE(auth.uid()::text, 'systeem:' || session_user::text);
7811
+
7812
+ IF p_uitvoeren THEN
7813
+ SELECT count(*), md5(coalesce(string_agg(t::text, '|' ORDER BY t::text), '')) INTO v_cache_n, v_cache_h FROM public.floriday_trade_items_cache t;
7814
+ SELECT count(*), md5(coalesce(string_agg(q::text, '|' ORDER BY q::text), '')) INTO v_queue_n, v_queue_h FROM public.floriday_continuous_stock_queue q;
7815
+ END IF;
7816
+
7817
+ FOR r IN
7818
+ SELECT p.id, p.article_number, p.plants_per_crate, p.crates_per_layer, p.layers_per_cart,
7819
+ c.connection_id, c.data->'packingConfigurations' AS cfg
7820
+ FROM public.products p
7821
+ JOIN public.floriday_trade_items_cache c
7822
+ ON c.floriday_id = NULLIF(btrim(p.floriday_trade_item_id), '')
7823
+ AND COALESCE(c.is_deleted, false) = false
7824
+ ORDER BY p.id
7825
+ LOOP
7826
+ v_prod := v_prod + 1;
7827
+ -- Twijfel = overslaan en melden. Sleutel per verpakking: Floriday-id, of zonder id het volgnummer in Floriday (product_id + volgnr).
7828
+ IF jsonb_typeof(r.cfg) IS DISTINCT FROM 'array' OR jsonb_array_length(r.cfg) = 0 THEN
7829
+ v_meld := v_meld + 1; IF jsonb_array_length(v_ex_meld) < 5 THEN v_ex_meld := v_ex_meld || jsonb_build_object('product_id', r.id, 'artikelcode', r.article_number, 'reden', 'geen verpakkingen in Floriday'); END IF; CONTINUE;
7830
+ END IF;
7831
+ IF (SELECT count(*) FROM public.floriday_trade_items_cache c2 WHERE c2.floriday_id = (SELECT NULLIF(btrim(floriday_trade_item_id),'') FROM public.products WHERE id = r.id) AND COALESCE(c2.is_deleted,false) = false) > 1 THEN
7832
+ v_meld := v_meld + 1; IF jsonb_array_length(v_ex_meld) < 5 THEN v_ex_meld := v_ex_meld || jsonb_build_object('product_id', r.id, 'artikelcode', r.article_number, 'reden', 'in meer dan één koppeling'); END IF; CONTINUE;
7833
+ END IF;
7834
+ IF (SELECT count(*) FROM jsonb_array_elements(r.cfg) x WHERE COALESCE((x->>'primary')::boolean, false)) <> 1 THEN
7835
+ v_meld := v_meld + 1; IF jsonb_array_length(v_ex_meld) < 5 THEN v_ex_meld := v_ex_meld || jsonb_build_object('product_id', r.id, 'artikelcode', r.article_number, 'reden', 'niet precies één primaire'); END IF; CONTINUE;
7836
+ END IF;
7837
+ IF EXISTS (SELECT 1 FROM jsonb_array_elements(r.cfg) x
7838
+ WHERE COALESCE(x->>'piecesPerPackage','') !~ '^[1-9][0-9]*$'
7839
+ OR COALESCE(x->>'packagesPerLayer','') !~ '^[1-9][0-9]*$'
7840
+ OR COALESCE(x->>'layersPerLoadCarrier','') !~ '^[1-9][0-9]*$'
7841
+ OR (x#>>'{package,vbnPackageCode}' IS NOT NULL AND x#>>'{package,vbnPackageCode}' !~ '^[0-9]+$'))
7842
+ OR (SELECT count(DISTINCT COALESCE(NULLIF(x->>'packingConfigurationId',''), NULLIF(x->>'id',''), '#' || o)) FROM jsonb_array_elements(r.cfg) WITH ORDINALITY t(x, o)) <> jsonb_array_length(r.cfg) THEN
7843
+ v_meld := v_meld + 1; IF jsonb_array_length(v_ex_meld) < 5 THEN v_ex_meld := v_ex_meld || jsonb_build_object('product_id', r.id, 'artikelcode', r.article_number, 'reden', 'veld ontbreekt of ongeldig'); END IF; CONTINUE;
7844
+ END IF;
7845
+ IF EXISTS (SELECT 1 FROM public.product_packings pp WHERE pp.product_id = r.id
7846
+ AND COALESCE(pp.floriday_packing_id, '#' || pp.sort_order) NOT IN
7847
+ (SELECT COALESCE(NULLIF(x->>'packingConfigurationId',''), NULLIF(x->>'id',''), '#' || o) FROM jsonb_array_elements(r.cfg) WITH ORDINALITY t(x, o)))
7848
+ OR EXISTS (SELECT 1 FROM public.product_packings pp WHERE pp.product_id = r.id
7849
+ GROUP BY COALESCE(pp.floriday_packing_id, '#' || pp.sort_order) HAVING count(*) > 1) THEN
7850
+ v_meld := v_meld + 1; IF jsonb_array_length(v_ex_meld) < 5 THEN v_ex_meld := v_ex_meld || jsonb_build_object('product_id', r.id, 'artikelcode', r.article_number, 'reden', 'lokale verpakking niet in Floriday (blijft staan)'); END IF; CONTINUE;
7851
+ END IF;
7852
+
7853
+ SELECT x INTO e FROM jsonb_array_elements(r.cfg) x WHERE COALESCE((x->>'primary')::boolean, false);
7854
+ IF (r.plants_per_crate, r.crates_per_layer, r.layers_per_cart) IS DISTINCT FROM
7855
+ ((e.x->>'piecesPerPackage')::int, (e.x->>'packagesPerLayer')::int, (e.x->>'layersPerLoadCarrier')::int) THEN
7856
+ v_art := v_art + 1;
7857
+ IF jsonb_array_length(v_ex_art) < 5 THEN
7858
+ v_ex_art := v_ex_art || jsonb_build_object('product_id', r.id, 'artikelcode', r.article_number,
7859
+ 'oud', concat_ws('/', r.plants_per_crate, r.crates_per_layer, r.layers_per_cart),
7860
+ 'nieuw', concat_ws('/', e.x->>'piecesPerPackage', e.x->>'packagesPerLayer', e.x->>'layersPerLoadCarrier'));
7861
+ END IF;
7862
+ END IF;
7863
+
7864
+ FOR e IN
7865
+ SELECT n.*, pp.id AS pp_id, to_jsonb(pp) AS pp_row
7866
+ FROM (
7867
+ SELECT o AS sort_order,
7868
+ NULLIF(COALESCE(NULLIF(x->>'packingConfigurationId',''), x->>'id'), '') AS fpid,
7869
+ COALESCE(NULLIF(x->>'packingConfigurationId',''), NULLIF(x->>'id',''), '#' || o) AS k,
7870
+ (x#>>'{package,vbnPackageCode}')::int AS vbn,
7871
+ x#>>'{package,customPackageId}' AS cpid,
7872
+ (SELECT f.id FROM public.fust_types f WHERE f.connection_id IS NULL AND f.vbn_code IS NOT NULL
7873
+ AND f.vbn_code::text = x#>>'{package,vbnPackageCode}' ORDER BY f.id LIMIT 1) AS fust,
7874
+ x->>'loadCarrier' AS lc,
7875
+ (x->>'piecesPerPackage')::int AS pcs,
7876
+ (x->>'packagesPerLayer')::int AS ppl,
7877
+ (x->>'layersPerLoadCarrier')::int AS lay,
7878
+ COALESCE((x->>'primary')::boolean, false) AS prim,
7879
+ COALESCE(ARRAY(SELECT jsonb_array_elements_text(CASE WHEN jsonb_typeof(x->'allowedCustomerOrganizationIds') = 'array' THEN x->'allowedCustomerOrganizationIds' ELSE '[]'::jsonb END)), '{}'::text[]) AS cust
7880
+ FROM jsonb_array_elements(r.cfg) WITH ORDINALITY t(x, o)
7881
+ ) n
7882
+ LEFT JOIN public.product_packings pp ON pp.product_id = r.id AND COALESCE(pp.floriday_packing_id, '#' || pp.sort_order) = n.k
7883
+ ORDER BY n.prim, n.sort_order
7884
+ LOOP
7885
+ IF e.pp_id IS NULL THEN
7886
+ v_ins := v_ins + 1;
7887
+ IF jsonb_array_length(v_ex_ins) < 5 THEN
7888
+ v_ex_ins := v_ex_ins || jsonb_build_object('product_id', r.id, 'artikelcode', r.article_number, 'floriday_packing_id', e.fpid, 'volgnr', e.sort_order,
7889
+ 'nieuw', concat_ws('/', e.pcs, e.ppl, e.lay) || CASE WHEN e.prim THEN ' (primair)' ELSE '' END);
7890
+ END IF;
7891
+ IF p_uitvoeren THEN
7892
+ INSERT INTO public.product_packings(product_id, source, floriday_packing_id, vbn_package_code, custom_package_id, fust_type_id,
7893
+ load_carrier, pieces_per_package, packages_per_layer, layers_per_load_carrier, is_primary, customer_organization_ids, sort_order)
7894
+ VALUES (r.id, 'floriday', e.fpid, e.vbn, e.cpid, e.fust, e.lc, e.pcs, e.ppl, e.lay, e.prim, e.cust, e.sort_order);
7895
+ END IF;
7896
+ ELSIF (e.pp_row->>'source', (e.pp_row->>'vbn_package_code')::int, e.pp_row->>'custom_package_id', (e.pp_row->>'fust_type_id')::uuid,
7897
+ e.pp_row->>'load_carrier', (e.pp_row->>'pieces_per_package')::int, (e.pp_row->>'packages_per_layer')::int,
7898
+ (e.pp_row->>'layers_per_load_carrier')::int, (e.pp_row->>'is_primary')::boolean,
7899
+ ARRAY(SELECT jsonb_array_elements_text(e.pp_row->'customer_organization_ids')), (e.pp_row->>'sort_order')::int)
7900
+ IS DISTINCT FROM ('floriday', e.vbn, e.cpid, e.fust, e.lc, e.pcs, e.ppl, e.lay, e.prim, e.cust, e.sort_order::int) THEN
7901
+ v_upd := v_upd + 1;
7902
+ IF jsonb_array_length(v_ex_upd) < 5 THEN
7903
+ v_ex_upd := v_ex_upd || jsonb_build_object('id', e.pp_id, 'product_id', r.id, 'artikelcode', r.article_number,
7904
+ 'oud', concat_ws('/', e.pp_row->>'pieces_per_package', e.pp_row->>'packages_per_layer', e.pp_row->>'layers_per_load_carrier'),
7905
+ 'nieuw', concat_ws('/', e.pcs, e.ppl, e.lay));
7906
+ END IF;
7907
+ IF p_uitvoeren THEN
7908
+ UPDATE public.product_packings SET source = 'floriday', vbn_package_code = e.vbn, custom_package_id = e.cpid,
7909
+ fust_type_id = e.fust, load_carrier = e.lc, pieces_per_package = e.pcs, packages_per_layer = e.ppl,
7910
+ layers_per_load_carrier = e.lay, is_primary = e.prim, customer_organization_ids = e.cust,
7911
+ sort_order = e.sort_order, updated_at = now()
7912
+ WHERE id = e.pp_id;
7913
+ END IF;
7914
+ END IF;
7915
+ END LOOP;
7916
+ END LOOP;
7917
+
7918
+ IF p_uitvoeren THEN
7919
+ v_hash_ok := v_cache_n = (SELECT count(*) FROM public.floriday_trade_items_cache)
7920
+ AND v_cache_h = (SELECT md5(coalesce(string_agg(t::text, '|' ORDER BY t::text), '')) FROM public.floriday_trade_items_cache t)
7921
+ AND v_queue_n = (SELECT count(*) FROM public.floriday_continuous_stock_queue)
7922
+ AND v_queue_h = (SELECT md5(coalesce(string_agg(q::text, '|' ORDER BY q::text), '')) FROM public.floriday_continuous_stock_queue q);
7923
+ IF NOT v_hash_ok THEN
7924
+ RAISE EXCEPTION 'Floriday-kopie of wachtrij is gewijzigd; alles teruggedraaid' USING ERRCODE = 'P0001';
7925
+ END IF;
7926
+ END IF;
7927
+
7928
+ v_res := jsonb_build_object(
7929
+ 'uitgevoerd', p_uitvoeren,
7930
+ 'artikelen_bekeken', v_prod,
7931
+ 'toevoegen', jsonb_build_object('aantal', v_ins, 'voorbeelden', v_ex_ins),
7932
+ 'bijwerken', jsonb_build_object('aantal', v_upd, 'voorbeelden', v_ex_upd),
7933
+ 'artikelvelden', jsonb_build_object('aantal', v_art, 'voorbeelden', v_ex_art),
7934
+ 'meldingen', jsonb_build_object('aantal', v_meld, 'voorbeelden', v_ex_meld),
7935
+ 'floriday_kopie_en_wachtrij_ongewijzigd', v_hash_ok);
7936
+ INSERT INTO public.voorraad_correcties(correctie, uitgevoerd_door, proef, aantallen, hash_ok)
7937
+ VALUES ('1098', v_door, NOT p_uitvoeren, v_res - '{toevoegen,bijwerken,artikelvelden,meldingen}'::text[]
7938
+ || jsonb_build_object('toevoegen', v_ins, 'bijwerken', v_upd, 'artikelvelden', v_art, 'meldingen', v_meld), v_hash_ok);
7939
+ RETURN v_res;
7940
+ END $function$
7941
+ ;
7942
+ REVOKE ALL ON FUNCTION voorraad_correctie_1098(boolean) FROM PUBLIC;
7943
+ REVOKE ALL ON FUNCTION voorraad_correctie_1098(boolean) FROM anon;
7944
+ GRANT EXECUTE ON FUNCTION voorraad_correctie_1098(boolean) TO authenticated;
7945
+ GRANT EXECUTE ON FUNCTION voorraad_correctie_1098(boolean) TO service_role;
7946
+ CREATE OR REPLACE FUNCTION public.voorraad_correctie_status(p_correctie text DEFAULT '1098'::text)
7947
+ RETURNS jsonb
7948
+ LANGUAGE plpgsql
7949
+ STABLE SECURITY DEFINER
7950
+ SET search_path TO 'public'
7951
+ AS $function$
7952
+ DECLARE r record;
7953
+ BEGIN
7954
+ IF NOT (public.voorraad_is_server() OR auth.uid() IS NOT NULL) THEN
7955
+ RAISE EXCEPTION 'Niet ingelogd' USING ERRCODE = '42501';
7956
+ END IF;
7957
+ SELECT * INTO r FROM public.voorraad_correcties
7958
+ WHERE correctie = p_correctie AND NOT proef AND hash_ok IS TRUE
7959
+ ORDER BY uitgevoerd_op DESC LIMIT 1;
7960
+ IF NOT FOUND THEN RETURN jsonb_build_object('correctie', p_correctie, 'uitgevoerd', false); END IF;
7961
+ RETURN jsonb_build_object('correctie', p_correctie, 'uitgevoerd', true, 'laatst_op', r.uitgevoerd_op,
7962
+ 'door', r.uitgevoerd_door, 'aantallen', r.aantallen);
7963
+ END $function$
7964
+ ;
7965
+ REVOKE ALL ON FUNCTION voorraad_correctie_status(text) FROM PUBLIC;
7966
+ REVOKE ALL ON FUNCTION voorraad_correctie_status(text) FROM anon;
7967
+ GRANT EXECUTE ON FUNCTION voorraad_correctie_status(text) TO authenticated;
7968
+ GRANT EXECUTE ON FUNCTION voorraad_correctie_status(text) TO service_role;
7864
7969
  CREATE OR REPLACE FUNCTION public.voorraad_external_fulfilled_ids(p_cf jsonb)
7865
7970
  RETURNS text[]
7866
7971
  LANGUAGE sql
@@ -7906,6 +8011,7 @@ CREATE OR REPLACE FUNCTION public.voorraad_hook_base_url()
7906
8011
  AS $function$
7907
8012
  DECLARE v_target text; v_shell text; v_old text; v_probe text; v_last text; v_status int;
7908
8013
  BEGIN
8014
+ PERFORM public.voorraad_vereis('server', 'voorraad_hook_base_url');
7909
8015
  SELECT value INTO v_target FROM module_runtime_config WHERE key = 'voorraad_hook_target';
7910
8016
  SELECT NULLIF(rtrim(value,'/'),'') INTO v_shell FROM module_runtime_config WHERE key = 'shell_base_url';
7911
8017
  SELECT NULLIF(rtrim(value,'/'),'') INTO v_old FROM module_runtime_config WHERE key = 'voorraad_base_url';
@@ -7960,6 +8066,7 @@ CREATE OR REPLACE FUNCTION public.voorraad_hook_post(p_path text, p_timeout inte
7960
8066
  AS $function$
7961
8067
  DECLARE v_base text; v_key text;
7962
8068
  BEGIN
8069
+ PERFORM public.voorraad_vereis('server', 'voorraad_hook_post');
7963
8070
  v_base := public.voorraad_hook_base_url();
7964
8071
  SELECT value INTO v_key FROM module_runtime_config WHERE key = 'voorraad_hook_apikey';
7965
8072
  IF COALESCE(v_base,'') = '' OR COALESCE(v_key,'') = '' THEN
@@ -8007,6 +8114,74 @@ END $function$
8007
8114
  REVOKE ALL ON FUNCTION voorraad_hook_switch_jobs() FROM PUBLIC;
8008
8115
  REVOKE ALL ON FUNCTION voorraad_hook_switch_jobs() FROM anon;
8009
8116
  GRANT EXECUTE ON FUNCTION voorraad_hook_switch_jobs() TO service_role;
8117
+ CREATE OR REPLACE FUNCTION public.voorraad_install_check()
8118
+ RETURNS jsonb
8119
+ LANGUAGE plpgsql
8120
+ STABLE SECURITY DEFINER
8121
+ SET search_path TO 'public'
8122
+ AS $function$
8123
+ DECLARE v_f text[] := ARRAY['_floriday_queue_cleanup_batch','_migrate_rewrite_stock_key','_stock_import_caller','apply_fust_stock_mutation','apply_stock_consumption','apply_stock_reservation','autolink_product_to_floriday','backfill_open_order_reservations','calculate_floriday_desired_stock','calculate_structure_available','canonicalize_trade_item_stock_key','check_product_structure','claim_floriday_base_supply','claim_floriday_stock_queue','cleanup_floriday_stock_queue','cleanup_voorraad_module_history','complete_floriday_base_supply','complete_floriday_stock_queue','complete_floriday_stock_queue_outcome','consume_bom_for_production','consume_order_item','drain_inventory_recompute_queue','enqueue_floriday_continuous_stock','enqueue_floriday_continuous_stock_after_warehouse_preference','enqueue_structure_recompute','ensure_inventory_recompute_drain','ensure_trade_item_stock_row','ensure_trade_item_stock_row_key','fcsq_block_disabled','fcsq_fallback_supersede','fcsq_wake_base_supply','find_trade_item_key','floriday_adopt_connection','floriday_autoheal_connections','floriday_capture_stock_snapshot','floriday_cleanup_stock_queue_v2','floriday_compact_stock_snapshots','floriday_default_owner','floriday_desired_stock_regression_check','floriday_ensure_inventory_settings','floriday_erp_leading_preview','floriday_fill_publish_horizon','floriday_follow_apply','floriday_ingest_sales_orders','floriday_leading_summary','floriday_make_erp_leading','floriday_norm_offer_forms','floriday_orphan_settings_count','floriday_publish_horizon_weeks','floriday_queue_erp_guard','floriday_queue_first_positive','floriday_recompute_inventory','floriday_relink_products','floriday_requeue_dead_letters','floriday_settings_change_recompute','floriday_settings_change_recompute_stmt','floriday_stock_change_recompute','floriday_stock_queue_supersede','floriday_takeover_apply','floriday_takeover_fetch_state','floriday_takeover_store_forms','floriday_takeover_undo','fust_negative_stock_report','fust_types_duplicate_guard','get_active_floriday_context','get_group_allocation_keys','get_group_head_key','get_product_stock','get_product_structure_totals','get_product_structure_totals_batch','inventory_inline_budget_ok','merge_products','normalize_order_source','process_inventory_recompute_queue','produce_designed_product','product_active_group_id','product_id_from_ref','product_is_set','product_label','product_member_group_ids','product_own_available','product_packings_derive','product_packings_replace','product_packings_sync_from_floriday','product_primary_pieces','product_set_depth_above','product_set_depth_below','product_structure_ancestors','product_structure_max_depth','product_structure_path','product_tray_stickers_touch','product_type','promote_trade_item_key','recompute_all_reserved_stock','recompute_order_status','recompute_reserved_for_order','recompute_reserved_stock','requeue_skipped_stock_rows','reset_stalled_floriday_stock_queue','resolve_group_allocation','resolve_group_member_stock_key','resolve_product_id_for_item','resolve_stock_trade_item_key','reverse_production','run_floriday_base_supply_process','run_floriday_continuous_stock_sync','run_floriday_follow','run_floriday_stock_queue_process','run_floriday_stock_reconcile','run_logistics_tasks_dispatch','self_heal_floriday_links','set_module_runtime_config_updated_at','set_product_bom_tenant','set_product_stock','set_updated_at_order_fust_bookings','stock_import_cleanup','stock_import_create','stock_import_process_chunk','stock_import_stop','stock_import_undo_chunk','sync_order_item_fusten','sync_products_quantity_from_stock','touch_fust_types_updated_at','trade_item_drafts_readonly','trade_item_prices_touch_updated_at','trade_item_stock_duplicates','trg_cart_numbers_sync_order_status_fn','trg_cart_session_consume_fn','trg_check_product_structure','trg_floriday_cache_relink_fn','trg_floriday_trade_items_autolink_fn','trg_order_items_autolink_fn','trg_order_items_fusten','trg_order_items_reserve_fn','trg_orders_cancel_release_fn','trg_orders_consume_on_complete_fn','trg_orders_default_phase_fn','trg_orders_external_fulfilled_fn','trg_orders_recompute_reserved_fn','trg_pgi_assign_sort_order','trg_pgi_order_taken_check','trg_process_recompute_on_commit','trg_product_packings_derive_fn','trg_products_autolink_fn','trg_products_floriday_link_enqueue','trg_products_seed_stock_fn','trg_requeue_no_warehouse_cache','trg_requeue_no_warehouse_connection','trg_requeue_no_warehouse_item_pref','trg_requeue_skipped_no_price','trg_stock_structure_recompute_ins','trg_stock_structure_recompute_upd','trg_structure_change_recompute','trg_trade_item_drafts_autolink_fn','trg_trade_item_stock_auto_bom','trg_trade_item_stock_auto_bom_reverse','trg_trade_item_stock_log_change','trg_trade_item_stock_reserve_fn','trg_trade_item_stock_zero_watch','trg_trade_items_cache_autolink_fn','update_updated_at_column','voorraad_alert_floriday_not_updated','voorraad_can_write','voorraad_cleanup_old_rows','voorraad_consume_external_fulfilled','voorraad_correctie_1098','voorraad_correctie_status','voorraad_external_fulfilled_ids','voorraad_floriday_leads_key','voorraad_hook_base_url','voorraad_hook_on','voorraad_hook_post','voorraad_hook_switch_jobs','voorraad_install_check','voorraad_is_server','voorraad_mark_floriday_sold_before_switch','voorraad_production_enabled','voorraad_schema_ddl','voorraad_touch_updated_at','voorraad_vereis','voorraad_week_is_past'];
8124
+ v_t text[] := ARRAY['aa_fcsq_erp_leading_guard@floriday_continuous_stock_queue','aa_fis_norm_offer_forms@floriday_inventory_settings','aa_pgi_assign_sort_order@product_group_items','fcsq_block_disabled@floriday_continuous_stock_queue','fcsq_wake_base_supply_ins@floriday_continuous_stock_queue','fcsq_wake_base_supply_upd@floriday_continuous_stock_queue','fust_stock_mutations_apply@fust_stock_mutations','fust_types_duplicate_guard@fust_types','fust_types_touch@fust_types','product_packings_derive_trg@product_packings','product_packings_updated_at@product_packings','set_product_bom_tenant_trigger@product_bom_items','trade_item_prices_set_updated_at@trade_item_prices','trg_cart_numbers_sync_order_status@cart_numbers','trg_cart_session_consume@cart_control_sessions','trg_check_product_structure@product_bom_items','trg_check_product_structure@product_group_items','trg_check_product_structure@product_groups','trg_fcs_queue_updated_at@floriday_continuous_stock_queue','trg_fcsq_fallback_supersede@floriday_continuous_stock_queue','trg_fcsq_supersede@floriday_continuous_stock_queue','trg_floriday_cache_relink@floriday_trade_items_cache','trg_floriday_ensure_inventory_settings@products','trg_floriday_gate_stock_queue@floriday_continuous_stock_queue','trg_floriday_settings_change_recompute_ins@floriday_inventory_settings','trg_floriday_settings_change_recompute_upd@floriday_inventory_settings','trg_floriday_stock_change_recompute@trade_item_stock','trg_floriday_takeover_runs_updated_at@floriday_takeover_runs','trg_floriday_trade_items_autolink@floriday_trade_items_cache','trg_fust_types_requeue_blocked@fust_types','trg_module_runtime_config_updated_at@module_runtime_config','trg_order_fust_bookings_updated_at@order_fust_bookings','trg_order_items_autolink@order_items','trg_order_items_fusten_aiud@order_items','trg_order_items_reserve@order_items','trg_orders_cancel_release@orders','trg_orders_consume_on_complete@orders','trg_orders_default_phase@orders','trg_orders_external_fulfilled@orders','trg_orders_recompute_reserved@orders','trg_prevent_duplicate_product_draft@trade_item_drafts','trg_product_tray_stickers_touch@product_tray_stickers','trg_products_autolink@products','trg_products_floriday_link_enqueue@products','trg_products_seed_stock@products','trg_requeue_no_warehouse_cache@floriday_trade_items_cache','trg_requeue_no_warehouse_connection@floriday_connections','trg_requeue_no_warehouse_item_pref@trade_item_warehouse_preferences','trg_requeue_skipped_no_price@products','trg_stock_mutation_alert@stock_mutation_log','trg_stock_structure_recompute_ins@trade_item_stock','trg_stock_structure_recompute_upd@trade_item_stock','trg_structure_change_recompute@product_bom_items','trg_structure_change_recompute@product_group_items','trg_structure_change_recompute@product_groups','trg_sync_layer_stock_from_trade_stock@trade_item_stock','trg_sync_products_quantity_from_stock_del@trade_item_stock','trg_sync_products_quantity_from_stock_iu@trade_item_stock','trg_trade_item_drafts_autolink@trade_item_drafts','trg_trade_item_drafts_readonly@trade_item_drafts','trg_trade_item_stock_canonical_key@trade_item_stock','trg_trade_item_stock_publish@trade_item_stock','trg_trade_item_stock_reserve@trade_item_stock','trg_trade_item_stock_updated_at@trade_item_stock','trg_trade_item_stock_zero_watch@trade_item_stock','trg_trade_item_supply_lines_touch@trade_item_supply_lines','trg_trade_item_warehouse_preference_enqueue_stock@trade_item_warehouse_preferences','trg_trade_items_cache_autolink@floriday_trade_items_cache','trg_voorraad_buffer_state_updated_at@voorraad_buffer_state','trg_voorraad_event_log_updated_at@voorraad_logistics_event_log','update_trade_item_drafts_updated_at@trade_item_drafts','update_trade_item_warehouse_preferences_updated_at@trade_item_warehouse_preferences','update_voorraad_custom_fields_updated_at@voorraad_custom_fields','update_voorraad_publish_jobs_updated_at@voorraad_publish_jobs','update_voorraad_publish_learnings_updated_at@voorraad_publish_learnings','update_voorraad_publish_steps_updated_at@voorraad_publish_steps','voorraad_hook_switch_jobs@module_runtime_config','zz_fcsq_first_positive@floriday_continuous_stock_queue'];
8125
+ v_p text[] := ARRAY['Authenticated can add learnings@voorraad_publish_learnings','Authenticated can delete voorraad custom fields@voorraad_custom_fields','Authenticated can insert vbn required characteristics@floriday_vbn_required_characteristics','Authenticated can insert voorraad custom fields@voorraad_custom_fields','Authenticated can read continuous stock cache@floriday_continuous_stock_cache','Authenticated can read learnings@voorraad_publish_learnings','Authenticated can read vbn required characteristics@floriday_vbn_required_characteristics','Authenticated can read voorraad custom fields@voorraad_custom_fields','Authenticated can update learnings@voorraad_publish_learnings','Authenticated can update vbn required characteristics@floriday_vbn_required_characteristics','Authenticated can update voorraad custom fields@voorraad_custom_fields','Authenticated delete trade_item_warehouse_preferences@trade_item_warehouse_preferences','Authenticated insert trade_item_warehouse_preferences@trade_item_warehouse_preferences','Authenticated select trade_item_warehouse_preferences@trade_item_warehouse_preferences','Authenticated update trade_item_warehouse_preferences@trade_item_warehouse_preferences','Eigen momentopnamen lezen@inventory_stock_snapshots','Eigen voorraadinstellingen beheren@floriday_inventory_settings','Eigenaar leest importporties@stock_import_chunks','Eigenaar leest importregels@stock_import_rows','Fust mutations delete@fust_stock_mutations','Fust mutations insert@fust_stock_mutations','Fust mutations read@fust_stock_mutations','Fust mutations update@fust_stock_mutations','Fust types delete@fust_types','Fust types insert@fust_types','Fust types read@fust_types','Fust types update@fust_types','Ingelogde gebruikers kunnen bufferstatus lezen@voorraad_buffer_state','Ingelogde gebruikers kunnen de eventlog lezen@voorraad_logistics_event_log','Ingelogde gebruikers kunnen fustboekingen lezen@order_fust_bookings','Ingelogde gebruikers lezen drempels@inventory_recompute_limits','Ingelogde gebruikers lezen opruimlogboek@structure_cleanup_log','Ingelogde gebruikers lezen opruimruns@floriday_queue_cleanup_runs','Ingelogden beheren traystickers@product_tray_stickers','Ingelogden lezen herberekenwachtrij@inventory_recompute_queue','Ingelogden lezen voorraadmeldingen@stock_integrity_alerts','Ingelogden markeren voorraadmeldingen als gezien@stock_integrity_alerts','Own queue rows select@floriday_continuous_stock_queue','Owner delete trade_item_drafts@trade_item_drafts','Owner insert trade_item_drafts@trade_item_drafts','Owner select trade_item_drafts@trade_item_drafts','Owner update trade_item_drafts@trade_item_drafts','Signed-in users read reconcile runs@floriday_reconcile_runs','Stock delete@trade_item_stock','Stock insert@trade_item_stock','Stock read@trade_item_stock','Stock update@trade_item_stock','Synchronisatieposities lezen@floriday_sync_cursors','Users can insert own publish attempts@voorraad_publish_attempts','Users can read own publish attempts@voorraad_publish_attempts','Users can read own stock mutations@stock_mutation_log','Users manage own import runs@stock_import_runs','Users manage own supply lines@trade_item_supply_lines','Users manage own trade item prices@trade_item_prices','Users manage their own publish jobs@voorraad_publish_jobs','Users manage their own publish steps@voorraad_publish_steps','Verwerkte orders lezen@floriday_sales_order_ingest','Voorraadschrijvers lezen overnameregels@floriday_takeover_items','Voorraadschrijvers lezen overnames@floriday_takeover_runs','authenticated can insert merge log@product_merge_log','authenticated can read merge log@product_merge_log','service role only@voorraad_media_upload_failures','product_packings_read@product_packings','product_packings_write@product_packings'];
8126
+ v_open text[] := ARRAY['voorraad_is_server','voorraad_vereis'];
8127
+ v_func jsonb; v_trig jsonb; v_pol jsonb;
8128
+ BEGIN
8129
+ IF NOT public.voorraad_is_server() THEN
8130
+ RAISE EXCEPTION 'Alleen de server mag de installatiecontrole uitvoeren' USING ERRCODE = '42501';
8131
+ END IF;
8132
+ SELECT jsonb_agg(x ORDER BY x->>'functie', x->>'argumenten') INTO v_func FROM (
8133
+ SELECT jsonb_build_object('functie', n.f, 'argumenten', pg_get_function_identity_arguments(p.oid), 'aanwezig', p.oid IS NOT NULL,
8134
+ 'soort', CASE WHEN p.oid IS NULL THEN NULL WHEN pg_get_function_result(p.oid) = 'trigger' THEN 'trigger'
8135
+ WHEN p.provolatile = 'i' THEN 'rekenhulp' WHEN l.lanname = 'sql' THEN 'sql' ELSE 'plpgsql' END,
8136
+ 'interne_controle', CASE WHEN p.oid IS NULL THEN NULL
8137
+ WHEN n.f = ANY(v_open) THEN false
8138
+ ELSE p.prosrc ~* 'voorraad_vereis\(|voorraad_is_server\(|42501|auth\.uid\(\)|auth\.role\(\)|voorraad_can_write\(|_stock_import_caller\(' END,
8139
+ 'controle_niveau', substring(p.prosrc from 'voorraad_vereis\(''([a-z]+)'''),
8140
+ 'anon_uitvoerrecht', CASE WHEN p.oid IS NULL THEN NULL ELSE has_function_privilege('anon', p.oid, 'EXECUTE') END,
8141
+ 'authenticated_uitvoerrecht', CASE WHEN p.oid IS NULL THEN NULL ELSE has_function_privilege('authenticated', p.oid, 'EXECUTE') END) x
8142
+ FROM unnest(v_f) n(f)
8143
+ LEFT JOIN pg_proc p ON p.proname = n.f AND p.pronamespace = 'public'::regnamespace
8144
+ LEFT JOIN pg_language l ON l.oid = p.prolang) s;
8145
+ SELECT jsonb_agg(jsonb_build_object('trigger', split_part(t, '@', 1), 'tabel', split_part(t, '@', 2),
8146
+ 'aanwezig', EXISTS (SELECT 1 FROM pg_trigger g WHERE NOT g.tgisinternal AND g.tgname = split_part(t, '@', 1)
8147
+ AND g.tgrelid = to_regclass('public.' || split_part(t, '@', 2)))) ORDER BY t) INTO v_trig FROM unnest(v_t) t;
8148
+ SELECT jsonb_agg(jsonb_build_object('policy', split_part(t, '@', 1), 'tabel', split_part(t, '@', 2),
8149
+ 'aanwezig', EXISTS (SELECT 1 FROM pg_policies x WHERE x.schemaname = 'public' AND x.policyname = split_part(t, '@', 1)
8150
+ AND x.tablename = split_part(t, '@', 2))) ORDER BY t) INTO v_pol FROM unnest(v_p) t;
8151
+ RETURN jsonb_build_object(
8152
+ 'samenvatting', jsonb_build_object(
8153
+ 'functies', jsonb_array_length(v_func),
8154
+ 'functies_ontbrekend', (SELECT count(*) FROM jsonb_array_elements(v_func) e WHERE NOT (e->>'aanwezig')::boolean),
8155
+ 'functies_zonder_controle_plpgsql', (SELECT count(*) FROM jsonb_array_elements(v_func) e WHERE e->>'soort' = 'plpgsql' AND NOT COALESCE((e->>'interne_controle')::boolean, false)),
8156
+ 'triggers_ontbrekend', (SELECT count(*) FROM jsonb_array_elements(v_trig) e WHERE NOT (e->>'aanwezig')::boolean),
8157
+ 'policies_ontbrekend', (SELECT count(*) FROM jsonb_array_elements(v_pol) e WHERE NOT (e->>'aanwezig')::boolean),
8158
+ 'bewust_open', 'geen pickscherm/scanner-functies zonder inlog in Voorraad'),
8159
+ 'correctie_1098', public.voorraad_correctie_status('1098'),
8160
+ 'functies', v_func, 'triggers', v_trig, 'policies', v_pol);
8161
+ END $function$
8162
+ ;
8163
+ REVOKE ALL ON FUNCTION voorraad_install_check() FROM PUBLIC;
8164
+ REVOKE ALL ON FUNCTION voorraad_install_check() FROM anon;
8165
+ GRANT EXECUTE ON FUNCTION voorraad_install_check() TO authenticated;
8166
+ GRANT EXECUTE ON FUNCTION voorraad_install_check() TO service_role;
8167
+ CREATE OR REPLACE FUNCTION public.voorraad_is_server()
8168
+ RETURNS boolean
8169
+ LANGUAGE sql
8170
+ STABLE
8171
+ SET search_path TO 'public'
8172
+ AS $function$
8173
+ SELECT COALESCE(NULLIF(current_setting('request.jwt.claims', true), '')::jsonb ->> 'role', '') = 'service_role'
8174
+ OR session_user::text <> 'authenticator'
8175
+ $function$
8176
+ ;
8177
+ REVOKE ALL ON FUNCTION voorraad_is_server() FROM PUBLIC;
8178
+ REVOKE ALL ON FUNCTION voorraad_is_server() FROM anon;
8179
+ REVOKE EXECUTE ON FUNCTION voorraad_is_server() FROM PUBLIC;
8180
+ GRANT EXECUTE ON FUNCTION voorraad_is_server() TO authenticated;
8181
+ GRANT EXECUTE ON FUNCTION voorraad_is_server() TO service_role;
8182
+ GRANT EXECUTE ON FUNCTION voorraad_is_server() TO anon;
8183
+ GRANT EXECUTE ON FUNCTION voorraad_is_server() TO authenticated;
8184
+ GRANT EXECUTE ON FUNCTION voorraad_is_server() TO service_role;
8010
8185
  CREATE OR REPLACE FUNCTION public.voorraad_mark_floriday_sold_before_switch(p_product_id uuid)
8011
8186
  RETURNS integer
8012
8187
  LANGUAGE plpgsql
@@ -8015,6 +8190,7 @@ CREATE OR REPLACE FUNCTION public.voorraad_mark_floriday_sold_before_switch(p_pr
8015
8190
  AS $function$
8016
8191
  DECLARE r record; n int := 0; v_key text;
8017
8192
  BEGIN
8193
+ PERFORM public.voorraad_vereis('ingelogd', 'voorraad_mark_floriday_sold_before_switch');
8018
8194
  SELECT COALESCE(NULLIF(btrim(p.floriday_trade_item_id),''), p.trade_item_key, 'product:'||p.id::text) INTO v_key
8019
8195
  FROM public.products p WHERE p.id = p_product_id;
8020
8196
  FOR r IN
@@ -8062,6 +8238,7 @@ CREATE OR REPLACE FUNCTION public.voorraad_schema_ddl(p_tables text[], p_functio
8062
8238
  AS $function$
8063
8239
  DECLARE o text := ''; r record; q text; g text;
8064
8240
  BEGIN
8241
+ PERFORM public.voorraad_vereis('server', 'voorraad_schema_ddl');
8065
8242
  -- reeksen
8066
8243
  FOR r IN SELECT DISTINCT substring(pg_get_expr(d.adbin, d.adrelid) from 'nextval\(''(?:public\.)?([^'']+)''') AS s
8067
8244
  FROM pg_attrdef d JOIN pg_class c ON c.oid = d.adrelid JOIN pg_namespace n ON n.oid = c.relnamespace
@@ -8175,6 +8352,32 @@ GRANT EXECUTE ON FUNCTION voorraad_touch_updated_at() TO service_role;
8175
8352
  REVOKE EXECUTE ON FUNCTION voorraad_touch_updated_at() FROM anon;
8176
8353
  GRANT EXECUTE ON FUNCTION voorraad_touch_updated_at() TO authenticated;
8177
8354
  GRANT EXECUTE ON FUNCTION voorraad_touch_updated_at() TO service_role;
8355
+ CREATE OR REPLACE FUNCTION public.voorraad_vereis(p_niveau text, p_functie text)
8356
+ RETURNS void
8357
+ LANGUAGE plpgsql
8358
+ STABLE
8359
+ SET search_path TO 'public'
8360
+ AS $function$
8361
+ BEGIN
8362
+ -- Server (service_role of directe databaseverbinding) mag altijd.
8363
+ IF public.voorraad_is_server() THEN RETURN; END IF;
8364
+ -- Keten: binnen een trigger, of aangeroepen door een andere functie (de bovenste aanroep noemt deze functie niet).
8365
+ IF pg_trigger_depth() > 0 THEN RETURN; END IF;
8366
+ IF COALESCE(current_query(), '') !~* ('\m' || p_functie || '\M') THEN RETURN; END IF;
8367
+ IF p_niveau = 'ingelogd' AND auth.uid() IS NOT NULL THEN RETURN; END IF;
8368
+ RAISE EXCEPTION 'Geen toegang tot %: %', p_functie,
8369
+ CASE WHEN p_niveau = 'server' THEN 'alleen de server mag deze functie aanroepen' ELSE 'log eerst in' END
8370
+ USING ERRCODE = '42501';
8371
+ END $function$
8372
+ ;
8373
+ REVOKE ALL ON FUNCTION voorraad_vereis(text,text) FROM PUBLIC;
8374
+ REVOKE ALL ON FUNCTION voorraad_vereis(text,text) FROM anon;
8375
+ REVOKE EXECUTE ON FUNCTION voorraad_vereis(text,text) FROM PUBLIC;
8376
+ GRANT EXECUTE ON FUNCTION voorraad_vereis(text,text) TO authenticated;
8377
+ GRANT EXECUTE ON FUNCTION voorraad_vereis(text,text) TO service_role;
8378
+ GRANT EXECUTE ON FUNCTION voorraad_vereis(text,text) TO anon;
8379
+ GRANT EXECUTE ON FUNCTION voorraad_vereis(text,text) TO authenticated;
8380
+ GRANT EXECUTE ON FUNCTION voorraad_vereis(text,text) TO service_role;
8178
8381
  CREATE OR REPLACE FUNCTION public.voorraad_week_is_past(p_year integer, p_week integer)
8179
8382
  RETURNS boolean
8180
8383
  LANGUAGE sql
@@ -8224,6 +8427,7 @@ DO $c$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'trade_i
8224
8427
  DO $c$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'trade_item_supply_lines_pkey' AND conrelid = 'public.trade_item_supply_lines'::regclass) THEN ALTER TABLE public.trade_item_supply_lines ADD CONSTRAINT trade_item_supply_lines_pkey PRIMARY KEY (id); END IF; END $c$;
8225
8428
  DO $c$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'trade_item_warehouse_preferences_pkey' AND conrelid = 'public.trade_item_warehouse_preferences'::regclass) THEN ALTER TABLE public.trade_item_warehouse_preferences ADD CONSTRAINT trade_item_warehouse_preferences_pkey PRIMARY KEY (id); END IF; END $c$;
8226
8429
  DO $c$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'voorraad_buffer_state_pkey' AND conrelid = 'public.voorraad_buffer_state'::regclass) THEN ALTER TABLE public.voorraad_buffer_state ADD CONSTRAINT voorraad_buffer_state_pkey PRIMARY KEY (buffer_id); END IF; END $c$;
8430
+ DO $c$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'voorraad_correcties_pkey' AND conrelid = 'public.voorraad_correcties'::regclass) THEN ALTER TABLE public.voorraad_correcties ADD CONSTRAINT voorraad_correcties_pkey PRIMARY KEY (id); END IF; END $c$;
8227
8431
  DO $c$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'voorraad_custom_fields_pkey' AND conrelid = 'public.voorraad_custom_fields'::regclass) THEN ALTER TABLE public.voorraad_custom_fields ADD CONSTRAINT voorraad_custom_fields_pkey PRIMARY KEY (id); END IF; END $c$;
8228
8432
  DO $c$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'voorraad_logistics_event_log_pkey' AND conrelid = 'public.voorraad_logistics_event_log'::regclass) THEN ALTER TABLE public.voorraad_logistics_event_log ADD CONSTRAINT voorraad_logistics_event_log_pkey PRIMARY KEY (event_id); END IF; END $c$;
8229
8433
  DO $c$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'voorraad_media_upload_failures_pkey' AND conrelid = 'public.voorraad_media_upload_failures'::regclass) THEN ALTER TABLE public.voorraad_media_upload_failures ADD CONSTRAINT voorraad_media_upload_failures_pkey PRIMARY KEY (id); END IF; END $c$;
@@ -8467,6 +8671,8 @@ ALTER TABLE public.voorraad_buffer_state ENABLE ROW LEVEL SECURITY;
8467
8671
  REVOKE ALL ON public.voorraad_buffer_state FROM anon;
8468
8672
  GRANT DELETE, INSERT, REFERENCES, SELECT, TRIGGER, TRUNCATE, UPDATE ON public.voorraad_buffer_state TO authenticated;
8469
8673
  GRANT DELETE, INSERT, REFERENCES, SELECT, TRIGGER, TRUNCATE, UPDATE ON public.voorraad_buffer_state TO service_role;
8674
+ ALTER TABLE public.voorraad_correcties ENABLE ROW LEVEL SECURITY;
8675
+ GRANT DELETE, INSERT, REFERENCES, SELECT, TRIGGER, TRUNCATE, UPDATE ON public.voorraad_correcties TO service_role;
8470
8676
  ALTER TABLE public.voorraad_custom_fields ENABLE ROW LEVEL SECURITY;
8471
8677
  REVOKE ALL ON public.voorraad_custom_fields FROM anon;
8472
8678
  GRANT DELETE, INSERT, REFERENCES, SELECT, TRIGGER, TRUNCATE, UPDATE ON public.voorraad_custom_fields TO authenticated;