@flowselections/floriday-voorraad 1.0.143 → 1.0.147

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 (91) hide show
  1. package/dist-lib/components/voorraad/ArtikelWizard.d.ts +3 -1
  2. package/dist-lib/components/voorraad/ArtikelWizard.d.ts.map +1 -1
  3. package/dist-lib/components/voorraad/ArtikelWizard.js +57 -53
  4. package/dist-lib/components/voorraad/EditArtikelPage.d.ts.map +1 -1
  5. package/dist-lib/components/voorraad/EditArtikelPage.js +3 -4
  6. package/dist-lib/components/voorraad/FustAdministratie.d.ts +12 -0
  7. package/dist-lib/components/voorraad/FustAdministratie.d.ts.map +1 -0
  8. package/dist-lib/components/voorraad/FustAdministratie.js +69 -0
  9. package/dist-lib/components/voorraad/FustenPage.d.ts.map +1 -1
  10. package/dist-lib/components/voorraad/FustenPage.js +9 -14
  11. package/dist-lib/components/voorraad/KoppelingStatusPanel.d.ts +6 -0
  12. package/dist-lib/components/voorraad/KoppelingStatusPanel.d.ts.map +1 -0
  13. package/dist-lib/components/voorraad/KoppelingStatusPanel.js +47 -0
  14. package/dist-lib/components/voorraad/NieuwArtikelPage.js +2 -2
  15. package/dist-lib/components/voorraad/ProductCustomFieldsCard.d.ts.map +1 -1
  16. package/dist-lib/components/voorraad/ProductCustomFieldsCard.js +29 -13
  17. package/dist-lib/components/voorraad/ProductSalePriceField.d.ts.map +1 -1
  18. package/dist-lib/components/voorraad/ProductSalePriceField.js +9 -8
  19. package/dist-lib/components/voorraad/StepSamenstelling.d.ts.map +1 -1
  20. package/dist-lib/components/voorraad/StepSamenstelling.js +35 -10
  21. package/dist-lib/components/voorraad/TelmomentButton.d.ts +8 -0
  22. package/dist-lib/components/voorraad/TelmomentButton.d.ts.map +1 -0
  23. package/dist-lib/components/voorraad/TelmomentButton.js +65 -0
  24. package/dist-lib/components/voorraad/TrayStickerCard.d.ts +5 -1
  25. package/dist-lib/components/voorraad/TrayStickerCard.d.ts.map +1 -1
  26. package/dist-lib/components/voorraad/TrayStickerCard.js +64 -66
  27. package/dist-lib/components/voorraad/ValidationOverviewTable.d.ts +0 -5
  28. package/dist-lib/components/voorraad/ValidationOverviewTable.d.ts.map +1 -1
  29. package/dist-lib/components/voorraad/ValidationOverviewTable.js +10 -4
  30. package/dist-lib/components/voorraad/ValidationPage.d.ts.map +1 -1
  31. package/dist-lib/components/voorraad/ValidationPage.js +2 -1
  32. package/dist-lib/components/voorraad/VoorraadPage.d.ts +1 -13
  33. package/dist-lib/components/voorraad/VoorraadPage.d.ts.map +1 -1
  34. package/dist-lib/components/voorraad/VoorraadPage.js +103 -140
  35. package/dist-lib/components/voorraad/VoorraadSturingCard.d.ts +6 -0
  36. package/dist-lib/components/voorraad/VoorraadSturingCard.d.ts.map +1 -0
  37. package/dist-lib/components/voorraad/VoorraadSturingCard.js +47 -0
  38. package/dist-lib/components/voorraad/artikel/ArtikelPagina.d.ts +5 -0
  39. package/dist-lib/components/voorraad/artikel/ArtikelPagina.d.ts.map +1 -0
  40. package/dist-lib/components/voorraad/artikel/ArtikelPagina.js +263 -0
  41. package/dist-lib/components/voorraad/artikel/TwijfelKiezen.d.ts +7 -0
  42. package/dist-lib/components/voorraad/artikel/TwijfelKiezen.d.ts.map +1 -0
  43. package/dist-lib/components/voorraad/artikel/TwijfelKiezen.js +54 -0
  44. package/dist-lib/components/voorraad/artikel/opslaan-context.d.ts +21 -0
  45. package/dist-lib/components/voorraad/artikel/opslaan-context.d.ts.map +1 -0
  46. package/dist-lib/components/voorraad/artikel/opslaan-context.js +15 -0
  47. package/dist-lib/components/voorraad/lijst/ImporteerKnop.d.ts +5 -0
  48. package/dist-lib/components/voorraad/lijst/ImporteerKnop.d.ts.map +1 -0
  49. package/dist-lib/components/voorraad/lijst/ImporteerKnop.js +20 -0
  50. package/dist-lib/components/voorraad/lijst/LijstExportKnop.d.ts +6 -0
  51. package/dist-lib/components/voorraad/lijst/LijstExportKnop.d.ts.map +1 -0
  52. package/dist-lib/components/voorraad/lijst/LijstExportKnop.js +43 -0
  53. package/dist-lib/components/voorraad/lijst/SnelActieDialogen.d.ts +16 -0
  54. package/dist-lib/components/voorraad/lijst/SnelActieDialogen.d.ts.map +1 -0
  55. package/dist-lib/components/voorraad/lijst/SnelActieDialogen.js +95 -0
  56. package/dist-lib/components/voorraad/lijst/VoorraadLijstTabel.d.ts +21 -0
  57. package/dist-lib/components/voorraad/lijst/VoorraadLijstTabel.d.ts.map +1 -0
  58. package/dist-lib/components/voorraad/lijst/VoorraadLijstTabel.js +38 -0
  59. package/dist-lib/hooks/useVoorraadLijst.d.ts +21 -0
  60. package/dist-lib/hooks/useVoorraadLijst.d.ts.map +1 -0
  61. package/dist-lib/hooks/useVoorraadLijst.js +71 -0
  62. package/dist-lib/index.d.ts.map +1 -1
  63. package/dist-lib/index.js +2 -0
  64. package/dist-lib/integrations/supabase/types.d.ts +989 -0
  65. package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
  66. package/dist-lib/lib/artikel-pagina.d.ts +57 -0
  67. package/dist-lib/lib/artikel-pagina.d.ts.map +1 -0
  68. package/dist-lib/lib/artikel-pagina.js +93 -0
  69. package/dist-lib/lib/count-stock.d.ts +17 -0
  70. package/dist-lib/lib/count-stock.d.ts.map +1 -0
  71. package/dist-lib/lib/count-stock.js +26 -0
  72. package/dist-lib/lib/floriday-import-validator.d.ts +9 -0
  73. package/dist-lib/lib/floriday-import-validator.d.ts.map +1 -1
  74. package/dist-lib/lib/floriday-import-validator.js +13 -0
  75. package/dist-lib/lib/fust-administratie.d.ts +29 -0
  76. package/dist-lib/lib/fust-administratie.d.ts.map +1 -0
  77. package/dist-lib/lib/fust-administratie.js +63 -0
  78. package/dist-lib/lib/fusten.d.ts +4 -0
  79. package/dist-lib/lib/fusten.d.ts.map +1 -1
  80. package/dist-lib/lib/pending-photos.d.ts +20 -0
  81. package/dist-lib/lib/pending-photos.d.ts.map +1 -0
  82. package/dist-lib/lib/pending-photos.js +90 -0
  83. package/dist-lib/lib/voorraad-custom-fields.d.ts +16 -1
  84. package/dist-lib/lib/voorraad-custom-fields.d.ts.map +1 -1
  85. package/dist-lib/lib/voorraad-custom-fields.js +12 -6
  86. package/dist-lib/lib/voorraad-lijst.d.ts +91 -0
  87. package/dist-lib/lib/voorraad-lijst.d.ts.map +1 -0
  88. package/dist-lib/lib/voorraad-lijst.js +82 -0
  89. package/package.json +1 -1
  90. package/sql/AGENTS.md +7 -0
  91. package/sql/floriday-voorraad-schema.sql +1627 -62
@@ -1,9 +1,24 @@
1
- -- @flowselections/floriday-voorraad 1.0.142 – volledig schema (idempotent, geen gegevens).
2
- -- Laatste migratie: 20261102125718_08100bc6-2bc9-4905-9e62-4c7aca9ce3ff.sql
1
+ -- @flowselections/floriday-voorraad 1.0.146 – volledig schema (idempotent, geen gegevens).
2
+ -- Laatste migratie: 20261103225325_855bad38-f69b-4701-85b7-2d35ee33c08f.sql
3
3
  -- Gegenereerd uit de database met scripts/generate-voorraad-schema.py. Niet met de hand wijzigen.
4
4
  -- Volgorde 20. Ontbrekende tabellen van andere modules worden overgeslagen met een NOTICE; draai opnieuw na die module.
5
5
  -- De statuscontrole van floriday_continuous_stock_queue is van Voorraad en wordt hier opnieuw gemaakt.
6
6
  SET check_function_bodies = off;
7
+ -- eigen kolommen/indexen op tabellen van andere modules
8
+ DO $fk$ BEGIN IF to_regclass('public.products') IS NOT NULL THEN EXECUTE 'CREATE INDEX IF NOT EXISTS idx_products_trade_item_key ON public.products(trade_item_key);'; ELSE RAISE NOTICE 'Voorraad: tabel products ontbreekt, overgeslagen'; END IF; END $fk$;
9
+ DO $fk$ BEGIN IF to_regclass('public.product_groups') IS NOT NULL THEN EXECUTE 'CREATE UNIQUE INDEX IF NOT EXISTS product_groups_one_active_per_head_product ON public.product_groups(head_product_id) WHERE is_active AND head_product_id IS NOT NULL;'; ELSE RAISE NOTICE 'Voorraad: tabel product_groups ontbreekt, overgeslagen'; END IF; END $fk$;
10
+ DO $fk$ BEGIN IF to_regclass('public.product_groups') IS NOT NULL THEN EXECUTE 'CREATE UNIQUE INDEX IF NOT EXISTS product_groups_one_active_per_head_floriday ON public.product_groups(head_trade_item_floriday_id) WHERE is_active AND head_trade_item_floriday_id IS NOT NULL;'; ELSE RAISE NOTICE 'Voorraad: tabel product_groups ontbreekt, overgeslagen'; END IF; END $fk$;
11
+ DO $fk$ BEGIN IF to_regclass('public.product_groups') IS NOT NULL THEN EXECUTE 'CREATE UNIQUE INDEX IF NOT EXISTS product_groups_active_name_uq ON public.product_groups (lower(btrim(name))) WHERE is_active IS TRUE;'; ELSE RAISE NOTICE 'Voorraad: tabel product_groups ontbreekt, overgeslagen'; END IF; END $fk$;
12
+ DO $fk$ BEGIN IF to_regclass('public.products') IS NOT NULL THEN EXECUTE 'ALTER TABLE public.products ADD COLUMN IF NOT EXISTS floriday_velden_aangepast text[];'; ELSE RAISE NOTICE 'Voorraad: tabel products ontbreekt, overgeslagen'; END IF; END $fk$;
13
+ DO $fk$ BEGIN IF to_regclass('public.products') IS NOT NULL THEN EXECUTE 'ALTER TABLE public.products ADD COLUMN IF NOT EXISTS floriday_verwijderd_at timestamptz;'; ELSE RAISE NOTICE 'Voorraad: tabel products ontbreekt, overgeslagen'; END IF; END $fk$;
14
+ DO $fk$ BEGIN IF to_regclass('public.products') IS NOT NULL THEN EXECUTE 'ALTER TABLE public.products ADD COLUMN IF NOT EXISTS gearchiveerd_at timestamptz;'; ELSE RAISE NOTICE 'Voorraad: tabel products ontbreekt, overgeslagen'; END IF; END $fk$;
15
+ DO $fk$ BEGIN IF to_regclass('public.products') IS NOT NULL THEN EXECUTE 'ALTER TABLE public.products ADD COLUMN IF NOT EXISTS gearchiveerd_door uuid;'; ELSE RAISE NOTICE 'Voorraad: tabel products ontbreekt, overgeslagen'; END IF; END $fk$;
16
+ DO $fk$ BEGIN IF to_regclass('public.products') IS NOT NULL THEN EXECUTE 'CREATE INDEX IF NOT EXISTS products_gearchiveerd_at_idx ON public.products (gearchiveerd_at) WHERE gearchiveerd_at IS NOT NULL;'; ELSE RAISE NOTICE 'Voorraad: tabel products ontbreekt, overgeslagen'; END IF; END $fk$;
17
+ DO $fk$ BEGIN IF to_regclass('public.product_bom_items') IS NOT NULL THEN EXECUTE 'CREATE INDEX IF NOT EXISTS product_bom_items_component_idx ON public.product_bom_items (component_product_id);'; ELSE RAISE NOTICE 'Voorraad: tabel product_bom_items ontbreekt, overgeslagen'; END IF; END $fk$;
18
+ DO $fk$ BEGIN IF to_regclass('public.product_bom_items') IS NOT NULL THEN EXECUTE 'CREATE INDEX IF NOT EXISTS product_bom_items_parent_idx ON public.product_bom_items (parent_product_id);'; ELSE RAISE NOTICE 'Voorraad: tabel product_bom_items ontbreekt, overgeslagen'; END IF; END $fk$;
19
+ DO $fk$ BEGIN IF to_regclass('public.products') IS NOT NULL THEN EXECUTE 'CREATE INDEX IF NOT EXISTS products_id_text_idx ON public.products ((id::text));'; ELSE RAISE NOTICE 'Voorraad: tabel products ontbreekt, overgeslagen'; END IF; END $fk$;
20
+ DO $fk$ BEGIN IF to_regclass('public.products') IS NOT NULL THEN EXECUTE 'ALTER TABLE public.products ADD COLUMN IF NOT EXISTS gearchiveerd_floriday_nul boolean NOT NULL DEFAULT false;'; ELSE RAISE NOTICE 'Voorraad: tabel products ontbreekt, overgeslagen'; END IF; END $fk$;
21
+ DO $fk$ BEGIN IF to_regclass('public.floriday_salesorders_cache') IS NOT NULL THEN EXECUTE 'CREATE INDEX IF NOT EXISTS voorraad_fsc_floriday_id_idx ON public.floriday_salesorders_cache (floriday_id);'; ELSE RAISE NOTICE 'Voorraad: tabel floriday_salesorders_cache ontbreekt, overgeslagen'; END IF; END $fk$;
7
22
 
8
23
  -- floriday_continuous_stock_cache
9
24
  CREATE TABLE IF NOT EXISTS public.floriday_continuous_stock_cache ();
@@ -178,6 +193,30 @@ ALTER TABLE public.floriday_vbn_required_characteristics ADD COLUMN IF NOT EXIST
178
193
  ALTER TABLE public.floriday_vbn_required_characteristics ADD COLUMN IF NOT EXISTS updated_at timestamp with time zone DEFAULT now() NOT NULL;
179
194
  ALTER TABLE public.floriday_vbn_required_characteristics ADD COLUMN IF NOT EXISTS updated_by uuid;
180
195
 
196
+ -- fust_startmoment
197
+ CREATE TABLE IF NOT EXISTS public.fust_startmoment ();
198
+ ALTER TABLE public.fust_startmoment ADD COLUMN IF NOT EXISTS id uuid DEFAULT gen_random_uuid() NOT NULL;
199
+ ALTER TABLE public.fust_startmoment ADD COLUMN IF NOT EXISTS fust_type_id uuid;
200
+ ALTER TABLE public.fust_startmoment ADD COLUMN IF NOT EXISTS moment timestamp with time zone NOT NULL;
201
+ ALTER TABLE public.fust_startmoment ADD COLUMN IF NOT EXISTS beginstand integer NOT NULL;
202
+ ALTER TABLE public.fust_startmoment ADD COLUMN IF NOT EXISTS automatisch boolean DEFAULT false NOT NULL;
203
+ ALTER TABLE public.fust_startmoment ADD COLUMN IF NOT EXISTS door uuid DEFAULT auth.uid();
204
+ ALTER TABLE public.fust_startmoment ADD COLUMN IF NOT EXISTS created_at timestamp with time zone DEFAULT now() NOT NULL;
205
+ ALTER TABLE public.fust_startmoment ADD COLUMN IF NOT EXISTS updated_at timestamp with time zone DEFAULT now() NOT NULL;
206
+
207
+ -- fust_startmoment_log
208
+ CREATE TABLE IF NOT EXISTS public.fust_startmoment_log ();
209
+ ALTER TABLE public.fust_startmoment_log ADD COLUMN IF NOT EXISTS id uuid DEFAULT gen_random_uuid() NOT NULL;
210
+ ALTER TABLE public.fust_startmoment_log ADD COLUMN IF NOT EXISTS fust_type_id uuid;
211
+ ALTER TABLE public.fust_startmoment_log ADD COLUMN IF NOT EXISTS moment timestamp with time zone NOT NULL;
212
+ ALTER TABLE public.fust_startmoment_log ADD COLUMN IF NOT EXISTS beginstand integer NOT NULL;
213
+ ALTER TABLE public.fust_startmoment_log ADD COLUMN IF NOT EXISTS saldo_voor integer;
214
+ ALTER TABLE public.fust_startmoment_log ADD COLUMN IF NOT EXISTS saldo_na integer;
215
+ ALTER TABLE public.fust_startmoment_log ADD COLUMN IF NOT EXISTS automatisch boolean DEFAULT false NOT NULL;
216
+ ALTER TABLE public.fust_startmoment_log ADD COLUMN IF NOT EXISTS notitie text;
217
+ ALTER TABLE public.fust_startmoment_log ADD COLUMN IF NOT EXISTS door uuid DEFAULT auth.uid();
218
+ ALTER TABLE public.fust_startmoment_log ADD COLUMN IF NOT EXISTS created_at timestamp with time zone DEFAULT now() NOT NULL;
219
+
181
220
  -- fust_stock_mutations
182
221
  CREATE TABLE IF NOT EXISTS public.fust_stock_mutations ();
183
222
  ALTER TABLE public.fust_stock_mutations ADD COLUMN IF NOT EXISTS id uuid DEFAULT gen_random_uuid() NOT NULL;
@@ -211,6 +250,8 @@ ALTER TABLE public.fust_types ADD COLUMN IF NOT EXISTS width_mm integer;
211
250
  ALTER TABLE public.fust_types ADD COLUMN IF NOT EXISTS height_mm integer;
212
251
  ALTER TABLE public.fust_types ADD COLUMN IF NOT EXISTS capacity_units integer;
213
252
  ALTER TABLE public.fust_types ADD COLUMN IF NOT EXISTS merged_into uuid;
253
+ ALTER TABLE public.fust_types ADD COLUMN IF NOT EXISTS saldo_bijhouden boolean;
254
+ ALTER TABLE public.fust_types ADD COLUMN IF NOT EXISTS eigen_naam text;
214
255
 
215
256
  -- inventory_recompute_limits
216
257
  CREATE TABLE IF NOT EXISTS public.inventory_recompute_limits ();
@@ -538,6 +579,43 @@ ALTER TABLE public.voorraad_defaults_state ADD COLUMN IF NOT EXISTS versie integ
538
579
  ALTER TABLE public.voorraad_defaults_state ADD COLUMN IF NOT EXISTS toegepast_at timestamp with time zone DEFAULT now() NOT NULL;
539
580
  ALTER TABLE public.voorraad_defaults_state ADD COLUMN IF NOT EXISTS resultaat jsonb DEFAULT '{}'::jsonb NOT NULL;
540
581
 
582
+ -- voorraad_floriday_artikel_wachtrij
583
+ CREATE TABLE IF NOT EXISTS public.voorraad_floriday_artikel_wachtrij ();
584
+ ALTER TABLE public.voorraad_floriday_artikel_wachtrij ADD COLUMN IF NOT EXISTS floriday_id text NOT NULL;
585
+ ALTER TABLE public.voorraad_floriday_artikel_wachtrij ADD COLUMN IF NOT EXISTS connection_id uuid;
586
+ ALTER TABLE public.voorraad_floriday_artikel_wachtrij ADD COLUMN IF NOT EXISTS aangemeld_at timestamp with time zone DEFAULT now() NOT NULL;
587
+
588
+ -- voorraad_instellingen
589
+ CREATE TABLE IF NOT EXISTS public.voorraad_instellingen ();
590
+ ALTER TABLE public.voorraad_instellingen ADD COLUMN IF NOT EXISTS id boolean DEFAULT true NOT NULL;
591
+ ALTER TABLE public.voorraad_instellingen ADD COLUMN IF NOT EXISTS standaard_sturing_nieuwe_floriday text DEFAULT 'lokaal'::text NOT NULL;
592
+ ALTER TABLE public.voorraad_instellingen ADD COLUMN IF NOT EXISTS updated_at timestamp with time zone DEFAULT now() NOT NULL;
593
+ ALTER TABLE public.voorraad_instellingen ADD COLUMN IF NOT EXISTS updated_by uuid;
594
+
595
+ -- voorraad_koppel_log
596
+ CREATE TABLE IF NOT EXISTS public.voorraad_koppel_log ();
597
+ ALTER TABLE public.voorraad_koppel_log ADD COLUMN IF NOT EXISTS id uuid DEFAULT gen_random_uuid() NOT NULL;
598
+ ALTER TABLE public.voorraad_koppel_log ADD COLUMN IF NOT EXISTS run_id uuid;
599
+ ALTER TABLE public.voorraad_koppel_log ADD COLUMN IF NOT EXISTS soort text NOT NULL;
600
+ ALTER TABLE public.voorraad_koppel_log ADD COLUMN IF NOT EXISTS floriday_id text;
601
+ ALTER TABLE public.voorraad_koppel_log ADD COLUMN IF NOT EXISTS product_id uuid;
602
+ ALTER TABLE public.voorraad_koppel_log ADD COLUMN IF NOT EXISTS order_item_id uuid;
603
+ ALTER TABLE public.voorraad_koppel_log ADD COLUMN IF NOT EXISTS oud jsonb;
604
+ ALTER TABLE public.voorraad_koppel_log ADD COLUMN IF NOT EXISTS nieuw jsonb;
605
+ ALTER TABLE public.voorraad_koppel_log ADD COLUMN IF NOT EXISTS door uuid DEFAULT auth.uid();
606
+ ALTER TABLE public.voorraad_koppel_log ADD COLUMN IF NOT EXISTS created_at timestamp with time zone DEFAULT now() NOT NULL;
607
+
608
+ -- voorraad_koppel_twijfel
609
+ CREATE TABLE IF NOT EXISTS public.voorraad_koppel_twijfel ();
610
+ ALTER TABLE public.voorraad_koppel_twijfel ADD COLUMN IF NOT EXISTS floriday_id text NOT NULL;
611
+ ALTER TABLE public.voorraad_koppel_twijfel ADD COLUMN IF NOT EXISTS connection_id uuid;
612
+ ALTER TABLE public.voorraad_koppel_twijfel ADD COLUMN IF NOT EXISTS naam text;
613
+ ALTER TABLE public.voorraad_koppel_twijfel ADD COLUMN IF NOT EXISTS reden text NOT NULL;
614
+ ALTER TABLE public.voorraad_koppel_twijfel ADD COLUMN IF NOT EXISTS kandidaten jsonb;
615
+ ALTER TABLE public.voorraad_koppel_twijfel ADD COLUMN IF NOT EXISTS eerst_gezien timestamp with time zone DEFAULT now() NOT NULL;
616
+ ALTER TABLE public.voorraad_koppel_twijfel ADD COLUMN IF NOT EXISTS laatst_gezien timestamp with time zone DEFAULT now() NOT NULL;
617
+ ALTER TABLE public.voorraad_koppel_twijfel ADD COLUMN IF NOT EXISTS opgelost_at timestamp with time zone;
618
+
541
619
  -- voorraad_load_carrier_types
542
620
  CREATE TABLE IF NOT EXISTS public.voorraad_load_carrier_types ();
543
621
  ALTER TABLE public.voorraad_load_carrier_types ADD COLUMN IF NOT EXISTS code text NOT NULL;
@@ -657,6 +735,23 @@ ALTER TABLE public.voorraad_publish_steps ADD COLUMN IF NOT EXISTS finished_at t
657
735
  ALTER TABLE public.voorraad_publish_steps ADD COLUMN IF NOT EXISTS created_at timestamp with time zone DEFAULT now() NOT NULL;
658
736
  ALTER TABLE public.voorraad_publish_steps ADD COLUMN IF NOT EXISTS updated_at timestamp with time zone DEFAULT now() NOT NULL;
659
737
 
738
+ -- voorraad_telmoment
739
+ CREATE TABLE IF NOT EXISTS public.voorraad_telmoment ();
740
+ ALTER TABLE public.voorraad_telmoment ADD COLUMN IF NOT EXISTS id uuid DEFAULT gen_random_uuid() NOT NULL;
741
+ ALTER TABLE public.voorraad_telmoment ADD COLUMN IF NOT EXISTS telmoment timestamp with time zone NOT NULL;
742
+ ALTER TABLE public.voorraad_telmoment ADD COLUMN IF NOT EXISTS trade_item_keys text[];
743
+ ALTER TABLE public.voorraad_telmoment ADD COLUMN IF NOT EXISTS notitie text;
744
+ ALTER TABLE public.voorraad_telmoment ADD COLUMN IF NOT EXISTS door uuid DEFAULT auth.uid();
745
+ ALTER TABLE public.voorraad_telmoment ADD COLUMN IF NOT EXISTS created_at timestamp with time zone DEFAULT now() NOT NULL;
746
+
747
+ -- voorraad_telmoment_regels
748
+ CREATE TABLE IF NOT EXISTS public.voorraad_telmoment_regels ();
749
+ ALTER TABLE public.voorraad_telmoment_regels ADD COLUMN IF NOT EXISTS order_item_id uuid NOT NULL;
750
+ ALTER TABLE public.voorraad_telmoment_regels ADD COLUMN IF NOT EXISTS telmoment_id uuid NOT NULL;
751
+ ALTER TABLE public.voorraad_telmoment_regels ADD COLUMN IF NOT EXISTS reden text NOT NULL;
752
+ ALTER TABLE public.voorraad_telmoment_regels ADD COLUMN IF NOT EXISTS door uuid DEFAULT auth.uid();
753
+ ALTER TABLE public.voorraad_telmoment_regels ADD COLUMN IF NOT EXISTS created_at timestamp with time zone DEFAULT now() NOT NULL;
754
+
660
755
  -- functies
661
756
  CREATE OR REPLACE FUNCTION public._floriday_queue_cleanup_batch(p_statuses text[], p_days integer, p_keep_latest boolean, p_limit integer)
662
757
  RETURNS integer
@@ -1005,6 +1100,10 @@ BEGIN
1005
1100
  IF v_type IS DISTINCT FROM 'GROUP' AND cardinality(public.product_member_group_ids(p_product_id)) > 0 THEN
1006
1101
  desired_quantity := 0; desired_availability := false; reason := 'GROUP_MEMBER_OFF'; RETURN NEXT; RETURN;
1007
1102
  END IF;
1103
+ -- 1.0.146: gearchiveerd met "Ook in Floriday" = 0 op Floriday (nooit raisen).
1104
+ IF EXISTS (SELECT 1 FROM public.products ap WHERE ap.id = p_product_id AND ap.gearchiveerd_at IS NOT NULL AND ap.gearchiveerd_floriday_nul) THEN
1105
+ desired_quantity := 0; desired_availability := false; reason := 'ARCHIVED'; RETURN NEXT; RETURN;
1106
+ END IF;
1008
1107
  IF stock_mode = 'DISABLED' THEN
1009
1108
  desired_quantity := 0; desired_availability := false; RETURN NEXT; RETURN;
1010
1109
  END IF;
@@ -2914,7 +3013,7 @@ CREATE OR REPLACE FUNCTION public.floriday_desired_stock_regression_check()
2914
3013
  AS $function$
