@flowselections/floriday-voorraad 1.0.139 → 1.0.141

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.
@@ -1,4 +1,4 @@
1
- -- @flowselections/floriday-voorraad 1.0.136 – volledig schema (idempotent, geen gegevens).
1
+ -- @flowselections/floriday-voorraad 1.0.141 – 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.
@@ -2291,7 +2291,6 @@ BEGIN
2291
2291
  RETURN NEW;
2292
2292
  END IF;
2293
2293
 
2294
- -- Alleen gekoppelde artikelen: Floriday-id op het artikel, of bekend in de Floriday-cache.
2295
2294
  SELECT p.id INTO v_pid FROM public.products p WHERE p.floriday_trade_item_id = NEW.trade_item_key LIMIT 1;
2296
2295
  IF v_pid IS NULL AND NOT EXISTS (
2297
2296
  SELECT 1 FROM public.floriday_trade_items_cache c
@@ -2300,21 +2299,30 @@ BEGIN
2300
2299
  RETURN NEW;
2301
2300
  END IF;
2302
2301
 
2303
- UPDATE public.floriday_continuous_stock_queue q
2304
- SET status = 'superseded', updated_at = now()
2305
- WHERE q.connection_id = NEW.connection_id
2306
- AND q.trade_item_id = v_uuid
2307
- AND q.status = 'pending'
2308
- AND q.target_year IS NULL
2309
- AND q.target_week IS NULL;
2310
-
2311
- INSERT INTO public.floriday_continuous_stock_queue (
2312
- user_id, connection_id, trade_item_id, product_id, number_of_pieces,
2313
- desired_available, status, next_attempt_at
2314
- ) VALUES (
2315
- NEW.user_id, NEW.connection_id, v_uuid, v_pid, COALESCE(NEW.physical_stock, 0),
2316
- COALESCE(NEW.physical_stock, 0) > 0, 'pending', now()
2317
- );
2302
+ -- Lokaal opslaan hangt nooit af van de Floriday-poort: dicht = niets klaarzetten.
2303
+ IF NOT public.voorraad_floriday_klaarzetten_toegestaan() THEN
2304
+ RETURN NEW;
2305
+ END IF;
2306
+
2307
+ BEGIN
2308
+ UPDATE public.floriday_continuous_stock_queue q
2309
+ SET status = 'superseded', updated_at = now()
2310
+ WHERE q.connection_id = NEW.connection_id
2311
+ AND q.trade_item_id = v_uuid
2312
+ AND q.status = 'pending'
2313
+ AND q.target_year IS NULL
2314
+ AND q.target_week IS NULL;
2315
+
2316
+ INSERT INTO public.floriday_continuous_stock_queue (
2317
+ user_id, connection_id, trade_item_id, product_id, number_of_pieces,
2318
+ desired_available, status, next_attempt_at
2319
+ ) VALUES (
2320
+ NEW.user_id, NEW.connection_id, v_uuid, v_pid, COALESCE(NEW.physical_stock, 0),
2321
+ COALESCE(NEW.physical_stock, 0) > 0, 'pending', now()
2322
+ );
2323
+ EXCEPTION WHEN OTHERS THEN
2324
+ RAISE WARNING '[voorraad] niet klaargezet voor Floriday: %', SQLERRM;
2325
+ END;
2318
2326
 
2319
2327
  RETURN NEW;
2320
2328
  END;
@@ -2343,27 +2351,23 @@ BEGIN
2343
2351
  RETURN NEW;
2344
2352
  END;
2345
2353
 
2346
- IF TG_OP = 'UPDATE'
2347
- AND NEW.warehouse_id IS NOT DISTINCT FROM OLD.warehouse_id THEN
2354
+ IF TG_OP = 'UPDATE' AND NEW.warehouse_id IS NOT DISTINCT FROM OLD.warehouse_id THEN
2348
2355
  RETURN NEW;
2349
2356
  END IF;
2350
2357
 
2351
- INSERT INTO public.floriday_continuous_stock_queue (
2352
- user_id,
2353
- connection_id,
2354
- trade_item_id,
2355
- number_of_pieces,
2356
- status
2357
- )
2358
- SELECT
2359
- s.user_id,
2360
- s.connection_id,
2361
- v_trade_item_uuid,
2362
- s.physical_stock,
2363
- 'pending'
2364
- FROM public.trade_item_stock s
2365
- WHERE s.connection_id = NEW.connection_id
2366
- AND s.trade_item_key = NEW.trade_item_id;
2358
+ IF NOT public.voorraad_floriday_klaarzetten_toegestaan() THEN
2359
+ RETURN NEW;
2360
+ END IF;
2361
+
2362
+ BEGIN
2363
+ INSERT INTO public.floriday_continuous_stock_queue (user_id, connection_id, trade_item_id, number_of_pieces, status)
2364
+ SELECT s.user_id, s.connection_id, v_trade_item_uuid, s.physical_stock, 'pending'
2365
+ FROM public.trade_item_stock s
2366
+ WHERE s.connection_id = NEW.connection_id
2367
+ AND s.trade_item_key = NEW.trade_item_id;
2368
+ EXCEPTION WHEN OTHERS THEN
2369
+ RAISE WARNING '[voorraad] niet klaargezet voor Floriday: %', SQLERRM;
2370
+ END;
2367
2371
 
2368
2372
  RETURN NEW;
2369
2373
  END;
@@ -3582,21 +3586,26 @@ BEGIN
3582
3586
  WHERE id = s.id;
3583
3587
 
3584
3588
  IF NOT v_changed OR v_item IS NULL THEN RETURN; END IF;
3589
+ IF NOT public.voorraad_floriday_klaarzetten_toegestaan() THEN RETURN; END IF;
3585
3590
 
3586
- FOR v_offset IN 0..v_horizon LOOP
3587
- v_date := ((now() AT TIME ZONE 'Europe/Amsterdam')::date + (v_offset * 7));
3588
- v_year := to_char(v_date, 'IYYY')::int;
3589
- v_week := to_char(v_date, 'IW')::int;
3590
- UPDATE public.floriday_continuous_stock_queue q
3591
- SET status = 'superseded', updated_at = now()
3592
- WHERE q.connection_id = p_connection_id AND q.trade_item_id = v_item
3593
- AND q.status = 'pending' AND q.target_year = v_year AND q.target_week = v_week;
3594
- INSERT INTO public.floriday_continuous_stock_queue (
3595
- user_id, connection_id, trade_item_id, product_id, number_of_pieces, desired_available,
3596
- stock_mode, target_year, target_week, status, next_attempt_at
3597
- ) VALUES (v_user, p_connection_id, v_item, p_product_id, calc.desired_quantity, calc.desired_availability,
3598
- calc.stock_mode, v_year, v_week, 'pending', now());
3599
- END LOOP;
3591
+ BEGIN
3592
+ FOR v_offset IN 0..v_horizon LOOP
3593
+ v_date := ((now() AT TIME ZONE 'Europe/Amsterdam')::date + (v_offset * 7));
3594
+ v_year := to_char(v_date, 'IYYY')::int;
3595
+ v_week := to_char(v_date, 'IW')::int;
3596
+ UPDATE public.floriday_continuous_stock_queue q
3597
+ SET status = 'superseded', updated_at = now()
3598
+ WHERE q.connection_id = p_connection_id AND q.trade_item_id = v_item
3599
+ AND q.status = 'pending' AND q.target_year = v_year AND q.target_week = v_week;
3600
+ INSERT INTO public.floriday_continuous_stock_queue (
3601
+ user_id, connection_id, trade_item_id, product_id, number_of_pieces, desired_available,
3602
+ stock_mode, target_year, target_week, status, next_attempt_at
3603
+ ) VALUES (v_user, p_connection_id, v_item, p_product_id, calc.desired_quantity, calc.desired_availability,
3604
+ calc.stock_mode, v_year, v_week, 'pending', now());
3605
+ END LOOP;
3606
+ EXCEPTION WHEN OTHERS THEN
3607
+ RAISE WARNING '[voorraad] niet klaargezet voor Floriday: %', SQLERRM;
3608
+ END;
3600
3609
  END;
3601
3610
  $function$
3602
3611
  ;
@@ -5944,6 +5953,7 @@ AS $function$
5944
5953
  #variable_conflict use_column
5945
5954
  DECLARE
5946
5955
  v_user_id uuid := auth.uid(); v_ctx record; v_connection_id uuid; v_key text; v_row_key text; v_product_key text; v_floriday_key text; v_source text;
5956
+ v_keep boolean := COALESCE(current_setting('stock.keep_notes', true), '') = '1';
5947
5957
  BEGIN
5948
5958
  PERFORM public.voorraad_vereis('ingelogd', 'set_product_stock');
5949
5959
  IF v_user_id IS NULL THEN RAISE EXCEPTION 'Niet ingelogd' USING ERRCODE = '42501'; END IF;
@@ -5951,8 +5961,7 @@ BEGIN
5951
5961
  IF p_physical_stock < 0 OR p_minimum_stock < 0 OR p_incoming_stock < 0 THEN
5952
5962
  RAISE EXCEPTION 'Voorraadaantallen mogen niet negatief zijn'; END IF;
5953
5963
  IF p_change_source IS NOT NULL AND btrim(p_change_source) <> '' THEN
5954
- IF btrim(p_change_source) !~ '^[A-Za-z_]{2,64}$' THEN
5955
- RAISE EXCEPTION 'Ongeldige bronaanduiding'; END IF;
5964
+ IF btrim(p_change_source) !~ '^[A-Za-z_]{2,64}$' THEN RAISE EXCEPTION 'Ongeldige bronaanduiding'; END IF;
5956
5965
  v_source := btrim(p_change_source);
5957
5966
  PERFORM set_config('stock.change_source', v_source, true);
5958
5967
  END IF;
@@ -5960,27 +5969,22 @@ BEGIN
5960
5969
  INTO v_floriday_key, v_product_key FROM public.products p WHERE p.id = p_product_id;
5961
5970
  IF NOT FOUND THEN RAISE EXCEPTION 'Product niet gevonden'; END IF;
5962
5971
  v_key := COALESCE(v_product_key, v_floriday_key, 'product:' || p_product_id::text);
5963
-
5964
- -- Context: Floriday-koppeling (omgeving, anders de enige actieve) of de lokale context.
5965
5972
  SELECT * INTO v_ctx FROM public.get_stock_context(true);
5966
5973
  v_connection_id := v_ctx.connection_id;
5967
5974
  IF v_connection_id IS NULL THEN RAISE EXCEPTION 'Geen voorraadcontext beschikbaar' USING ERRCODE = 'VR004'; END IF;
5968
-
5969
5975
  SELECT s.trade_item_key INTO v_row_key FROM public.trade_item_stock s
5970
5976
  WHERE s.connection_id = v_connection_id
5971
5977
  AND s.trade_item_key IN (v_key, v_floriday_key, v_product_key, 'product:' || p_product_id::text, p_product_id::text)
5972
5978
  ORDER BY CASE WHEN s.trade_item_key = v_key THEN 0 ELSE 1 END, s.updated_at DESC NULLS LAST LIMIT 1;
5973
5979
  v_row_key := COALESCE(v_row_key, v_key);
5974
-
5975
5980
  INSERT INTO public.trade_item_stock AS t (user_id, connection_id, trade_item_key, physical_stock, minimum_stock, reserved_stock, incoming_stock, notes, change_source)
5976
5981
  VALUES (v_user_id, v_connection_id, v_row_key, p_physical_stock, COALESCE(p_minimum_stock, 0), 0, COALESCE(p_incoming_stock, 0), p_notes, COALESCE(v_source, 'MANUAL_STOCK'))
5977
5982
  ON CONFLICT (connection_id, trade_item_key) DO UPDATE
5978
5983
  SET physical_stock = EXCLUDED.physical_stock,
5979
5984
  minimum_stock = COALESCE(p_minimum_stock, t.minimum_stock, 0),
5980
5985
  incoming_stock = COALESCE(p_incoming_stock, t.incoming_stock, 0),
5981
- notes = EXCLUDED.notes, updated_at = now(),
5986
+ notes = CASE WHEN v_keep THEN t.notes ELSE EXCLUDED.notes END, updated_at = now(),
5982
5987
  change_source = COALESCE(v_source, t.change_source);
5983
-
5984
5988
  RETURN QUERY
5985
5989
  SELECT p_product_id, s.connection_id, s.trade_item_key, s.physical_stock::numeric, s.minimum_stock::numeric,
5986
5990
  s.reserved_stock::numeric, s.incoming_stock::numeric, s.notes, s.updated_at
@@ -7077,6 +7081,8 @@ AS $function$
7077
7081
  DECLARE v_ctx record;
7078
7082
  BEGIN
7079
7083
  BEGIN
7084
+ IF NOT public.voorraad_floriday_klaarzetten_toegestaan() THEN RETURN NULL; END IF;
7085
+
7080
7086
  CREATE TEMP TABLE IF NOT EXISTS _link_enq (product_id uuid, fid uuid, connection_id uuid, user_id uuid, pieces int) ON COMMIT DROP;
7081
7087
  DELETE FROM _link_enq WHERE product_id IS NOT NULL OR product_id IS NULL;
7082
7088
 
@@ -7513,7 +7519,7 @@ CREATE OR REPLACE FUNCTION public.trg_trade_item_stock_log_change()
7513
7519
  SET search_path TO 'public'
7514
7520
  AS $function$
7515
7521
  DECLARE
7516
- v_src text; v_tx text; v_handled text;
7522
+ v_src text; v_tx text; v_handled text; v_note text;
7517
7523
  v_phys_before numeric := CASE WHEN TG_OP = 'UPDATE' THEN COALESCE(OLD.physical_stock, 0) ELSE 0 END;
7518
7524
  v_res_before numeric := CASE WHEN TG_OP = 'UPDATE' THEN COALESCE(OLD.reserved_stock, 0) ELSE 0 END;
7519
7525
  v_inc_before numeric := CASE WHEN TG_OP = 'UPDATE' THEN COALESCE(OLD.incoming_stock, 0) ELSE 0 END;
@@ -7524,33 +7530,23 @@ DECLARE
7524
7530
  BEGIN
7525
7531
  IF NEW.trade_item_key IS NULL OR NEW.trade_item_key = '' THEN RETURN NULL; END IF;
7526
7532
  IF v_phys_before = v_phys_after AND v_res_before = v_res_after AND v_inc_before = v_inc_after THEN RETURN NULL; END IF;
7527
-
7528
7533
  v_handled := COALESCE(current_setting('stock.log_handled', true), '');
7529
7534
  IF v_handled <> '' AND position(';' || NEW.connection_id || '|' || NEW.trade_item_key || '|' || v_phys_after || '|' || v_res_after || ';' IN v_handled) > 0 THEN
7530
7535
  RETURN NULL;
7531
7536
  END IF;
7532
-
7533
- IF TG_OP = 'INSERT' OR NEW.change_source IS DISTINCT FROM OLD.change_source THEN
7534
- v_src := COALESCE(NEW.change_source, '');
7535
- ELSE
7536
- v_src := '';
7537
- END IF;
7537
+ IF TG_OP = 'INSERT' OR NEW.change_source IS DISTINCT FROM OLD.change_source THEN v_src := COALESCE(NEW.change_source, '');
7538
+ ELSE v_src := ''; END IF;
7538
7539
  IF v_src = '' THEN
7539
7540
  BEGIN v_tx := btrim(COALESCE(current_setting('stock.change_source', true), ''));
7540
7541
  EXCEPTION WHEN others THEN v_tx := ''; END;
7541
7542
  IF v_tx ~ '^[A-Za-z_]{2,64}$' THEN v_src := v_tx; END IF;
7542
7543
  END IF;
7543
-
7544
7544
  IF v_phys_before = v_phys_after AND EXISTS (
7545
7545
  SELECT 1 FROM public.stock_mutation_log l
7546
- WHERE l.trade_item_key = NEW.trade_item_key
7547
- AND l.created_at >= now() - interval '30 seconds'
7548
- AND COALESCE(l.physical_after, 0) = v_phys_after
7549
- AND COALESCE(l.reserved_after, 0) = v_res_after
7546
+ WHERE l.trade_item_key = NEW.trade_item_key AND l.created_at >= now() - interval '30 seconds'
7547
+ AND COALESCE(l.physical_after, 0) = v_phys_after AND COALESCE(l.reserved_after, 0) = v_res_after
7550
7548
  ) THEN RETURN NULL; END IF;
7551
-
7552
7549
  v_delta := CASE WHEN v_phys_after <> v_phys_before THEN v_phys_after - v_phys_before ELSE v_res_after - v_res_before END;
7553
-
7554
7550
  IF v_src = '' THEN v_type := 'unknown'; v_reason := 'Bron onbekend';
7555
7551
  ELSIF v_src = 'NEW_ARTICLE' THEN v_type := 'new_article'; v_reason := 'Nieuw artikel';
7556
7552
  ELSIF v_src = 'RESERVATION_RECOMPUTE' THEN v_type := 'reservation_recompute'; v_reason := 'Reservering herberekend van ' || v_res_before || ' naar ' || v_res_after;
@@ -7564,9 +7560,9 @@ BEGIN
7564
7560
  v_reason := v_reason || ' — reservering van ' || v_res_before || ' naar ' || v_res_after; END IF;
7565
7561
  IF v_phys_after <> v_phys_before THEN v_reason := v_reason || ' — fysiek van ' || v_phys_before || ' naar ' || v_phys_after; END IF;
7566
7562
  IF v_inc_after <> v_inc_before THEN v_reason := v_reason || ' — inkomend van ' || v_inc_before || ' naar ' || v_inc_after; END IF;
7567
- IF v_src <> 'NEW_ARTICLE' AND COALESCE(NEW.notes, '') <> '' AND (TG_OP = 'INSERT' OR NEW.notes IS DISTINCT FROM OLD.notes) THEN
7568
- v_reason := v_reason || ' — notitie: ' || NEW.notes; END IF;
7569
-
7563
+ BEGIN v_note := btrim(COALESCE(current_setting('stock.change_note', true), ''));
7564
+ EXCEPTION WHEN others THEN v_note := ''; END;
7565
+ IF v_note <> '' THEN v_reason := v_reason || ' — notitie: ' || left(v_note, 500); END IF;
7570
7566
  INSERT INTO public.stock_mutation_log(trade_item_key, connection_id, mutation_type, qty_delta,
7571
7567
  physical_before, physical_after, reserved_before, reserved_after, reason, created_by)
7572
7568
  VALUES (NEW.trade_item_key, NEW.connection_id, v_type, v_delta,
@@ -7747,6 +7743,67 @@ REVOKE ALL ON FUNCTION voorraad_active_fust_id(text,uuid) FROM PUBLIC;
7747
7743
  REVOKE ALL ON FUNCTION voorraad_active_fust_id(text,uuid) FROM anon;
7748
7744
  GRANT EXECUTE ON FUNCTION voorraad_active_fust_id(text,uuid) TO authenticated;
7749
7745
  GRANT EXECUTE ON FUNCTION voorraad_active_fust_id(text,uuid) TO service_role;
7746
+ CREATE OR REPLACE FUNCTION public.voorraad_afboeken(p_connection_id uuid, p_trade_item_key text, p_aantal numeric, p_reden text, p_notitie text DEFAULT NULL::text)
7747
+ RETURNS jsonb
7748
+ LANGUAGE plpgsql
7749
+ SECURITY DEFINER
7750
+ SET search_path TO 'public'
7751
+ AS $function$
7752
+ DECLARE
7753
+ r public.trade_item_stock%ROWTYPE;
7754
+ v_label text;
7755
+ v_after numeric;
7756
+ BEGIN
7757
+ PERFORM public.voorraad_vereis('ingelogd', 'voorraad_afboeken');
7758
+ IF NOT public.voorraad_can_write() THEN
7759
+ RAISE EXCEPTION 'Je hebt geen schrijfrechten op de voorraad.' USING ERRCODE = '42501';
7760
+ END IF;
7761
+ v_label := CASE p_reden WHEN 'derving' THEN 'derving' WHEN 'kapot' THEN 'kapot'
7762
+ WHEN 'eigen_gebruik' THEN 'eigen gebruik' WHEN 'overig' THEN 'overig' END;
7763
+ IF v_label IS NULL THEN
7764
+ RAISE EXCEPTION 'Kies een reden: derving, kapot, eigen gebruik of overig.' USING ERRCODE = '22023';
7765
+ END IF;
7766
+ IF p_aantal IS NULL OR p_aantal <= 0 THEN
7767
+ RAISE EXCEPTION 'Vul een aantal groter dan 0 in.' USING ERRCODE = '22023';
7768
+ END IF;
7769
+
7770
+ SELECT * INTO r FROM public.trade_item_stock
7771
+ WHERE connection_id IS NOT DISTINCT FROM p_connection_id AND trade_item_key = p_trade_item_key
7772
+ ORDER BY id FOR UPDATE;
7773
+ IF r.id IS NULL THEN
7774
+ RAISE EXCEPTION 'Geen voorraadregel gevonden voor dit artikel.' USING ERRCODE = 'P0002';
7775
+ END IF;
7776
+ IF p_aantal > COALESCE(r.physical_stock, 0) THEN
7777
+ RAISE EXCEPTION 'Je kunt niet meer afboeken (%) dan er ligt (%).', p_aantal, COALESCE(r.physical_stock,0) USING ERRCODE = '22023';
7778
+ END IF;
7779
+ v_after := COALESCE(r.physical_stock, 0) - p_aantal;
7780
+
7781
+ -- Eigen logregel; de algemene logtrigger slaat deze wijziging over.
7782
+ PERFORM set_config('stock.log_handled',
7783
+ COALESCE(current_setting('stock.log_handled', true), '') || ';' || r.connection_id || '|' || r.trade_item_key || '|' || v_after || '|' || COALESCE(r.reserved_stock,0) || ';', true);
7784
+ PERFORM set_config('stock.change_source', 'MANUAL_WRITEOFF', true);
7785
+
7786
+ UPDATE public.trade_item_stock SET physical_stock = v_after, updated_at = now() WHERE id = r.id;
7787
+
7788
+ INSERT INTO public.stock_mutation_log(trade_item_key, connection_id, mutation_type, qty_delta,
7789
+ physical_before, physical_after, reserved_before, reserved_after, reason, created_by)
7790
+ VALUES (r.trade_item_key, r.connection_id, 'manual_writeoff', -p_aantal,
7791
+ r.physical_stock, v_after, r.reserved_stock, r.reserved_stock,
7792
+ 'Afgeboekt: ' || v_label || ' — fysiek van ' || COALESCE(r.physical_stock,0) || ' naar ' || v_after
7793
+ || CASE WHEN COALESCE(btrim(p_notitie),'') <> '' THEN ' — notitie: ' || btrim(p_notitie) ELSE '' END,
7794
+ auth.uid());
7795
+
7796
+ RETURN jsonb_build_object('physical_before', r.physical_stock, 'physical_after', v_after, 'reden', p_reden);
7797
+ END $function$
7798
+ ;
7799
+ REVOKE ALL ON FUNCTION voorraad_afboeken(uuid,text,numeric,text,text) FROM PUBLIC;
7800
+ REVOKE ALL ON FUNCTION voorraad_afboeken(uuid,text,numeric,text,text) FROM anon;
7801
+ REVOKE EXECUTE ON FUNCTION voorraad_afboeken(uuid,text,numeric,text,text) FROM PUBLIC;
7802
+ GRANT EXECUTE ON FUNCTION voorraad_afboeken(uuid,text,numeric,text,text) TO authenticated;
7803
+ GRANT EXECUTE ON FUNCTION voorraad_afboeken(uuid,text,numeric,text,text) TO service_role;
7804
+ REVOKE EXECUTE ON FUNCTION voorraad_afboeken(uuid,text,numeric,text,text) FROM anon;
7805
+ GRANT EXECUTE ON FUNCTION voorraad_afboeken(uuid,text,numeric,text,text) TO authenticated;
7806
+ GRANT EXECUTE ON FUNCTION voorraad_afboeken(uuid,text,numeric,text,text) TO service_role;
7750
7807
  CREATE OR REPLACE FUNCTION public.voorraad_alert_floriday_not_updated(p_key text, p_qty numeric, p_order_id uuid, p_order_item_id uuid)
7751
7808
  RETURNS void
7752
7809
  LANGUAGE plpgsql
@@ -8822,6 +8879,40 @@ GRANT EXECUTE ON FUNCTION voorraad_correctie_1136(boolean) TO service_role;
8822
8879
  REVOKE EXECUTE ON FUNCTION voorraad_correctie_1136(boolean) FROM anon;
8823
8880
  GRANT EXECUTE ON FUNCTION voorraad_correctie_1136(boolean) TO authenticated;
8824
8881
  GRANT EXECUTE ON FUNCTION voorraad_correctie_1136(boolean) TO service_role;
8882
+ CREATE OR REPLACE FUNCTION public.voorraad_correctie_1140(p_uitvoeren boolean DEFAULT false)
8883
+ RETURNS jsonb
8884
+ LANGUAGE plpgsql
8885
+ SECURITY DEFINER
8886
+ SET search_path TO 'public'
8887
+ AS $function$
8888
+ DECLARE pid constant uuid := '90209296-f569-44fd-9750-dc7033872b64'; p record; v_keys text[]; v_rows jsonb; v_status text; v_n int; v_result jsonb;
8889
+ BEGIN
8890
+ PERFORM public.voorraad_vereis('server', 'voorraad_correctie_1140');
8891
+ PERFORM pg_advisory_xact_lock(hashtextextended('voorraad_correctie_1140', 0));
8892
+ SELECT id, name, article_number, NULLIF(btrim(COALESCE(trade_item_key,'')),'') tk, NULLIF(btrim(COALESCE(floriday_trade_item_id,'')),'') fk
8893
+ INTO p FROM public.products WHERE id = pid;
8894
+ v_keys := array_remove(ARRAY[p.tk, p.fk, 'product:' || pid::text, pid::text], NULL);
8895
+ SELECT COALESCE(jsonb_agg(jsonb_build_object('id', s.id, 'trade_item_key', s.trade_item_key, 'connection_id', s.connection_id, 'notitie', s.notes) ORDER BY s.id), '[]'::jsonb),
8896
+ count(*) FILTER (WHERE COALESCE(btrim(s.notes),'') <> '')
8897
+ INTO v_rows, v_n
8898
+ FROM public.trade_item_stock s WHERE s.trade_item_key = ANY(v_keys);
8899
+ IF p.id IS NULL AND jsonb_array_length(v_rows) = 0 THEN v_status := 'niets te doen: artikel bestaat niet';
8900
+ ELSIF v_n = 0 THEN v_status := 'niets te doen: notitie is al leeg';
8901
+ ELSE v_status := CASE WHEN p_uitvoeren THEN 'uitgevoerd' ELSE 'voorstel' END; END IF;
8902
+ IF v_status = 'uitgevoerd' THEN
8903
+ UPDATE public.trade_item_stock s SET notes = NULL, updated_at = now()
8904
+ WHERE s.id IN (SELECT (e->>'id')::uuid FROM jsonb_array_elements(v_rows) e WHERE COALESCE(btrim(e->>'notitie'),'') <> '');
8905
+ END IF;
8906
+ v_result := jsonb_build_object('ok', true, 'status', v_status, 'artikel', pid, 'naam', p.name, 'artikelnummer', p.article_number,
8907
+ 'regels_met_notitie', v_n, 'voor', v_rows);
8908
+ INSERT INTO public.voorraad_correcties(correctie, uitgevoerd_door, proef, aantallen, hash_ok)
8909
+ VALUES ('voorraad_correctie_1140', COALESCE(auth.uid()::text, session_user::text), NOT p_uitvoeren, v_result, true);
8910
+ RETURN v_result;
8911
+ END $function$
8912
+ ;
8913
+ REVOKE ALL ON FUNCTION voorraad_correctie_1140(boolean) FROM PUBLIC;
8914
+ REVOKE ALL ON FUNCTION voorraad_correctie_1140(boolean) FROM anon;
8915
+ GRANT EXECUTE ON FUNCTION voorraad_correctie_1140(boolean) TO service_role;
8825
8916
  CREATE OR REPLACE FUNCTION public.voorraad_correctie_status(p_correctie text DEFAULT '1098'::text)
8826
8917
  RETURNS jsonb
8827
8918
  LANGUAGE plpgsql
@@ -8909,6 +9000,31 @@ REVOKE ALL ON FUNCTION voorraad_fcsq_local_guard() FROM PUBLIC;
8909
9000
  REVOKE ALL ON FUNCTION voorraad_fcsq_local_guard() FROM anon;
8910
9001
  GRANT EXECUTE ON FUNCTION voorraad_fcsq_local_guard() TO authenticated;
8911
9002
  GRANT EXECUTE ON FUNCTION voorraad_fcsq_local_guard() TO service_role;
9003
+ CREATE OR REPLACE FUNCTION public.voorraad_floriday_klaarzetten_toegestaan()
9004
+ RETURNS boolean
9005
+ LANGUAGE plpgsql
9006
+ STABLE SECURITY DEFINER
9007
+ SET search_path TO 'public'
9008
+ AS $function$
9009
+ DECLARE v boolean;
9010
+ BEGIN
9011
+ PERFORM public.voorraad_vereis('ingelogd', 'voorraad_floriday_klaarzetten_toegestaan');
9012
+ BEGIN
9013
+ EXECUTE 'SELECT public.logistics_floriday_write_allowed($1)' INTO v USING 'stock_publish';
9014
+ EXCEPTION WHEN OTHERS THEN
9015
+ v := false; -- poort onleesbaar = niets klaarzetten
9016
+ END;
9017
+ RETURN COALESCE(v, false);
9018
+ END $function$
9019
+ ;
9020
+ REVOKE ALL ON FUNCTION voorraad_floriday_klaarzetten_toegestaan() FROM PUBLIC;
9021
+ REVOKE ALL ON FUNCTION voorraad_floriday_klaarzetten_toegestaan() FROM anon;
9022
+ REVOKE EXECUTE ON FUNCTION voorraad_floriday_klaarzetten_toegestaan() FROM PUBLIC;
9023
+ GRANT EXECUTE ON FUNCTION voorraad_floriday_klaarzetten_toegestaan() TO authenticated;
9024
+ GRANT EXECUTE ON FUNCTION voorraad_floriday_klaarzetten_toegestaan() TO service_role;
9025
+ REVOKE EXECUTE ON FUNCTION voorraad_floriday_klaarzetten_toegestaan() FROM anon;
9026
+ GRANT EXECUTE ON FUNCTION voorraad_floriday_klaarzetten_toegestaan() TO authenticated;
9027
+ GRANT EXECUTE ON FUNCTION voorraad_floriday_klaarzetten_toegestaan() TO service_role;
8912
9028
  CREATE OR REPLACE FUNCTION public.voorraad_floriday_leads_key(p_key text)
8913
9029
  RETURNS boolean
8914
9030
  LANGUAGE sql
@@ -9185,7 +9301,7 @@ CREATE OR REPLACE FUNCTION public.voorraad_install_check()
9185
9301
  STABLE SECURITY DEFINER
9186
9302
  SET search_path TO 'public'
9187
9303
  AS $function$
9188
- DECLARE v_f text[] := ARRAY['voorraad_canoniek_artikel','voorraad_canoniek_sleutel','voorraad_artikel_prijs','voorraad_dubbele_voorraadregels','voorraad_correctie_1131','voorraad_correctie_1132','voorraad_gateway_routes_check','voorraad_placeholder_code_reason','trg_products_placeholder_code','voorraad_fust_juist','voorraad_correctie_1121','voorraad_correctie_1121_regels','voorraad_correctie_1123','voorraad_correctie_1124','voorraad_load_carriers','voorraad_packing_from_floriday','voorraad_packing_missing','voorraad_correctie_1126','voorraad_correctie_1127','trg_voorraad_link_primary_packing','voorraad_order_geannuleerd','voorraad_article_vbn','voorraad_correctie_1112','voorraad_reverse_order_mutations','voorraad_withdraw_stock_row','ensure_voorraad_local_context','get_stock_context','voorraad_stock_context_status','voorraad_fcsq_local_guard','_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_fust_dimensions','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'];
9304
+ DECLARE v_f text[] := ARRAY['voorraad_canoniek_artikel','voorraad_canoniek_sleutel','voorraad_artikel_prijs','voorraad_dubbele_voorraadregels','voorraad_correctie_1131','voorraad_correctie_1132','voorraad_gateway_routes_check','voorraad_placeholder_code_reason','trg_products_placeholder_code','voorraad_fust_juist','voorraad_correctie_1121','voorraad_correctie_1121_regels','voorraad_correctie_1123','voorraad_correctie_1124','voorraad_load_carriers','voorraad_packing_from_floriday','voorraad_packing_missing','voorraad_correctie_1126','voorraad_correctie_1127','trg_voorraad_link_primary_packing','voorraad_order_geannuleerd','voorraad_article_vbn','voorraad_correctie_1112','voorraad_reverse_order_mutations','voorraad_withdraw_stock_row','voorraad_floriday_klaarzetten_toegestaan','voorraad_afboeken','voorraad_voorraad_opslaan','voorraad_correctie_1140','voorraad_overzicht_orderregel_koppeling','voorraad_orderregel_sleutel','ensure_voorraad_local_context','get_stock_context','voorraad_stock_context_status','voorraad_fcsq_local_guard','_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_fust_dimensions','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'];
9189
9305
  v_t text[] := ARRAY['trg_voorraad_link_primary_packing@products','aa0_fcsq_local_context_guard@floriday_continuous_stock_queue','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'];
9190
9306
  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'];
9191
9307
  v_open text[] := ARRAY['voorraad_is_server','voorraad_vereis'];
@@ -9333,6 +9449,69 @@ REVOKE ALL ON FUNCTION voorraad_orderregel_sleutel(text) FROM PUBLIC;
9333
9449
  REVOKE ALL ON FUNCTION voorraad_orderregel_sleutel(text) FROM anon;
9334
9450
  GRANT EXECUTE ON FUNCTION voorraad_orderregel_sleutel(text) TO authenticated;
9335
9451
  GRANT EXECUTE ON FUNCTION voorraad_orderregel_sleutel(text) TO service_role;
9452
+ CREATE OR REPLACE FUNCTION public.voorraad_overzicht_orderregel_koppeling(p_voorbeelden integer DEFAULT 5)
9453
+ RETURNS jsonb
9454
+ LANGUAGE plpgsql
9455
+ STABLE SECURITY DEFINER
9456
+ SET search_path TO 'public'
9457
+ AS $function$
9458
+ DECLARE v jsonb;
9459
+ BEGIN
9460
+ PERFORM public.voorraad_vereis('ingelogd', 'voorraad_overzicht_orderregel_koppeling');
9461
+ WITH regels AS (
9462
+ SELECT oi.id AS order_item_id, o.id AS order_id, o.order_number, oi.product_id, oi.product_name,
9463
+ oi.source_line_key, oi.quantity,
9464
+ CASE WHEN jsonb_typeof(o.custom_fields->'floriday_lines') = 'array'
9465
+ THEN jsonb_array_length(o.custom_fields->'floriday_lines') ELSE 0 END AS n_fl,
9466
+ (SELECT count(DISTINCT l->>'sales_order_id') FROM jsonb_array_elements(
9467
+ CASE WHEN jsonb_typeof(o.custom_fields->'floriday_lines')='array' THEN o.custom_fields->'floriday_lines' ELSE '[]'::jsonb END) l) AS n_so,
9468
+ (SELECT count(*) FROM order_items x WHERE x.order_id = o.id) AS n_regels,
9469
+ (SELECT array_agg(DISTINCT l->>'trade_item_id') FROM jsonb_array_elements(
9470
+ CASE WHEN jsonb_typeof(o.custom_fields->'floriday_lines')='array' THEN o.custom_fields->'floriday_lines' ELSE '[]'::jsonb END) l
9471
+ WHERE oi.source_line_key IS NOT NULL AND l->>'sales_order_id' = oi.source_line_key
9472
+ AND COALESCE(l->>'trade_item_id','') <> '') AS kandidaten
9473
+ FROM order_items oi JOIN orders o ON o.id = oi.order_id
9474
+ ), ingedeeld AS (
9475
+ SELECT r.*,
9476
+ CASE
9477
+ WHEN r.product_id IS NOT NULL THEN 'heeft_product_id'
9478
+ WHEN r.n_fl = 0 THEN 'geen_floriday_lines'
9479
+ WHEN r.source_line_key IS NULL THEN 'geen_source_line_key'
9480
+ WHEN r.kandidaten IS NULL THEN 'geen_match_op_source_line_key'
9481
+ WHEN array_length(r.kandidaten,1) > 1 THEN 'meerdere_kandidaten'
9482
+ WHEN public.voorraad_orderregel_sleutel(r.kandidaten[1]) IS NULL THEN 'trade_item_onbekend'
9483
+ WHEN NOT EXISTS (SELECT 1 FROM trade_item_stock s WHERE s.trade_item_key = public.voorraad_orderregel_sleutel(r.kandidaten[1])) THEN 'eenduidig_zonder_voorraadrij'
9484
+ ELSE 'eenduidig_met_voorraadrij'
9485
+ END AS categorie
9486
+ FROM regels r
9487
+ )
9488
+ SELECT jsonb_build_object(
9489
+ 'totaal', (SELECT count(*) FROM ingedeeld),
9490
+ 'per_categorie', (SELECT jsonb_object_agg(categorie, n) FROM (SELECT categorie, count(*) n FROM ingedeeld GROUP BY 1) c),
9491
+ 'samengevoegde_orders', jsonb_build_object(
9492
+ 'orders', (SELECT count(DISTINCT order_id) FROM ingedeeld WHERE n_so > 1),
9493
+ 'regels_per_categorie', (SELECT jsonb_object_agg(categorie, n) FROM (SELECT categorie, count(*) n FROM ingedeeld WHERE n_so > 1 GROUP BY 1) c)),
9494
+ 'meerregelige_orders', jsonb_build_object(
9495
+ 'orders', (SELECT count(DISTINCT order_id) FROM ingedeeld WHERE n_regels > 1),
9496
+ 'regels_per_categorie', (SELECT jsonb_object_agg(categorie, n) FROM (SELECT categorie, count(*) n FROM ingedeeld WHERE n_regels > 1 GROUP BY 1) c)),
9497
+ 'voorbeelden', (SELECT jsonb_object_agg(categorie, vb) FROM (
9498
+ SELECT categorie, jsonb_agg(jsonb_build_object('order', order_number, 'order_item_id', order_item_id,
9499
+ 'product_name', product_name, 'source_line_key', source_line_key, 'kandidaten', kandidaten,
9500
+ 'floriday_orders_in_order', n_so, 'regels_in_order', n_regels)) vb
9501
+ FROM (SELECT *, row_number() OVER (PARTITION BY categorie ORDER BY (n_so > 1) DESC, (n_regels > 1) DESC, order_number) rn FROM ingedeeld) z
9502
+ WHERE rn <= GREATEST(p_voorbeelden, 1) GROUP BY categorie) e)
9503
+ ) INTO v;
9504
+ RETURN v;
9505
+ END $function$
9506
+ ;
9507
+ REVOKE ALL ON FUNCTION voorraad_overzicht_orderregel_koppeling(integer) FROM PUBLIC;
9508
+ REVOKE ALL ON FUNCTION voorraad_overzicht_orderregel_koppeling(integer) FROM anon;
9509
+ REVOKE EXECUTE ON FUNCTION voorraad_overzicht_orderregel_koppeling(integer) FROM PUBLIC;
9510
+ GRANT EXECUTE ON FUNCTION voorraad_overzicht_orderregel_koppeling(integer) TO authenticated;
9511
+ GRANT EXECUTE ON FUNCTION voorraad_overzicht_orderregel_koppeling(integer) TO service_role;
9512
+ REVOKE EXECUTE ON FUNCTION voorraad_overzicht_orderregel_koppeling(integer) FROM anon;
9513
+ GRANT EXECUTE ON FUNCTION voorraad_overzicht_orderregel_koppeling(integer) TO authenticated;
9514
+ GRANT EXECUTE ON FUNCTION voorraad_overzicht_orderregel_koppeling(integer) TO service_role;
9336
9515
  CREATE OR REPLACE FUNCTION public.voorraad_packing_from_floriday(p_product_id uuid, p_uitvoeren boolean)
9337
9516
  RETURNS jsonb
9338
9517
  LANGUAGE plpgsql
@@ -9702,6 +9881,51 @@ GRANT EXECUTE ON FUNCTION voorraad_vereis(text,text) TO service_role;
9702
9881
  GRANT EXECUTE ON FUNCTION voorraad_vereis(text,text) TO anon;
9703
9882
  GRANT EXECUTE ON FUNCTION voorraad_vereis(text,text) TO authenticated;
9704
9883
  GRANT EXECUTE ON FUNCTION voorraad_vereis(text,text) TO service_role;
9884
+ CREATE OR REPLACE FUNCTION public.voorraad_voorraad_opslaan(p_product_id uuid, p_connection_id uuid, p_trade_item_key text, p_physical_stock numeric, p_minimum_stock numeric, p_incoming_stock numeric, p_wijziging_notitie text DEFAULT NULL::text, p_artikel_notitie text DEFAULT NULL::text, p_artikel_notitie_wijzigen boolean DEFAULT false)
9885
+ RETURNS jsonb
9886
+ LANGUAGE plpgsql
9887
+ SECURITY DEFINER
9888
+ SET search_path TO 'public'
9889
+ AS $function$
9890
+ DECLARE r record; v_klaar boolean;
9891
+ BEGIN
9892
+ PERFORM public.voorraad_vereis('ingelogd', 'voorraad_voorraad_opslaan');
9893
+ IF NOT public.voorraad_can_write() THEN
9894
+ RAISE EXCEPTION 'Je hebt geen schrijfrechten op de voorraad.' USING ERRCODE = '42501'; END IF;
9895
+ IF p_physical_stock IS NULL OR p_physical_stock < 0 OR COALESCE(p_minimum_stock,0) < 0 OR COALESCE(p_incoming_stock,0) < 0 THEN
9896
+ RAISE EXCEPTION 'Voorraadaantallen mogen niet negatief zijn' USING ERRCODE = '22023'; END IF;
9897
+ PERFORM set_config('stock.change_note', left(btrim(COALESCE(p_wijziging_notitie, '')), 500), true);
9898
+ PERFORM set_config('stock.keep_notes', CASE WHEN p_artikel_notitie_wijzigen THEN '0' ELSE '1' END, true);
9899
+ IF p_product_id IS NOT NULL THEN
9900
+ SELECT * INTO r FROM public.set_product_stock(p_product_id => p_product_id, p_physical_stock => p_physical_stock,
9901
+ p_minimum_stock => p_minimum_stock, p_incoming_stock => p_incoming_stock,
9902
+ p_notes => NULLIF(btrim(COALESCE(p_artikel_notitie, '')), ''), p_change_source => 'MANUAL_STOCK');
9903
+ ELSE
9904
+ IF p_connection_id IS NULL OR COALESCE(btrim(p_trade_item_key), '') = '' THEN
9905
+ RAISE EXCEPTION 'Geen voorraadregel opgegeven' USING ERRCODE = '22023'; END IF;
9906
+ PERFORM set_config('stock.change_source', 'MANUAL_STOCK', true);
9907
+ INSERT INTO public.trade_item_stock AS t (user_id, connection_id, trade_item_key, physical_stock, minimum_stock, reserved_stock, incoming_stock, notes, change_source)
9908
+ VALUES (auth.uid(), p_connection_id, p_trade_item_key, p_physical_stock, COALESCE(p_minimum_stock,0), 0, COALESCE(p_incoming_stock,0),
9909
+ CASE WHEN p_artikel_notitie_wijzigen THEN NULLIF(btrim(COALESCE(p_artikel_notitie,'')), '') END, 'MANUAL_STOCK')
9910
+ ON CONFLICT (connection_id, trade_item_key) DO UPDATE
9911
+ SET physical_stock = EXCLUDED.physical_stock, minimum_stock = EXCLUDED.minimum_stock, incoming_stock = EXCLUDED.incoming_stock,
9912
+ notes = CASE WHEN p_artikel_notitie_wijzigen THEN EXCLUDED.notes ELSE t.notes END,
9913
+ change_source = 'MANUAL_STOCK', updated_at = now()
9914
+ RETURNING t.connection_id, t.trade_item_key, t.physical_stock, t.minimum_stock, t.reserved_stock, t.incoming_stock, t.notes, t.updated_at INTO r;
9915
+ END IF;
9916
+ -- De geschiedenis wordt bij afronden geschreven (uitgestelde trigger); notitie blijft tot einde transactie.
9917
+ BEGIN v_klaar := public.voorraad_floriday_klaarzetten_toegestaan();
9918
+ EXCEPTION WHEN others THEN v_klaar := false; END;
9919
+ RETURN jsonb_build_object('opgeslagen', true, 'floriday_klaargezet_toegestaan', COALESCE(v_klaar, false),
9920
+ 'connection_id', r.connection_id, 'trade_item_key', r.trade_item_key, 'physical_stock', r.physical_stock,
9921
+ 'minimum_stock', r.minimum_stock, 'reserved_stock', r.reserved_stock, 'incoming_stock', r.incoming_stock,
9922
+ 'notes', r.notes, 'updated_at', r.updated_at);
9923
+ END $function$
9924
+ ;
9925
+ REVOKE ALL ON FUNCTION voorraad_voorraad_opslaan(uuid,uuid,text,numeric,numeric,numeric,text,text,boolean) FROM PUBLIC;
9926
+ REVOKE ALL ON FUNCTION voorraad_voorraad_opslaan(uuid,uuid,text,numeric,numeric,numeric,text,text,boolean) FROM anon;
9927
+ GRANT EXECUTE ON FUNCTION voorraad_voorraad_opslaan(uuid,uuid,text,numeric,numeric,numeric,text,text,boolean) TO authenticated;
9928
+ GRANT EXECUTE ON FUNCTION voorraad_voorraad_opslaan(uuid,uuid,text,numeric,numeric,numeric,text,text,boolean) TO service_role;
9705
9929
  CREATE OR REPLACE FUNCTION public.voorraad_week_is_past(p_year integer, p_week integer)
9706
9930
  RETURNS boolean
9707
9931
  LANGUAGE sql
@@ -9874,7 +10098,7 @@ ALTER TABLE public.stock_import_runs ADD CONSTRAINT stock_import_runs_status_che
9874
10098
  ALTER TABLE public.stock_integrity_alerts DROP CONSTRAINT IF EXISTS stock_integrity_alerts_kind_check;
9875
10099
  ALTER TABLE public.stock_integrity_alerts ADD CONSTRAINT stock_integrity_alerts_kind_check CHECK ((alert_kind = ANY (ARRAY['ZERO_BATCH'::text, 'NEGATIVE_STOCK'::text, 'FLORIDAY_NOT_UPDATED'::text])));
9876
10100
  ALTER TABLE public.stock_mutation_log DROP CONSTRAINT IF EXISTS stock_mutation_log_mutation_type_check;
9877
- ALTER TABLE public.stock_mutation_log ADD CONSTRAINT stock_mutation_log_mutation_type_check CHECK ((mutation_type = ANY (ARRAY['reservation'::text, 'reservation_release'::text, 'consumption'::text, 'stock_check'::text, 'reservation_skipped'::text, 'consumption_skipped'::text, 'consumption_blocked'::text, 'production_consume'::text, 'production_output'::text, 'production_reverse'::text, 'manual_correction'::text, 'order_sync'::text, 'import'::text, 'production'::text, 'stock_update'::text, 'reservation_recompute'::text, 'unknown'::text, 'consumption_done'::text, 'new_article'::text])));
10101
+ ALTER TABLE public.stock_mutation_log ADD CONSTRAINT stock_mutation_log_mutation_type_check CHECK ((mutation_type = ANY (ARRAY['reservation'::text, 'reservation_release'::text, 'consumption'::text, 'stock_check'::text, 'reservation_skipped'::text, 'consumption_skipped'::text, 'consumption_blocked'::text, 'production_consume'::text, 'production_output'::text, 'production_reverse'::text, 'manual_correction'::text, 'order_sync'::text, 'import'::text, 'production'::text, 'stock_update'::text, 'reservation_recompute'::text, 'unknown'::text, 'consumption_done'::text, 'new_article'::text, 'manual_writeoff'::text])));
9878
10102
  ALTER TABLE public.trade_item_drafts DROP CONSTRAINT IF EXISTS trade_item_drafts_link_status_chk;
9879
10103
  ALTER TABLE public.trade_item_drafts ADD CONSTRAINT trade_item_drafts_link_status_chk CHECK ((floriday_link_status = ANY (ARRAY['unlinked'::text, 'linked'::text, 'waiting_for_data'::text, 'ready_to_publish'::text, 'publish_failed'::text, 'ambiguous_match'::text, 'published'::text])));
9880
10104
  ALTER TABLE public.voorraad_local_context DROP CONSTRAINT IF EXISTS voorraad_local_context_singleton_check;