2915
3014
  WITH b AS (
2916
3015
  SELECT s.connection_id, s.product_id, p.product, s.stock_mode, s.buffer_quantity, s.maximum_quantity, s.fixed_quantity, s.manual_quantity,
2917
- public.product_primary_pieces(p.id) AS pack,
3016
+ public.product_primary_pieces(p.id) AS pack, (p.gearchiveerd_at IS NOT NULL AND p.gearchiveerd_floriday_nul) AS archief_nul,
2918
3017
  (SELECT GREATEST(0, floor(ts.physical_stock - ts.reserved_stock))::int FROM public.trade_item_stock ts
2919
3018
  WHERE ts.connection_id = s.connection_id
2920
3019
  AND ts.trade_item_key = COALESCE(NULLIF(btrim(p.floriday_trade_item_id),''), NULLIF(btrim(p.trade_item_key),''))) AS avail
@@ -2926,6 +3025,7 @@ AS $function$
2926
3025
  ), e AS (
2927
3026
  SELECT b.*, (public.calculate_floriday_desired_stock(b.connection_id, b.product_id)).desired_quantity AS act,
2928
3027
  CASE WHEN b.avail IS NULL OR b.pack IS NULL THEN 0
3028
+ WHEN b.archief_nul THEN 0
2929
3029
  WHEN b.stock_mode = 'DISABLED' THEN 0
2930
3030
  ELSE GREATEST(0, (GREATEST(0, CASE b.stock_mode
2931
3031
  WHEN 'MAXIMUM' THEN LEAST(b.avail - b.buffer_quantity, COALESCE(b.maximum_quantity, b.avail - b.buffer_quantity))
@@ -3166,6 +3266,7 @@ DECLARE
3166
3266
  v_owner uuid;
3167
3267
  BEGIN
3168
3268
  PERFORM public.voorraad_vereis('ingelogd', 'floriday_ingest_sales_orders');
3269
+ PERFORM public.voorraad_fust_startmoment_auto();
3169
3270
  FOR r IN
3170
3271
  SELECT c.connection_id,
3171
3272
  c.floriday_id,
@@ -3174,7 +3275,8 @@ BEGIN
3174
3275
  c.is_deleted,
3175
3276
  c.data->>'tradeItemId' AS trade_item_id,
3176
3277
  COALESCE((c.data->>'numberOfPieces')::int, 0) AS pieces,
3177
- c.data->'packingConfiguration' AS order_packing
3278
+ c.data->'packingConfiguration' AS order_packing,
3279
+ c.data->'delivery'->>'latestDeliveryDateTime' AS lever_at
3178
3280
  FROM public.floriday_salesorders_cache c
3179
3281
  WHERE NOT EXISTS (
3180
3282
  SELECT 1 FROM public.floriday_sales_order_ingest i
@@ -3287,7 +3389,9 @@ BEGIN
3287
3389
  END IF;
3288
3390
  END IF;
3289
3391
 
3290
- IF v_fust_id IS NOT NULL THEN
3392
+ IF v_fust_id IS NOT NULL AND public.voorraad_fust_voor_startmoment(v_fust_id, public.voorraad_lever_datum(r.lever_at)) THEN
3393
+ v_packages := 0; -- leverdatum vóór het fust-startmoment: geen fustboeking
3394
+ ELSIF v_fust_id IS NOT NULL THEN
3291
3395
  -- Eerder geboekt aantal voor deze order (vorige versie) terugdraaien.
3292
3396
  SELECT COALESCE(MAX((i.impact->>'fustPackages')::int), 0)
3293
3397
  INTO v_prev_packages
@@ -3325,7 +3429,8 @@ BEGIN
3325
3429
  'reservedDelta', v_delta,
3326
3430
  'fustTypeId', v_fust_id,
3327
3431
  'fustKey', v_fust_key,
3328
- 'fustPackages', v_packages
3432
+ 'fustPackages', v_packages,
3433
+ 'voorStartmoment', public.voorraad_fust_voor_startmoment(v_fust_id, public.voorraad_lever_datum(r.lever_at))
3329
3434
  )
3330
3435
  )
3331
3436
  ON CONFLICT (connection_id, sales_order_id, order_version) DO NOTHING;
@@ -6331,10 +6436,10 @@ AS $function$
6331
6436
  DECLARE
6332
6437
  v_item record; v_order_number text; v_packs jsonb; v_pack jsonb; v_key text;
6333
6438
  v_units numeric; v_needed integer; v_fust_id uuid; v_prev integer; v_delta integer;
6334
- v_wanted jsonb := '{}'::jsonb; v_row record; v_user uuid; v_ing jsonb;
6439
+ v_wanted jsonb := '{}'::jsonb; v_row record; v_user uuid; v_ing jsonb; v_voor boolean := false;
6335
6440
  BEGIN
6336
6441
  PERFORM public.voorraad_vereis('ingelogd', 'sync_order_item_fusten');
6337
- SELECT oi.*, o.order_number, x.quantity_crates, x.packing_source, x.vbn_package_code, x.quarantined_at AS q_at, public.voorraad_order_geannuleerd(o.status, o.cancelled_at, o.deleted_at) AS o_geannuleerd INTO v_item
6442
+ SELECT oi.*, o.order_number, x.quantity_crates, x.packing_source, x.vbn_package_code, x.quarantined_at AS q_at, public.voorraad_order_geannuleerd(o.status, o.cancelled_at, o.deleted_at) AS o_geannuleerd, o.delivery_date AS o_lever INTO v_item
6338
6443
  FROM logistics_active.order_items oi
6339
6444
  LEFT JOIN public.orders o ON o.id = oi.order_id
6340
6445
  LEFT JOIN public.order_items x ON x.id = oi.id
@@ -6396,7 +6501,9 @@ BEGIN
6396
6501
  ON CONFLICT DO NOTHING;
6397
6502
  v_fust_id := public.voorraad_active_fust_id(v_key, NULL);
6398
6503
  END IF;
6399
- IF v_fust_id IS NOT NULL THEN
6504
+ IF v_fust_id IS NOT NULL AND public.voorraad_fust_voor_startmoment(v_fust_id, public.voorraad_lever_datum(v_item.o_lever)) THEN
6505
+ v_voor := true; -- leverdatum vóór het fust-startmoment: niets boeken
6506
+ ELSIF v_fust_id IS NOT NULL THEN
6400
6507
  v_wanted := jsonb_build_object(v_fust_id::text, v_needed);
6401
6508
  END IF;
6402
6509
  END IF;
@@ -6404,7 +6511,7 @@ BEGIN
6404
6511
 
6405
6512
  -- Floriday-order al afgeboekt bij het binnenhalen: die boeking overnemen (geen tweede mutatie).
6406
6513
  IF v_item.id IS NOT NULL AND v_item.q_at IS NULL AND NOT COALESCE(v_item.o_geannuleerd, false) AND v_item.source_line_key IS NOT NULL
6407
- AND NOT EXISTS (SELECT 1 FROM public.order_fust_bookings b WHERE b.order_item_id = _order_item_id) THEN
6514
+ AND NOT EXISTS (SELECT 1 FROM public.order_fust_bookings b WHERE b.order_item_id = _order_item_id) AND NOT v_voor THEN
6408
6515
  SELECT i.impact INTO v_ing FROM public.floriday_sales_order_ingest i
6409
6516
  WHERE i.sales_order_id = v_item.source_line_key AND (i.impact->>'fustTypeId') IS NOT NULL
6410
6517
  ORDER BY i.order_version DESC LIMIT 1;
@@ -6417,6 +6524,7 @@ BEGIN
6417
6524
 
6418
6525
  FOR v_row IN SELECT b.fust_type_id, b.quantity FROM public.order_fust_bookings b WHERE b.order_item_id = _order_item_id
6419
6526
  LOOP
6527
+ IF public.voorraad_fust_voor_startmoment(v_row.fust_type_id, public.voorraad_lever_datum(v_item.o_lever)) THEN CONTINUE; END IF;
6420
6528
  v_prev := v_row.quantity;
6421
6529
  v_needed := COALESCE((v_wanted->>v_row.fust_type_id::text)::int, 0);
6422
6530
  v_delta := v_prev - v_needed;
@@ -6694,24 +6802,17 @@ CREATE OR REPLACE FUNCTION public.trg_floriday_trade_items_autolink_fn()
6694
6802
  SECURITY DEFINER
6695
6803
  SET search_path TO 'public'
6696
6804
  AS $function$
6697
- DECLARE r RECORD; v_name text; v_gtin text; v_article text;
6698
6805
  BEGIN
6699
- IF NEW.is_deleted THEN RETURN NEW; END IF;
6700
- v_name := COALESCE(NEW.data->>'tradeItemName', NEW.data->'tradeItemName'->>'nl', NEW.data->'tradeItemName'->>'en');
6701
- v_gtin := NEW.data->>'gtin';
6702
- v_article := NEW.data->>'articleCode';
6703
-
6704
- FOR r IN
6705
- SELECT id FROM public.products
6706
- WHERE (trade_item_key IS NULL OR trade_item_key LIKE 'product:%' OR trade_item_key LIKE 'draft:%')
6707
- AND (
6708
- (v_name IS NOT NULL AND lower(btrim(product)) = lower(btrim(v_name)))
6709
- OR (v_gtin IS NOT NULL AND btrim(barcode) = btrim(v_gtin))
6710
- OR (v_article IS NOT NULL AND btrim(barcode) = btrim(v_article))
6711
- )
6712
- LOOP
6713
- PERFORM public.autolink_product_to_floriday(r.id);
6714
- END LOOP;
6806
+ BEGIN
6807
+ INSERT INTO public.voorraad_floriday_artikel_wachtrij(floriday_id, connection_id, aangemeld_at)
6808
+ VALUES (NEW.floriday_id, NEW.connection_id, now())
6809
+ ON CONFLICT (floriday_id) DO UPDATE SET aangemeld_at = now(), connection_id = EXCLUDED.connection_id;
6810
+ IF NOT EXISTS (SELECT 1 FROM cron.job WHERE jobname = 'voorraad-floriday-artikelen-drain') THEN
6811
+ PERFORM cron.schedule('voorraad-floriday-artikelen-drain', '* * * * *', 'SELECT public.voorraad_floriday_artikelen_vangnet()');
6812
+ END IF;
6813
+ EXCEPTION WHEN others THEN
6814
+ RAISE WARNING '[voorraad] Floriday-artikel niet aangemeld: %', SQLERRM;
6815
+ END;
6715
6816
  RETURN NEW;
6716
6817
  END $function$
6717
6818
  ;
@@ -6729,8 +6830,35 @@ CREATE OR REPLACE FUNCTION public.trg_order_items_autolink_fn()
6729
6830
  SECURITY DEFINER
6730
6831
  SET search_path TO 'public'
6731
6832
  AS $function$
6732
- DECLARE v_resolved uuid;
6833
+ DECLARE v_resolved uuid; v_m jsonb;
6733
6834
  BEGIN
6835
+ -- Floriday-orderregel: koppelen op het Floriday-artikel (eenduidig), nooit op naam.
6836
+ -- Een al ingevuld artikel wordt nooit overschreven (Engine of gebruiker koos het).
6837
+ IF NEW.source_line_key IS NOT NULL AND btrim(NEW.source_line_key) <> '' THEN
6838
+ BEGIN
6839
+ v_m := public.voorraad_orderregel_trade_item_bron(NEW.order_id, NEW.source_line_key);
6840
+ EXCEPTION WHEN others THEN v_m := NULL;
6841
+ END;
6842
+ IF v_m->>'status' = 'eenduidig' THEN
6843
+ IF NEW.product_id IS NULL THEN
6844
+ BEGIN
6845
+ INSERT INTO public.voorraad_koppel_log(soort, floriday_id, product_id, order_item_id, oud, nieuw)
6846
+ VALUES ('orderregel_op_floriday_artikel', v_m->>'trade_item_id', (v_m->>'product_id')::uuid, NEW.id,
6847
+ jsonb_build_object('product_id', NULL), jsonb_build_object('product_id', v_m->>'product_id'));
6848
+ EXCEPTION WHEN others THEN NULL;
6849
+ END;
6850
+ NEW.product_id := (v_m->>'product_id')::uuid;
6851
+ ELSIF NEW.product_id IS DISTINCT FROM (v_m->>'product_id')::uuid THEN
6852
+ BEGIN
6853
+ INSERT INTO public.voorraad_koppel_log(soort, floriday_id, product_id, order_item_id, oud, nieuw)
6854
+ VALUES ('orderregel_afwijkend_behouden', v_m->>'trade_item_id', NEW.product_id, NEW.id,
6855
+ jsonb_build_object('product_id', NEW.product_id), jsonb_build_object('floriday_product_id', v_m->>'product_id'));
6856
+ EXCEPTION WHEN others THEN NULL;
6857
+ END;
6858
+ END IF;
6859
+ END IF;
6860
+ RETURN NEW;
6861
+ END IF;
6734
6862
  IF NEW.product_id IS NULL AND NEW.product_name IS NOT NULL THEN
6735
6863
  v_resolved := public.resolve_product_id_for_item(NEW.product_name);
6736
6864
  IF v_resolved IS NOT NULL THEN NEW.product_id := v_resolved; END IF;
@@ -7131,6 +7259,28 @@ END $function$
7131
7259
  REVOKE ALL ON FUNCTION trg_products_floriday_link_enqueue() FROM PUBLIC;
7132
7260
  REVOKE ALL ON FUNCTION trg_products_floriday_link_enqueue() FROM anon;
7133
7261
  GRANT EXECUTE ON FUNCTION trg_products_floriday_link_enqueue() TO service_role;
7262
+ CREATE OR REPLACE FUNCTION public.trg_products_floriday_velden_aangepast()
7263
+ RETURNS trigger
7264
+ LANGUAGE plpgsql
7265
+ SECURITY DEFINER
7266
+ SET search_path TO 'public'
7267
+ AS $function$
7268
+ DECLARE v text[];
7269
+ BEGIN
7270
+ IF COALESCE(current_setting('voorraad.floriday_sync', true), '') = 'on' THEN RETURN NEW; END IF;
7271
+ IF COALESCE(NEW.custom_fields->>'voorraad_auto_aangemaakt', '') <> 'true' THEN RETURN NEW; END IF;
7272
+ v := COALESCE(NEW.floriday_velden_aangepast, ARRAY[]::text[]);
7273
+ IF NEW.product IS DISTINCT FROM OLD.product AND NOT ('product' = ANY(v)) THEN v := v || 'product'; END IF;
7274
+ IF NEW.image_url IS DISTINCT FROM OLD.image_url AND NOT ('image_url' = ANY(v)) THEN v := v || 'image_url'; END IF;
7275
+ IF NEW.barcode IS DISTINCT FROM OLD.barcode AND NOT ('barcode' = ANY(v)) THEN v := v || 'barcode'; END IF;
7276
+ NEW.floriday_velden_aangepast := NULLIF(v, ARRAY[]::text[]);
7277
+ RETURN NEW;
7278
+ END $function$
7279
+ ;
7280
+ REVOKE ALL ON FUNCTION trg_products_floriday_velden_aangepast() FROM PUBLIC;
7281
+ REVOKE ALL ON FUNCTION trg_products_floriday_velden_aangepast() FROM anon;
7282
+ GRANT EXECUTE ON FUNCTION trg_products_floriday_velden_aangepast() TO authenticated;
7283
+ GRANT EXECUTE ON FUNCTION trg_products_floriday_velden_aangepast() TO service_role;
7134
7284
  CREATE OR REPLACE FUNCTION public.trg_products_placeholder_code()
7135
7285
  RETURNS trigger
7136
7286
  LANGUAGE plpgsql
@@ -7231,7 +7381,8 @@ AS $function$
7231
7381
  BEGIN
7232
7382
  IF COALESCE(NEW.sale_price,0) > 0
7233
7383
  AND lower(trim(COALESCE(NEW.unit,'stuks'))) IN ('stuks','stuk','')
7234
- AND (NEW.sale_price IS DISTINCT FROM OLD.sale_price OR NEW.unit IS DISTINCT FROM OLD.unit) THEN
7384
+ AND (NEW.sale_price IS DISTINCT FROM OLD.sale_price OR NEW.unit IS DISTINCT FROM OLD.unit)
7385
+ AND public.voorraad_floriday_volgt(NEW.id) THEN
7235
7386
  WITH latest AS (
7236
7387
  SELECT DISTINCT ON (target_year, target_week) id, target_year, target_week
7237
7388
  FROM public.floriday_continuous_stock_queue
@@ -7245,12 +7396,10 @@ BEGIN
7245
7396
  AND q.id <> l.id
7246
7397
  AND q.target_year IS NOT DISTINCT FROM l.target_year
7247
7398
  AND q.target_week IS NOT DISTINCT FROM l.target_week;
7248
-
7249
7399
  UPDATE public.floriday_continuous_stock_queue q
7250
7400
  SET status = 'pending', next_attempt_at = now(), skip_reason = NULL,
7251
7401
  last_error = NULL, result_label = NULL, updated_at = now()
7252
7402
  WHERE q.product_id = NEW.id AND q.status = 'skipped_no_price';
7253
-
7254
7403
  PERFORM public.enqueue_structure_recompute(ARRAY[NEW.id], 'price_change', true);
7255
7404
  END IF;
7256
7405
  RETURN NEW;
@@ -7665,24 +7814,8 @@ CREATE OR REPLACE FUNCTION public.trg_trade_items_cache_autolink_fn()
7665
7814
  SECURITY DEFINER
7666
7815
  SET search_path TO 'public'
7667
7816
  AS $function$
7668
- DECLARE p RECORD; v_new text;
7669
7817
  BEGIN
7670
- IF NEW.is_deleted THEN RETURN NEW; END IF;
7671
- v_new := NEW.floriday_id;
7672
- FOR p IN
7673
- SELECT id, trade_item_key FROM public.products WHERE
7674
- (NEW.data->>'gtin' IS NOT NULL AND barcode = NEW.data->>'gtin')
7675
- OR (NEW.data->>'articleCode' IS NOT NULL AND barcode = NEW.data->>'articleCode')
7676
- OR (NEW.data->>'tradeItemName' IS NOT NULL
7677
- AND lower(btrim(product)) = lower(btrim(NEW.data->>'tradeItemName')))
7678
- LOOP
7679
- IF p.trade_item_key IS DISTINCT FROM v_new THEN
7680
- IF p.trade_item_key IS NOT NULL AND p.trade_item_key LIKE 'product:%' THEN
7681
- PERFORM public.promote_trade_item_key(p.trade_item_key, v_new);
7682
- END IF;
7683
- UPDATE public.products SET trade_item_key = v_new WHERE id = p.id;
7684
- END IF;
7685
- END LOOP;
7818
+ -- Koppelen gebeurt eenduidig in voorraad_floriday_artikelen_aanmaken (via trg_floriday_trade_items_autolink_fn).
7686
7819
  RETURN NEW;
7687
7820
  END $function$
7688
7821
  ;
@@ -7928,6 +8061,176 @@ REVOKE ALL ON FUNCTION voorraad_article_vbn(uuid) FROM PUBLIC;
7928
8061
  REVOKE ALL ON FUNCTION voorraad_article_vbn(uuid) FROM anon;
7929
8062
  GRANT EXECUTE ON FUNCTION voorraad_article_vbn(uuid) TO authenticated;
7930
8063
  GRANT EXECUTE ON FUNCTION voorraad_article_vbn(uuid) TO service_role;
8064
+ CREATE OR REPLACE FUNCTION public.voorraad_artikel_archiveren(p_id uuid, p_archiveren boolean, p_bevestigd boolean DEFAULT false, p_ook_floriday boolean DEFAULT false)
8065
+ RETURNS jsonb
8066
+ LANGUAGE plpgsql
8067
+ SECURITY DEFINER
8068
+ SET search_path TO 'public'
8069
+ AS $function$
8070
+ DECLARE v_d jsonb; v_res numeric; v_open int; v_fl text; v_was boolean;
8071
+ BEGIN
8072
+ PERFORM public.voorraad_vereis('ingelogd', 'voorraad_artikel_archiveren');
8073
+ IF NOT public.voorraad_can_write() THEN
8074
+ RAISE EXCEPTION 'Je hebt geen schrijfrechten op de voorraad.' USING ERRCODE = '42501'; END IF;
8075
+ SELECT NULLIF(btrim(COALESCE(floriday_trade_item_id,'')),''), gearchiveerd_floriday_nul INTO v_fl, v_was
8076
+ FROM public.products WHERE id = p_id FOR UPDATE;
8077
+ IF NOT FOUND THEN RAISE EXCEPTION 'Artikel niet gevonden.' USING ERRCODE = 'P0002'; END IF;
8078
+ IF p_archiveren THEN
8079
+ IF p_ook_floriday AND v_fl IS NULL THEN
8080
+ RAISE EXCEPTION 'Dit artikel is niet aan Floriday gekoppeld; kies "Alleen hier".' USING ERRCODE = '22023'; END IF;
8081
+ v_d := public.voorraad_artikel_detail(p_id);
8082
+ v_res := COALESCE((v_d->>'gereserveerd')::numeric, 0); v_open := COALESCE((v_d->>'open_orders')::int, 0);
8083
+ IF (v_res > 0 OR v_open > 0) AND NOT COALESCE(p_bevestigd,false) THEN
8084
+ RETURN jsonb_build_object('ok', false, 'waarschuwing', true, 'gereserveerd', v_res, 'open_orders', v_open);
8085
+ END IF;
8086
+ UPDATE public.products SET gearchiveerd_at = COALESCE(gearchiveerd_at, now()), gearchiveerd_door = auth.uid(),
8087
+ gearchiveerd_floriday_nul = COALESCE(p_ook_floriday, false) WHERE id = p_id;
8088
+ ELSE
8089
+ UPDATE public.products SET gearchiveerd_at = NULL, gearchiveerd_door = NULL, gearchiveerd_floriday_nul = false WHERE id = p_id;
8090
+ END IF;
8091
+ -- Floriday-voorraad opnieuw laten berekenen via de gewone wachtrij (schrijfstop en ERP-leidend-controle blijven gelden).
8092
+ IF v_fl IS NOT NULL AND (COALESCE(p_ook_floriday,false) OR COALESCE(v_was,false)) THEN
8093
+ BEGIN PERFORM public.enqueue_structure_recompute(ARRAY[p_id], 'ARCHIVED', true);
8094
+ EXCEPTION WHEN others THEN NULL; END;
8095
+ END IF;
8096
+ RETURN jsonb_build_object('ok', true, 'ook_floriday', COALESCE(p_ook_floriday,false) AND p_archiveren);
8097
+ END $function$
8098
+ ;
8099
+ REVOKE ALL ON FUNCTION voorraad_artikel_archiveren(uuid,boolean,boolean,boolean) FROM PUBLIC;
8100
+ REVOKE ALL ON FUNCTION voorraad_artikel_archiveren(uuid,boolean,boolean,boolean) FROM anon;
8101
+ GRANT EXECUTE ON FUNCTION voorraad_artikel_archiveren(uuid,boolean,boolean,boolean) TO authenticated;
8102
+ GRANT EXECUTE ON FUNCTION voorraad_artikel_archiveren(uuid,boolean,boolean,boolean) TO service_role;
8103
+ CREATE OR REPLACE FUNCTION public.voorraad_artikel_detail(p_id uuid)
8104
+ RETURNS jsonb
8105
+ LANGUAGE plpgsql
8106
+ STABLE SECURITY DEFINER
8107
+ SET search_path TO 'public'
8108
+ AS $function$
8109
+ DECLARE
8110
+ p public.products%ROWTYPE; v_conn uuid; v_ctx uuid; v_key text; s public.trade_item_stock%ROWTYPE;
8111
+ v_soort text; v_vrij numeric; v_open int; v_erp boolean; v_tel timestamptz; v_pak boolean; v_pak_ok boolean;
8112
+ BEGIN
8113
+ PERFORM public.voorraad_vereis('ingelogd', 'voorraad_artikel_detail');
8114
+ SELECT * INTO p FROM public.products WHERE id = p_id;
8115
+ IF NOT FOUND THEN RETURN jsonb_build_object('gevonden', false); END IF;
8116
+ BEGIN SELECT x.connection_id INTO v_conn FROM public.get_active_floriday_context() x LIMIT 1;
8117
+ EXCEPTION WHEN others THEN v_conn := NULL; END;
8118
+ BEGIN SELECT c.connection_id INTO v_ctx FROM public.get_stock_context() c LIMIT 1;
8119
+ EXCEPTION WHEN others THEN v_ctx := NULL; END;
8120
+
8121
+ SELECT x.* INTO s FROM public.trade_item_stock x
8122
+ WHERE x.archived_at IS NULL AND x.trade_item_key IN (
8123
+ NULLIF(btrim(COALESCE(p.trade_item_key,'')),''), NULLIF(btrim(COALESCE(p.floriday_trade_item_id,'')),''), 'product:' || p.id::text)
8124
+ ORDER BY CASE x.trade_item_key WHEN NULLIF(btrim(COALESCE(p.trade_item_key,'')),'') THEN 1
8125
+ WHEN NULLIF(btrim(COALESCE(p.floriday_trade_item_id,'')),'') THEN 2 ELSE 3 END,
8126
+ (x.connection_id IS NOT DISTINCT FROM v_ctx) DESC, x.updated_at DESC
8127
+ LIMIT 1;
8128
+ v_key := s.trade_item_key;
8129
+
8130
+ v_soort := CASE WHEN EXISTS (SELECT 1 FROM public.product_groups g WHERE g.head_product_id = p.id AND g.is_active) THEN 'GROUP'
8131
+ WHEN EXISTS (SELECT 1 FROM public.product_bom_items b WHERE b.parent_product_id = p.id) THEN 'SET' ELSE 'NORMAL' END;
8132
+ v_vrij := GREATEST(COALESCE(s.physical_stock,0) - COALESCE(s.reserved_stock,0), 0);
8133
+ IF v_soort <> 'NORMAL' AND v_ctx IS NOT NULL THEN
8134
+ BEGIN v_vrij := COALESCE((public.calculate_structure_available(v_ctx, p.id)->>'available')::numeric, v_vrij);
8135
+ EXCEPTION WHEN others THEN NULL; END;
8136
+ END IF;
8137
+
8138
+ SELECT count(*) INTO v_open FROM public.order_items oi JOIN public.orders o ON o.id = oi.order_id
8139
+ WHERE oi.product_id = p.id AND oi.quarantined_at IS NULL
8140
+ AND NOT public.voorraad_order_geannuleerd(o.status, o.cancelled_at, o.deleted_at)
8141
+ AND lower(COALESCE(o.status,'')) NOT IN ('verzonden','gereed voor facturatie');
8142
+
8143
+ IF NULLIF(btrim(COALESCE(p.floriday_trade_item_id,'')),'') IS NOT NULL THEN
8144
+ SELECT bool_or(f.erp_leading) INTO v_erp FROM public.floriday_inventory_settings f
8145
+ WHERE f.product_id = p.id;
8146
+ END IF;
8147
+ SELECT max(t.telmoment) INTO v_tel FROM public.voorraad_telmoment t;
8148
+ v_pak := EXISTS (SELECT 1 FROM public.product_packings k WHERE k.product_id = p.id AND k.is_primary);
8149
+ v_pak_ok := COALESCE((p.custom_fields#>>'{voorraad_packing_link,ok}')::boolean, true);
8150
+
8151
+ RETURN jsonb_build_object(
8152
+ 'gevonden', true, 'id', p.id,
8153
+ 'velden', jsonb_build_object('product', p.product, 'article_number', p.article_number, 'sale_price', p.sale_price,
8154
+ 'unit', p.unit, 'barcode', p.barcode),
8155
+ 'naam', COALESCE(NULLIF(btrim(p.product),''), NULLIF(p.custom_fields#>>'{floriday_payload,name}',''), '(naamloos)'),
8156
+ 'artikelnummer', COALESCE(NULLIF(btrim(p.article_number),''), NULLIF(p.custom_fields#>>'{floriday_payload,articleCode}','')),
8157
+ 'foto', COALESCE(NULLIF(p.image_url,''), NULLIF(p.custom_fields#>>'{floriday_payload,images,0,url}',''), NULLIF(p.custom_fields#>>'{floriday_payload,images,0,previewUrl}','')),
8158
+ 'floriday_id', NULLIF(btrim(COALESCE(p.floriday_trade_item_id,'')),''),
8159
+ 'heeft_koppeling', v_conn IS NOT NULL,
8160
+ 'soort', v_soort,
8161
+ 'is_onderdeel', EXISTS (SELECT 1 FROM public.product_bom_items b WHERE b.component_product_id = p.id),
8162
+ 'voorraad_sleutel', v_key,
8163
+ 'liggend', COALESCE(s.physical_stock,0), 'gereserveerd', COALESCE(s.reserved_stock,0),
8164
+ 'inkomend', COALESCE(s.incoming_stock,0), 'minimum', COALESCE(s.minimum_stock,0), 'vrij', v_vrij,
8165
+ 'notitie', s.notes, 'open_orders', v_open,
8166
+ 'floriday_stuurt', COALESCE(v_erp, false),
8167
+ 'telmoment', v_tel,
8168
+ 'verpakking_ok', (v_soort = 'GROUP' OR (v_pak AND v_pak_ok)),
8169
+ 'gearchiveerd_at', p.gearchiveerd_at, 'orders_gearchiveerd', CASE WHEN p.gearchiveerd_at IS NULL THEN 0 ELSE COALESCE((public.voorraad_orders_gearchiveerd(p.id)->0->>'regels')::int, 0) END,
8170
+ 'bijgewerkt', now());
8171
+ END $function$
8172
+ ;
8173
+ REVOKE ALL ON FUNCTION voorraad_artikel_detail(uuid) FROM PUBLIC;
8174
+ REVOKE ALL ON FUNCTION voorraad_artikel_detail(uuid) FROM anon;
8175
+ GRANT EXECUTE ON FUNCTION voorraad_artikel_detail(uuid) TO authenticated;
8176
+ GRANT EXECUTE ON FUNCTION voorraad_artikel_detail(uuid) TO service_role;
8177
+ CREATE OR REPLACE FUNCTION public.voorraad_artikel_opslaan(p_id uuid, p_oud jsonb, p_nieuw jsonb)
8178
+ RETURNS jsonb
8179
+ LANGUAGE plpgsql
8180
+ SECURITY DEFINER
8181
+ SET search_path TO 'public'
8182
+ AS $function$
8183
+ DECLARE
8184
+ v_cur jsonb; k text; v_conf jsonb := '[]'::jsonb; v_set jsonb := '{}'::jsonb;
8185
+ v_toegestaan text[] := ARRAY['product','article_number','sale_price','unit','barcode'];
8186
+ BEGIN
8187
+ PERFORM public.voorraad_vereis('ingelogd', 'voorraad_artikel_opslaan');
8188
+ IF NOT public.voorraad_can_write() THEN
8189
+ RAISE EXCEPTION 'Je hebt geen schrijfrechten op de voorraad.' USING ERRCODE = '42501'; END IF;
8190
+ IF jsonb_typeof(p_nieuw) IS DISTINCT FROM 'object' OR jsonb_typeof(p_oud) IS DISTINCT FROM 'object' THEN
8191
+ RAISE EXCEPTION 'Ongeldige invoer voor opslaan.' USING ERRCODE = '22023'; END IF;
8192
+
8193
+ SELECT jsonb_build_object('product', product, 'article_number', article_number, 'sale_price', sale_price,
8194
+ 'unit', unit, 'barcode', barcode)
8195
+ INTO v_cur FROM public.products WHERE id = p_id FOR UPDATE;
8196
+ IF v_cur IS NULL THEN RAISE EXCEPTION 'Artikel niet gevonden.' USING ERRCODE = 'P0002'; END IF;
8197
+
8198
+ FOR k IN SELECT jsonb_object_keys(p_nieuw) LOOP
8199
+ IF NOT k = ANY(v_toegestaan) THEN
8200
+ RAISE EXCEPTION 'Veld "%" kan hier niet worden opgeslagen.', k USING ERRCODE = '22023'; END IF;
8201
+ IF NOT (p_oud ? k) THEN
8202
+ RAISE EXCEPTION 'Stand bij openen ontbreekt voor veld "%".', k USING ERRCODE = '22023'; END IF;
8203
+ -- Alleen velden die de gebruiker echt wijzigde.
8204
+ CONTINUE WHEN (p_nieuw->k) = (p_oud->k);
8205
+ IF (v_cur->k) IS DISTINCT FROM (p_oud->k) AND (v_cur->k) IS DISTINCT FROM (p_nieuw->k) THEN
8206
+ v_conf := v_conf || jsonb_build_object('veld', k, 'bij_openen', p_oud->k, 'jouw', p_nieuw->k, 'hun', v_cur->k);
8207
+ ELSE
8208
+ v_set := v_set || jsonb_build_object(k, p_nieuw->k);
8209
+ END IF;
8210
+ END LOOP;
8211
+
8212
+ IF jsonb_array_length(v_conf) > 0 THEN
8213
+ RETURN jsonb_build_object('ok', false, 'conflicten', v_conf);
8214
+ END IF;
8215
+ IF v_set ? 'sale_price' AND jsonb_typeof(v_set->'sale_price') = 'number' AND (v_set->>'sale_price')::numeric < 0 THEN
8216
+ RAISE EXCEPTION 'Verkoopprijs kan niet negatief zijn.' USING ERRCODE = '22023'; END IF;
8217
+
8218
+ IF v_set <> '{}'::jsonb THEN
8219
+ UPDATE public.products SET
8220
+ product = CASE WHEN v_set ? 'product' THEN NULLIF(btrim(v_set->>'product'),'') ELSE product END,
8221
+ article_number = CASE WHEN v_set ? 'article_number' THEN NULLIF(btrim(v_set->>'article_number'),'') ELSE article_number END,
8222
+ sale_price = CASE WHEN v_set ? 'sale_price' THEN (v_set->>'sale_price')::numeric ELSE sale_price END,
8223
+ unit = CASE WHEN v_set ? 'unit' THEN NULLIF(btrim(v_set->>'unit'),'') ELSE unit END,
8224
+ barcode = CASE WHEN v_set ? 'barcode' THEN NULLIF(btrim(v_set->>'barcode'),'') ELSE barcode END
8225
+ WHERE id = p_id;
8226
+ END IF;
8227
+ RETURN jsonb_build_object('ok', true, 'opgeslagen', v_set);
8228
+ END $function$
8229
+ ;
8230
+ REVOKE ALL ON FUNCTION voorraad_artikel_opslaan(uuid,jsonb,jsonb) FROM PUBLIC;
8231
+ REVOKE ALL ON FUNCTION voorraad_artikel_opslaan(uuid,jsonb,jsonb) FROM anon;
8232
+ GRANT EXECUTE ON FUNCTION voorraad_artikel_opslaan(uuid,jsonb,jsonb) TO authenticated;
8233
+ GRANT EXECUTE ON FUNCTION voorraad_artikel_opslaan(uuid,jsonb,jsonb) TO service_role;
7931
8234
  CREATE OR REPLACE FUNCTION public.voorraad_artikel_prijs(p_id text)
7932
8235
  RETURNS TABLE(product_id uuid, sale_price numeric, unit text)
7933
8236
  LANGUAGE plpgsql
@@ -8925,6 +9228,77 @@ END $function$
8925
9228
  REVOKE ALL ON FUNCTION voorraad_correctie_1140(boolean) FROM PUBLIC;
8926
9229
  REVOKE ALL ON FUNCTION voorraad_correctie_1140(boolean) FROM anon;
8927
9230
  GRANT EXECUTE ON FUNCTION voorraad_correctie_1140(boolean) TO service_role;
9231
+ CREATE OR REPLACE FUNCTION public.voorraad_correctie_1144(p_uitvoeren boolean DEFAULT false, p_limiet integer DEFAULT 200, p_max_seconden integer DEFAULT 20)
9232
+ RETURNS jsonb
9233
+ LANGUAGE plpgsql
9234
+ SECURITY DEFINER
9235
+ SET search_path TO 'public'
9236
+ AS $function$
9237
+ DECLARE r record; v_m jsonb; v_cat text; v_start timestamptz := clock_timestamp(); v_run uuid := gen_random_uuid();
9238
+ v_tel jsonb := '{}'::jsonb; v_status jsonb := '{}'::jsonb; v_orders uuid[] := ARRAY[]::uuid[]; n_ing int := 0; n_voor_fust int := 0;
9239
+ n_gedaan int := 0; n_rest int := 0; v_res jsonb; v_vb jsonb := '[]'::jsonb;
9240
+ BEGIN
9241
+ PERFORM public.voorraad_vereis('server', 'voorraad_correctie_1144');
9242
+ IF p_uitvoeren AND NOT pg_try_advisory_xact_lock(hashtext('voorraad_correctie_1144')) THEN
9243
+ RETURN jsonb_build_object('ok', false, 'melding', 'Er loopt al een ronde');
9244
+ END IF;
9245
+ FOR r IN
9246
+ SELECT oi.id, oi.order_id, oi.source_line_key, oi.quarantined_at, o.order_number, o.status, o.cancelled_at, o.deleted_at, o.delivery_date
9247
+ FROM public.order_items oi JOIN public.orders o ON o.id = oi.order_id
9248
+ WHERE oi.product_id IS NULL AND NULLIF(btrim(COALESCE(oi.source_line_key,'')),'') IS NOT NULL
9249
+ ORDER BY o.delivery_date NULLS LAST, oi.id
9250
+ LOOP
9251
+ v_m := public.voorraad_orderregel_trade_item_bron(r.order_id, r.source_line_key);
9252
+ v_cat := CASE
9253
+ WHEN v_m->>'status' <> 'eenduidig' THEN 'niet_gekoppeld_' || (v_m->>'status')
9254
+ WHEN r.quarantined_at IS NOT NULL OR public.voorraad_order_geannuleerd(r.status, r.cancelled_at, r.deleted_at) THEN 'overgeslagen_geannuleerd'
9255
+ WHEN lower(COALESCE(r.status,'')) IN ('verzonden','shipped','geleverd','delivered') THEN 'overgeslagen_verzonden'
9256
+ WHEN EXISTS (SELECT 1 FROM public.voorraad_telmoment_regels t WHERE t.order_item_id = r.id)
9257
+ OR EXISTS (SELECT 1 FROM public.stock_mutation_log m WHERE m.order_item_id = r.id AND m.mutation_type = 'consumption') THEN 'overgeslagen_verwerkt_voor_telling'
9258
+ ELSE 'invullen' END;
9259
+ v_tel := jsonb_set(v_tel, ARRAY[v_cat], to_jsonb(COALESCE((v_tel->>v_cat)::int, 0) + 1));
9260
+ IF v_cat = 'invullen' THEN
9261
+ IF NOT (r.order_id = ANY(v_orders)) THEN
9262
+ v_orders := v_orders || r.order_id;
9263
+ v_status := jsonb_set(v_status, ARRAY[COALESCE(r.status,'(leeg)')], to_jsonb(COALESCE((v_status->>COALESCE(r.status,'(leeg)'))::int, 0) + 1));
9264
+ END IF;
9265
+ IF EXISTS (SELECT 1 FROM public.floriday_sales_order_ingest i WHERE i.sales_order_id = r.source_line_key AND COALESCE((i.impact->>'fustPackages')::int,0) > 0) THEN
9266
+ n_ing := n_ing + 1;
9267
+ END IF;
9268
+ IF EXISTS (SELECT 1 FROM public.fust_startmoment) AND public.voorraad_lever_datum(r.delivery_date) <
9269
+ (SELECT (min(s.moment) AT TIME ZONE 'Europe/Amsterdam')::date FROM public.fust_startmoment s) THEN
9270
+ n_voor_fust := n_voor_fust + 1;
9271
+ END IF;
9272
+ IF jsonb_array_length(v_vb) < 20 THEN
9273
+ v_vb := v_vb || jsonb_build_object('order', r.order_number, 'order_item_id', r.id, 'status', r.status, 'leverdatum', r.delivery_date,
9274
+ 'trade_item_id', v_m->>'trade_item_id', 'product_id', v_m->>'product_id');
9275
+ END IF;
9276
+ IF p_uitvoeren THEN
9277
+ IF n_gedaan >= GREATEST(p_limiet, 1) OR clock_timestamp() - v_start > make_interval(secs => GREATEST(p_max_seconden, 1)) THEN
9278
+ n_rest := n_rest + 1;
9279
+ ELSE
9280
+ UPDATE public.order_items SET product_id = (v_m->>'product_id')::uuid WHERE id = r.id AND product_id IS NULL;
9281
+ IF FOUND THEN
9282
+ INSERT INTO public.voorraad_koppel_log(run_id, soort, floriday_id, product_id, order_item_id, oud, nieuw)
9283
+ VALUES (v_run, 'correctie_1144', v_m->>'trade_item_id', (v_m->>'product_id')::uuid, r.id,
9284
+ jsonb_build_object('product_id', NULL), jsonb_build_object('product_id', v_m->>'product_id'));
9285
+ n_gedaan := n_gedaan + 1;
9286
+ END IF;
9287
+ END IF;
9288
+ END IF;
9289
+ END IF;
9290
+ END LOOP;
9291
+ v_res := jsonb_build_object('per_categorie', v_tel, 'orders_per_status_invullen', v_status, 'orders_invullen', COALESCE(array_length(v_orders,1),0),
9292
+ 'regels_met_fustboeking_bij_binnenhalen', n_ing, 'regels_voor_fust_startmoment', n_voor_fust,
9293
+ 'ingevuld', n_gedaan, 'resterend', n_rest, 'run_id', CASE WHEN p_uitvoeren THEN v_run END);
9294
+ INSERT INTO public.voorraad_correcties(correctie, uitgevoerd_door, proef, aantallen, hash_ok)
9295
+ VALUES ('1144', COALESCE(auth.uid()::text, session_user::text), NOT p_uitvoeren, v_res, true);
9296
+ RETURN jsonb_build_object('ok', true, 'uitgevoerd', p_uitvoeren, 'voorbeelden', v_vb) || v_res;
9297
+ END $function$
9298
+ ;
9299
+ REVOKE ALL ON FUNCTION voorraad_correctie_1144(boolean,integer,integer) FROM PUBLIC;
9300
+ REVOKE ALL ON FUNCTION voorraad_correctie_1144(boolean,integer,integer) FROM anon;
9301
+ GRANT EXECUTE ON FUNCTION voorraad_correctie_1144(boolean,integer,integer) TO service_role;
8928
9302
  CREATE OR REPLACE FUNCTION public.voorraad_correctie_status(p_correctie text DEFAULT '1098'::text)
8929
9303
  RETURNS jsonb
8930
9304
  LANGUAGE plpgsql
@@ -8988,7 +9362,7 @@ CREATE OR REPLACE FUNCTION public.voorraad_defaults_versie()
8988
9362
  LANGUAGE sql
8989
9363
  IMMUTABLE
8990
9364
  SET search_path TO 'public'
8991
- AS $function$ SELECT 1 $function$
9365
+ AS $function$ SELECT 2 $function$
8992
9366
  ;
8993
9367
  REVOKE ALL ON FUNCTION voorraad_defaults_versie() FROM PUBLIC;
8994
9368
  REVOKE ALL ON FUNCTION voorraad_defaults_versie() FROM anon;
@@ -9025,6 +9399,43 @@ REVOKE ALL ON FUNCTION voorraad_dubbele_voorraadregels() FROM PUBLIC;
9025
9399
  REVOKE ALL ON FUNCTION voorraad_dubbele_voorraadregels() FROM anon;
9026
9400
  GRANT EXECUTE ON FUNCTION voorraad_dubbele_voorraadregels() TO authenticated;
9027
9401
  GRANT EXECUTE ON FUNCTION voorraad_dubbele_voorraadregels() TO service_role;
9402
+ CREATE OR REPLACE FUNCTION public.voorraad_eigen_velden_opslaan(p_id uuid, p_oud jsonb, p_nieuw jsonb)
9403
+ RETURNS jsonb
9404
+ LANGUAGE plpgsql
9405
+ SECURITY DEFINER
9406
+ SET search_path TO 'public'
9407
+ AS $function$
9408
+ DECLARE v_cur jsonb; k text; v_conf jsonb := '[]'::jsonb; v_set jsonb := '{}'::jsonb;
9409
+ BEGIN
9410
+ PERFORM public.voorraad_vereis('ingelogd', 'voorraad_eigen_velden_opslaan');
9411
+ IF NOT public.voorraad_can_write() THEN
9412
+ RAISE EXCEPTION 'Je hebt geen schrijfrechten op de voorraad.' USING ERRCODE = '42501'; END IF;
9413
+ IF jsonb_typeof(p_nieuw) IS DISTINCT FROM 'object' OR jsonb_typeof(p_oud) IS DISTINCT FROM 'object' THEN
9414
+ RAISE EXCEPTION 'Ongeldige invoer voor opslaan.' USING ERRCODE = '22023'; END IF;
9415
+ SELECT COALESCE(custom_fields, '{}'::jsonb) INTO v_cur FROM public.products WHERE id = p_id FOR UPDATE;
9416
+ IF NOT FOUND THEN RAISE EXCEPTION 'Artikel niet gevonden.' USING ERRCODE = 'P0002'; END IF;
9417
+ FOR k IN SELECT jsonb_object_keys(p_nieuw) LOOP
9418
+ IF NOT EXISTS (SELECT 1 FROM public.voorraad_custom_fields f WHERE f.field_key = k) THEN
9419
+ RAISE EXCEPTION 'Veld "%" is geen eigen veld.', k USING ERRCODE = '22023'; END IF;
9420
+ CONTINUE WHEN COALESCE(p_nieuw->k, 'null'::jsonb) = COALESCE(p_oud->k, 'null'::jsonb);
9421
+ IF COALESCE(v_cur->k,'null'::jsonb) IS DISTINCT FROM COALESCE(p_oud->k,'null'::jsonb)
9422
+ AND COALESCE(v_cur->k,'null'::jsonb) IS DISTINCT FROM COALESCE(p_nieuw->k,'null'::jsonb) THEN
9423
+ v_conf := v_conf || jsonb_build_object('veld', k, 'bij_openen', p_oud->k, 'jouw', p_nieuw->k, 'hun', v_cur->k);
9424
+ ELSE
9425
+ v_set := v_set || jsonb_build_object(k, p_nieuw->k);
9426
+ END IF;
9427
+ END LOOP;
9428
+ IF jsonb_array_length(v_conf) > 0 THEN RETURN jsonb_build_object('ok', false, 'conflicten', v_conf); END IF;
9429
+ IF v_set <> '{}'::jsonb THEN
9430
+ UPDATE public.products SET custom_fields = COALESCE(custom_fields,'{}'::jsonb) || v_set, updated_at = now() WHERE id = p_id;
9431
+ END IF;
9432
+ RETURN jsonb_build_object('ok', true, 'opgeslagen', v_set);
9433
+ END $function$
9434
+ ;
9435
+ REVOKE ALL ON FUNCTION voorraad_eigen_velden_opslaan(uuid,jsonb,jsonb) FROM PUBLIC;
9436
+ REVOKE ALL ON FUNCTION voorraad_eigen_velden_opslaan(uuid,jsonb,jsonb) FROM anon;
9437
+ GRANT EXECUTE ON FUNCTION voorraad_eigen_velden_opslaan(uuid,jsonb,jsonb) TO authenticated;
9438
+ GRANT EXECUTE ON FUNCTION voorraad_eigen_velden_opslaan(uuid,jsonb,jsonb) TO service_role;
9028
9439
  CREATE OR REPLACE FUNCTION public.voorraad_ensure_defaults()
9029
9440
  RETURNS jsonb
9030
9441
  LANGUAGE plpgsql
@@ -9033,26 +9444,29 @@ CREATE OR REPLACE FUNCTION public.voorraad_ensure_defaults()
9033
9444
  AS $function$
9034
9445
  DECLARE
9035
9446
  v_versie int := public.voorraad_defaults_versie();
9036
- -- Systeemgegevens (standaardlijst versie 1). Wijzig je deze lijst: verhoog voorraad_defaults_versie().
9447
+ -- Systeemgegevens (standaardlijst). Wijzig je deze lijst: verhoog voorraad_defaults_versie().
9037
9448
  v_ph text[] := ARRAY['artikelcode','artikelnummer','naam','name','code','article code','articlecode','test','-','x','nvt','n.v.t.','tbd','todo'];
9038
9449
  v_lc jsonb := '[{"code":"AUCTION_TROLLEY","naam":"Stapelwagen","volgorde":1},{"code":"DANISH_TROLLEY","naam":"Deense container","volgorde":2},{"code":"EURO_TROLLEY","naam":"Euro-rolcontainer","volgorde":3},{"code":"PALLET","naam":"Pallet","volgorde":4},{"code":"EURO_PALLET","naam":"Euro-pallet","volgorde":5},{"code":"NONE","naam":"Geen ladingdrager","volgorde":6}]';
9039
- -- Standaardcodes die vervallen zijn: worden op niet actief gezet, nooit verwijderd.
9040
9450
  v_vervallen text[] := ARRAY[]::text[];
9041
- n_ph int := 0; n_lc_nieuw int := 0; n_lc_bij int := 0; n_lc_uit int := 0; n_lim int := 0; v_res jsonb;
9451
+ n_ph int := 0; n_lc_nieuw int := 0; n_lc_bij int := 0; n_lc_uit int := 0; n_lim int := 0; n_inst int := 0; n_fust int := 0;
9452
+ v_idx text := 'bestaat'; v_job text := 'bestaat'; v_res jsonb; v_job_ok boolean;
9042
9453
  BEGIN
9043
9454
  PERFORM public.voorraad_vereis('ingelogd', 'voorraad_ensure_defaults');
9044
9455
  BEGIN
9456
+ BEGIN v_job_ok := EXISTS (SELECT 1 FROM cron.job WHERE jobname = 'voorraad-floriday-artikelen-uurlijks'); EXCEPTION WHEN others THEN v_job_ok := true; END;
9045
9457
  IF EXISTS (SELECT 1 FROM public.voorraad_defaults_state s WHERE s.id AND s.versie = v_versie)
9046
9458
  AND EXISTS (SELECT 1 FROM public.inventory_recompute_limits l WHERE l.id)
9047
9459
  AND EXISTS (SELECT 1 FROM public.voorraad_placeholder_codes)
9048
- AND EXISTS (SELECT 1 FROM public.voorraad_load_carrier_types) THEN
9460
+ AND EXISTS (SELECT 1 FROM public.voorraad_load_carrier_types)
9461
+ AND EXISTS (SELECT 1 FROM public.voorraad_instellingen WHERE id)
9462
+ AND NOT EXISTS (SELECT 1 FROM public.fust_types WHERE saldo_bijhouden IS NULL)
9463
+ AND v_job_ok THEN
9049
9464
  RETURN jsonb_build_object('ok', true, 'versie', v_versie, 'overgeslagen', true);
9050
9465
  END IF;
9051
9466
  PERFORM pg_advisory_xact_lock(hashtext('voorraad_ensure_defaults'));
9052
9467
 
9053
9468
  INSERT INTO public.voorraad_placeholder_codes(code) SELECT c FROM unnest(v_ph) c ON CONFLICT (code) DO NOTHING;
9054
9469
  GET DIAGNOSTICS n_ph = ROW_COUNT;
9055
-
9056
9470
  INSERT INTO public.voorraad_load_carrier_types(code, naam, volgorde)
9057
9471
  SELECT e->>'code', e->>'naam', (e->>'volgorde')::int FROM jsonb_array_elements(v_lc) e
9058
9472
  ON CONFLICT (code) DO NOTHING;
@@ -9065,16 +9479,42 @@ BEGIN
9065
9479
  WHERE t.code = e->>'code'
9066
9480
  AND (t.volgorde IS DISTINCT FROM (e->>'volgorde')::int OR (NOT t.naam_aangepast AND t.naam IS DISTINCT FROM e->>'naam'));
9067
9481
  GET DIAGNOSTICS n_lc_bij = ROW_COUNT;
9068
- UPDATE public.voorraad_load_carrier_types t SET actief = false, updated_at = now()
9069
- WHERE t.code = ANY(v_vervallen) AND t.actief;
9482
+ UPDATE public.voorraad_load_carrier_types t SET actief = false, updated_at = now() WHERE t.code = ANY(v_vervallen) AND t.actief;
9070
9483
  GET DIAGNOSTICS n_lc_uit = ROW_COUNT;
9071
9484
 
9072
- -- Klantinstelling: alleen aanmaken als hij ontbreekt, met de kolomstandaarden.
9485
+ -- Klantinstellingen: alleen aanmaken als ze ontbreken, met de kolomstandaarden.
9073
9486
  INSERT INTO public.inventory_recompute_limits DEFAULT VALUES ON CONFLICT (id) DO NOTHING;
9074
9487
  GET DIAGNOSTICS n_lim = ROW_COUNT;
9488
+ INSERT INTO public.voorraad_instellingen DEFAULT VALUES ON CONFLICT (id) DO NOTHING;
9489
+ GET DIAGNOSTICS n_inst = ROW_COUNT;
9490
+ -- Saldo bijhouden: alleen invullen waar nog leeg (eenmalige verpakking 999 en 800 uit).
9491
+ UPDATE public.fust_types SET saldo_bijhouden = NOT COALESCE(vbn_code IN (999, 800), false) WHERE saldo_bijhouden IS NULL;
9492
+ GET DIAGNOSTICS n_fust = ROW_COUNT;
9493
+
9494
+ -- Unieke index op het Floriday-id; overslaan (en melden in de installatiecontrole) bij dubbelen.
9495
+ IF NOT EXISTS (SELECT 1 FROM pg_indexes WHERE schemaname = 'public' AND indexname = 'products_floriday_trade_item_id_uniek') THEN
9496
+ IF jsonb_array_length(public.voorraad_floriday_dubbele_ids()) = 0 THEN
9497
+ BEGIN
9498
+ EXECUTE 'CREATE UNIQUE INDEX IF NOT EXISTS products_floriday_trade_item_id_uniek ON public.products (lower(btrim(floriday_trade_item_id))) WHERE NULLIF(btrim(COALESCE(floriday_trade_item_id, '''')), '''') IS NOT NULL';
9499
+ v_idx := 'aangemaakt';
9500
+ EXCEPTION WHEN others THEN v_idx := 'mislukt: ' || left(SQLERRM, 200);
9501
+ END;
9502
+ ELSE v_idx := 'overgeslagen: dubbele Floriday-id''s';
9503
+ END IF;
9504
+ END IF;
9505
+
9506
+ -- Uurlijkse controle van Floriday-artikelen (herhaalbaar, nooit dubbel).
9507
+ IF NOT v_job_ok THEN
9508
+ BEGIN
9509
+ PERFORM cron.schedule('voorraad-floriday-artikelen-uurlijks', '41 * * * *', 'SELECT public.voorraad_floriday_artikelen_vangnet()');
9510
+ v_job := 'ingepland';
9511
+ EXCEPTION WHEN others THEN v_job := 'mislukt: ' || left(SQLERRM, 200);
9512
+ END;
9513
+ END IF;
9075
9514
 
9076
9515
  v_res := jsonb_build_object('plaatshoudercodes_toegevoegd', n_ph, 'ladingdragers_toegevoegd', n_lc_nieuw,
9077
- 'ladingdragers_bijgewerkt', n_lc_bij, 'ladingdragers_niet_actief', n_lc_uit, 'herberekenlimieten_aangemaakt', n_lim);
9516
+ 'ladingdragers_bijgewerkt', n_lc_bij, 'ladingdragers_niet_actief', n_lc_uit, 'herberekenlimieten_aangemaakt', n_lim,
9517
+ 'instellingen_aangemaakt', n_inst, 'fustsoorten_saldo_ingevuld', n_fust, 'unieke_index', v_idx, 'uurlijkse_controle', v_job);
9078
9518
  INSERT INTO public.voorraad_defaults_state(id, versie, toegepast_at, resultaat) VALUES (true, v_versie, now(), v_res)
9079
9519
  ON CONFLICT (id) DO UPDATE SET versie = EXCLUDED.versie, toegepast_at = EXCLUDED.toegepast_at, resultaat = EXCLUDED.resultaat;
9080
9520
  RETURN jsonb_build_object('ok', true, 'versie', v_versie, 'overgeslagen', false) || v_res;
@@ -9124,6 +9564,234 @@ REVOKE ALL ON FUNCTION voorraad_fcsq_local_guard() FROM PUBLIC;
9124
9564
  REVOKE ALL ON FUNCTION voorraad_fcsq_local_guard() FROM anon;
9125
9565
  GRANT EXECUTE ON FUNCTION voorraad_fcsq_local_guard() TO authenticated;
9126
9566
  GRANT EXECUTE ON FUNCTION voorraad_fcsq_local_guard() TO service_role;
9567
+ CREATE OR REPLACE FUNCTION public.voorraad_floriday_artikelen_aanmaken(p_connection_id uuid DEFAULT NULL::uuid, p_limiet integer DEFAULT 200, p_uitvoeren boolean DEFAULT false, p_max_seconden integer DEFAULT 20)
9568
+ RETURNS jsonb
9569
+ LANGUAGE plpgsql
9570
+ SECURITY DEFINER
9571
+ SET search_path TO 'public'
9572
+ AS $function$
9573
+ DECLARE
9574
+ v_conn uuid; v_start timestamptz := clock_timestamp(); v_run uuid := gen_random_uuid(); v_dups jsonb;
9575
+ c record; v_name text; v_gtin text; v_code text; v_photo text; v_ver text; v_del boolean;
9576
+ v_p record; v_c uuid[]; v_tier text; v_new uuid; v_sturing text; v_own_key text;
9577
+ n_aan int := 0; n_kop int := 0; n_twijfel int := 0; n_uit int := 0; n_bij int := 0; n_fout int := 0; n_rest int := 0;
9578
+ v_vb jsonb := '[]'::jsonb; v_aangepast text[];
9579
+ BEGIN
9580
+ PERFORM public.voorraad_vereis('ingelogd', 'voorraad_floriday_artikelen_aanmaken');
9581
+ v_dups := public.voorraad_floriday_dubbele_ids();
9582
+ IF jsonb_array_length(v_dups) > 0 THEN
9583
+ RETURN jsonb_build_object('ok', false, 'melding', 'Er zijn artikelen met hetzelfde Floriday-id. Los die eerst op; er is niets aangemaakt.', 'dubbelen', v_dups);
9584
+ END IF;
9585
+ v_conn := COALESCE(p_connection_id, (SELECT x.connection_id FROM public.get_active_floriday_context() x LIMIT 1));
9586
+ IF v_conn IS NULL THEN RETURN jsonb_build_object('ok', true, 'melding', 'Geen actieve Floriday-koppeling'); END IF;
9587
+ IF p_uitvoeren AND NOT pg_try_advisory_xact_lock(hashtext('voorraad_floriday_artikelen_aanmaken')) THEN
9588
+ RETURN jsonb_build_object('ok', false, 'melding', 'Er loopt al een ronde');
9589
+ END IF;
9590
+ SELECT COALESCE(standaard_sturing_nieuwe_floriday, 'lokaal') INTO v_sturing FROM public.voorraad_instellingen WHERE id;
9591
+ v_sturing := COALESCE(v_sturing, 'lokaal');
9592
+ IF p_uitvoeren THEN PERFORM set_config('voorraad.floriday_sync', 'on', true); END IF;
9593
+
9594
+ FOR c IN
9595
+ SELECT t.floriday_id, t.data, (COALESCE(t.is_deleted,false) OR COALESCE((t.data->>'isDeleted')::boolean,false)) AS del
9596
+ FROM public.floriday_trade_items_cache t
9597
+ WHERE t.connection_id = v_conn
9598
+ AND (
9599
+ (NOT (COALESCE(t.is_deleted,false) OR COALESCE((t.data->>'isDeleted')::boolean,false))
9600
+ AND NOT EXISTS (SELECT 1 FROM public.products p WHERE lower(btrim(p.floriday_trade_item_id)) = lower(t.floriday_id))
9601
+ AND NOT EXISTS (SELECT 1 FROM public.voorraad_koppel_twijfel w WHERE w.floriday_id = t.floriday_id AND w.opgelost_at IS NULL AND w.laatst_gezien > now() - interval '1 hour' AND p_uitvoeren))
9602
+ OR ((COALESCE(t.is_deleted,false) OR COALESCE((t.data->>'isDeleted')::boolean,false))
9603
+ AND EXISTS (SELECT 1 FROM public.products p WHERE lower(btrim(p.floriday_trade_item_id)) = lower(t.floriday_id) AND p.floriday_verwijderd_at IS NULL))
9604
+ OR (NOT (COALESCE(t.is_deleted,false) OR COALESCE((t.data->>'isDeleted')::boolean,false))
9605
+ AND EXISTS (SELECT 1 FROM public.products p WHERE lower(btrim(p.floriday_trade_item_id)) = lower(t.floriday_id)
9606
+ AND (p.floriday_verwijderd_at IS NOT NULL
9607
+ OR (p.custom_fields->>'voorraad_auto_aangemaakt' = 'true'
9608
+ AND p.custom_fields->>'voorraad_floriday_versie' IS DISTINCT FROM t.data->>'tradeItemVersion'))))
9609
+ )
9610
+ ORDER BY t.floriday_id
9611
+ LOOP
9612
+ IF n_aan + n_kop + n_twijfel + n_uit + n_bij + n_fout >= GREATEST(p_limiet, 1)
9613
+ OR clock_timestamp() - v_start > make_interval(secs => GREATEST(p_max_seconden, 1)) THEN
9614
+ n_rest := n_rest + 1; CONTINUE;
9615
+ END IF;
9616
+ v_name := NULLIF(btrim(COALESCE(CASE WHEN jsonb_typeof(c.data->'tradeItemName') = 'string' THEN c.data->>'tradeItemName' END,
9617
+ c.data->'tradeItemName'->>'nl', c.data->'tradeItemName'->>'en')), '');
9618
+ v_gtin := NULLIF(btrim(COALESCE(c.data->>'articleGtin', c.data->>'gtin')), '');
9619
+ v_code := NULLIF(btrim(COALESCE(c.data->>'supplierArticleCode', c.data->>'articleCode')), '');
9620
+ SELECT e->>'url' INTO v_photo FROM jsonb_array_elements(CASE WHEN jsonb_typeof(c.data->'photos') = 'array' THEN c.data->'photos' ELSE '[]'::jsonb END) e
9621
+ ORDER BY COALESCE((e->>'primary')::boolean, false) DESC LIMIT 1;
9622
+ v_ver := c.data->>'tradeItemVersion';
9623
+ SELECT p.* INTO v_p FROM public.products p WHERE lower(btrim(p.floriday_trade_item_id)) = lower(c.floriday_id) LIMIT 1;
9624
+
9625
+ BEGIN
9626
+ IF c.del THEN
9627
+ -- Verwijderd in Floriday: eigen artikel op niet actief, nooit verwijderen.
9628
+ IF p_uitvoeren THEN
9629
+ UPDATE public.products SET floriday_verwijderd_at = now(), updated_at = now() WHERE id = v_p.id;
9630
+ INSERT INTO public.voorraad_koppel_log(run_id, soort, floriday_id, product_id, oud, nieuw)
9631
+ VALUES (v_run, 'niet_actief', c.floriday_id, v_p.id, jsonb_build_object('floriday_verwijderd_at', NULL), jsonb_build_object('floriday_verwijderd_at', now()));
9632
+ END IF;
9633
+ n_uit := n_uit + 1;
9634
+ ELSIF v_p.id IS NOT NULL THEN
9635
+ -- Bestaand artikel bijwerken uit Floriday, alleen velden die de klant niet zelf aanpaste.
9636
+ v_aangepast := COALESCE(v_p.floriday_velden_aangepast, ARRAY[]::text[]);
9637
+ IF p_uitvoeren THEN
9638
+ UPDATE public.products SET
9639
+ floriday_verwijderd_at = NULL,
9640
+ product = CASE WHEN v_p.custom_fields->>'voorraad_auto_aangemaakt' = 'true' AND NOT ('product' = ANY(v_aangepast)) AND v_name IS NOT NULL THEN v_name ELSE product END,
9641
+ image_url = CASE WHEN v_p.custom_fields->>'voorraad_auto_aangemaakt' = 'true' AND NOT ('image_url' = ANY(v_aangepast)) AND v_photo IS NOT NULL THEN v_photo ELSE image_url END,
9642
+ barcode = CASE WHEN v_p.custom_fields->>'voorraad_auto_aangemaakt' = 'true' AND NOT ('barcode' = ANY(v_aangepast)) AND v_gtin IS NOT NULL THEN v_gtin ELSE barcode END,
9643
+ custom_fields = CASE WHEN v_p.custom_fields->>'voorraad_auto_aangemaakt' = 'true'
9644
+ THEN COALESCE(custom_fields, '{}'::jsonb) || jsonb_build_object('voorraad_floriday_versie', v_ver) ELSE custom_fields END,
9645
+ updated_at = now()
9646
+ WHERE id = v_p.id;
9647
+ INSERT INTO public.voorraad_koppel_log(run_id, soort, floriday_id, product_id, oud, nieuw)
9648
+ VALUES (v_run, 'bijgewerkt', c.floriday_id, v_p.id,
9649
+ jsonb_build_object('product', v_p.product, 'image_url', v_p.image_url, 'barcode', v_p.barcode, 'floriday_verwijderd_at', v_p.floriday_verwijderd_at),
9650
+ jsonb_build_object('naam', v_name, 'foto', v_photo, 'gtin', v_gtin, 'versie', v_ver, 'aangepast_door_klant', to_jsonb(v_aangepast)));
9651
+ END IF;
9652
+ n_bij := n_bij + 1;
9653
+ ELSE
9654
+ -- Zoeken: eerst GTIN, dan artikelcode, dan naam. Eerste niveau met treffers telt.
9655
+ v_c := NULL; v_tier := NULL;
9656
+ IF v_gtin IS NOT NULL THEN
9657
+ SELECT array_agg(p.id ORDER BY p.id) INTO v_c FROM public.products p
9658
+ WHERE NULLIF(btrim(COALESCE(p.floriday_trade_item_id,'')),'') IS NULL AND p.floriday_verwijderd_at IS NULL
9659
+ AND COALESCE(p.custom_fields->>'source','') <> 'sales-module-floriday-mirror' AND btrim(COALESCE(p.barcode,'')) = v_gtin;
9660
+ IF v_c IS NOT NULL THEN v_tier := 'GTIN'; END IF;
9661
+ END IF;
9662
+ IF v_c IS NULL AND v_code IS NOT NULL THEN
9663
+ SELECT array_agg(p.id ORDER BY p.id) INTO v_c FROM public.products p
9664
+ WHERE NULLIF(btrim(COALESCE(p.floriday_trade_item_id,'')),'') IS NULL AND p.floriday_verwijderd_at IS NULL
9665
+ AND COALESCE(p.custom_fields->>'source','') <> 'sales-module-floriday-mirror'
9666
+ AND (lower(btrim(COALESCE(p.article_number,''))) = lower(v_code) OR btrim(COALESCE(p.barcode,'')) = v_code);
9667
+ IF v_c IS NOT NULL THEN v_tier := 'artikelcode'; END IF;
9668
+ END IF;
9669
+ IF v_c IS NULL AND v_name IS NOT NULL THEN
9670
+ SELECT array_agg(p.id ORDER BY p.id) INTO v_c FROM public.products p
9671
+ WHERE NULLIF(btrim(COALESCE(p.floriday_trade_item_id,'')),'') IS NULL AND p.floriday_verwijderd_at IS NULL
9672
+ AND COALESCE(p.custom_fields->>'source','') <> 'sales-module-floriday-mirror' AND lower(btrim(COALESCE(p.product,''))) = lower(v_name);
9673
+ IF v_c IS NOT NULL THEN v_tier := 'naam'; END IF;
9674
+ END IF;
9675
+
9676
+ IF v_c IS NOT NULL AND array_length(v_c, 1) > 1 THEN
9677
+ IF p_uitvoeren THEN
9678
+ INSERT INTO public.voorraad_koppel_twijfel(floriday_id, connection_id, naam, reden, kandidaten)
9679
+ VALUES (c.floriday_id, v_conn, v_name, 'Meerdere eigen artikelen met dezelfde ' || v_tier, to_jsonb(v_c))
9680
+ ON CONFLICT (floriday_id) DO UPDATE SET reden = EXCLUDED.reden, kandidaten = EXCLUDED.kandidaten, naam = EXCLUDED.naam, laatst_gezien = now(), opgelost_at = NULL;
9681
+ END IF;
9682
+ n_twijfel := n_twijfel + 1;
9683
+ v_vb := v_vb || jsonb_build_object('floriday_id', c.floriday_id, 'naam', v_name, 'uitkomst', 'twijfel', 'reden', 'meerdere op ' || v_tier);
9684
+ ELSIF v_c IS NOT NULL THEN
9685
+ v_own_key := public.voorraad_canoniek_sleutel(v_c[1]);
9686
+ IF lower(v_own_key) <> lower(c.floriday_id)
9687
+ AND EXISTS (SELECT 1 FROM public.trade_item_stock s WHERE s.trade_item_key = v_own_key AND s.archived_at IS NULL)
9688
+ AND EXISTS (SELECT 1 FROM public.trade_item_stock s WHERE lower(s.trade_item_key) = lower(c.floriday_id) AND s.archived_at IS NULL) THEN
9689
+ IF p_uitvoeren THEN
9690
+ INSERT INTO public.voorraad_koppel_twijfel(floriday_id, connection_id, naam, reden, kandidaten)
9691
+ VALUES (c.floriday_id, v_conn, v_name, 'Eigen artikel en Floriday-product hebben elk een eigen voorraadregel; eerst samenvoegen', to_jsonb(v_c))
9692
+ ON CONFLICT (floriday_id) DO UPDATE SET reden = EXCLUDED.reden, kandidaten = EXCLUDED.kandidaten, naam = EXCLUDED.naam, laatst_gezien = now(), opgelost_at = NULL;
9693
+ END IF;
9694
+ n_twijfel := n_twijfel + 1;
9695
+ v_vb := v_vb || jsonb_build_object('floriday_id', c.floriday_id, 'naam', v_name, 'uitkomst', 'twijfel', 'reden', 'twee voorraadregels');
9696
+ ELSE
9697
+ IF p_uitvoeren THEN
9698
+ UPDATE public.products SET floriday_trade_item_id = c.floriday_id, updated_at = now() WHERE id = v_c[1];
9699
+ INSERT INTO public.voorraad_koppel_log(run_id, soort, floriday_id, product_id, oud, nieuw)
9700
+ VALUES (v_run, 'gekoppeld', c.floriday_id, v_c[1], jsonb_build_object('floriday_trade_item_id', NULL), jsonb_build_object('floriday_trade_item_id', c.floriday_id, 'op', v_tier));
9701
+ UPDATE public.voorraad_koppel_twijfel SET opgelost_at = now() WHERE floriday_id = c.floriday_id AND opgelost_at IS NULL;
9702
+ END IF;
9703
+ n_kop := n_kop + 1;
9704
+ v_vb := v_vb || jsonb_build_object('floriday_id', c.floriday_id, 'naam', v_name, 'uitkomst', 'gekoppeld', 'op', v_tier);
9705
+ END IF;
9706
+ ELSE
9707
+ -- Geen match: eigen artikel aanmaken op dezelfde voorraadsleutel als het Floriday-product.
9708
+ IF v_name IS NULL THEN
9709
+ IF p_uitvoeren THEN
9710
+ INSERT INTO public.voorraad_koppel_twijfel(floriday_id, connection_id, naam, reden)
9711
+ VALUES (c.floriday_id, v_conn, NULL, 'Floriday-product heeft geen naam')
9712
+ ON CONFLICT (floriday_id) DO UPDATE SET reden = EXCLUDED.reden, laatst_gezien = now(), opgelost_at = NULL;
9713
+ END IF;
9714
+ n_twijfel := n_twijfel + 1;
9715
+ ELSE
9716
+ IF p_uitvoeren THEN
9717
+ INSERT INTO public.products(product, batch, location, barcode, article_number, image_url, floriday_trade_item_id, trade_item_key, custom_fields)
9718
+ VALUES (v_name, '', '', v_gtin, v_code, v_photo, c.floriday_id,
9719
+ COALESCE((SELECT s.trade_item_key FROM public.trade_item_stock s WHERE lower(s.trade_item_key) = lower(c.floriday_id) AND s.archived_at IS NULL ORDER BY s.created_at LIMIT 1), c.floriday_id),
9720
+ jsonb_build_object('voorraad_auto_aangemaakt', true, 'voorraad_floriday_versie', v_ver, 'voorraad_auto_aangemaakt_at', now()))
9721
+ RETURNING id INTO v_new;
9722
+ IF v_sturing = 'lokaal' THEN
9723
+ -- Lokaal: eigen voorraad leidt, Floriday volgt niet (geen ERP-leidend, sturing uit).
9724
+ UPDATE public.floriday_inventory_settings SET erp_leading = NULL, stock_mode = 'DISABLED', updated_at = now()
9725
+ WHERE product_id = v_new;
9726
+ END IF;
9727
+ INSERT INTO public.voorraad_koppel_log(run_id, soort, floriday_id, product_id, oud, nieuw)
9728
+ VALUES (v_run, 'aangemaakt', c.floriday_id, v_new, NULL,
9729
+ jsonb_build_object('naam', v_name, 'gtin', v_gtin, 'artikelcode', v_code, 'sturing', v_sturing));
9730
+ UPDATE public.voorraad_koppel_twijfel SET opgelost_at = now() WHERE floriday_id = c.floriday_id AND opgelost_at IS NULL;
9731
+ END IF;
9732
+ n_aan := n_aan + 1;
9733
+ END IF;
9734
+ END IF;
9735
+ END IF;
9736
+ EXCEPTION WHEN others THEN
9737
+ n_fout := n_fout + 1;
9738
+ BEGIN
9739
+ INSERT INTO public.voorraad_koppel_twijfel(floriday_id, connection_id, naam, reden)
9740
+ VALUES (c.floriday_id, v_conn, v_name, 'Aanmaken of koppelen mislukt: ' || left(SQLERRM, 300))
9741
+ ON CONFLICT (floriday_id) DO UPDATE SET reden = EXCLUDED.reden, laatst_gezien = now(), opgelost_at = NULL;
9742
+ EXCEPTION WHEN others THEN NULL;
9743
+ END;
9744
+ END;
9745
+ END LOOP;
9746
+
9747
+ RETURN jsonb_build_object('ok', true, 'uitgevoerd', p_uitvoeren, 'run_id', CASE WHEN p_uitvoeren THEN v_run END,
9748
+ 'aangemaakt', n_aan, 'gekoppeld', n_kop, 'twijfel', n_twijfel, 'niet_actief', n_uit, 'bijgewerkt', n_bij,
9749
+ 'fouten', n_fout, 'resterend', n_rest, 'sturing', v_sturing,
9750
+ 'voorbeelden', (SELECT COALESCE(jsonb_agg(e), '[]'::jsonb) FROM (SELECT e FROM jsonb_array_elements(v_vb) e LIMIT 25) z));
9751
+ END $function$
9752
+ ;
9753
+ REVOKE ALL ON FUNCTION voorraad_floriday_artikelen_aanmaken(uuid,integer,boolean,integer) FROM PUBLIC;
9754
+ REVOKE ALL ON FUNCTION voorraad_floriday_artikelen_aanmaken(uuid,integer,boolean,integer) FROM anon;
9755
+ GRANT EXECUTE ON FUNCTION voorraad_floriday_artikelen_aanmaken(uuid,integer,boolean,integer) TO authenticated;
9756
+ GRANT EXECUTE ON FUNCTION voorraad_floriday_artikelen_aanmaken(uuid,integer,boolean,integer) TO service_role;
9757
+ CREATE OR REPLACE FUNCTION public.voorraad_floriday_artikelen_vangnet()
9758
+ RETURNS jsonb
9759
+ LANGUAGE plpgsql
9760
+ SECURITY DEFINER
9761
+ SET search_path TO 'public'
9762
+ AS $function$
9763
+ DECLARE v jsonb; v_t timestamptz := now();
9764
+ BEGIN
9765
+ PERFORM public.voorraad_vereis('server', 'voorraad_floriday_artikelen_vangnet');
9766
+ v := public.voorraad_floriday_artikelen_aanmaken(NULL, 500, true, 45);
9767
+ IF COALESCE((v->>'resterend')::int, 0) = 0 THEN
9768
+ DELETE FROM public.voorraad_floriday_artikel_wachtrij WHERE aangemeld_at <= v_t;
9769
+ END IF;
9770
+ IF NOT EXISTS (SELECT 1 FROM public.voorraad_floriday_artikel_wachtrij) THEN
9771
+ BEGIN PERFORM cron.unschedule('voorraad-floriday-artikelen-drain'); EXCEPTION WHEN others THEN NULL; END;
9772
+ END IF;
9773
+ RETURN v;
9774
+ END $function$
9775
+ ;
9776
+ REVOKE ALL ON FUNCTION voorraad_floriday_artikelen_vangnet() FROM PUBLIC;
9777
+ REVOKE ALL ON FUNCTION voorraad_floriday_artikelen_vangnet() FROM anon;
9778
+ GRANT EXECUTE ON FUNCTION voorraad_floriday_artikelen_vangnet() TO service_role;
9779
+ CREATE OR REPLACE FUNCTION public.voorraad_floriday_dubbele_ids()
9780
+ RETURNS jsonb
9781
+ LANGUAGE sql
9782
+ STABLE SECURITY DEFINER
9783
+ SET search_path TO 'public'
9784
+ AS $function$
9785
+ SELECT COALESCE(jsonb_agg(jsonb_build_object('floriday_id', k, 'artikelen', ids) ORDER BY k), '[]'::jsonb)
9786
+ FROM (SELECT lower(btrim(floriday_trade_item_id)) k, jsonb_agg(id ORDER BY id) ids
9787
+ FROM public.products WHERE NULLIF(btrim(COALESCE(floriday_trade_item_id,'')),'') IS NOT NULL
9788
+ GROUP BY 1 HAVING count(*) > 1) d
9789
+ $function$
9790
+ ;
9791
+ REVOKE ALL ON FUNCTION voorraad_floriday_dubbele_ids() FROM PUBLIC;
9792
+ REVOKE ALL ON FUNCTION voorraad_floriday_dubbele_ids() FROM anon;
9793
+ GRANT EXECUTE ON FUNCTION voorraad_floriday_dubbele_ids() TO authenticated;
9794
+ GRANT EXECUTE ON FUNCTION voorraad_floriday_dubbele_ids() TO service_role;
9127
9795
  CREATE OR REPLACE FUNCTION public.voorraad_floriday_klaarzetten_toegestaan()
9128
9796
  RETURNS boolean
9129
9797
  LANGUAGE plpgsql
@@ -9167,6 +9835,36 @@ $function$
9167
9835
  REVOKE ALL ON FUNCTION voorraad_floriday_leads_key(text) FROM PUBLIC;
9168
9836
  REVOKE ALL ON FUNCTION voorraad_floriday_leads_key(text) FROM anon;
9169
9837
  GRANT EXECUTE ON FUNCTION voorraad_floriday_leads_key(text) TO service_role;
9838
+ CREATE OR REPLACE FUNCTION public.voorraad_floriday_volgt(p_product_id uuid)
9839
+ RETURNS boolean
9840
+ LANGUAGE plpgsql
9841
+ STABLE SECURITY DEFINER
9842
+ SET search_path TO 'public'
9843
+ AS $function$
9844
+ BEGIN
9845
+ PERFORM public.voorraad_vereis('server', 'voorraad_floriday_volgt');
9846
+ RETURN EXISTS (SELECT 1 FROM public.floriday_inventory_settings s
9847
+ WHERE s.product_id = p_product_id AND s.erp_leading IS TRUE
9848
+ AND COALESCE(s.stock_mode,'') <> 'DISABLED');
9849
+ END $function$
9850
+ ;
9851
+ REVOKE ALL ON FUNCTION voorraad_floriday_volgt(uuid) FROM PUBLIC;
9852
+ REVOKE ALL ON FUNCTION voorraad_floriday_volgt(uuid) FROM anon;
9853
+ GRANT EXECUTE ON FUNCTION voorraad_floriday_volgt(uuid) TO service_role;
9854
+ CREATE OR REPLACE FUNCTION public.voorraad_fust_bijhouden(p_fust_type_id uuid)
9855
+ RETURNS boolean
9856
+ LANGUAGE sql
9857
+ STABLE SECURITY DEFINER
9858
+ SET search_path TO 'public'
9859
+ AS $function$
9860
+ SELECT COALESCE(f.saldo_bijhouden, NOT COALESCE(f.vbn_code IN (999, 800), false))
9861
+ FROM public.fust_types f WHERE f.id = p_fust_type_id
9862
+ $function$
9863
+ ;
9864
+ REVOKE ALL ON FUNCTION voorraad_fust_bijhouden(uuid) FROM PUBLIC;
9865
+ REVOKE ALL ON FUNCTION voorraad_fust_bijhouden(uuid) FROM anon;
9866
+ GRANT EXECUTE ON FUNCTION voorraad_fust_bijhouden(uuid) TO authenticated;
9867
+ GRANT EXECUTE ON FUNCTION voorraad_fust_bijhouden(uuid) TO service_role;
9170
9868
  CREATE OR REPLACE FUNCTION public.voorraad_fust_dimensions()
9171
9869
  RETURNS TABLE(fust_type_id uuid, fust_key text, vbn_code integer, name text, floricode_name text, length_mm numeric, width_mm numeric, height_mm numeric, dimension_source text)
9172
9870
  LANGUAGE plpgsql
@@ -9215,6 +9913,25 @@ REVOKE ALL ON FUNCTION voorraad_fust_dimensions() FROM PUBLIC;
9215
9913
  REVOKE ALL ON FUNCTION voorraad_fust_dimensions() FROM anon;
9216
9914
  GRANT EXECUTE ON FUNCTION voorraad_fust_dimensions() TO authenticated;
9217
9915
  GRANT EXECUTE ON FUNCTION voorraad_fust_dimensions() TO service_role;
9916
+ CREATE OR REPLACE FUNCTION public.voorraad_fust_instellingen_opslaan(p_fust_type_id uuid, p_saldo_bijhouden boolean, p_eigen_naam text)
9917
+ RETURNS jsonb
9918
+ LANGUAGE plpgsql
9919
+ SECURITY DEFINER
9920
+ SET search_path TO 'public'
9921
+ AS $function$
9922
+ BEGIN
9923
+ PERFORM public.voorraad_vereis('ingelogd', 'voorraad_fust_instellingen_opslaan');
9924
+ UPDATE public.fust_types SET saldo_bijhouden = COALESCE(p_saldo_bijhouden, saldo_bijhouden),
9925
+ eigen_naam = NULLIF(btrim(COALESCE(p_eigen_naam, '')), ''), updated_at = now()
9926
+ WHERE id = p_fust_type_id;
9927
+ IF NOT FOUND THEN RETURN jsonb_build_object('ok', false, 'melding', 'Fustsoort niet gevonden'); END IF;
9928
+ RETURN jsonb_build_object('ok', true);
9929
+ END $function$
9930
+ ;
9931
+ REVOKE ALL ON FUNCTION voorraad_fust_instellingen_opslaan(uuid,boolean,text) FROM PUBLIC;
9932
+ REVOKE ALL ON FUNCTION voorraad_fust_instellingen_opslaan(uuid,boolean,text) FROM anon;
9933
+ GRANT EXECUTE ON FUNCTION voorraad_fust_instellingen_opslaan(uuid,boolean,text) TO authenticated;
9934
+ GRANT EXECUTE ON FUNCTION voorraad_fust_instellingen_opslaan(uuid,boolean,text) TO service_role;
9218
9935
  CREATE OR REPLACE FUNCTION public.voorraad_fust_juist(p_order_item_id uuid)
9219
9936
  RETURNS TABLE(fust_type_id uuid, fust_key text, aantal integer, bron text)
9220
9937
  LANGUAGE plpgsql
@@ -9222,10 +9939,10 @@ CREATE OR REPLACE FUNCTION public.voorraad_fust_juist(p_order_item_id uuid)
9222
9939
  SET search_path TO 'public'
9223
9940
  AS $function$
9224
9941
  DECLARE
9225
- v_item record; v_packs jsonb; v_pack jsonb; v_key text; v_units numeric; v_needed integer; v_bron text;
9942
+ v_item record; v_packs jsonb; v_pack jsonb; v_key text; v_units numeric; v_needed integer; v_bron text; v_ft uuid;
9226
9943
  BEGIN
9227
9944
  PERFORM public.voorraad_vereis('ingelogd', 'voorraad_fust_juist');
9228
- SELECT oi.*, x.quantity_crates, x.vbn_package_code, o.status AS o_status, o.cancelled_at AS o_cancel, o.deleted_at AS o_del
9945
+ SELECT oi.*, x.quantity_crates, x.vbn_package_code, o.status AS o_status, o.cancelled_at AS o_cancel, o.deleted_at AS o_del, o.delivery_date AS o_lever
9229
9946
  INTO v_item
9230
9947
  FROM logistics_active.order_items oi
9231
9948
  JOIN public.order_items x ON x.id = oi.id
@@ -9273,6 +9990,12 @@ BEGIN
9273
9990
  IF v_key IS NULL OR COALESCE(v_needed, 0) <= 0 THEN RETURN; END IF;
9274
9991
  fust_type_id := public.voorraad_active_fust_id(v_key, NULL);
9275
9992
  fust_key := v_key; aantal := v_needed; bron := v_bron;
9993
+ v_ft := fust_type_id;
9994
+ IF public.voorraad_fust_voor_startmoment(v_ft, public.voorraad_lever_datum(v_item.o_lever)) THEN
9995
+ -- Vóór het fust-startmoment: geen herberekening, het geboekte aantal blijft juist.
9996
+ aantal := COALESCE((SELECT sum(b.quantity)::int FROM public.order_fust_bookings b WHERE b.order_item_id = p_order_item_id AND b.fust_type_id = v_ft), 0);
9997
+ bron := 'vóór fust-startmoment: niet herberekend';
9998
+ END IF;
9276
9999
  RETURN NEXT;
9277
10000
  END $function$
9278
10001
  ;
@@ -9280,6 +10003,96 @@ REVOKE ALL ON FUNCTION voorraad_fust_juist(uuid) FROM PUBLIC;
9280
10003
  REVOKE ALL ON FUNCTION voorraad_fust_juist(uuid) FROM anon;
9281
10004
  GRANT EXECUTE ON FUNCTION voorraad_fust_juist(uuid) TO authenticated;
9282
10005
  GRANT EXECUTE ON FUNCTION voorraad_fust_juist(uuid) TO service_role;
10006
+ CREATE OR REPLACE FUNCTION public.voorraad_fust_startmoment_auto()
10007
+ RETURNS boolean
10008
+ LANGUAGE plpgsql
10009
+ SECURITY DEFINER
10010
+ SET search_path TO 'public'
10011
+ AS $function$
10012
+ BEGIN
10013
+ PERFORM public.voorraad_vereis('ingelogd', 'voorraad_fust_startmoment_auto');
10014
+ IF EXISTS (SELECT 1 FROM public.fust_startmoment) OR EXISTS (SELECT 1 FROM public.fust_stock_mutations) THEN RETURN false; END IF;
10015
+ PERFORM pg_advisory_xact_lock(hashtext('voorraad_fust_startmoment'));
10016
+ IF EXISTS (SELECT 1 FROM public.fust_startmoment) OR EXISTS (SELECT 1 FROM public.fust_stock_mutations) THEN RETURN false; END IF;
10017
+ INSERT INTO public.fust_startmoment(fust_type_id, moment, beginstand, automatisch, door) VALUES (NULL, now(), 0, true, NULL);
10018
+ INSERT INTO public.fust_startmoment_log(fust_type_id, moment, beginstand, saldo_voor, saldo_na, automatisch, notitie, door)
10019
+ VALUES (NULL, now(), 0, 0, 0, true, 'Automatisch bij de eerste Floriday-ordersynchronisatie; vul de beginstand in', NULL);
10020
+ RETURN true;
10021
+ END $function$
10022
+ ;
10023
+ REVOKE ALL ON FUNCTION voorraad_fust_startmoment_auto() FROM PUBLIC;
10024
+ REVOKE ALL ON FUNCTION voorraad_fust_startmoment_auto() FROM anon;
10025
+ GRANT EXECUTE ON FUNCTION voorraad_fust_startmoment_auto() TO authenticated;
10026
+ GRANT EXECUTE ON FUNCTION voorraad_fust_startmoment_auto() TO service_role;
10027
+ CREATE OR REPLACE FUNCTION public.voorraad_fust_startmoment_opslaan(p_fust_type_id uuid, p_moment timestamp with time zone, p_beginstand integer, p_beginstanden jsonb DEFAULT NULL::jsonb, p_notitie text DEFAULT NULL::text)
10028
+ RETURNS jsonb
10029
+ LANGUAGE plpgsql
10030
+ SECURITY DEFINER
10031
+ SET search_path TO 'public'
10032
+ AS $function$
10033
+ DECLARE f record; v_begin int; v_na_start int; v_doel int; n int := 0;
10034
+ BEGIN
10035
+ PERFORM public.voorraad_vereis('ingelogd', 'voorraad_fust_startmoment_opslaan');
10036
+ IF p_moment IS NULL OR p_beginstand IS NULL THEN RETURN jsonb_build_object('ok', false, 'melding', 'Vul een startmoment en een beginstand in'); END IF;
10037
+ PERFORM pg_advisory_xact_lock(hashtext('voorraad_fust_startmoment'));
10038
+ INSERT INTO public.fust_startmoment(fust_type_id, moment, beginstand, automatisch, door)
10039
+ VALUES (p_fust_type_id, p_moment, p_beginstand, false, auth.uid())
10040
+ ON CONFLICT ((COALESCE(fust_type_id, '00000000-0000-0000-0000-000000000000'::uuid)))
10041
+ DO UPDATE SET moment = EXCLUDED.moment, beginstand = EXCLUDED.beginstand, automatisch = false, door = auth.uid(), updated_at = now();
10042
+ FOR f IN SELECT t.id, t.stock FROM public.fust_types t
10043
+ WHERE t.merged_into IS NULL AND (t.id = p_fust_type_id OR (p_fust_type_id IS NULL AND NOT EXISTS (SELECT 1 FROM public.fust_startmoment s WHERE s.fust_type_id = t.id)))
10044
+ ORDER BY t.id FOR UPDATE
10045
+ LOOP
10046
+ v_begin := COALESCE(NULLIF(p_beginstanden->>f.id::text, '')::int, p_beginstand);
10047
+ SELECT COALESCE(sum(m.delta), 0)::int INTO v_na_start FROM public.fust_stock_mutations m
10048
+ WHERE m.fust_type_id = f.id AND m.created_at >= p_moment AND m.reason <> 'startmoment';
10049
+ v_doel := v_begin + v_na_start;
10050
+ IF v_doel <> f.stock THEN
10051
+ INSERT INTO public.fust_stock_mutations(user_id, fust_type_id, delta, reason, note)
10052
+ VALUES (COALESCE(auth.uid(), (SELECT user_id FROM public.fust_types WHERE id = f.id)), f.id, v_doel - f.stock, 'startmoment',
10053
+ 'Startmoment ' || to_char(p_moment AT TIME ZONE 'Europe/Amsterdam', 'DD-MM-YYYY HH24:MI') || ', beginstand ' || v_begin
10054
+ || COALESCE(' — ' || NULLIF(btrim(COALESCE(p_notitie,'')),''), ''));
10055
+ END IF;
10056
+ INSERT INTO public.fust_startmoment_log(fust_type_id, moment, beginstand, saldo_voor, saldo_na, automatisch, notitie)
10057
+ VALUES (f.id, p_moment, v_begin, f.stock, v_doel, false, NULLIF(btrim(COALESCE(p_notitie,'')),''));
10058
+ n := n + 1;
10059
+ END LOOP;
10060
+ RETURN jsonb_build_object('ok', true, 'fustsoorten', n);
10061
+ END $function$
10062
+ ;
10063
+ REVOKE ALL ON FUNCTION voorraad_fust_startmoment_opslaan(uuid,timestamp with time zone,integer,jsonb,text) FROM PUBLIC;
10064
+ REVOKE ALL ON FUNCTION voorraad_fust_startmoment_opslaan(uuid,timestamp with time zone,integer,jsonb,text) FROM anon;
10065
+ GRANT EXECUTE ON FUNCTION voorraad_fust_startmoment_opslaan(uuid,timestamp with time zone,integer,jsonb,text) TO authenticated;
10066
+ GRANT EXECUTE ON FUNCTION voorraad_fust_startmoment_opslaan(uuid,timestamp with time zone,integer,jsonb,text) TO service_role;
10067
+ CREATE OR REPLACE FUNCTION public.voorraad_fust_startmoment_van(p_fust_type_id uuid)
10068
+ RETURNS timestamp with time zone
10069
+ LANGUAGE sql
10070
+ STABLE SECURITY DEFINER
10071
+ SET search_path TO 'public'
10072
+ AS $function$
10073
+ SELECT COALESCE(
10074
+ (SELECT s.moment FROM public.fust_startmoment s WHERE s.fust_type_id = public.voorraad_active_fust_id(NULL, p_fust_type_id)),
10075
+ (SELECT s.moment FROM public.fust_startmoment s WHERE s.fust_type_id IS NULL))
10076
+ $function$
10077
+ ;
10078
+ REVOKE ALL ON FUNCTION voorraad_fust_startmoment_van(uuid) FROM PUBLIC;
10079
+ REVOKE ALL ON FUNCTION voorraad_fust_startmoment_van(uuid) FROM anon;
10080
+ GRANT EXECUTE ON FUNCTION voorraad_fust_startmoment_van(uuid) TO authenticated;
10081
+ GRANT EXECUTE ON FUNCTION voorraad_fust_startmoment_van(uuid) TO service_role;
10082
+ CREATE OR REPLACE FUNCTION public.voorraad_fust_voor_startmoment(p_fust_type_id uuid, p_lever date)
10083
+ RETURNS boolean
10084
+ LANGUAGE sql
10085
+ STABLE SECURITY DEFINER
10086
+ SET search_path TO 'public'
10087
+ AS $function$
10088
+ SELECT p_fust_type_id IS NOT NULL AND p_lever IS NOT NULL
10089
+ AND COALESCE(p_lever < (public.voorraad_fust_startmoment_van(p_fust_type_id) AT TIME ZONE 'Europe/Amsterdam')::date, false)
10090
+ $function$
10091
+ ;
10092
+ REVOKE ALL ON FUNCTION voorraad_fust_voor_startmoment(uuid,date) FROM PUBLIC;
10093
+ REVOKE ALL ON FUNCTION voorraad_fust_voor_startmoment(uuid,date) FROM anon;
10094
+ GRANT EXECUTE ON FUNCTION voorraad_fust_voor_startmoment(uuid,date) TO authenticated;
10095
+ GRANT EXECUTE ON FUNCTION voorraad_fust_voor_startmoment(uuid,date) TO service_role;
9283
10096
  CREATE OR REPLACE FUNCTION public.voorraad_gateway_routes_check()
9284
10097
  RETURNS jsonb
9285
10098
  LANGUAGE plpgsql
@@ -9425,7 +10238,7 @@ CREATE OR REPLACE FUNCTION public.voorraad_install_check()
9425
10238
  STABLE SECURITY DEFINER
9426
10239
  SET search_path TO 'public'
9427
10240
  AS $function$
9428
- DECLARE v_f text[] := ARRAY['voorraad_ensure_defaults','voorraad_defaults_check','voorraad_defaults_versie','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'];
10241
+ DECLARE v_f text[] := ARRAY['voorraad_instellingen_lezen','voorraad_twijfel_nieuw_artikel','voorraad_eigen_velden_opslaan','voorraad_traysticker_opslaan','voorraad_floriday_volgt','voorraad_artikel_archiveren','voorraad_orders_gearchiveerd','voorraad_twijfel_kiezen','voorraad_twijfel_lijst','voorraad_voorraad_instellingen_opslaan','voorraad_artikel_detail','voorraad_artikel_opslaan','voorraad_voorraad_corrigeren','voorraad_lijst','voorraad_floriday_artikelen_aanmaken','voorraad_floriday_artikelen_vangnet','voorraad_orderregel_trade_item','voorraad_orderregel_trade_item_bron','voorraad_correctie_1144','voorraad_telmoment_lijst','voorraad_telmoment_opslaan','voorraad_order_vertrokken_voor','voorraad_fust_startmoment_opslaan','voorraad_fust_startmoment_auto','voorraad_fust_instellingen_opslaan','voorraad_koppeling_check','voorraad_instellingen_opslaan','voorraad_ensure_defaults','voorraad_defaults_check','voorraad_defaults_versie','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'];
9429
10242
  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'];
9430
10243
  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','Ingelogd mag standaardstatus lezen@voorraad_defaults_state','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'];
9431
10244
  v_open text[] := ARRAY['voorraad_is_server','voorraad_vereis'];
@@ -9453,7 +10266,7 @@ BEGIN
9453
10266
  SELECT jsonb_agg(jsonb_build_object('policy', split_part(t, '@', 1), 'tabel', split_part(t, '@', 2),
9454
10267
  'aanwezig', EXISTS (SELECT 1 FROM pg_policies x WHERE x.schemaname = 'public' AND x.policyname = split_part(t, '@', 1)
9455
10268
  AND x.tablename = split_part(t, '@', 2))) ORDER BY t) INTO v_pol FROM unnest(v_p) t;
9456
- RETURN jsonb_build_object('standaardgegevens', public.voorraad_defaults_check(), 'dubbele_voorraadregels', public.voorraad_dubbele_voorraadregels(), 'gateway_routes', public.voorraad_gateway_routes_check(),
10269
+ RETURN jsonb_build_object('koppeling', public.voorraad_koppeling_check(), 'standaardgegevens', public.voorraad_defaults_check(), 'dubbele_voorraadregels', public.voorraad_dubbele_voorraadregels(), 'gateway_routes', public.voorraad_gateway_routes_check(),
9457
10270
  'samenvatting', jsonb_build_object(
9458
10271
  'functies', jsonb_array_length(v_func),
9459
10272
  'functies_ontbrekend', (SELECT count(*) FROM jsonb_array_elements(v_func) e WHERE NOT (e->>'aanwezig')::boolean),
@@ -9470,6 +10283,43 @@ REVOKE ALL ON FUNCTION voorraad_install_check() FROM PUBLIC;
9470
10283
  REVOKE ALL ON FUNCTION voorraad_install_check() FROM anon;
9471
10284
  GRANT EXECUTE ON FUNCTION voorraad_install_check() TO authenticated;
9472
10285
  GRANT EXECUTE ON FUNCTION voorraad_install_check() TO service_role;
10286
+ CREATE OR REPLACE FUNCTION public.voorraad_instellingen_lezen()
10287
+ RETURNS jsonb
10288
+ LANGUAGE plpgsql
10289
+ STABLE SECURITY DEFINER
10290
+ SET search_path TO 'public'
10291
+ AS $function$
10292
+ BEGIN
10293
+ PERFORM public.voorraad_vereis('ingelogd', 'voorraad_instellingen_lezen');
10294
+ RETURN jsonb_build_object('standaard_sturing',
10295
+ COALESCE((SELECT standaard_sturing_nieuwe_floriday FROM public.voorraad_instellingen WHERE id), 'lokaal'));
10296
+ END $function$
10297
+ ;
10298
+ REVOKE ALL ON FUNCTION voorraad_instellingen_lezen() FROM PUBLIC;
10299
+ REVOKE ALL ON FUNCTION voorraad_instellingen_lezen() FROM anon;
10300
+ GRANT EXECUTE ON FUNCTION voorraad_instellingen_lezen() TO authenticated;
10301
+ GRANT EXECUTE ON FUNCTION voorraad_instellingen_lezen() TO service_role;
10302
+ CREATE OR REPLACE FUNCTION public.voorraad_instellingen_opslaan(p_standaard_sturing text)
10303
+ RETURNS jsonb
10304
+ LANGUAGE plpgsql
10305
+ SECURITY DEFINER
10306
+ SET search_path TO 'public'
10307
+ AS $function$
10308
+ BEGIN
10309
+ PERFORM public.voorraad_vereis('ingelogd', 'voorraad_instellingen_opslaan');
10310
+ IF p_standaard_sturing NOT IN ('lokaal', 'floriday') THEN
10311
+ RETURN jsonb_build_object('ok', false, 'melding', 'Kies lokaal of Floriday');
10312
+ END IF;
10313
+ INSERT INTO public.voorraad_instellingen(id, standaard_sturing_nieuwe_floriday, updated_at, updated_by)
10314
+ VALUES (true, p_standaard_sturing, now(), auth.uid())
10315
+ ON CONFLICT (id) DO UPDATE SET standaard_sturing_nieuwe_floriday = EXCLUDED.standaard_sturing_nieuwe_floriday, updated_at = now(), updated_by = auth.uid();
10316
+ RETURN jsonb_build_object('ok', true, 'standaard_sturing', p_standaard_sturing);
10317
+ END $function$
10318
+ ;
10319
+ REVOKE ALL ON FUNCTION voorraad_instellingen_opslaan(text) FROM PUBLIC;
10320
+ REVOKE ALL ON FUNCTION voorraad_instellingen_opslaan(text) FROM anon;
10321
+ GRANT EXECUTE ON FUNCTION voorraad_instellingen_opslaan(text) TO authenticated;
10322
+ GRANT EXECUTE ON FUNCTION voorraad_instellingen_opslaan(text) TO service_role;
9473
10323
  CREATE OR REPLACE FUNCTION public.voorraad_is_server()
9474
10324
  RETURNS boolean
9475
10325
  LANGUAGE sql
@@ -9488,6 +10338,207 @@ GRANT EXECUTE ON FUNCTION voorraad_is_server() TO service_role;
9488
10338
  GRANT EXECUTE ON FUNCTION voorraad_is_server() TO anon;
9489
10339
  GRANT EXECUTE ON FUNCTION voorraad_is_server() TO authenticated;
9490
10340
  GRANT EXECUTE ON FUNCTION voorraad_is_server() TO service_role;
10341
+ CREATE OR REPLACE FUNCTION public.voorraad_koppeling_check()
10342
+ RETURNS jsonb
10343
+ LANGUAGE plpgsql
10344
+ STABLE SECURITY DEFINER
10345
+ SET search_path TO 'public'
10346
+ AS $function$
10347
+ DECLARE v_dups jsonb := public.voorraad_floriday_dubbele_ids(); v_idx boolean; v_job boolean; v_zonder int; v_twijfel int; v_start boolean; v_f jsonb := '[]'::jsonb;
10348
+ BEGIN
10349
+ PERFORM public.voorraad_vereis('ingelogd', 'voorraad_koppeling_check');
10350
+ v_idx := EXISTS (SELECT 1 FROM pg_indexes WHERE schemaname = 'public' AND indexname = 'products_floriday_trade_item_id_uniek');
10351
+ BEGIN v_job := EXISTS (SELECT 1 FROM cron.job WHERE jobname = 'voorraad-floriday-artikelen-uurlijks'); EXCEPTION WHEN others THEN v_job := false; END;
10352
+ SELECT count(*) INTO v_zonder FROM public.floriday_trade_items_cache t
10353
+ WHERE NOT (COALESCE(t.is_deleted,false) OR COALESCE((t.data->>'isDeleted')::boolean,false))
10354
+ AND t.connection_id = (SELECT x.connection_id FROM public.get_active_floriday_context() x LIMIT 1)
10355
+ AND NOT EXISTS (SELECT 1 FROM public.products p WHERE lower(btrim(p.floriday_trade_item_id)) = lower(t.floriday_id));
10356
+ SELECT count(*) INTO v_twijfel FROM public.voorraad_koppel_twijfel WHERE opgelost_at IS NULL;
10357
+ v_start := EXISTS (SELECT 1 FROM public.fust_startmoment);
10358
+ IF jsonb_array_length(v_dups) > 0 THEN v_f := v_f || to_jsonb('Er zijn artikelen met hetzelfde Floriday-id; automatisch aanmaken staat stil'::text); END IF;
10359
+ IF NOT v_idx THEN v_f := v_f || to_jsonb('Unieke index op het Floriday-id ontbreekt'::text); END IF;
10360
+ IF NOT v_job THEN v_f := v_f || to_jsonb('Uurlijkse controle van Floriday-artikelen is niet ingepland'::text); END IF;
10361
+ RETURN jsonb_build_object('fouten', v_f, 'dubbele_floriday_ids', v_dups, 'unieke_index_aanwezig', v_idx, 'vangnet_job_aanwezig', v_job,
10362
+ 'floriday_zonder_eigen_artikel', v_zonder, 'twijfel_open', v_twijfel, 'fust_startmoment_ingesteld', v_start,
10363
+ 'melding_fust', CASE WHEN NOT v_start THEN 'Stel een startmoment en beginstand in' END);
10364
+ END $function$
10365
+ ;
10366
+ REVOKE ALL ON FUNCTION voorraad_koppeling_check() FROM PUBLIC;
10367
+ REVOKE ALL ON FUNCTION voorraad_koppeling_check() FROM anon;
10368
+ GRANT EXECUTE ON FUNCTION voorraad_koppeling_check() TO authenticated;
10369
+ GRANT EXECUTE ON FUNCTION voorraad_koppeling_check() TO service_role;
10370
+ CREATE OR REPLACE FUNCTION public.voorraad_lever_datum(p text)
10371
+ RETURNS date
10372
+ LANGUAGE plpgsql
10373
+ IMMUTABLE
10374
+ SET search_path TO 'public'
10375
+ AS $function$
10376
+ BEGIN
10377
+ IF p IS NULL OR btrim(p) = '' THEN RETURN NULL; END IF;
10378
+ IF p ~ '^\d{4}-\d{2}-\d{2}$' THEN RETURN p::date; END IF;
10379
+ IF p ~ '^\d{4}-\d{2}-\d{2}T' THEN RETURN (p::timestamptz AT TIME ZONE 'Europe/Amsterdam')::date; END IF;
10380
+ RETURN NULL;
10381
+ EXCEPTION WHEN others THEN RETURN NULL;
10382
+ END $function$
10383
+ ;
10384
+ REVOKE ALL ON FUNCTION voorraad_lever_datum(text) FROM PUBLIC;
10385
+ REVOKE ALL ON FUNCTION voorraad_lever_datum(text) FROM anon;
10386
+ GRANT EXECUTE ON FUNCTION voorraad_lever_datum(text) TO authenticated;
10387
+ GRANT EXECUTE ON FUNCTION voorraad_lever_datum(text) TO service_role;
10388
+ CREATE OR REPLACE FUNCTION public.voorraad_lijst(p_zoek text DEFAULT NULL::text, p_filter text DEFAULT 'alles'::text, p_sort text DEFAULT 'naam'::text, p_oplopend boolean DEFAULT true, p_pagina integer DEFAULT 1, p_grootte integer DEFAULT 25)
10389
+ RETURNS jsonb
10390
+ LANGUAGE plpgsql
10391
+ STABLE SECURITY DEFINER
10392
+ SET search_path TO 'public'
10393
+ AS $function$
10394
+ DECLARE
10395
+ v_conn uuid; v_ctx uuid; v_kop boolean; v_gr int; v_off int; v_q text;
10396
+ v_tel jsonb; v_totaal int; v_rijen jsonb; v_meld jsonb; v_twijfel int; v_twijfel_rijen jsonb := '[]'::jsonb;
10397
+ BEGIN
10398
+ PERFORM public.voorraad_vereis('ingelogd', 'voorraad_lijst');
10399
+ IF COALESCE(p_filter,'alles') NOT IN ('alles','onder_minimum','verpakking','niet_gekoppeld','prijs','twijfel','gearchiveerd') THEN
10400
+ RAISE EXCEPTION 'Onbekend filter "%"', p_filter USING ERRCODE = '22023';
10401
+ END IF;
10402
+ IF COALESCE(p_sort,'naam') NOT IN ('naam','artikelnummer','liggend','gereserveerd','vrij','inkomend','prijs') THEN
10403
+ RAISE EXCEPTION 'Onbekende sortering "%"', p_sort USING ERRCODE = '22023';
10404
+ END IF;
10405
+ v_gr := LEAST(GREATEST(COALESCE(p_grootte,25),1),500);
10406
+ v_off := (GREATEST(COALESCE(p_pagina,1),1) - 1) * v_gr;
10407
+ v_q := NULLIF(btrim(COALESCE(p_zoek,'')),'');
10408
+ BEGIN SELECT x.connection_id INTO v_conn FROM public.get_active_floriday_context() x LIMIT 1;
10409
+ EXCEPTION WHEN others THEN v_conn := NULL; END;
10410
+ v_kop := v_conn IS NOT NULL;
10411
+ BEGIN SELECT c.connection_id INTO v_ctx FROM public.get_stock_context() c LIMIT 1;
10412
+ EXCEPTION WHEN others THEN v_ctx := NULL; END;
10413
+
10414
+ WITH st AS (
10415
+ SELECT DISTINCT ON (x.trade_item_key) x.trade_item_key, x.physical_stock, x.reserved_stock, x.minimum_stock, x.incoming_stock
10416
+ FROM public.trade_item_stock x WHERE x.archived_at IS NULL
10417
+ ORDER BY x.trade_item_key, (x.connection_id IS NOT DISTINCT FROM v_ctx) DESC, x.updated_at DESC
10418
+ ), basis AS (
10419
+ SELECT p.id,
10420
+ COALESCE(NULLIF(btrim(p.product),''), NULLIF(p.custom_fields#>>'{floriday_payload,name}',''), '(naamloos)') AS naam,
10421
+ COALESCE(NULLIF(btrim(p.article_number),''), NULLIF(p.custom_fields#>>'{floriday_payload,articleCode}','')) AS artikelnummer,
10422
+ NULLIF(btrim(COALESCE(p.floriday_trade_item_id,'')),'') AS floriday_id,
10423
+ p.sale_price AS prijs, p.unit AS eenheid, p.gearchiveerd_at, p.barcode,
10424
+ -- Voorraadkolommen zijn NOT NULL: COALESCE kiest dus de eerste bestaande rij (s1, dan s2, dan s3).
10425
+ COALESCE(s1.physical_stock, s2.physical_stock, s3.physical_stock, 0) AS liggend,
10426
+ COALESCE(s1.reserved_stock, s2.reserved_stock, s3.reserved_stock, 0) AS gereserveerd,
10427
+ COALESCE(s1.minimum_stock, s2.minimum_stock, s3.minimum_stock, 0) AS minimum,
10428
+ COALESCE(s1.incoming_stock, s2.incoming_stock, s3.incoming_stock, 0) AS inkomend,
10429
+ COALESCE(s1.trade_item_key, s2.trade_item_key, s3.trade_item_key) AS voorraad_sleutel,
10430
+ CASE WHEN EXISTS (SELECT 1 FROM public.product_groups g WHERE g.head_product_id = p.id AND g.is_active) THEN 'GROUP'
10431
+ WHEN EXISTS (SELECT 1 FROM public.product_bom_items b WHERE b.parent_product_id = p.id) THEN 'SET'
10432
+ ELSE 'NORMAL' END AS soort,
10433
+ EXISTS (SELECT 1 FROM public.product_bom_items b WHERE b.component_product_id = p.id) AS is_onderdeel,
10434
+ EXISTS (SELECT 1 FROM public.product_packings k WHERE k.product_id = p.id AND k.is_primary) AS heeft_verpakking,
10435
+ COALESCE((p.custom_fields#>>'{voorraad_packing_link,ok}')::boolean, true) AS verpakking_ok
10436
+ FROM public.products p
10437
+ -- Zelfde volgorde als voorraad_canoniek_sleutel: trade_item_key, dan Floriday-id, dan product:<id>.
10438
+ LEFT JOIN st s1 ON s1.trade_item_key = NULLIF(btrim(COALESCE(p.trade_item_key,'')),'')
10439
+ LEFT JOIN st s2 ON s2.trade_item_key = NULLIF(btrim(COALESCE(p.floriday_trade_item_id,'')),'')
10440
+ LEFT JOIN st s3 ON s3.trade_item_key = 'product:' || p.id::text
10441
+ -- Spiegels (id = Floriday-id van een ander artikel) tellen niet mee.
10442
+ WHERE NOT EXISTS (SELECT 1 FROM public.products q
10443
+ WHERE q.id <> p.id AND NULLIF(btrim(COALESCE(q.floriday_trade_item_id,'')),'') IS NOT NULL
10444
+ AND lower(btrim(q.floriday_trade_item_id)) = lower(p.id::text))
10445
+ ), gemarkeerd AS (
10446
+ SELECT b.*,
10447
+ GREATEST(b.liggend - b.gereserveerd, 0) AS vrij,
10448
+ (b.minimum > 0 AND (b.liggend - b.gereserveerd) < b.minimum) AS f_onder_minimum,
10449
+ (b.soort <> 'GROUP' AND (NOT b.heeft_verpakking OR NOT b.verpakking_ok)) AS f_verpakking,
10450
+ (v_kop AND b.floriday_id IS NULL) AS f_niet_gekoppeld,
10451
+ (lower(btrim(COALESCE(b.eenheid,''))) <> 'stuks' OR b.prijs IS NULL OR b.prijs <= 0) AS f_prijs
10452
+ FROM basis b
10453
+ WHERE v_q IS NULL
10454
+ OR b.naam ILIKE '%' || v_q || '%' OR COALESCE(b.artikelnummer,'') ILIKE '%' || v_q || '%'
10455
+ OR COALESCE(b.barcode,'') ILIKE '%' || v_q || '%' OR COALESCE(b.floriday_id,'') ILIKE v_q || '%'
10456
+ ), tel AS (
10457
+ SELECT jsonb_build_object(
10458
+ 'alles', count(*) FILTER (WHERE gearchiveerd_at IS NULL),
10459
+ 'onder_minimum', count(*) FILTER (WHERE gearchiveerd_at IS NULL AND f_onder_minimum),
10460
+ 'verpakking', count(*) FILTER (WHERE gearchiveerd_at IS NULL AND f_verpakking),
10461
+ 'niet_gekoppeld', count(*) FILTER (WHERE gearchiveerd_at IS NULL AND f_niet_gekoppeld),
10462
+ 'prijs', count(*) FILTER (WHERE gearchiveerd_at IS NULL AND f_prijs),
10463
+ 'gearchiveerd', count(*) FILTER (WHERE gearchiveerd_at IS NOT NULL),
10464
+ 'onderdelen_zonder_prijs', count(*) FILTER (WHERE gearchiveerd_at IS NULL AND is_onderdeel AND f_prijs)) AS t
10465
+ FROM gemarkeerd
10466
+ ), gefilterd AS (
10467
+ SELECT * FROM gemarkeerd g
10468
+ WHERE CASE COALESCE(p_filter,'alles')
10469
+ WHEN 'gearchiveerd' THEN g.gearchiveerd_at IS NOT NULL
10470
+ WHEN 'twijfel' THEN false
10471
+ ELSE g.gearchiveerd_at IS NULL AND CASE p_filter
10472
+ WHEN 'onder_minimum' THEN g.f_onder_minimum
10473
+ WHEN 'verpakking' THEN g.f_verpakking
10474
+ WHEN 'niet_gekoppeld' THEN g.f_niet_gekoppeld
10475
+ WHEN 'prijs' THEN g.f_prijs
10476
+ ELSE true END
10477
+ END
10478
+ ), pagina AS (
10479
+ SELECT * FROM gefilterd
10480
+ ORDER BY
10481
+ CASE WHEN p_oplopend AND p_sort = 'naam' THEN lower(naam) END ASC,
10482
+ CASE WHEN NOT p_oplopend AND p_sort = 'naam' THEN lower(naam) END DESC,
10483
+ CASE WHEN p_oplopend AND p_sort = 'artikelnummer' THEN lower(artikelnummer) END ASC NULLS LAST,
10484
+ CASE WHEN NOT p_oplopend AND p_sort = 'artikelnummer' THEN lower(artikelnummer) END DESC NULLS LAST,
10485
+ CASE WHEN p_sort NOT IN ('naam','artikelnummer') THEN
10486
+ (CASE p_sort WHEN 'liggend' THEN liggend WHEN 'gereserveerd' THEN gereserveerd WHEN 'vrij' THEN vrij
10487
+ WHEN 'inkomend' THEN inkomend ELSE prijs END)::numeric * CASE WHEN p_oplopend THEN 1 ELSE -1 END
10488
+ END ASC NULLS LAST,
10489
+ lower(naam), id
10490
+ LIMIT v_gr OFFSET v_off
10491
+ )
10492
+ SELECT (SELECT t FROM tel), (SELECT count(*) FROM gefilterd),
10493
+ (SELECT COALESCE(jsonb_agg(jsonb_build_object(
10494
+ 'id', pg.id, 'naam', pg.naam, 'artikelnummer', pg.artikelnummer,
10495
+ 'foto', COALESCE(NULLIF(p.image_url,''), NULLIF(p.custom_fields#>>'{floriday_payload,images,0,url}',''), NULLIF(p.custom_fields#>>'{floriday_payload,images,0,previewUrl}','')),
10496
+ 'floriday_id', pg.floriday_id,
10497
+ 'prijs', pg.prijs, 'eenheid', pg.eenheid, 'soort', pg.soort, 'is_onderdeel', pg.is_onderdeel,
10498
+ 'liggend', pg.liggend, 'gereserveerd', pg.gereserveerd, 'minimum', pg.minimum, 'inkomend', pg.inkomend, 'vrij', pg.vrij,
10499
+ 'voorraad_sleutel', pg.voorraad_sleutel, 'gearchiveerd_at', pg.gearchiveerd_at,
10500
+ 'onder_minimum', pg.f_onder_minimum,
10501
+ 'status', CASE WHEN pg.gearchiveerd_at IS NOT NULL THEN 'gearchiveerd'
10502
+ WHEN pg.f_verpakking THEN 'verpakking' WHEN pg.f_niet_gekoppeld THEN 'niet_gekoppeld'
10503
+ WHEN pg.f_prijs THEN 'prijs' ELSE 'ok' END)
10504
+ ORDER BY pg.rn), '[]'::jsonb)
10505
+ FROM (SELECT pagina.*, row_number() OVER () AS rn FROM pagina) pg JOIN public.products p ON p.id = pg.id)
10506
+ INTO v_tel, v_totaal, v_rijen;
10507
+
10508
+ -- Samenstellingen en productgroepen: vrij beschikbaar uit de gedeelde rekenfunctie (alleen voor deze pagina).
10509
+ IF v_ctx IS NOT NULL THEN
10510
+ SELECT COALESCE(jsonb_agg(CASE WHEN r->>'soort' IN ('SET','GROUP') THEN
10511
+ r || jsonb_build_object('vrij', COALESCE((public.calculate_structure_available(v_ctx, (r->>'id')::uuid)->>'available')::numeric, 0))
10512
+ ELSE r END ORDER BY o), '[]'::jsonb)
10513
+ INTO v_rijen FROM jsonb_array_elements(v_rijen) WITH ORDINALITY AS e(r, o);
10514
+ END IF;
10515
+
10516
+ SELECT count(*) INTO v_twijfel FROM public.voorraad_koppel_twijfel WHERE opgelost_at IS NULL;
10517
+ IF p_filter = 'twijfel' THEN
10518
+ v_totaal := v_twijfel;
10519
+ SELECT COALESCE(jsonb_agg(jsonb_build_object('floriday_id', t.floriday_id, 'naam', t.naam, 'reden', t.reden,
10520
+ 'kandidaten', COALESCE(jsonb_array_length(CASE WHEN jsonb_typeof(t.kandidaten)='array' THEN t.kandidaten END),0),
10521
+ 'laatst_gezien', t.laatst_gezien) ORDER BY t.laatst_gezien DESC), '[]'::jsonb)
10522
+ INTO v_twijfel_rijen
10523
+ FROM (SELECT * FROM public.voorraad_koppel_twijfel WHERE opgelost_at IS NULL
10524
+ ORDER BY laatst_gezien DESC LIMIT v_gr OFFSET v_off) t;
10525
+ END IF;
10526
+
10527
+ v_meld := jsonb_build_object(
10528
+ 'telmoment_ontbreekt', NOT EXISTS (SELECT 1 FROM public.voorraad_telmoment),
10529
+ 'onderdelen_zonder_prijs', COALESCE((v_tel->>'onderdelen_zonder_prijs')::int, 0),
10530
+ 'orders_gearchiveerd', public.voorraad_orders_gearchiveerd(NULL));
10531
+
10532
+ RETURN jsonb_build_object('tellers', v_tel || jsonb_build_object('twijfel', v_twijfel),
10533
+ 'totaal', v_totaal, 'rijen', v_rijen, 'twijfel_rijen', v_twijfel_rijen,
10534
+ 'meldingen', v_meld, 'heeft_koppeling', v_kop, 'pagina', GREATEST(COALESCE(p_pagina,1),1), 'grootte', v_gr,
10535
+ 'bijgewerkt', now());
10536
+ END $function$
10537
+ ;
10538
+ REVOKE ALL ON FUNCTION voorraad_lijst(text,text,text,boolean,integer,integer) FROM PUBLIC;
10539
+ REVOKE ALL ON FUNCTION voorraad_lijst(text,text,text,boolean,integer,integer) FROM anon;
10540
+ GRANT EXECUTE ON FUNCTION voorraad_lijst(text,text,text,boolean,integer,integer) TO authenticated;
10541
+ GRANT EXECUTE ON FUNCTION voorraad_lijst(text,text,text,boolean,integer,integer) TO service_role;
9491
10542
  CREATE OR REPLACE FUNCTION public.voorraad_load_carriers()
9492
10543
  RETURNS TABLE(code text, naam text, volgorde integer)
9493
10544
  LANGUAGE plpgsql
@@ -9551,6 +10602,33 @@ GRANT EXECUTE ON FUNCTION voorraad_order_geannuleerd(text,timestamp with time zo
9551
10602
  REVOKE EXECUTE ON FUNCTION voorraad_order_geannuleerd(text,timestamp with time zone,timestamp with time zone) FROM anon;
9552
10603
  GRANT EXECUTE ON FUNCTION voorraad_order_geannuleerd(text,timestamp with time zone,timestamp with time zone) TO authenticated;
9553
10604
  GRANT EXECUTE ON FUNCTION voorraad_order_geannuleerd(text,timestamp with time zone,timestamp with time zone) TO service_role;
10605
+ CREATE OR REPLACE FUNCTION public.voorraad_order_vertrokken_voor(p_order_id uuid, p_moment timestamp with time zone)
10606
+ RETURNS boolean
10607
+ LANGUAGE plpgsql
10608
+ STABLE SECURITY DEFINER
10609
+ SET search_path TO 'public'
10610
+ AS $function$
10611
+ DECLARE v_status text; v_changed timestamptz; v_carts int; v_done int; v_last timestamptz; v_unc int;
10612
+ BEGIN
10613
+ PERFORM public.voorraad_vereis('ingelogd', 'voorraad_order_vertrokken_voor');
10614
+ SELECT lower(COALESCE(o.status,'')), o.status_changed_at INTO v_status, v_changed FROM public.orders o WHERE o.id = p_order_id;
10615
+ IF v_status IN ('verzonden','shipped','geleverd','delivered') AND v_changed IS NOT NULL AND v_changed <= p_moment THEN RETURN true; END IF;
10616
+ SELECT count(*), count(*) FILTER (WHERE lower(COALESCE(status,'')) IN ('shipped','delivered','verzonden','geleverd') AND delivered_at IS NOT NULL AND delivered_at <= p_moment)
10617
+ INTO v_carts, v_done FROM public.cart_numbers WHERE order_id = p_order_id::text;
10618
+ IF v_carts > 0 AND v_done = v_carts THEN RETURN true; END IF;
10619
+ IF EXISTS (SELECT 1 FROM public.order_shipments s WHERE s.order_id = p_order_id AND s.status = 'shipped' AND s.shipped_at <= p_moment) THEN
10620
+ SELECT count(*) INTO v_unc FROM logistics_active.order_items oi
10621
+ WHERE oi.order_id = p_order_id AND oi.quantity > COALESCE((SELECT sum(si.quantity) FROM public.order_shipment_items si
10622
+ JOIN public.order_shipments s ON s.id = si.shipment_id WHERE si.order_item_id = oi.id AND s.status = 'shipped' AND s.shipped_at <= p_moment), 0);
10623
+ IF v_unc = 0 THEN RETURN true; END IF;
10624
+ END IF;
10625
+ RETURN false;
10626
+ END $function$
10627
+ ;
10628
+ REVOKE ALL ON FUNCTION voorraad_order_vertrokken_voor(uuid,timestamp with time zone) FROM PUBLIC;
10629
+ REVOKE ALL ON FUNCTION voorraad_order_vertrokken_voor(uuid,timestamp with time zone) FROM anon;
10630
+ GRANT EXECUTE ON FUNCTION voorraad_order_vertrokken_voor(uuid,timestamp with time zone) TO authenticated;
10631
+ GRANT EXECUTE ON FUNCTION voorraad_order_vertrokken_voor(uuid,timestamp with time zone) TO service_role;
9554
10632
  CREATE OR REPLACE FUNCTION public.voorraad_orderregel_sleutel(p_ref text)
9555
10633
  RETURNS text
9556
10634
  LANGUAGE plpgsql
@@ -9573,6 +10651,84 @@ REVOKE ALL ON FUNCTION voorraad_orderregel_sleutel(text) FROM PUBLIC;
9573
10651
  REVOKE ALL ON FUNCTION voorraad_orderregel_sleutel(text) FROM anon;
9574
10652
  GRANT EXECUTE ON FUNCTION voorraad_orderregel_sleutel(text) TO authenticated;
9575
10653
  GRANT EXECUTE ON FUNCTION voorraad_orderregel_sleutel(text) TO service_role;
10654
+ CREATE OR REPLACE FUNCTION public.voorraad_orderregel_trade_item(p_order_item_id uuid)
10655
+ RETURNS jsonb
10656
+ LANGUAGE plpgsql
10657
+ STABLE SECURITY DEFINER
10658
+ SET search_path TO 'public'
10659
+ AS $function$
10660
+ DECLARE v_o uuid; v_s text;
10661
+ BEGIN
10662
+ PERFORM public.voorraad_vereis('ingelogd', 'voorraad_orderregel_trade_item');
10663
+ SELECT oi.order_id, oi.source_line_key INTO v_o, v_s FROM public.order_items oi WHERE oi.id = p_order_item_id;
10664
+ IF NOT FOUND THEN RETURN jsonb_build_object('status', 'onbekende_orderregel'); END IF;
10665
+ RETURN public.voorraad_orderregel_trade_item_bron(v_o, v_s);
10666
+ END $function$
10667
+ ;
10668
+ REVOKE ALL ON FUNCTION voorraad_orderregel_trade_item(uuid) FROM PUBLIC;
10669
+ REVOKE ALL ON FUNCTION voorraad_orderregel_trade_item(uuid) FROM anon;
10670
+ GRANT EXECUTE ON FUNCTION voorraad_orderregel_trade_item(uuid) TO authenticated;
10671
+ GRANT EXECUTE ON FUNCTION voorraad_orderregel_trade_item(uuid) TO service_role;
10672
+ CREATE OR REPLACE FUNCTION public.voorraad_orderregel_trade_item_bron(p_order_id uuid, p_source_line_key text)
10673
+ RETURNS jsonb
10674
+ LANGUAGE plpgsql
10675
+ STABLE SECURITY DEFINER
10676
+ SET search_path TO 'public'
10677
+ AS $function$
10678
+ DECLARE v_cf jsonb; v_k text[]; v_p uuid[];
10679
+ BEGIN
10680
+ PERFORM public.voorraad_vereis('ingelogd', 'voorraad_orderregel_trade_item_bron');
10681
+ IF p_source_line_key IS NULL OR btrim(p_source_line_key) = '' THEN
10682
+ RETURN jsonb_build_object('status', 'geen_source_line_key');
10683
+ END IF;
10684
+ SELECT o.custom_fields INTO v_cf FROM public.orders o WHERE o.id = p_order_id;
10685
+ SELECT array_agg(DISTINCT k) INTO v_k FROM (
10686
+ SELECT lower(btrim(l->>'trade_item_id')) k
10687
+ FROM jsonb_array_elements(CASE WHEN jsonb_typeof(v_cf->'floriday_lines') = 'array' THEN v_cf->'floriday_lines' ELSE '[]'::jsonb END) l
10688
+ WHERE l->>'sales_order_id' = p_source_line_key AND COALESCE(l->>'trade_item_id','') <> ''
10689
+ UNION
10690
+ SELECT lower(btrim(c.data->>'tradeItemId')) FROM public.floriday_salesorders_cache c
10691
+ WHERE c.floriday_id = p_source_line_key AND COALESCE(c.data->>'tradeItemId','') <> ''
10692
+ ) x;
10693
+ IF v_k IS NULL THEN RETURN jsonb_build_object('status', 'geen_match'); END IF;
10694
+ IF array_length(v_k, 1) > 1 THEN RETURN jsonb_build_object('status', 'meerdere_kandidaten', 'kandidaten', to_jsonb(v_k)); END IF;
10695
+ SELECT array_agg(p.id) INTO v_p FROM public.products p WHERE lower(btrim(p.floriday_trade_item_id)) = v_k[1];
10696
+ IF v_p IS NULL THEN RETURN jsonb_build_object('status', 'geen_eigen_artikel', 'trade_item_id', v_k[1]); END IF;
10697
+ IF array_length(v_p, 1) > 1 THEN RETURN jsonb_build_object('status', 'meerdere_artikelen', 'trade_item_id', v_k[1], 'artikelen', to_jsonb(v_p)); END IF;
10698
+ RETURN jsonb_build_object('status', 'eenduidig', 'trade_item_id', v_k[1], 'product_id', v_p[1]);
10699
+ END $function$
10700
+ ;
10701
+ REVOKE ALL ON FUNCTION voorraad_orderregel_trade_item_bron(uuid,text) FROM PUBLIC;
10702
+ REVOKE ALL ON FUNCTION voorraad_orderregel_trade_item_bron(uuid,text) FROM anon;
10703
+ GRANT EXECUTE ON FUNCTION voorraad_orderregel_trade_item_bron(uuid,text) TO authenticated;
10704
+ GRANT EXECUTE ON FUNCTION voorraad_orderregel_trade_item_bron(uuid,text) TO service_role;
10705
+ CREATE OR REPLACE FUNCTION public.voorraad_orders_gearchiveerd(p_product_id uuid DEFAULT NULL::uuid)
10706
+ RETURNS jsonb
10707
+ LANGUAGE plpgsql
10708
+ STABLE SECURITY DEFINER
10709
+ SET search_path TO 'public'
10710
+ AS $function$
10711
+ BEGIN
10712
+ PERFORM public.voorraad_vereis('ingelogd', 'voorraad_orders_gearchiveerd');
10713
+ RETURN COALESCE((
10714
+ SELECT jsonb_agg(jsonb_build_object('id', x.id, 'naam', x.naam, 'regels', x.regels, 'laatste', x.laatste) ORDER BY x.laatste DESC)
10715
+ FROM (
10716
+ SELECT p.id, COALESCE(NULLIF(btrim(p.product),''), '(naamloos)') AS naam, count(*) AS regels, max(oi.created_at) AS laatste
10717
+ FROM public.products p
10718
+ JOIN public.order_items oi ON oi.product_id = p.id AND oi.created_at >= p.gearchiveerd_at AND oi.quarantined_at IS NULL
10719
+ JOIN public.orders o ON o.id = oi.order_id
10720
+ WHERE p.gearchiveerd_at IS NOT NULL
10721
+ AND (p_product_id IS NULL OR p.id = p_product_id)
10722
+ AND NOT public.voorraad_order_geannuleerd(o.status, o.cancelled_at, o.deleted_at)
10723
+ AND lower(COALESCE(o.status,'')) NOT IN ('verzonden','gereed voor facturatie')
10724
+ GROUP BY p.id
10725
+ LIMIT 20) x), '[]'::jsonb);
10726
+ END $function$
10727
+ ;
10728
+ REVOKE ALL ON FUNCTION voorraad_orders_gearchiveerd(uuid) FROM PUBLIC;
10729
+ REVOKE ALL ON FUNCTION voorraad_orders_gearchiveerd(uuid) FROM anon;
10730
+ GRANT EXECUTE ON FUNCTION voorraad_orders_gearchiveerd(uuid) TO authenticated;
10731
+ GRANT EXECUTE ON FUNCTION voorraad_orders_gearchiveerd(uuid) TO service_role;
9576
10732
  CREATE OR REPLACE FUNCTION public.voorraad_overzicht_orderregel_koppeling(p_voorbeelden integer DEFAULT 5)
9577
10733
  RETURNS jsonb
9578
10734
  LANGUAGE plpgsql
@@ -9960,6 +11116,81 @@ REVOKE ALL ON FUNCTION voorraad_stock_context_status() FROM PUBLIC;
9960
11116
  REVOKE ALL ON FUNCTION voorraad_stock_context_status() FROM anon;
9961
11117
  GRANT EXECUTE ON FUNCTION voorraad_stock_context_status() TO authenticated;
9962
11118
  GRANT EXECUTE ON FUNCTION voorraad_stock_context_status() TO service_role;
11119
+ CREATE OR REPLACE FUNCTION public.voorraad_telmoment_lijst(p_telmoment timestamp with time zone, p_keys text[] DEFAULT NULL::text[])
11120
+ RETURNS jsonb
11121
+ LANGUAGE plpgsql
11122
+ STABLE SECURITY DEFINER
11123
+ SET search_path TO 'public'
11124
+ AS $function$
11125
+ DECLARE v_dag date := (p_telmoment AT TIME ZONE 'Europe/Amsterdam')::date; v jsonb;
11126
+ BEGIN
11127
+ PERFORM public.voorraad_vereis('ingelogd', 'voorraad_telmoment_lijst');
11128
+ WITH r AS (
11129
+ SELECT oi.id, o.id AS order_id, o.order_number, o.customer_name, public.voorraad_lever_datum(o.delivery_date) AS lever,
11130
+ oi.quantity, oi.product_name, public.voorraad_orderregel_sleutel(oi.product_id::text) AS sleutel
11131
+ FROM logistics_active.order_items oi JOIN public.orders o ON o.id = oi.order_id
11132
+ WHERE oi.product_id IS NOT NULL
11133
+ AND NOT public.voorraad_order_geannuleerd(o.status, o.cancelled_at, o.deleted_at)
11134
+ AND NOT EXISTS (SELECT 1 FROM public.stock_mutation_log m WHERE m.order_item_id = oi.id AND m.mutation_type = 'consumption')
11135
+ AND NOT EXISTS (SELECT 1 FROM public.voorraad_telmoment_regels t WHERE t.order_item_id = oi.id)
11136
+ ), k AS (
11137
+ SELECT r.*, CASE WHEN r.lever < v_dag THEN 'voor_teldag'
11138
+ WHEN public.voorraad_order_vertrokken_voor(r.order_id, p_telmoment) THEN 'vertrokken'
11139
+ ELSE 'teldag_open' END AS categorie
11140
+ FROM r WHERE r.lever IS NOT NULL AND r.lever <= v_dag AND (p_keys IS NULL OR r.sleutel = ANY(p_keys))
11141
+ )
11142
+ SELECT jsonb_build_object('teldag', v_dag,
11143
+ 'buiten_telling', (SELECT count(*) FROM k WHERE categorie IN ('voor_teldag','vertrokken')),
11144
+ 'regels', COALESCE((SELECT jsonb_agg(jsonb_build_object('order_item_id', id, 'order_number', order_number, 'klant', customer_name,
11145
+ 'leverdatum', lever, 'aantal', quantity, 'artikel', product_name, 'categorie', categorie) ORDER BY lever, order_number) FROM k), '[]'::jsonb))
11146
+ INTO v;
11147
+ RETURN v;
11148
+ END $function$
11149
+ ;
11150
+ REVOKE ALL ON FUNCTION voorraad_telmoment_lijst(timestamp with time zone,text[]) FROM PUBLIC;
11151
+ REVOKE ALL ON FUNCTION voorraad_telmoment_lijst(timestamp with time zone,text[]) FROM anon;
11152
+ GRANT EXECUTE ON FUNCTION voorraad_telmoment_lijst(timestamp with time zone,text[]) TO authenticated;
11153
+ GRANT EXECUTE ON FUNCTION voorraad_telmoment_lijst(timestamp with time zone,text[]) TO service_role;
11154
+ CREATE OR REPLACE FUNCTION public.voorraad_telmoment_opslaan(p_telmoment timestamp with time zone, p_keys text[] DEFAULT NULL::text[], p_al_weg uuid[] DEFAULT NULL::uuid[], p_notitie text DEFAULT NULL::text)
11155
+ RETURNS jsonb
11156
+ LANGUAGE plpgsql
11157
+ SECURITY DEFINER
11158
+ SET search_path TO 'public'
11159
+ AS $function$
11160
+ DECLARE v_id uuid; v_lijst jsonb; e jsonb; v_reden text; n int := 0; v_keys text[] := ARRAY[]::text[]; v_key text; v_oid uuid;
11161
+ BEGIN
11162
+ PERFORM public.voorraad_vereis('ingelogd', 'voorraad_telmoment_opslaan');
11163
+ IF p_telmoment IS NULL THEN RETURN jsonb_build_object('ok', false, 'melding', 'Kies een telmoment'); END IF;
11164
+ v_lijst := public.voorraad_telmoment_lijst(p_telmoment, p_keys);
11165
+ INSERT INTO public.voorraad_telmoment(telmoment, trade_item_keys, notitie) VALUES (p_telmoment, p_keys, NULLIF(btrim(COALESCE(p_notitie,'')),'')) RETURNING id INTO v_id;
11166
+ FOR e IN SELECT * FROM jsonb_array_elements(v_lijst->'regels') LOOP
11167
+ v_reden := CASE e->>'categorie' WHEN 'voor_teldag' THEN 'leverdatum_voor_teldag' WHEN 'vertrokken' THEN 'vertrokken_voor_telmoment'
11168
+ ELSE CASE WHEN (e->>'order_item_id')::uuid = ANY(COALESCE(p_al_weg, ARRAY[]::uuid[])) THEN 'is_al_weg' END END;
11169
+ CONTINUE WHEN v_reden IS NULL;
11170
+ INSERT INTO public.voorraad_telmoment_regels(order_item_id, telmoment_id, reden) VALUES ((e->>'order_item_id')::uuid, v_id, v_reden)
11171
+ ON CONFLICT (order_item_id) DO NOTHING;
11172
+ IF FOUND THEN
11173
+ SELECT oi.order_id, public.voorraad_orderregel_sleutel(oi.product_id::text) INTO v_oid, v_key FROM public.order_items oi WHERE oi.id = (e->>'order_item_id')::uuid;
11174
+ INSERT INTO public.stock_mutation_log(order_id, order_item_id, mutation_type, qty_delta, reason, created_by)
11175
+ VALUES (v_oid, (e->>'order_item_id')::uuid, 'consumption_done', 0, 'Verwerkt vóór telling', auth.uid())
11176
+ ON CONFLICT (order_item_id) WHERE mutation_type = 'consumption_done' AND order_item_id IS NOT NULL DO NOTHING;
11177
+ INSERT INTO public.stock_mutation_log(trade_item_key, order_id, order_item_id, mutation_type, qty_delta, reason, created_by)
11178
+ VALUES (v_key, v_oid, (e->>'order_item_id')::uuid, 'consumption', 0,
11179
+ 'Verwerkt vóór telling (telmoment ' || to_char(p_telmoment AT TIME ZONE 'Europe/Amsterdam', 'DD-MM-YYYY HH24:MI') || '): niet afgeboekt', auth.uid());
11180
+ IF v_key IS NOT NULL AND NOT (v_key = ANY(v_keys)) THEN v_keys := v_keys || v_key; END IF;
11181
+ n := n + 1;
11182
+ END IF;
11183
+ END LOOP;
11184
+ FOREACH v_key IN ARRAY v_keys LOOP
11185
+ BEGIN PERFORM public.recompute_reserved_stock(v_key); EXCEPTION WHEN others THEN NULL; END;
11186
+ END LOOP;
11187
+ RETURN jsonb_build_object('ok', true, 'telmoment_id', v_id, 'buiten_telling', n, 'artikelen_herberekend', array_length(v_keys, 1));
11188
+ END $function$
11189
+ ;
11190
+ REVOKE ALL ON FUNCTION voorraad_telmoment_opslaan(timestamp with time zone,text[],uuid[],text) FROM PUBLIC;
11191
+ REVOKE ALL ON FUNCTION voorraad_telmoment_opslaan(timestamp with time zone,text[],uuid[],text) FROM anon;
11192
+ GRANT EXECUTE ON FUNCTION voorraad_telmoment_opslaan(timestamp with time zone,text[],uuid[],text) TO authenticated;
11193
+ GRANT EXECUTE ON FUNCTION voorraad_telmoment_opslaan(timestamp with time zone,text[],uuid[],text) TO service_role;
9963
11194
  CREATE OR REPLACE FUNCTION public.voorraad_touch_updated_at()
9964
11195
  RETURNS trigger
9965
11196
  LANGUAGE plpgsql
@@ -9979,6 +11210,165 @@ GRANT EXECUTE ON FUNCTION voorraad_touch_updated_at() TO service_role;
9979
11210
  REVOKE EXECUTE ON FUNCTION voorraad_touch_updated_at() FROM anon;
9980
11211
  GRANT EXECUTE ON FUNCTION voorraad_touch_updated_at() TO authenticated;
9981
11212
  GRANT EXECUTE ON FUNCTION voorraad_touch_updated_at() TO service_role;
11213
+ CREATE OR REPLACE FUNCTION public.voorraad_traysticker_opslaan(p_product_id uuid, p_oud_pad text, p_nieuw jsonb)
11214
+ RETURNS jsonb
11215
+ LANGUAGE plpgsql
11216
+ SECURITY DEFINER
11217
+ SET search_path TO 'public'
11218
+ AS $function$
11219
+ DECLARE v_cur text; v_bestaat boolean;
11220
+ BEGIN
11221
+ PERFORM public.voorraad_vereis('ingelogd', 'voorraad_traysticker_opslaan');
11222
+ IF NOT public.voorraad_can_write() THEN
11223
+ RAISE EXCEPTION 'Je hebt geen schrijfrechten op de voorraad.' USING ERRCODE = '42501'; END IF;
11224
+ PERFORM 1 FROM public.products WHERE id = p_product_id FOR UPDATE;
11225
+ IF NOT FOUND THEN RAISE EXCEPTION 'Artikel niet gevonden.' USING ERRCODE = 'P0002'; END IF;
11226
+ SELECT true, bestandspad INTO v_bestaat, v_cur FROM public.product_tray_stickers WHERE product_id = p_product_id;
11227
+ IF v_cur IS DISTINCT FROM p_oud_pad THEN
11228
+ RETURN jsonb_build_object('ok', false, 'conflicten', jsonb_build_array(jsonb_build_object(
11229
+ 'veld', 'traysticker', 'bij_openen', p_oud_pad, 'hun', v_cur)));
11230
+ END IF;
11231
+ IF p_nieuw IS NULL THEN
11232
+ DELETE FROM public.product_tray_stickers WHERE product_id = p_product_id;
11233
+ ELSE
11234
+ IF NULLIF(btrim(COALESCE(p_nieuw->>'bestand_url','')),'') IS NULL THEN
11235
+ RAISE EXCEPTION 'Geen bestand opgegeven.' USING ERRCODE = '22023'; END IF;
11236
+ INSERT INTO public.product_tray_stickers(product_id, bestand_url, bestandspad, bestandsnaam, bestandsgrootte)
11237
+ VALUES (p_product_id, p_nieuw->>'bestand_url', p_nieuw->>'bestandspad', p_nieuw->>'bestandsnaam', (p_nieuw->>'bestandsgrootte')::int)
11238
+ ON CONFLICT (product_id) DO UPDATE SET bestand_url = EXCLUDED.bestand_url, bestandspad = EXCLUDED.bestandspad,
11239
+ bestandsnaam = EXCLUDED.bestandsnaam, bestandsgrootte = EXCLUDED.bestandsgrootte, updated_at = now();
11240
+ END IF;
11241
+ RETURN jsonb_build_object('ok', true, 'oud_pad', v_cur);
11242
+ END $function$
11243
+ ;
11244
+ REVOKE ALL ON FUNCTION voorraad_traysticker_opslaan(uuid,text,jsonb) FROM PUBLIC;
11245
+ REVOKE ALL ON FUNCTION voorraad_traysticker_opslaan(uuid,text,jsonb) FROM anon;
11246
+ GRANT EXECUTE ON FUNCTION voorraad_traysticker_opslaan(uuid,text,jsonb) TO authenticated;
11247
+ GRANT EXECUTE ON FUNCTION voorraad_traysticker_opslaan(uuid,text,jsonb) TO service_role;
11248
+ CREATE OR REPLACE FUNCTION public.voorraad_twijfel_kiezen(p_floriday_id text, p_product_id uuid)
11249
+ RETURNS jsonb
11250
+ LANGUAGE plpgsql
11251
+ SECURITY DEFINER
11252
+ SET search_path TO 'public'
11253
+ AS $function$
11254
+ DECLARE t public.voorraad_koppel_twijfel%ROWTYPE; p public.products%ROWTYPE; v_eigen text; v_fl_stock numeric; v_eigen_stock numeric;
11255
+ BEGIN
11256
+ PERFORM public.voorraad_vereis('ingelogd', 'voorraad_twijfel_kiezen');
11257
+ IF NOT public.voorraad_can_write() THEN
11258
+ RAISE EXCEPTION 'Je hebt geen schrijfrechten op de voorraad.' USING ERRCODE = '42501'; END IF;
11259
+ SELECT * INTO t FROM public.voorraad_koppel_twijfel WHERE floriday_id = p_floriday_id FOR UPDATE;
11260
+ IF NOT FOUND OR t.opgelost_at IS NOT NULL THEN
11261
+ RAISE EXCEPTION 'Dit twijfelgeval is al opgelost of bestaat niet meer.' USING ERRCODE = 'P0002'; END IF;
11262
+ SELECT * INTO p FROM public.products WHERE id = p_product_id FOR UPDATE;
11263
+ IF NOT FOUND THEN RAISE EXCEPTION 'Artikel niet gevonden.' USING ERRCODE = 'P0002'; END IF;
11264
+ IF NULLIF(btrim(COALESCE(p.floriday_trade_item_id,'')),'') IS NOT NULL AND lower(btrim(p.floriday_trade_item_id)) <> lower(p_floriday_id) THEN
11265
+ RAISE EXCEPTION 'Dit artikel is al aan een ander Floriday-product gekoppeld.' USING ERRCODE = '23505'; END IF;
11266
+ IF EXISTS (SELECT 1 FROM public.products q WHERE q.id <> p.id AND lower(btrim(COALESCE(q.floriday_trade_item_id,''))) = lower(p_floriday_id)) THEN
11267
+ RAISE EXCEPTION 'Een ander artikel is al aan dit Floriday-product gekoppeld.' USING ERRCODE = '23505'; END IF;
11268
+ -- Twee eigen voorraadregels met voorraad: eerst samenvoegen (nooit stil optellen of weggooien).
11269
+ v_eigen := public.voorraad_canoniek_sleutel(p.id);
11270
+ SELECT COALESCE(sum(physical_stock),0) INTO v_fl_stock FROM public.trade_item_stock WHERE trade_item_key = p_floriday_id AND archived_at IS NULL;
11271
+ SELECT COALESCE(sum(physical_stock),0) INTO v_eigen_stock FROM public.trade_item_stock WHERE trade_item_key = v_eigen AND archived_at IS NULL;
11272
+ IF v_eigen IS DISTINCT FROM p_floriday_id AND v_fl_stock > 0 AND v_eigen_stock > 0 THEN
11273
+ RAISE EXCEPTION 'Het artikel en het Floriday-product hebben elk een eigen voorraad (% en %). Voeg die eerst samen.', v_eigen_stock, v_fl_stock USING ERRCODE = '23505'; END IF;
11274
+
11275
+ UPDATE public.products SET floriday_trade_item_id = p_floriday_id, updated_at = now() WHERE id = p.id;
11276
+ INSERT INTO public.voorraad_koppel_log(run_id, soort, floriday_id, product_id, oud, nieuw)
11277
+ VALUES (gen_random_uuid(), 'zelf_gekozen', p_floriday_id, p.id,
11278
+ jsonb_build_object('floriday_trade_item_id', p.floriday_trade_item_id),
11279
+ jsonb_build_object('floriday_trade_item_id', p_floriday_id, 'door', auth.uid(), 'reden', t.reden));
11280
+ UPDATE public.voorraad_koppel_twijfel SET opgelost_at = now() WHERE floriday_id = p_floriday_id;
11281
+ RETURN jsonb_build_object('ok', true, 'product_id', p.id);
11282
+ END $function$
11283
+ ;
11284
+ REVOKE ALL ON FUNCTION voorraad_twijfel_kiezen(text,uuid) FROM PUBLIC;
11285
+ REVOKE ALL ON FUNCTION voorraad_twijfel_kiezen(text,uuid) FROM anon;
11286
+ GRANT EXECUTE ON FUNCTION voorraad_twijfel_kiezen(text,uuid) TO authenticated;
11287
+ GRANT EXECUTE ON FUNCTION voorraad_twijfel_kiezen(text,uuid) TO service_role;
11288
+ CREATE OR REPLACE FUNCTION public.voorraad_twijfel_lijst(p_product_id uuid DEFAULT NULL::uuid, p_floriday_id text DEFAULT NULL::text)
11289
+ RETURNS jsonb
11290
+ LANGUAGE plpgsql
11291
+ STABLE SECURITY DEFINER
11292
+ SET search_path TO 'public'
11293
+ AS $function$
11294
+ BEGIN
11295
+ PERFORM public.voorraad_vereis('ingelogd', 'voorraad_twijfel_lijst');
11296
+ RETURN COALESCE((
11297
+ SELECT jsonb_agg(jsonb_build_object(
11298
+ 'floriday_id', t.floriday_id, 'naam', t.naam, 'reden', t.reden, 'laatst_gezien', t.laatst_gezien,
11299
+ 'kandidaten', COALESCE((
11300
+ SELECT jsonb_agg(jsonb_build_object('id', p.id,
11301
+ 'naam', COALESCE(NULLIF(btrim(p.product),''), '(naamloos)'),
11302
+ 'artikelnummer', p.article_number, 'barcode', p.barcode,
11303
+ 'floriday_id', NULLIF(btrim(COALESCE(p.floriday_trade_item_id,'')),''),
11304
+ 'gearchiveerd', p.gearchiveerd_at IS NOT NULL) ORDER BY p.product)
11305
+ FROM public.products p
11306
+ WHERE jsonb_typeof(t.kandidaten) = 'array' AND t.kandidaten ? p.id::text), '[]'::jsonb))
11307
+ ORDER BY t.laatst_gezien DESC)
11308
+ FROM public.voorraad_koppel_twijfel t
11309
+ WHERE t.opgelost_at IS NULL
11310
+ AND (p_floriday_id IS NULL OR t.floriday_id = p_floriday_id)
11311
+ AND (p_product_id IS NULL OR (jsonb_typeof(t.kandidaten) = 'array' AND t.kandidaten ? p_product_id::text))
11312
+ LIMIT 50), '[]'::jsonb);
11313
+ END $function$
11314
+ ;
11315
+ REVOKE ALL ON FUNCTION voorraad_twijfel_lijst(uuid,text) FROM PUBLIC;
11316
+ REVOKE ALL ON FUNCTION voorraad_twijfel_lijst(uuid,text) FROM anon;
11317
+ GRANT EXECUTE ON FUNCTION voorraad_twijfel_lijst(uuid,text) TO authenticated;
11318
+ GRANT EXECUTE ON FUNCTION voorraad_twijfel_lijst(uuid,text) TO service_role;
11319
+ CREATE OR REPLACE FUNCTION public.voorraad_twijfel_nieuw_artikel(p_floriday_id text)
11320
+ RETURNS jsonb
11321
+ LANGUAGE plpgsql
11322
+ SECURITY DEFINER
11323
+ SET search_path TO 'public'
11324
+ AS $function$
11325
+ DECLARE t public.voorraad_koppel_twijfel%ROWTYPE; c record; v_name text; v_gtin text; v_code text; v_photo text;
11326
+ v_new uuid; v_sturing text;
11327
+ BEGIN
11328
+ PERFORM public.voorraad_vereis('ingelogd', 'voorraad_twijfel_nieuw_artikel');
11329
+ IF NOT public.voorraad_can_write() THEN
11330
+ RAISE EXCEPTION 'Je hebt geen schrijfrechten op de voorraad.' USING ERRCODE = '42501'; END IF;
11331
+ SELECT * INTO t FROM public.voorraad_koppel_twijfel WHERE floriday_id = p_floriday_id FOR UPDATE;
11332
+ IF NOT FOUND OR t.opgelost_at IS NOT NULL THEN
11333
+ RAISE EXCEPTION 'Dit twijfelgeval is al opgelost of bestaat niet meer.' USING ERRCODE = 'P0002'; END IF;
11334
+ IF EXISTS (SELECT 1 FROM public.products q WHERE lower(btrim(COALESCE(q.floriday_trade_item_id,''))) = lower(p_floriday_id)) THEN
11335
+ RAISE EXCEPTION 'Een artikel is al aan dit Floriday-product gekoppeld.' USING ERRCODE = '23505'; END IF;
11336
+ SELECT x.floriday_id, x.data INTO c FROM public.floriday_trade_items_cache x
11337
+ WHERE x.floriday_id = p_floriday_id
11338
+ AND (t.connection_id IS NULL OR x.connection_id = t.connection_id) LIMIT 1;
11339
+ IF NOT FOUND THEN
11340
+ RAISE EXCEPTION 'Het Floriday-artikel staat niet (meer) in de Floriday-gegevens.' USING ERRCODE = 'P0002'; END IF;
11341
+ v_name := NULLIF(btrim(COALESCE(CASE WHEN jsonb_typeof(c.data->'tradeItemName') = 'string' THEN c.data->>'tradeItemName' END,
11342
+ c.data->'tradeItemName'->>'nl', c.data->'tradeItemName'->>'en', t.naam)), '');
11343
+ IF v_name IS NULL THEN RAISE EXCEPTION 'Het Floriday-artikel heeft geen naam; kies een bestaand artikel.' USING ERRCODE = '22023'; END IF;
11344
+ v_gtin := NULLIF(btrim(COALESCE(c.data->>'articleGtin', c.data->>'gtin')), '');
11345
+ v_code := NULLIF(btrim(COALESCE(c.data->>'supplierArticleCode', c.data->>'articleCode')), '');
11346
+ SELECT e->>'url' INTO v_photo FROM jsonb_array_elements(CASE WHEN jsonb_typeof(c.data->'photos') = 'array' THEN c.data->'photos' ELSE '[]'::jsonb END) e
11347
+ ORDER BY COALESCE((e->>'primary')::boolean, false) DESC LIMIT 1;
11348
+ SELECT COALESCE(standaard_sturing_nieuwe_floriday, 'lokaal') INTO v_sturing FROM public.voorraad_instellingen WHERE id;
11349
+ v_sturing := COALESCE(v_sturing, 'lokaal');
11350
+ PERFORM set_config('voorraad.floriday_sync', 'on', true);
11351
+ INSERT INTO public.products(product, batch, location, barcode, article_number, image_url, floriday_trade_item_id, trade_item_key, custom_fields)
11352
+ VALUES (v_name, '', '', v_gtin, v_code, v_photo, c.floriday_id,
11353
+ COALESCE((SELECT s.trade_item_key FROM public.trade_item_stock s WHERE lower(s.trade_item_key) = lower(c.floriday_id) AND s.archived_at IS NULL ORDER BY s.created_at LIMIT 1), c.floriday_id),
11354
+ jsonb_build_object('voorraad_auto_aangemaakt', true, 'voorraad_floriday_versie', c.data->>'tradeItemVersion',
11355
+ 'voorraad_auto_aangemaakt_at', now(), 'voorraad_uit_twijfel', true))
11356
+ RETURNING id INTO v_new;
11357
+ IF v_sturing = 'lokaal' THEN
11358
+ UPDATE public.floriday_inventory_settings SET erp_leading = NULL, stock_mode = 'DISABLED', updated_at = now()
11359
+ WHERE product_id = v_new;
11360
+ END IF;
11361
+ INSERT INTO public.voorraad_koppel_log(run_id, soort, floriday_id, product_id, oud, nieuw)
11362
+ VALUES (gen_random_uuid(), 'twijfel_nieuw_artikel', c.floriday_id, v_new, jsonb_build_object('reden', t.reden),
11363
+ jsonb_build_object('naam', v_name, 'gtin', v_gtin, 'artikelcode', v_code, 'sturing', v_sturing, 'door', auth.uid()));
11364
+ UPDATE public.voorraad_koppel_twijfel SET opgelost_at = now() WHERE floriday_id = p_floriday_id;
11365
+ RETURN jsonb_build_object('ok', true, 'product_id', v_new);
11366
+ END $function$
11367
+ ;
11368
+ REVOKE ALL ON FUNCTION voorraad_twijfel_nieuw_artikel(text) FROM PUBLIC;
11369
+ REVOKE ALL ON FUNCTION voorraad_twijfel_nieuw_artikel(text) FROM anon;
11370
+ GRANT EXECUTE ON FUNCTION voorraad_twijfel_nieuw_artikel(text) TO authenticated;
11371
+ GRANT EXECUTE ON FUNCTION voorraad_twijfel_nieuw_artikel(text) TO service_role;
9982
11372
  CREATE OR REPLACE FUNCTION public.voorraad_vereis(p_niveau text, p_functie text)
9983
11373
  RETURNS void
9984
11374
  LANGUAGE plpgsql
@@ -10005,6 +11395,113 @@ GRANT EXECUTE ON FUNCTION voorraad_vereis(text,text) TO service_role;
10005
11395
  GRANT EXECUTE ON FUNCTION voorraad_vereis(text,text) TO anon;
10006
11396
  GRANT EXECUTE ON FUNCTION voorraad_vereis(text,text) TO authenticated;
10007
11397
  GRANT EXECUTE ON FUNCTION voorraad_vereis(text,text) TO service_role;
11398
+ CREATE OR REPLACE FUNCTION public.voorraad_voorraad_corrigeren(p_product_id uuid, p_delta integer, p_reden text)
11399
+ RETURNS jsonb
11400
+ LANGUAGE plpgsql
11401
+ SECURITY DEFINER
11402
+ SET search_path TO 'public'
11403
+ AS $function$
11404
+ DECLARE v_ctx record; v_fl text; v_tk text; v_voor integer; v_na integer; r record;
11405
+ BEGIN
11406
+ PERFORM public.voorraad_vereis('ingelogd', 'voorraad_voorraad_corrigeren');
11407
+ IF NOT public.voorraad_can_write() THEN
11408
+ RAISE EXCEPTION 'Je hebt geen schrijfrechten op de voorraad.' USING ERRCODE = '42501'; END IF;
11409
+ IF p_delta IS NULL OR p_delta = 0 THEN
11410
+ RAISE EXCEPTION 'Vul een aantal in dat niet 0 is.' USING ERRCODE = '22023'; END IF;
11411
+ IF COALESCE(btrim(p_reden),'') = '' THEN
11412
+ RAISE EXCEPTION 'Vul een reden in.' USING ERRCODE = '22023'; END IF;
11413
+ SELECT NULLIF(btrim(COALESCE(p.floriday_trade_item_id,'')),''), NULLIF(btrim(COALESCE(p.trade_item_key,'')),'')
11414
+ INTO v_fl, v_tk FROM public.products p WHERE p.id = p_product_id;
11415
+ IF NOT FOUND THEN RAISE EXCEPTION 'Artikel niet gevonden.' USING ERRCODE = 'P0002'; END IF;
11416
+ SELECT * INTO v_ctx FROM public.get_stock_context(true);
11417
+ IF v_ctx.connection_id IS NULL THEN RAISE EXCEPTION 'Geen voorraadcontext beschikbaar' USING ERRCODE = 'VR004'; END IF;
11418
+ -- Eén correctie tegelijk per artikel; bestaande rij vastzetten zodat een tussentijdse orderafboeking meetelt.
11419
+ PERFORM pg_advisory_xact_lock(hashtext('voorraad_corrigeren:' || p_product_id::text));
11420
+ SELECT s.physical_stock INTO v_voor FROM public.trade_item_stock s
11421
+ WHERE s.connection_id = v_ctx.connection_id
11422
+ AND s.trade_item_key IN (COALESCE(v_tk, v_fl, 'product:' || p_product_id::text), v_fl, v_tk, 'product:' || p_product_id::text, p_product_id::text)
11423
+ ORDER BY CASE WHEN s.trade_item_key = COALESCE(v_tk, v_fl, 'product:' || p_product_id::text) THEN 0 ELSE 1 END, s.updated_at DESC NULLS LAST
11424
+ LIMIT 1 FOR UPDATE;
11425
+ v_voor := COALESCE(v_voor, 0);
11426
+ v_na := v_voor + p_delta;
11427
+ IF v_na < 0 THEN
11428
+ RAISE EXCEPTION 'Je kunt niet meer weghalen (%) dan er ligt (%).', -p_delta, v_voor USING ERRCODE = '22023'; END IF;
11429
+ PERFORM set_config('stock.change_note', left('Aangepast ' || CASE WHEN p_delta > 0 THEN '+' ELSE '' END || p_delta || ': ' || btrim(p_reden), 500), true);
11430
+ PERFORM set_config('stock.keep_notes', '1', true);
11431
+ SELECT * INTO r FROM public.set_product_stock(p_product_id => p_product_id, p_physical_stock => v_na,
11432
+ p_minimum_stock => NULL, p_incoming_stock => NULL, p_notes => NULL, p_change_source => 'MANUAL_STOCK');
11433
+ RETURN jsonb_build_object('opgeslagen', true, 'physical_before', v_voor, 'physical_after', r.physical_stock,
11434
+ 'trade_item_key', r.trade_item_key, 'reserved_stock', r.reserved_stock);
11435
+ END $function$
11436
+ ;
11437
+ REVOKE ALL ON FUNCTION voorraad_voorraad_corrigeren(uuid,integer,text) FROM PUBLIC;
11438
+ REVOKE ALL ON FUNCTION voorraad_voorraad_corrigeren(uuid,integer,text) FROM anon;
11439
+ GRANT EXECUTE ON FUNCTION voorraad_voorraad_corrigeren(uuid,integer,text) TO authenticated;
11440
+ GRANT EXECUTE ON FUNCTION voorraad_voorraad_corrigeren(uuid,integer,text) TO service_role;
11441
+ CREATE OR REPLACE FUNCTION public.voorraad_voorraad_instellingen_opslaan(p_product_id uuid, p_oud jsonb, p_nieuw jsonb)
11442
+ RETURNS jsonb
11443
+ LANGUAGE plpgsql
11444
+ SECURITY DEFINER
11445
+ SET search_path TO 'public'
11446
+ AS $function$
11447
+ DECLARE
11448
+ v_key text; s public.trade_item_stock%ROWTYPE; v_cur jsonb; k text;
11449
+ v_conf jsonb := '[]'::jsonb; v_set jsonb := '{}'::jsonb; v_ctx uuid;
11450
+ BEGIN
11451
+ PERFORM public.voorraad_vereis('ingelogd', 'voorraad_voorraad_instellingen_opslaan');
11452
+ IF NOT public.voorraad_can_write() THEN
11453
+ RAISE EXCEPTION 'Je hebt geen schrijfrechten op de voorraad.' USING ERRCODE = '42501'; END IF;
11454
+ IF jsonb_typeof(p_nieuw) IS DISTINCT FROM 'object' OR jsonb_typeof(p_oud) IS DISTINCT FROM 'object' THEN
11455
+ RAISE EXCEPTION 'Ongeldige invoer voor opslaan.' USING ERRCODE = '22023'; END IF;
11456
+ PERFORM 1 FROM public.products WHERE id = p_product_id FOR UPDATE;
11457
+ IF NOT FOUND THEN RAISE EXCEPTION 'Artikel niet gevonden.' USING ERRCODE = 'P0002'; END IF;
11458
+
11459
+ v_key := public.voorraad_artikel_detail(p_product_id)->>'voorraad_sleutel';
11460
+ IF v_key IS NULL THEN
11461
+ -- Nog geen voorraadregel: aanmaken met 0 liggend via de gewone opslagroute.
11462
+ PERFORM public.set_product_stock(p_product_id => p_product_id, p_physical_stock => 0, p_change_source => 'MANUAL_STOCK');
11463
+ v_key := public.voorraad_artikel_detail(p_product_id)->>'voorraad_sleutel';
11464
+ END IF;
11465
+ BEGIN SELECT c.connection_id INTO v_ctx FROM public.get_stock_context() c LIMIT 1;
11466
+ EXCEPTION WHEN others THEN v_ctx := NULL; END;
11467
+ SELECT * INTO s FROM public.trade_item_stock x WHERE x.trade_item_key = v_key AND x.archived_at IS NULL
11468
+ ORDER BY (x.connection_id IS NOT DISTINCT FROM v_ctx) DESC, x.updated_at DESC LIMIT 1 FOR UPDATE;
11469
+ IF NOT FOUND THEN RAISE EXCEPTION 'Geen voorraadregel gevonden voor dit artikel.' USING ERRCODE = 'P0002'; END IF;
11470
+
11471
+ v_cur := jsonb_build_object('minimum_stock', s.minimum_stock, 'incoming_stock', s.incoming_stock, 'notes', s.notes);
11472
+ FOR k IN SELECT jsonb_object_keys(p_nieuw) LOOP
11473
+ IF k NOT IN ('minimum_stock','incoming_stock','notes') THEN
11474
+ RAISE EXCEPTION 'Veld "%" kan hier niet worden opgeslagen.', k USING ERRCODE = '22023'; END IF;
11475
+ IF NOT (p_oud ? k) THEN
11476
+ RAISE EXCEPTION 'Stand bij openen ontbreekt voor veld "%".', k USING ERRCODE = '22023'; END IF;
11477
+ CONTINUE WHEN (p_nieuw->k) = (p_oud->k);
11478
+ IF (v_cur->k) IS DISTINCT FROM (p_oud->k) AND (v_cur->k) IS DISTINCT FROM (p_nieuw->k) THEN
11479
+ v_conf := v_conf || jsonb_build_object('veld', k, 'bij_openen', p_oud->k, 'jouw', p_nieuw->k, 'hun', v_cur->k);
11480
+ ELSE
11481
+ v_set := v_set || jsonb_build_object(k, p_nieuw->k);
11482
+ END IF;
11483
+ END LOOP;
11484
+ IF jsonb_array_length(v_conf) > 0 THEN RETURN jsonb_build_object('ok', false, 'conflicten', v_conf); END IF;
11485
+ IF (v_set ? 'minimum_stock' AND COALESCE((v_set->>'minimum_stock')::numeric, 0) < 0)
11486
+ OR (v_set ? 'incoming_stock' AND COALESCE((v_set->>'incoming_stock')::numeric, 0) < 0) THEN
11487
+ RAISE EXCEPTION 'Voorraadaantallen mogen niet negatief zijn' USING ERRCODE = '22023'; END IF;
11488
+
11489
+ IF v_set <> '{}'::jsonb THEN
11490
+ PERFORM set_config('stock.change_source', 'MANUAL_STOCK', true);
11491
+ UPDATE public.trade_item_stock SET
11492
+ minimum_stock = CASE WHEN v_set ? 'minimum_stock' THEN COALESCE((v_set->>'minimum_stock')::numeric, 0) ELSE minimum_stock END,
11493
+ incoming_stock = CASE WHEN v_set ? 'incoming_stock' THEN COALESCE((v_set->>'incoming_stock')::numeric, 0) ELSE incoming_stock END,
11494
+ notes = CASE WHEN v_set ? 'notes' THEN NULLIF(btrim(COALESCE(v_set->>'notes','')), '') ELSE notes END,
11495
+ change_source = 'MANUAL_STOCK', updated_at = now()
11496
+ WHERE id = s.id;
11497
+ END IF;
11498
+ RETURN jsonb_build_object('ok', true, 'opgeslagen', v_set);
11499
+ END $function$
11500
+ ;
11501
+ REVOKE ALL ON FUNCTION voorraad_voorraad_instellingen_opslaan(uuid,jsonb,jsonb) FROM PUBLIC;
11502
+ REVOKE ALL ON FUNCTION voorraad_voorraad_instellingen_opslaan(uuid,jsonb,jsonb) FROM anon;
11503
+ GRANT EXECUTE ON FUNCTION voorraad_voorraad_instellingen_opslaan(uuid,jsonb,jsonb) TO authenticated;
11504
+ GRANT EXECUTE ON FUNCTION voorraad_voorraad_instellingen_opslaan(uuid,jsonb,jsonb) TO service_role;
10008
11505
  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)
10009
11506
  RETURNS jsonb
10010
11507
  LANGUAGE plpgsql
@@ -10136,6 +11633,8 @@ DO $c$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'florida
10136
11633
  DO $c$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'floriday_takeover_items_pkey' AND conrelid = 'public.floriday_takeover_items'::regclass) THEN ALTER TABLE public.floriday_takeover_items ADD CONSTRAINT floriday_takeover_items_pkey PRIMARY KEY (id); END IF; END $c$;
10137
11634
  DO $c$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'floriday_takeover_runs_pkey' AND conrelid = 'public.floriday_takeover_runs'::regclass) THEN ALTER TABLE public.floriday_takeover_runs ADD CONSTRAINT floriday_takeover_runs_pkey PRIMARY KEY (id); END IF; END $c$;
10138
11635
  DO $c$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'floriday_vbn_required_characteristics_pkey' AND conrelid = 'public.floriday_vbn_required_characteristics'::regclass) THEN ALTER TABLE public.floriday_vbn_required_characteristics ADD CONSTRAINT floriday_vbn_required_characteristics_pkey PRIMARY KEY (vbn_code); END IF; END $c$;
11636
+ DO $c$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fust_startmoment_pkey' AND conrelid = 'public.fust_startmoment'::regclass) THEN ALTER TABLE public.fust_startmoment ADD CONSTRAINT fust_startmoment_pkey PRIMARY KEY (id); END IF; END $c$;
11637
+ DO $c$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fust_startmoment_log_pkey' AND conrelid = 'public.fust_startmoment_log'::regclass) THEN ALTER TABLE public.fust_startmoment_log ADD CONSTRAINT fust_startmoment_log_pkey PRIMARY KEY (id); END IF; END $c$;
10139
11638
  DO $c$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fust_stock_mutations_pkey' AND conrelid = 'public.fust_stock_mutations'::regclass) THEN ALTER TABLE public.fust_stock_mutations ADD CONSTRAINT fust_stock_mutations_pkey PRIMARY KEY (id); END IF; END $c$;
10140
11639
  DO $c$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fust_types_pkey' AND conrelid = 'public.fust_types'::regclass) THEN ALTER TABLE public.fust_types ADD CONSTRAINT fust_types_pkey PRIMARY KEY (id); END IF; END $c$;
10141
11640
  DO $c$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'inventory_recompute_limits_pkey' AND conrelid = 'public.inventory_recompute_limits'::regclass) THEN ALTER TABLE public.inventory_recompute_limits ADD CONSTRAINT inventory_recompute_limits_pkey PRIMARY KEY (id); END IF; END $c$;
@@ -10160,6 +11659,10 @@ DO $c$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'voorraa
10160
11659
  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$;
10161
11660
  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$;
10162
11661
  DO $c$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'voorraad_defaults_state_pkey' AND conrelid = 'public.voorraad_defaults_state'::regclass) THEN ALTER TABLE public.voorraad_defaults_state ADD CONSTRAINT voorraad_defaults_state_pkey PRIMARY KEY (id); END IF; END $c$;
11662
+ DO $c$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'voorraad_floriday_artikel_wachtrij_pkey' AND conrelid = 'public.voorraad_floriday_artikel_wachtrij'::regclass) THEN ALTER TABLE public.voorraad_floriday_artikel_wachtrij ADD CONSTRAINT voorraad_floriday_artikel_wachtrij_pkey PRIMARY KEY (floriday_id); END IF; END $c$;
11663
+ DO $c$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'voorraad_instellingen_pkey' AND conrelid = 'public.voorraad_instellingen'::regclass) THEN ALTER TABLE public.voorraad_instellingen ADD CONSTRAINT voorraad_instellingen_pkey PRIMARY KEY (id); END IF; END $c$;
11664
+ DO $c$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'voorraad_koppel_log_pkey' AND conrelid = 'public.voorraad_koppel_log'::regclass) THEN ALTER TABLE public.voorraad_koppel_log ADD CONSTRAINT voorraad_koppel_log_pkey PRIMARY KEY (id); END IF; END $c$;
11665
+ DO $c$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'voorraad_koppel_twijfel_pkey' AND conrelid = 'public.voorraad_koppel_twijfel'::regclass) THEN ALTER TABLE public.voorraad_koppel_twijfel ADD CONSTRAINT voorraad_koppel_twijfel_pkey PRIMARY KEY (floriday_id); END IF; END $c$;
10163
11666
  DO $c$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'voorraad_load_carrier_types_pkey' AND conrelid = 'public.voorraad_load_carrier_types'::regclass) THEN ALTER TABLE public.voorraad_load_carrier_types ADD CONSTRAINT voorraad_load_carrier_types_pkey PRIMARY KEY (code); END IF; END $c$;
10164
11667
  DO $c$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'voorraad_local_context_pkey' AND conrelid = 'public.voorraad_local_context'::regclass) THEN ALTER TABLE public.voorraad_local_context ADD CONSTRAINT voorraad_local_context_pkey PRIMARY KEY (id); END IF; END $c$;
10165
11668
  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$;
@@ -10169,6 +11672,8 @@ DO $c$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'voorraa
10169
11672
  DO $c$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'voorraad_publish_jobs_pkey' AND conrelid = 'public.voorraad_publish_jobs'::regclass) THEN ALTER TABLE public.voorraad_publish_jobs ADD CONSTRAINT voorraad_publish_jobs_pkey PRIMARY KEY (id); END IF; END $c$;
10170
11673
  DO $c$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'voorraad_publish_learnings_pkey' AND conrelid = 'public.voorraad_publish_learnings'::regclass) THEN ALTER TABLE public.voorraad_publish_learnings ADD CONSTRAINT voorraad_publish_learnings_pkey PRIMARY KEY (id); END IF; END $c$;
10171
11674
  DO $c$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'voorraad_publish_steps_pkey' AND conrelid = 'public.voorraad_publish_steps'::regclass) THEN ALTER TABLE public.voorraad_publish_steps ADD CONSTRAINT voorraad_publish_steps_pkey PRIMARY KEY (id); END IF; END $c$;
11675
+ DO $c$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'voorraad_telmoment_pkey' AND conrelid = 'public.voorraad_telmoment'::regclass) THEN ALTER TABLE public.voorraad_telmoment ADD CONSTRAINT voorraad_telmoment_pkey PRIMARY KEY (id); END IF; END $c$;
11676
+ DO $c$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'voorraad_telmoment_regels_pkey' AND conrelid = 'public.voorraad_telmoment_regels'::regclass) THEN ALTER TABLE public.voorraad_telmoment_regels ADD CONSTRAINT voorraad_telmoment_regels_pkey PRIMARY KEY (order_item_id); END IF; END $c$;
10172
11677
  DO $c$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'floriday_inventory_settings_unique' AND conrelid = 'public.floriday_inventory_settings'::regclass) THEN ALTER TABLE public.floriday_inventory_settings ADD CONSTRAINT floriday_inventory_settings_unique UNIQUE (connection_id, product_id); END IF; END $c$;
10173
11678
  DO $c$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'floriday_sales_order_ingest_unique' AND conrelid = 'public.floriday_sales_order_ingest'::regclass) THEN ALTER TABLE public.floriday_sales_order_ingest ADD CONSTRAINT floriday_sales_order_ingest_unique UNIQUE (connection_id, sales_order_id, order_version); END IF; END $c$;
10174
11679
  DO $c$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'floriday_sync_cursors_unique' AND conrelid = 'public.floriday_sync_cursors'::regclass) THEN ALTER TABLE public.floriday_sync_cursors ADD CONSTRAINT floriday_sync_cursors_unique UNIQUE (connection_id, resource_type); END IF; END $c$;
@@ -10228,8 +11733,14 @@ ALTER TABLE public.trade_item_drafts DROP CONSTRAINT IF EXISTS trade_item_drafts
10228
11733
  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])));
10229
11734
  ALTER TABLE public.voorraad_defaults_state DROP CONSTRAINT IF EXISTS voorraad_defaults_state_id_check;
10230
11735
  ALTER TABLE public.voorraad_defaults_state ADD CONSTRAINT voorraad_defaults_state_id_check CHECK (id);
11736
+ ALTER TABLE public.voorraad_instellingen DROP CONSTRAINT IF EXISTS voorraad_instellingen_id_check;
11737
+ ALTER TABLE public.voorraad_instellingen ADD CONSTRAINT voorraad_instellingen_id_check CHECK (id);
11738
+ ALTER TABLE public.voorraad_instellingen DROP CONSTRAINT IF EXISTS voorraad_instellingen_standaard_sturing_nieuwe_floriday_check;
11739
+ ALTER TABLE public.voorraad_instellingen ADD CONSTRAINT voorraad_instellingen_standaard_sturing_nieuwe_floriday_check CHECK ((standaard_sturing_nieuwe_floriday = ANY (ARRAY['lokaal'::text, 'floriday'::text])));
10231
11740
  ALTER TABLE public.voorraad_local_context DROP CONSTRAINT IF EXISTS voorraad_local_context_singleton_check;
10232
11741
  ALTER TABLE public.voorraad_local_context ADD CONSTRAINT voorraad_local_context_singleton_check CHECK (singleton);
11742
+ ALTER TABLE public.voorraad_telmoment_regels DROP CONSTRAINT IF EXISTS voorraad_telmoment_regels_reden_check;
11743
+ ALTER TABLE public.voorraad_telmoment_regels ADD CONSTRAINT voorraad_telmoment_regels_reden_check CHECK ((reden = ANY (ARRAY['leverdatum_voor_teldag'::text, 'vertrokken_voor_telmoment'::text, 'is_al_weg'::text])));
10233
11744
  DO $g$ BEGIN IF to_regclass('public.products') IS NULL THEN RAISE NOTICE 'Voorraad: products (core) ontbreekt, controles overgeslagen; draai de schema-update opnieuw na die module'; ELSE EXECUTE $q$DO $c$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'floriday_inventory_settings_product_id_fkey' AND conrelid = 'public.floriday_inventory_settings'::regclass) THEN ALTER TABLE public.floriday_inventory_settings ADD CONSTRAINT floriday_inventory_settings_product_id_fkey FOREIGN KEY (product_id) REFERENCES products(id) ON DELETE CASCADE; END IF; END $c$$q$; END IF; END $g$;
10234
11745
  DO $c$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'floriday_takeover_items_run_id_fkey' AND conrelid = 'public.floriday_takeover_items'::regclass) THEN ALTER TABLE public.floriday_takeover_items ADD CONSTRAINT floriday_takeover_items_run_id_fkey FOREIGN KEY (run_id) REFERENCES floriday_takeover_runs(id) ON DELETE CASCADE; END IF; END $c$;
10235
11746
  DO $c$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'floriday_vbn_required_characteristics_updated_by_fkey' AND conrelid = 'public.floriday_vbn_required_characteristics'::regclass) THEN ALTER TABLE public.floriday_vbn_required_characteristics ADD CONSTRAINT floriday_vbn_required_characteristics_updated_by_fkey FOREIGN KEY (updated_by) REFERENCES auth.users(id) ON DELETE SET NULL; END IF; END $c$;
@@ -10244,6 +11755,7 @@ DO $c$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'stock_i
10244
11755
  DO $c$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'stock_import_runs_user_id_fkey' AND conrelid = 'public.stock_import_runs'::regclass) THEN ALTER TABLE public.stock_import_runs ADD CONSTRAINT stock_import_runs_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users(id) ON DELETE CASCADE; END IF; END $c$;
10245
11756
  DO $g$ BEGIN IF to_regclass('public.floriday_connections') IS NULL THEN RAISE NOTICE 'Voorraad: floriday_connections (Floriday-auth) ontbreekt, controles overgeslagen; draai de schema-update opnieuw na die module'; ELSE EXECUTE $q$DO $c$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'trade_item_warehouse_preferences_connection_id_fkey' AND conrelid = 'public.trade_item_warehouse_preferences'::regclass) THEN ALTER TABLE public.trade_item_warehouse_preferences ADD CONSTRAINT trade_item_warehouse_preferences_connection_id_fkey FOREIGN KEY (connection_id) REFERENCES floriday_connections(id) ON DELETE RESTRICT; END IF; END $c$$q$; END IF; END $g$;
10246
11757
  DO $c$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'voorraad_publish_steps_job_id_fkey' AND conrelid = 'public.voorraad_publish_steps'::regclass) THEN ALTER TABLE public.voorraad_publish_steps ADD CONSTRAINT voorraad_publish_steps_job_id_fkey FOREIGN KEY (job_id) REFERENCES voorraad_publish_jobs(id) ON DELETE CASCADE; END IF; END $c$;
11758
+ DO $c$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'voorraad_telmoment_regels_telmoment_id_fkey' AND conrelid = 'public.voorraad_telmoment_regels'::regclass) THEN ALTER TABLE public.voorraad_telmoment_regels ADD CONSTRAINT voorraad_telmoment_regels_telmoment_id_fkey FOREIGN KEY (telmoment_id) REFERENCES voorraad_telmoment(id); END IF; END $c$;
10247
11759
 
10248
11760
  -- indexen
10249
11761
  CREATE INDEX IF NOT EXISTS floriday_continuous_stock_cache_fetched_at_idx ON public.floriday_continuous_stock_cache USING btree (fetched_at);
@@ -10270,6 +11782,7 @@ CREATE INDEX IF NOT EXISTS idx_voorraad_buffer_state_closed_at ON public.voorraa
10270
11782
  CREATE INDEX IF NOT EXISTS idx_voorraad_event_log_processed_at ON public.voorraad_logistics_event_log USING btree (processed_at DESC);
10271
11783
  CREATE INDEX IF NOT EXISTS idx_voorraad_publish_attempts_user_created ON public.voorraad_publish_attempts USING btree (user_id, created_at DESC);
10272
11784
  CREATE INDEX IF NOT EXISTS inventory_stock_snapshots_time_idx ON public.inventory_stock_snapshots USING btree (connection_id, snapshot_at DESC);
11785
+ CREATE INDEX IF NOT EXISTS product_packings_primary_idx ON public.product_packings USING btree (product_id) WHERE is_primary;
10273
11786
  CREATE INDEX IF NOT EXISTS product_packings_product_idx ON public.product_packings USING btree (product_id);
10274
11787
  CREATE INDEX IF NOT EXISTS stock_import_rows_chunk_idx ON public.stock_import_rows USING btree (run_id, chunk_no);
10275
11788
  CREATE INDEX IF NOT EXISTS stock_import_runs_hash_idx ON public.stock_import_runs USING btree (file_hash) WHERE (file_hash IS NOT NULL);
@@ -10277,7 +11790,10 @@ CREATE INDEX IF NOT EXISTS stock_import_runs_user_created_idx ON public.stock_im
10277
11790
  CREATE INDEX IF NOT EXISTS stock_integrity_alerts_open_idx ON public.stock_integrity_alerts USING btree (created_at DESC) WHERE (acknowledged_at IS NULL);
10278
11791
  CREATE INDEX IF NOT EXISTS trade_item_prices_lookup ON public.trade_item_prices USING btree (trade_item_key, price_group_id);
10279
11792
  CREATE INDEX IF NOT EXISTS trade_item_stock_trade_item_key_idx ON public.trade_item_stock USING btree (trade_item_key);
11793
+ CREATE INDEX IF NOT EXISTS voorraad_koppel_log_item_idx ON public.voorraad_koppel_log USING btree (order_item_id);
11794
+ CREATE INDEX IF NOT EXISTS voorraad_koppel_log_run_idx ON public.voorraad_koppel_log USING btree (run_id);
10280
11795
  CREATE UNIQUE INDEX IF NOT EXISTS fcsq_one_open_per_week ON public.floriday_continuous_stock_queue USING btree (connection_id, trade_item_id, COALESCE(target_year, '-1'::integer), COALESCE(target_week, '-1'::integer)) WHERE (status = ANY (ARRAY['pending'::text, 'needs_base_supply'::text, 'skipped_no_price'::text, 'failed'::text]));
11796
+ CREATE UNIQUE INDEX IF NOT EXISTS fust_startmoment_uniek ON public.fust_startmoment USING btree (COALESCE(fust_type_id, '00000000-0000-0000-0000-000000000000'::uuid));
10281
11797
  CREATE UNIQUE INDEX IF NOT EXISTS fust_types_fust_key_active_uidx ON public.fust_types USING btree (fust_key) WHERE ((connection_id IS NULL) AND (merged_into IS NULL));
10282
11798
  CREATE UNIQUE INDEX IF NOT EXISTS fust_types_user_key_global_idx ON public.fust_types USING btree (user_id, fust_key) WHERE (connection_id IS NULL);
10283
11799
  CREATE UNIQUE INDEX IF NOT EXISTS product_packings_one_primary ON public.product_packings USING btree (product_id) WHERE is_primary;
@@ -10328,6 +11844,14 @@ ALTER TABLE public.floriday_vbn_required_characteristics ENABLE ROW LEVEL SECURI
10328
11844
  REVOKE ALL ON public.floriday_vbn_required_characteristics FROM anon;
10329
11845
  GRANT DELETE, INSERT, REFERENCES, SELECT, TRIGGER, TRUNCATE, UPDATE ON public.floriday_vbn_required_characteristics TO authenticated;
10330
11846
  GRANT DELETE, INSERT, REFERENCES, SELECT, TRIGGER, TRUNCATE, UPDATE ON public.floriday_vbn_required_characteristics TO service_role;
11847
+ ALTER TABLE public.fust_startmoment ENABLE ROW LEVEL SECURITY;
11848
+ REVOKE ALL ON public.fust_startmoment FROM anon;
11849
+ GRANT DELETE, INSERT, REFERENCES, SELECT, TRIGGER, TRUNCATE, UPDATE ON public.fust_startmoment TO authenticated;
11850
+ GRANT DELETE, INSERT, REFERENCES, SELECT, TRIGGER, TRUNCATE, UPDATE ON public.fust_startmoment TO service_role;
11851
+ ALTER TABLE public.fust_startmoment_log ENABLE ROW LEVEL SECURITY;
11852
+ REVOKE ALL ON public.fust_startmoment_log FROM anon;
11853
+ GRANT DELETE, INSERT, REFERENCES, SELECT, TRIGGER, TRUNCATE, UPDATE ON public.fust_startmoment_log TO authenticated;
11854
+ GRANT DELETE, INSERT, REFERENCES, SELECT, TRIGGER, TRUNCATE, UPDATE ON public.fust_startmoment_log TO service_role;
10331
11855
  ALTER TABLE public.fust_stock_mutations ENABLE ROW LEVEL SECURITY;
10332
11856
  REVOKE ALL ON public.fust_stock_mutations FROM anon;
10333
11857
  GRANT DELETE, INSERT, REFERENCES, SELECT, TRIGGER, TRUNCATE, UPDATE ON public.fust_stock_mutations TO authenticated;
@@ -10422,6 +11946,22 @@ ALTER TABLE public.voorraad_defaults_state ENABLE ROW LEVEL SECURITY;
10422
11946
  REVOKE ALL ON public.voorraad_defaults_state FROM anon;
10423
11947
  GRANT DELETE, INSERT, REFERENCES, SELECT, TRIGGER, TRUNCATE, UPDATE ON public.voorraad_defaults_state TO authenticated;
10424
11948
  GRANT DELETE, INSERT, REFERENCES, SELECT, TRIGGER, TRUNCATE, UPDATE ON public.voorraad_defaults_state TO service_role;
11949
+ ALTER TABLE public.voorraad_floriday_artikel_wachtrij ENABLE ROW LEVEL SECURITY;
11950
+ REVOKE ALL ON public.voorraad_floriday_artikel_wachtrij FROM anon;
11951
+ GRANT DELETE, INSERT, REFERENCES, SELECT, TRIGGER, TRUNCATE, UPDATE ON public.voorraad_floriday_artikel_wachtrij TO authenticated;
11952
+ GRANT DELETE, INSERT, REFERENCES, SELECT, TRIGGER, TRUNCATE, UPDATE ON public.voorraad_floriday_artikel_wachtrij TO service_role;
11953
+ ALTER TABLE public.voorraad_instellingen ENABLE ROW LEVEL SECURITY;
11954
+ REVOKE ALL ON public.voorraad_instellingen FROM anon;
11955
+ GRANT DELETE, INSERT, REFERENCES, SELECT, TRIGGER, TRUNCATE, UPDATE ON public.voorraad_instellingen TO authenticated;
11956
+ GRANT DELETE, INSERT, REFERENCES, SELECT, TRIGGER, TRUNCATE, UPDATE ON public.voorraad_instellingen TO service_role;
11957
+ ALTER TABLE public.voorraad_koppel_log ENABLE ROW LEVEL SECURITY;
11958
+ REVOKE ALL ON public.voorraad_koppel_log FROM anon;
11959
+ GRANT DELETE, INSERT, REFERENCES, SELECT, TRIGGER, TRUNCATE, UPDATE ON public.voorraad_koppel_log TO authenticated;
11960
+ GRANT DELETE, INSERT, REFERENCES, SELECT, TRIGGER, TRUNCATE, UPDATE ON public.voorraad_koppel_log TO service_role;
11961
+ ALTER TABLE public.voorraad_koppel_twijfel ENABLE ROW LEVEL SECURITY;
11962
+ REVOKE ALL ON public.voorraad_koppel_twijfel FROM anon;
11963
+ GRANT DELETE, INSERT, REFERENCES, SELECT, TRIGGER, TRUNCATE, UPDATE ON public.voorraad_koppel_twijfel TO authenticated;
11964
+ GRANT DELETE, INSERT, REFERENCES, SELECT, TRIGGER, TRUNCATE, UPDATE ON public.voorraad_koppel_twijfel TO service_role;
10425
11965
  ALTER TABLE public.voorraad_load_carrier_types ENABLE ROW LEVEL SECURITY;
10426
11966
  REVOKE ALL ON public.voorraad_load_carrier_types FROM anon;
10427
11967
  GRANT DELETE, INSERT, REFERENCES, SELECT, TRIGGER, TRUNCATE, UPDATE ON public.voorraad_load_carrier_types TO authenticated;
@@ -10457,6 +11997,14 @@ ALTER TABLE public.voorraad_publish_steps ENABLE ROW LEVEL SECURITY;
10457
11997
  REVOKE ALL ON public.voorraad_publish_steps FROM anon;
10458
11998
  GRANT DELETE, INSERT, REFERENCES, SELECT, TRIGGER, TRUNCATE, UPDATE ON public.voorraad_publish_steps TO authenticated;
10459
11999
  GRANT DELETE, INSERT, REFERENCES, SELECT, TRIGGER, TRUNCATE, UPDATE ON public.voorraad_publish_steps TO service_role;
12000
+ ALTER TABLE public.voorraad_telmoment ENABLE ROW LEVEL SECURITY;
12001
+ REVOKE ALL ON public.voorraad_telmoment FROM anon;
12002
+ GRANT DELETE, INSERT, REFERENCES, SELECT, TRIGGER, TRUNCATE, UPDATE ON public.voorraad_telmoment TO authenticated;
12003
+ GRANT DELETE, INSERT, REFERENCES, SELECT, TRIGGER, TRUNCATE, UPDATE ON public.voorraad_telmoment TO service_role;
12004
+ ALTER TABLE public.voorraad_telmoment_regels ENABLE ROW LEVEL SECURITY;
12005
+ REVOKE ALL ON public.voorraad_telmoment_regels FROM anon;
12006
+ GRANT DELETE, INSERT, REFERENCES, SELECT, TRIGGER, TRUNCATE, UPDATE ON public.voorraad_telmoment_regels TO authenticated;
12007
+ GRANT DELETE, INSERT, REFERENCES, SELECT, TRIGGER, TRUNCATE, UPDATE ON public.voorraad_telmoment_regels TO service_role;
10460
12008
  DROP POLICY IF EXISTS "Authenticated can read continuous stock cache" ON public.floriday_continuous_stock_cache;
10461
12009
  CREATE POLICY "Authenticated can read continuous stock cache" ON public.floriday_continuous_stock_cache AS PERMISSIVE FOR SELECT TO authenticated USING (true);
10462
12010
  DROP POLICY IF EXISTS "Own queue rows select" ON public.floriday_continuous_stock_queue;
@@ -10481,6 +12029,10 @@ DROP POLICY IF EXISTS "Authenticated can read vbn required characteristics" ON p
10481
12029
  CREATE POLICY "Authenticated can read vbn required characteristics" ON public.floriday_vbn_required_characteristics AS PERMISSIVE FOR SELECT TO authenticated USING (true);
10482
12030
  DROP POLICY IF EXISTS "Authenticated can update vbn required characteristics" ON public.floriday_vbn_required_characteristics;
10483
12031
  CREATE POLICY "Authenticated can update vbn required characteristics" ON public.floriday_vbn_required_characteristics AS PERMISSIVE FOR UPDATE TO authenticated USING (true) WITH CHECK (true);
12032
+ DROP POLICY IF EXISTS fust_startmoment_lezen ON public.fust_startmoment;
12033
+ CREATE POLICY fust_startmoment_lezen ON public.fust_startmoment AS PERMISSIVE FOR SELECT TO authenticated USING (true);
12034
+ DROP POLICY IF EXISTS fust_startmoment_log_lezen ON public.fust_startmoment_log;
12035
+ CREATE POLICY fust_startmoment_log_lezen ON public.fust_startmoment_log AS PERMISSIVE FOR SELECT TO authenticated USING (true);
10484
12036
  DROP POLICY IF EXISTS "Fust mutations delete" ON public.fust_stock_mutations;
10485
12037
  CREATE POLICY "Fust mutations delete" ON public.fust_stock_mutations AS PERMISSIVE FOR DELETE TO authenticated USING (voorraad_can_write());
10486
12038
  DROP POLICY IF EXISTS "Fust mutations insert" ON public.fust_stock_mutations;
@@ -10575,6 +12127,14 @@ DROP POLICY IF EXISTS "Authenticated can update voorraad custom fields" ON publi
10575
12127
  CREATE POLICY "Authenticated can update voorraad custom fields" ON public.voorraad_custom_fields AS PERMISSIVE FOR UPDATE TO authenticated USING (true) WITH CHECK (true);
10576
12128
  DROP POLICY IF EXISTS "Ingelogd mag standaardstatus lezen" ON public.voorraad_defaults_state;
10577
12129
  CREATE POLICY "Ingelogd mag standaardstatus lezen" ON public.voorraad_defaults_state AS PERMISSIVE FOR SELECT TO authenticated USING (true);
12130
+ DROP POLICY IF EXISTS voorraad_floriday_artikel_wachtrij_lezen ON public.voorraad_floriday_artikel_wachtrij;
12131
+ CREATE POLICY voorraad_floriday_artikel_wachtrij_lezen ON public.voorraad_floriday_artikel_wachtrij AS PERMISSIVE FOR SELECT TO authenticated USING (true);
12132
+ DROP POLICY IF EXISTS voorraad_instellingen_lezen ON public.voorraad_instellingen;
12133
+ CREATE POLICY voorraad_instellingen_lezen ON public.voorraad_instellingen AS PERMISSIVE FOR SELECT TO authenticated USING (true);
12134
+ DROP POLICY IF EXISTS voorraad_koppel_log_lezen ON public.voorraad_koppel_log;
12135
+ CREATE POLICY voorraad_koppel_log_lezen ON public.voorraad_koppel_log AS PERMISSIVE FOR SELECT TO authenticated USING (true);
12136
+ DROP POLICY IF EXISTS voorraad_koppel_twijfel_lezen ON public.voorraad_koppel_twijfel;
12137
+ CREATE POLICY voorraad_koppel_twijfel_lezen ON public.voorraad_koppel_twijfel AS PERMISSIVE FOR SELECT TO authenticated USING (true);
10578
12138
  DROP POLICY IF EXISTS "Ingelogd mag ladingdragers lezen" ON public.voorraad_load_carrier_types;
10579
12139
  CREATE POLICY "Ingelogd mag ladingdragers lezen" ON public.voorraad_load_carrier_types AS PERMISSIVE FOR SELECT TO authenticated USING (true);
10580
12140
  DROP POLICY IF EXISTS "Lokale context lezen" ON public.voorraad_local_context;
@@ -10599,6 +12159,10 @@ DROP POLICY IF EXISTS "Authenticated can update learnings" ON public.voorraad_pu
10599
12159
  CREATE POLICY "Authenticated can update learnings" ON public.voorraad_publish_learnings AS PERMISSIVE FOR UPDATE TO authenticated USING (true) WITH CHECK (true);
10600
12160
  DROP POLICY IF EXISTS "Users manage their own publish steps" ON public.voorraad_publish_steps;
10601
12161
  CREATE POLICY "Users manage their own publish steps" ON public.voorraad_publish_steps AS PERMISSIVE FOR ALL TO authenticated USING ((auth.uid() = user_id)) WITH CHECK ((auth.uid() = user_id));
12162
+ DROP POLICY IF EXISTS voorraad_telmoment_lezen ON public.voorraad_telmoment;
12163
+ CREATE POLICY voorraad_telmoment_lezen ON public.voorraad_telmoment AS PERMISSIVE FOR SELECT TO authenticated USING (true);
12164
+ DROP POLICY IF EXISTS voorraad_telmoment_regels_lezen ON public.voorraad_telmoment_regels;
12165
+ CREATE POLICY voorraad_telmoment_regels_lezen ON public.voorraad_telmoment_regels AS PERMISSIVE FOR SELECT TO authenticated USING (true);
10602
12166
 
10603
12167
  -- triggers
10604
12168
  DO $g$ BEGIN IF to_regclass('public.cart_control_sessions') IS NULL THEN RAISE NOTICE 'Voorraad: cart_control_sessions (andere module) ontbreekt, triggers overgeslagen; draai de schema-update opnieuw na die module'; ELSE EXECUTE $q$CREATE TRIGGER trg_cart_session_consume AFTER UPDATE OF stock_consumed_at ON public.cart_control_sessions FOR EACH ROW EXECUTE FUNCTION trg_cart_session_consume_fn()$q$; END IF; END $g$;
@@ -10677,6 +12241,7 @@ CREATE TRIGGER trg_product_tray_stickers_touch BEFORE UPDATE ON public.product_t
10677
12241
  DO $g$ BEGIN IF to_regclass('public.products') IS NULL THEN RAISE NOTICE 'Voorraad: products (core) ontbreekt, triggers overgeslagen; draai de schema-update opnieuw na die module'; ELSE EXECUTE $q$CREATE TRIGGER trg_floriday_ensure_inventory_settings AFTER INSERT ON public.products FOR EACH ROW EXECUTE FUNCTION floriday_ensure_inventory_settings()$q$; END IF; END $g$;
10678
12242
  DO $g$ BEGIN IF to_regclass('public.products') IS NULL THEN RAISE NOTICE 'Voorraad: products (core) ontbreekt, triggers overgeslagen; draai de schema-update opnieuw na die module'; ELSE EXECUTE $q$CREATE TRIGGER trg_products_autolink BEFORE INSERT OR UPDATE OF product, trade_item_key ON public.products FOR EACH ROW EXECUTE FUNCTION trg_products_autolink_fn()$q$; END IF; END $g$;
10679
12243
  DO $g$ BEGIN IF to_regclass('public.products') IS NULL THEN RAISE NOTICE 'Voorraad: products (core) ontbreekt, triggers overgeslagen; draai de schema-update opnieuw na die module'; ELSE EXECUTE $q$CREATE TRIGGER trg_products_floriday_link_enqueue AFTER UPDATE ON public.products REFERENCING OLD TABLE AS old_rows NEW TABLE AS new_rows FOR EACH STATEMENT EXECUTE FUNCTION trg_products_floriday_link_enqueue()$q$; END IF; END $g$;
12244
+ DO $g$ BEGIN IF to_regclass('public.products') IS NULL THEN RAISE NOTICE 'Voorraad: products (core) ontbreekt, triggers overgeslagen; draai de schema-update opnieuw na die module'; ELSE EXECUTE $q$CREATE TRIGGER trg_products_floriday_velden_aangepast BEFORE UPDATE OF product, image_url, barcode ON public.products FOR EACH ROW EXECUTE FUNCTION trg_products_floriday_velden_aangepast()$q$; END IF; END $g$;
10680
12245
  DO $g$ BEGIN IF to_regclass('public.products') IS NULL THEN RAISE NOTICE 'Voorraad: products (core) ontbreekt, triggers overgeslagen; draai de schema-update opnieuw na die module'; ELSE EXECUTE $q$CREATE TRIGGER trg_products_placeholder_code BEFORE INSERT OR UPDATE OF article_number ON public.products FOR EACH ROW EXECUTE FUNCTION trg_products_placeholder_code()$q$; END IF; END $g$;
10681
12246
  DO $g$ BEGIN IF to_regclass('public.products') IS NULL THEN RAISE NOTICE 'Voorraad: products (core) ontbreekt, triggers overgeslagen; draai de schema-update opnieuw na die module'; ELSE EXECUTE $q$CREATE TRIGGER trg_products_seed_stock AFTER INSERT OR UPDATE OF trade_item_key ON public.products FOR EACH ROW EXECUTE FUNCTION trg_products_seed_stock_fn()$q$; END IF; END $g$;
10682
12247
  DO $g$ BEGIN IF to_regclass('public.products') IS NULL THEN RAISE NOTICE 'Voorraad: products (core) ontbreekt, triggers overgeslagen; draai de schema-update opnieuw na die module'; ELSE EXECUTE $q$CREATE TRIGGER trg_requeue_skipped_no_price AFTER UPDATE OF sale_price, unit ON public.products FOR EACH ROW EXECUTE FUNCTION trg_requeue_skipped_no_price()$q$; END IF; END $g$;