@flowselections/floriday-voorraad 1.0.117 → 1.0.121

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flowselections/floriday-voorraad",
3
- "version": "1.0.117",
3
+ "version": "1.0.121",
4
4
  "sideEffects": false,
5
5
  "type": "module",
6
6
  "main": "./dist-lib/index.js",
package/sql/AGENTS.md CHANGED
@@ -2,3 +2,4 @@
2
2
  - Voorraad never creates or replaces functions owned by other modules (Engine `logistics_*`, Floriday-auth, Verkoop; list in `FOREIGN_FUNCS` of `scripts/generate-voorraad-schema.py`); the schema file contains own objects only, because the schema sync would overwrite another module's version.
3
3
  - Data corrections only via `voorraad_correctie_<nr>(p_uitvoeren default false)` functions: server-only, match on id, insert/update only (never DELETE), hash-check Floriday cache + queue, log every run in `voorraad_correcties`; `voorraad_correctie_status()` tells other modules whether it ran, because loose SQL per Shell is not repeatable.
4
4
  - Fust rows are resolved only via `voorraad_active_fust_id(key, id)` (follows `merged_into`), and an order line books fust once: the ERP line adopts an earlier Floriday-ingest booking and ingest skips lines the ERP already booked, because `fust_key LIMIT 1` hit merged rows and both paths booked the same order.
5
+ - An order line with `order_items.quarantined_at` set counts as cancelled for fust: `sync_order_item_fusten` wants 0 and the update trigger also fires on quarantine on/off, booking the difference against the line's own bookings, because quarantined duplicate orders otherwise kept fust booked.
@@ -1,4 +1,4 @@
1
- -- @flowselections/floriday-voorraad 1.0.112 – volledig schema (idempotent, geen gegevens).
1
+ -- @flowselections/floriday-voorraad 1.0.120 – volledig schema (idempotent, geen gegevens).
2
2
  -- Gegenereerd uit de database met scripts/generate-voorraad-schema.py. Niet met de hand wijzigen.
3
3
  -- Volgorde 20. Ontbrekende tabellen van andere modules worden overgeslagen met een NOTICE; draai opnieuw na die module.
4
4
  -- De statuscontrole van floriday_continuous_stock_queue is van Voorraad en wordt hier opnieuw gemaakt.
@@ -3261,10 +3261,8 @@ BEGIN
3261
3261
  v_packages := CEIL(v_pieces::numeric / v_per_package)::int;
3262
3262
  END IF;
3263
3263
 
3264
- IF v_fust_key IS NOT NULL AND v_packages > 0 THEN
3265
- SELECT id INTO v_fust_id FROM public.fust_types
3266
- WHERE fust_key = v_fust_key AND connection_id IS NULL
3267
- LIMIT 1;
3264
+ IF v_fust_key IS NOT NULL AND v_packages > 0 AND NOT EXISTS (SELECT 1 FROM public.order_fust_bookings b JOIN public.order_items oi ON oi.id = b.order_item_id WHERE oi.source_line_key = r.floriday_id) THEN
3265
+ v_fust_id := public.voorraad_active_fust_id(v_fust_key, NULL);
3268
3266
 
3269
3267
  IF v_fust_id IS NULL THEN
3270
3268
  SELECT user_id INTO v_owner FROM public.fust_types LIMIT 1;
@@ -3289,8 +3287,7 @@ BEGIN
3289
3287
  RETURNING id INTO v_fust_id;
3290
3288
 
3291
3289
  IF v_fust_id IS NULL THEN
3292
- SELECT id INTO v_fust_id FROM public.fust_types
3293
- WHERE fust_key = v_fust_key AND connection_id IS NULL LIMIT 1;
3290
+ v_fust_id := public.voorraad_active_fust_id(v_fust_key, NULL);
3294
3291
  END IF;
3295
3292
  END IF;
3296
3293
  END IF;
@@ -4056,9 +4053,9 @@ CREATE OR REPLACE FUNCTION public.fust_negative_stock_report()
4056
4053
  SET search_path TO 'public'
4057
4054
  AS $function$
4058
4055
  SELECT f.id, f.name, f.fust_key, f.stock, -f.stock,
4059
- (SELECT count(*)::int FROM public.fust_types d WHERE d.fust_key = f.fust_key AND d.id <> f.id)
4056
+ (SELECT count(*)::int FROM public.fust_types d WHERE d.fust_key = f.fust_key AND d.id <> f.id AND d.merged_into IS NULL)
4060
4057
  FROM public.fust_types f
4061
- WHERE f.stock < 0
4058
+ WHERE f.stock < 0 AND f.merged_into IS NULL
4062
4059
  ORDER BY f.stock;
4063
4060
  $function$
4064
4061
  ;
@@ -6331,96 +6328,93 @@ CREATE OR REPLACE FUNCTION public.sync_order_item_fusten(_order_item_id uuid)
6331
6328
  SET search_path TO 'public'
6332
6329
  AS $function$
6333
6330
  DECLARE
6334
- v_item record;
6335
- v_order_number text;
6336
- v_packs jsonb;
6337
- v_pack jsonb;
6338
- v_key text;
6339
- v_units numeric;
6340
- v_needed integer;
6341
- v_fust_id uuid;
6342
- v_prev integer;
6343
- v_delta integer;
6344
- v_wanted jsonb := '{}'::jsonb;
6345
- v_row record;
6346
- v_user uuid;
6331
+ v_item record; v_order_number text; v_packs jsonb; v_pack jsonb; v_key text;
6332
+ v_units numeric; v_needed integer; v_fust_id uuid; v_prev integer; v_delta integer;
6333
+ v_wanted jsonb := '{}'::jsonb; v_row record; v_user uuid; v_ing jsonb;
6347
6334
  BEGIN
6348
6335
  PERFORM public.voorraad_vereis('ingelogd', 'sync_order_item_fusten');
6349
- SELECT oi.*, o.order_number INTO v_item
6336
+ SELECT oi.*, o.order_number, x.quantity_crates, x.packing_source, x.vbn_package_code, x.quarantined_at AS q_at INTO v_item
6350
6337
  FROM logistics_active.order_items oi
6351
6338
  LEFT JOIN public.orders o ON o.id = oi.order_id
6339
+ LEFT JOIN public.order_items x ON x.id = oi.id
6352
6340
  WHERE oi.id = _order_item_id;
6353
6341
 
6354
6342
  v_order_number := COALESCE(v_item.order_number, 'order');
6355
6343
  v_user := COALESCE(auth.uid(), (SELECT user_id FROM public.fust_types WHERE user_id IS NOT NULL LIMIT 1));
6356
6344
 
6357
- IF v_item.id IS NOT NULL AND v_item.product_id IS NOT NULL AND COALESCE(v_item.quantity,0) > 0 THEN
6358
- SELECT COALESCE(
6359
- p.custom_fields->'floriday_payload'->'packingConfigurations',
6360
- p.custom_fields->'packingConfigurations'
6361
- )
6362
- INTO v_packs
6363
- FROM public.products p WHERE p.id = v_item.product_id;
6364
-
6365
- IF v_packs IS NULL THEN
6366
- SELECT c.data->'packingConfigurations' INTO v_packs
6367
- FROM public.floriday_trade_items_cache c
6368
- WHERE c.floriday_id = (SELECT p.trade_item_key FROM public.products p WHERE p.id = v_item.product_id)
6369
- LIMIT 1;
6345
+ IF v_item.id IS NOT NULL AND v_item.q_at IS NULL AND v_item.product_id IS NOT NULL AND COALESCE(v_item.quantity,0) > 0 THEN
6346
+ -- 1. Orderregel zelf: aantal fust, of (Floriday-order) planten per fust + fustcode van de order.
6347
+ IF COALESCE(v_item.quantity_crates, 0) > 0 THEN
6348
+ v_needed := v_item.quantity_crates;
6349
+ ELSIF COALESCE(v_item.plants_per_crate, 0) > 0 THEN
6350
+ v_needed := ceil(v_item.quantity::numeric / v_item.plants_per_crate)::int;
6351
+ END IF;
6352
+ IF COALESCE(v_item.vbn_package_code::text, '') <> '' THEN
6353
+ v_key := 'vbn:' || v_item.vbn_package_code::text;
6370
6354
  END IF;
6371
6355
 
6372
- IF jsonb_typeof(v_packs) = 'array' THEN
6373
- SELECT e INTO v_pack FROM jsonb_array_elements(v_packs) e
6374
- WHERE COALESCE((e->>'isPrimary')::boolean, false) LIMIT 1;
6375
- IF v_pack IS NULL THEN
6376
- SELECT e INTO v_pack FROM jsonb_array_elements(v_packs) e LIMIT 1;
6356
+ -- 2. Anders het artikel (wizard-payload, dan Floriday-cache).
6357
+ IF v_needed IS NULL OR v_key IS NULL THEN
6358
+ SELECT COALESCE(p.custom_fields->'floriday_payload'->'packingConfigurations', p.custom_fields->'packingConfigurations')
6359
+ INTO v_packs FROM public.products p WHERE p.id = v_item.product_id;
6360
+ IF v_packs IS NULL THEN
6361
+ SELECT c.data->'packingConfigurations' INTO v_packs
6362
+ FROM public.floriday_trade_items_cache c
6363
+ WHERE c.floriday_id = (SELECT p.trade_item_key FROM public.products p WHERE p.id = v_item.product_id)
6364
+ LIMIT 1;
6377
6365
  END IF;
6378
-
6379
- IF v_pack IS NOT NULL THEN
6380
- v_units := NULLIF((v_pack->>'unitsPerPackage')::numeric, 0);
6381
- IF v_units IS NOT NULL THEN
6382
- v_needed := ceil(v_item.quantity::numeric / v_units)::int;
6383
-
6384
- IF COALESCE(v_pack->>'source','') = 'SUPPLIER'
6385
- AND COALESCE(v_pack->>'packagingMaterialId','') <> '' THEN
6386
- v_key := 'custom:' || (v_pack->>'packagingMaterialId');
6387
- ELSIF COALESCE(v_pack->>'packagingType','') <> '' THEN
6388
- v_key := 'vbn:' || (v_pack->>'packagingType');
6389
- ELSE
6390
- v_key := NULL;
6366
+ IF jsonb_typeof(v_packs) = 'array' THEN
6367
+ SELECT e INTO v_pack FROM jsonb_array_elements(v_packs) e WHERE COALESCE((e->>'isPrimary')::boolean, false) LIMIT 1;
6368
+ IF v_pack IS NULL THEN SELECT e INTO v_pack FROM jsonb_array_elements(v_packs) e LIMIT 1; END IF;
6369
+ IF v_pack IS NOT NULL THEN
6370
+ IF v_needed IS NULL THEN
6371
+ v_units := NULLIF((v_pack->>'unitsPerPackage')::numeric, 0);
6372
+ IF v_units IS NOT NULL THEN v_needed := ceil(v_item.quantity::numeric / v_units)::int; END IF;
6391
6373
  END IF;
6392
-
6393
- IF v_key IS NOT NULL AND v_needed > 0 THEN
6394
- SELECT id INTO v_fust_id FROM public.fust_types
6395
- WHERE fust_key = v_key AND connection_id IS NULL LIMIT 1;
6396
-
6397
- IF v_fust_id IS NULL THEN
6398
- INSERT INTO public.fust_types (user_id, connection_id, fust_key, name, source, vbn_code, custom_package_id, in_use)
6399
- VALUES (
6400
- v_user, NULL, v_key,
6401
- CASE WHEN v_key LIKE 'vbn:%' THEN 'VBN-fust ' || split_part(v_key, ':', 2)
6402
- ELSE 'Eigen fust ' || left(split_part(v_key, ':', 2), 8) END,
6403
- CASE WHEN v_key LIKE 'vbn:%' THEN 'VBN' ELSE 'SUPPLIER' END,
6404
- CASE WHEN v_key LIKE 'vbn:%' THEN NULLIF(split_part(v_key, ':', 2), '')::int ELSE NULL END,
6405
- CASE WHEN v_key LIKE 'custom:%' THEN split_part(v_key, ':', 2) ELSE NULL END,
6406
- true
6407
- )
6408
- ON CONFLICT (fust_key) DO NOTHING;
6409
- SELECT id INTO v_fust_id FROM public.fust_types WHERE fust_key = v_key LIMIT 1;
6410
- END IF;
6411
-
6412
- IF v_fust_id IS NOT NULL THEN
6413
- v_wanted := v_wanted || jsonb_build_object(v_fust_id::text, v_needed);
6374
+ IF v_key IS NULL THEN
6375
+ IF COALESCE(v_pack->>'source','') = 'SUPPLIER' AND COALESCE(v_pack->>'packagingMaterialId','') <> '' THEN
6376
+ v_key := 'custom:' || (v_pack->>'packagingMaterialId');
6377
+ ELSIF COALESCE(v_pack->>'packagingType','') <> '' THEN
6378
+ v_key := 'vbn:' || (v_pack->>'packagingType');
6414
6379
  END IF;
6415
6380
  END IF;
6416
6381
  END IF;
6417
6382
  END IF;
6418
6383
  END IF;
6384
+
6385
+ IF v_key IS NOT NULL AND COALESCE(v_needed, 0) > 0 THEN
6386
+ v_fust_id := public.voorraad_active_fust_id(v_key, NULL);
6387
+ IF v_fust_id IS NULL THEN
6388
+ INSERT INTO public.fust_types (user_id, connection_id, fust_key, name, source, vbn_code, custom_package_id, in_use)
6389
+ VALUES (v_user, NULL, v_key,
6390
+ CASE WHEN v_key LIKE 'vbn:%' THEN 'VBN-fust ' || split_part(v_key, ':', 2) ELSE 'Eigen fust ' || left(split_part(v_key, ':', 2), 8) END,
6391
+ CASE WHEN v_key LIKE 'vbn:%' THEN 'VBN' ELSE 'SUPPLIER' END,
6392
+ CASE WHEN v_key ~ '^vbn:[0-9]+$' THEN split_part(v_key, ':', 2)::int ELSE NULL END,
6393
+ CASE WHEN v_key LIKE 'custom:%' THEN split_part(v_key, ':', 2) ELSE NULL END,
6394
+ true)
6395
+ ON CONFLICT DO NOTHING;
6396
+ v_fust_id := public.voorraad_active_fust_id(v_key, NULL);
6397
+ END IF;
6398
+ IF v_fust_id IS NOT NULL THEN
6399
+ v_wanted := jsonb_build_object(v_fust_id::text, v_needed);
6400
+ END IF;
6401
+ END IF;
6402
+ END IF;
6403
+
6404
+ -- Floriday-order al afgeboekt bij het binnenhalen: die boeking overnemen (geen tweede mutatie).
6405
+ IF v_item.id IS NOT NULL AND v_item.q_at IS NULL AND v_item.source_line_key IS NOT NULL
6406
+ AND NOT EXISTS (SELECT 1 FROM public.order_fust_bookings b WHERE b.order_item_id = _order_item_id) THEN
6407
+ SELECT i.impact INTO v_ing FROM public.floriday_sales_order_ingest i
6408
+ WHERE i.sales_order_id = v_item.source_line_key AND (i.impact->>'fustTypeId') IS NOT NULL
6409
+ ORDER BY i.order_version DESC LIMIT 1;
6410
+ IF v_ing IS NOT NULL AND COALESCE((v_ing->>'fustPackages')::int, 0) > 0 THEN
6411
+ INSERT INTO public.order_fust_bookings (order_item_id, fust_type_id, quantity)
6412
+ VALUES (_order_item_id, public.voorraad_active_fust_id(NULL, (v_ing->>'fustTypeId')::uuid), (v_ing->>'fustPackages')::int)
6413
+ ON CONFLICT (order_item_id, fust_type_id) DO NOTHING;
6414
+ END IF;
6419
6415
  END IF;
6420
6416
 
6421
- FOR v_row IN
6422
- SELECT b.fust_type_id, b.quantity FROM public.order_fust_bookings b
6423
- WHERE b.order_item_id = _order_item_id
6417
+ 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
6424
6418
  LOOP
6425
6419
  v_prev := v_row.quantity;
6426
6420
  v_needed := COALESCE((v_wanted->>v_row.fust_type_id::text)::int, 0);
@@ -6428,15 +6422,13 @@ BEGIN
6428
6422
  IF v_delta <> 0 THEN
6429
6423
  INSERT INTO public.fust_stock_mutations (user_id, fust_type_id, delta, reason, note)
6430
6424
  VALUES (COALESCE(v_user, (SELECT user_id FROM public.fust_types WHERE id = v_row.fust_type_id)),
6431
- v_row.fust_type_id, v_delta,
6432
- CASE WHEN v_delta > 0 THEN 'sale_cancelled' ELSE 'sale' END,
6433
- 'Order ' || v_order_number);
6425
+ public.voorraad_active_fust_id(NULL, v_row.fust_type_id), v_delta,
6426
+ CASE WHEN v_delta > 0 THEN 'sale_cancelled' ELSE 'sale' END, 'Order ' || v_order_number);
6434
6427
  END IF;
6435
6428
  IF v_needed = 0 THEN
6436
6429
  DELETE FROM public.order_fust_bookings WHERE order_item_id = _order_item_id AND fust_type_id = v_row.fust_type_id;
6437
6430
  ELSE
6438
- UPDATE public.order_fust_bookings SET quantity = v_needed
6439
- WHERE order_item_id = _order_item_id AND fust_type_id = v_row.fust_type_id;
6431
+ UPDATE public.order_fust_bookings SET quantity = v_needed WHERE order_item_id = _order_item_id AND fust_type_id = v_row.fust_type_id;
6440
6432
  END IF;
6441
6433
  v_wanted := v_wanted - v_row.fust_type_id::text;
6442
6434
  END LOOP;
@@ -6446,7 +6438,6 @@ BEGIN
6446
6438
  INSERT INTO public.order_fust_bookings (order_item_id, fust_type_id, quantity)
6447
6439
  VALUES (_order_item_id, v_row.fust_type_id::uuid, v_row.qty)
6448
6440
  ON CONFLICT (order_item_id, fust_type_id) DO UPDATE SET quantity = EXCLUDED.quantity;
6449
-
6450
6441
  INSERT INTO public.fust_stock_mutations (user_id, fust_type_id, delta, reason, note)
6451
6442
  VALUES (COALESCE(v_user, (SELECT user_id FROM public.fust_types WHERE id = v_row.fust_type_id::uuid)),
6452
6443
  v_row.fust_type_id::uuid, -v_row.qty, 'sale', 'Order ' || v_order_number);
@@ -7715,6 +7706,36 @@ REVOKE ALL ON FUNCTION update_updated_at_column() FROM anon;
7715
7706
  REVOKE EXECUTE ON FUNCTION update_updated_at_column() FROM anon;
7716
7707
  GRANT EXECUTE ON FUNCTION update_updated_at_column() TO authenticated;
7717
7708
  GRANT EXECUTE ON FUNCTION update_updated_at_column() TO service_role;
7709
+ CREATE OR REPLACE FUNCTION public.voorraad_active_fust_id(p_fust_key text, p_fust_id uuid)
7710
+ RETURNS uuid
7711
+ LANGUAGE plpgsql
7712
+ STABLE SECURITY DEFINER
7713
+ SET search_path TO 'public'
7714
+ AS $function$
7715
+ -- Actieve fustrij: volg merged_into vanaf een opgeslagen id, anders op fust_key (geen koppeling, niet samengevoegd).
7716
+ DECLARE v uuid; v_next uuid; i int := 0;
7717
+ BEGIN
7718
+ PERFORM public.voorraad_vereis('ingelogd', 'voorraad_active_fust_id');
7719
+ IF p_fust_id IS NOT NULL THEN
7720
+ v := p_fust_id;
7721
+ LOOP
7722
+ SELECT merged_into INTO v_next FROM public.fust_types WHERE id = v;
7723
+ EXIT WHEN v_next IS NULL OR i > 10;
7724
+ v := v_next; i := i + 1;
7725
+ END LOOP;
7726
+ RETURN v;
7727
+ END IF;
7728
+ IF p_fust_key IS NULL THEN RETURN NULL; END IF;
7729
+ SELECT id INTO v FROM public.fust_types
7730
+ WHERE fust_key = p_fust_key AND connection_id IS NULL AND merged_into IS NULL
7731
+ ORDER BY created_at, id LIMIT 1;
7732
+ RETURN v;
7733
+ END $function$
7734
+ ;
7735
+ REVOKE ALL ON FUNCTION voorraad_active_fust_id(text,uuid) FROM PUBLIC;
7736
+ REVOKE ALL ON FUNCTION voorraad_active_fust_id(text,uuid) FROM anon;
7737
+ GRANT EXECUTE ON FUNCTION voorraad_active_fust_id(text,uuid) TO authenticated;
7738
+ GRANT EXECUTE ON FUNCTION voorraad_active_fust_id(text,uuid) TO service_role;
7718
7739
  CREATE OR REPLACE FUNCTION public.voorraad_alert_floriday_not_updated(p_key text, p_qty numeric, p_order_id uuid, p_order_item_id uuid)
7719
7740
  RETURNS void
7720
7741
  LANGUAGE plpgsql
@@ -8125,6 +8146,229 @@ END $function$
8125
8146
  REVOKE ALL ON FUNCTION voorraad_correctie_1112(boolean) FROM PUBLIC;
8126
8147
  REVOKE ALL ON FUNCTION voorraad_correctie_1112(boolean) FROM anon;
8127
8148
  GRANT EXECUTE ON FUNCTION voorraad_correctie_1112(boolean) TO service_role;
8149
+ CREATE OR REPLACE FUNCTION public.voorraad_correctie_1114(p_uitvoeren boolean DEFAULT false)
8150
+ RETURNS jsonb
8151
+ LANGUAGE plpgsql
8152
+ SECURITY DEFINER
8153
+ SET search_path TO 'public'
8154
+ AS $function$
8155
+ DECLARE v_mut jsonb; v_ref jsonb; v_dup jsonb; r record; v_target uuid; v_from uuid;
8156
+ n_mut int := 0; n_dup int := 0; n_ref int := 0; v_skip jsonb := '[]'::jsonb; v_c int;
8157
+ BEGIN
8158
+ PERFORM public.voorraad_vereis('server', 'voorraad_correctie_1114');
8159
+ SELECT COALESCE(jsonb_agg(jsonb_build_object('mutatie_id', m.id, 'tijd', m.created_at, 'delta', m.delta, 'notitie', m.note,
8160
+ 'van', m.fust_type_id, 'naar', public.voorraad_active_fust_id(NULL, m.fust_type_id)) ORDER BY m.created_at), '[]')
8161
+ INTO v_mut FROM public.fust_stock_mutations m JOIN public.fust_types f ON f.id = m.fust_type_id WHERE f.merged_into IS NOT NULL;
8162
+ -- Dubbel: binnenhaal-boeking bestaat als mutatie EN de ERP-orderregel boekte opnieuw.
8163
+ SELECT COALESCE(jsonb_agg(jsonb_build_object('order', o.order_number, 'order_item_id', oi.id,
8164
+ 'erp_fust', b.quantity, 'juist_fust', (i.impact->>'fustPackages')::int)), '[]')
8165
+ INTO v_dup
8166
+ FROM public.order_items oi JOIN public.orders o ON o.id = oi.order_id
8167
+ JOIN public.order_fust_bookings b ON b.order_item_id = oi.id
8168
+ JOIN LATERAL (SELECT x.impact FROM public.floriday_sales_order_ingest x WHERE x.sales_order_id = oi.source_line_key
8169
+ AND COALESCE((x.impact->>'fustPackages')::int,0) > 0 ORDER BY x.order_version DESC LIMIT 1) i ON true
8170
+ WHERE EXISTS (SELECT 1 FROM public.fust_stock_mutations m WHERE m.note LIKE 'Floriday-order ' || oi.source_line_key || '%')
8171
+ AND NOT EXISTS (SELECT 1 FROM public.fust_stock_mutations m WHERE m.note = '[1114] tegenboeking dubbel ' || oi.id::text);
8172
+ SELECT jsonb_build_object(
8173
+ 'products', (SELECT count(*) FROM public.products p JOIN public.fust_types f ON f.id = p.fust_type_id WHERE f.merged_into IS NOT NULL),
8174
+ 'product_packings', (SELECT count(*) FROM public.product_packings p JOIN public.fust_types f ON f.id = p.fust_type_id WHERE f.merged_into IS NOT NULL),
8175
+ 'order_fust_bookings', (SELECT count(*) FROM public.order_fust_bookings p JOIN public.fust_types f ON f.id = p.fust_type_id WHERE f.merged_into IS NOT NULL),
8176
+ 'logistics_product_dimensions', (SELECT count(*) FROM public.logistics_product_dimensions p JOIN public.fust_types f ON f.id = p.fust_type_id WHERE f.merged_into IS NOT NULL)
8177
+ ) INTO v_ref;
8178
+
8179
+ IF NOT p_uitvoeren THEN
8180
+ INSERT INTO public.voorraad_correcties(correctie, uitgevoerd_door, proef, aantallen, hash_ok)
8181
+ VALUES ('voorraad_correctie_1114', COALESCE(auth.uid()::text, session_user::text), true,
8182
+ jsonb_build_object('mutaties_verplaatsen', jsonb_array_length(v_mut), 'dubbele_orders', jsonb_array_length(v_dup)) || v_ref, true);
8183
+ RETURN jsonb_build_object('ok', true, 'proef', true, 'mutaties_verplaatsen', v_mut, 'dubbele_orderboekingen', v_dup, 'verwijzingen_samengevoegd', v_ref);
8184
+ END IF;
8185
+
8186
+ -- a. mutaties op samengevoegde rijen naar de actieve rij (op id)
8187
+ FOR r IN SELECT m.id, m.delta, m.fust_type_id AS van, public.voorraad_active_fust_id(NULL, m.fust_type_id) AS naar
8188
+ FROM public.fust_stock_mutations m JOIN public.fust_types f ON f.id = m.fust_type_id
8189
+ WHERE f.merged_into IS NOT NULL ORDER BY m.id FOR UPDATE OF m
8190
+ LOOP
8191
+ IF r.naar IS NULL OR r.naar = r.van THEN
8192
+ v_skip := v_skip || jsonb_build_object('mutatie_id', r.id, 'reden', 'geen actieve rij'); CONTINUE;
8193
+ END IF;
8194
+ UPDATE public.fust_stock_mutations SET fust_type_id = r.naar WHERE id = r.id AND fust_type_id = r.van;
8195
+ GET DIAGNOSTICS v_c = ROW_COUNT;
8196
+ IF v_c = 1 THEN
8197
+ UPDATE public.fust_types SET stock = stock - r.delta, updated_at = now() WHERE id = r.van;
8198
+ UPDATE public.fust_types SET stock = stock + r.delta, updated_at = now() WHERE id = r.naar;
8199
+ n_mut := n_mut + 1;
8200
+ END IF;
8201
+ END LOOP;
8202
+
8203
+ -- b. dubbele orderboekingen: binnenhaal-boeking blijft, ERP-boeking krijgt tegenboeking
8204
+ FOR r IN SELECT oi.id AS item, o.order_number, b.fust_type_id AS bf, b.quantity AS bq,
8205
+ (i.impact->>'fustPackages')::int AS ip, NULLIF(i.impact->>'fustTypeId','')::uuid AS ifid,
8206
+ (SELECT COALESCE(sum(m.delta),0) FROM public.fust_stock_mutations m WHERE m.note LIKE 'Floriday-order ' || oi.source_line_key || '%') AS ing_sum
8207
+ FROM public.order_items oi JOIN public.orders o ON o.id = oi.order_id
8208
+ JOIN public.order_fust_bookings b ON b.order_item_id = oi.id
8209
+ JOIN LATERAL (SELECT x.impact FROM public.floriday_sales_order_ingest x WHERE x.sales_order_id = oi.source_line_key
8210
+ AND COALESCE((x.impact->>'fustPackages')::int,0) > 0 ORDER BY x.order_version DESC LIMIT 1) i ON true
8211
+ WHERE EXISTS (SELECT 1 FROM public.fust_stock_mutations m WHERE m.note LIKE 'Floriday-order ' || oi.source_line_key || '%')
8212
+ ORDER BY oi.id
8213
+ LOOP
8214
+ IF EXISTS (SELECT 1 FROM public.fust_stock_mutations m WHERE m.note = '[1114] tegenboeking dubbel ' || r.item::text) THEN
8215
+ v_skip := v_skip || jsonb_build_object('order', r.order_number, 'order_item_id', r.item, 'reden', 'al gecorrigeerd'); CONTINUE;
8216
+ END IF;
8217
+ IF r.ing_sum <> -r.ip THEN
8218
+ v_skip := v_skip || jsonb_build_object('order', r.order_number, 'order_item_id', r.item, 'reden', 'binnenhaal-boeking wijkt af', 'mutaties', r.ing_sum, 'verwacht', -r.ip); CONTINUE;
8219
+ END IF;
8220
+ v_target := public.voorraad_active_fust_id(NULL, COALESCE(r.ifid, r.bf));
8221
+ v_from := public.voorraad_active_fust_id(NULL, r.bf);
8222
+ IF v_target IS NULL OR v_from IS NULL THEN
8223
+ v_skip := v_skip || jsonb_build_object('order', r.order_number, 'order_item_id', r.item, 'reden', 'fust onbekend'); CONTINUE;
8224
+ END IF;
8225
+ IF v_target <> r.bf AND EXISTS (SELECT 1 FROM public.order_fust_bookings x WHERE x.order_item_id = r.item AND x.fust_type_id = v_target) THEN
8226
+ v_skip := v_skip || jsonb_build_object('order', r.order_number, 'order_item_id', r.item, 'reden', 'tweede boeking op ander fust'); CONTINUE;
8227
+ END IF;
8228
+ PERFORM 1 FROM public.order_fust_bookings WHERE order_item_id = r.item AND fust_type_id = r.bf FOR UPDATE;
8229
+ INSERT INTO public.fust_stock_mutations (user_id, fust_type_id, delta, reason, note)
8230
+ VALUES ((SELECT user_id FROM public.fust_types WHERE id = v_from), v_from, r.bq, 'sale_cancelled',
8231
+ '[1114] tegenboeking dubbel ' || r.item::text);
8232
+ UPDATE public.order_fust_bookings SET fust_type_id = v_target, quantity = r.ip
8233
+ WHERE order_item_id = r.item AND fust_type_id = r.bf;
8234
+ n_dup := n_dup + 1;
8235
+ END LOOP;
8236
+
8237
+ -- c. verwijzingen naar samengevoegde rijen
8238
+ UPDATE public.products p SET fust_type_id = public.voorraad_active_fust_id(NULL, p.fust_type_id)
8239
+ FROM public.fust_types f WHERE f.id = p.fust_type_id AND f.merged_into IS NOT NULL;
8240
+ GET DIAGNOSTICS v_c = ROW_COUNT; n_ref := n_ref + v_c;
8241
+ UPDATE public.product_packings p SET fust_type_id = public.voorraad_active_fust_id(NULL, p.fust_type_id)
8242
+ FROM public.fust_types f WHERE f.id = p.fust_type_id AND f.merged_into IS NOT NULL;
8243
+ GET DIAGNOSTICS v_c = ROW_COUNT; n_ref := n_ref + v_c;
8244
+ UPDATE public.order_fust_bookings p SET fust_type_id = public.voorraad_active_fust_id(NULL, p.fust_type_id)
8245
+ FROM public.fust_types f WHERE f.id = p.fust_type_id AND f.merged_into IS NOT NULL
8246
+ AND NOT EXISTS (SELECT 1 FROM public.order_fust_bookings x WHERE x.order_item_id = p.order_item_id
8247
+ AND x.fust_type_id = public.voorraad_active_fust_id(NULL, p.fust_type_id));
8248
+ GET DIAGNOSTICS v_c = ROW_COUNT; n_ref := n_ref + v_c;
8249
+
8250
+ INSERT INTO public.voorraad_correcties(correctie, uitgevoerd_door, proef, aantallen, hash_ok)
8251
+ VALUES ('voorraad_correctie_1114', COALESCE(auth.uid()::text, session_user::text), false,
8252
+ jsonb_build_object('mutaties_verplaatst', n_mut, 'tegenboekingen', n_dup, 'verwijzingen', n_ref, 'overgeslagen', v_skip), true);
8253
+ RETURN jsonb_build_object('ok', true, 'proef', false, 'mutaties_verplaatst', n_mut, 'tegenboekingen', n_dup,
8254
+ 'verwijzingen_omgezet', n_ref, 'overgeslagen', v_skip);
8255
+ END;
8256
+ $function$
8257
+ ;
8258
+ REVOKE ALL ON FUNCTION voorraad_correctie_1114(boolean) FROM PUBLIC;
8259
+ REVOKE ALL ON FUNCTION voorraad_correctie_1114(boolean) FROM anon;
8260
+ GRANT EXECUTE ON FUNCTION voorraad_correctie_1114(boolean) TO authenticated;
8261
+ GRANT EXECUTE ON FUNCTION voorraad_correctie_1114(boolean) TO service_role;
8262
+ CREATE OR REPLACE FUNCTION public.voorraad_correctie_1119(p_uitvoeren boolean DEFAULT false)
8263
+ RETURNS jsonb
8264
+ LANGUAGE plpgsql
8265
+ SECURITY DEFINER
8266
+ SET search_path TO 'public'
8267
+ AS $function$
8268
+ DECLARE v_item constant uuid := 'c976982c-1831-4021-b0f5-4e03c7ce8e14'; v_b record; v_need int; v_q int; v_ppc int; v_order text;
8269
+ BEGIN
8270
+ PERFORM public.voorraad_vereis('server', 'voorraad_correctie_1119');
8271
+ IF EXISTS (SELECT 1 FROM public.fust_stock_mutations WHERE note = '[1119] tegenboeking ' || v_item::text) THEN
8272
+ RETURN jsonb_build_object('ok', true, 'al_uitgevoerd', true);
8273
+ END IF;
8274
+ SELECT oi.quantity, oi.plants_per_crate, o.order_number INTO v_q, v_ppc, v_order
8275
+ FROM public.order_items oi JOIN public.orders o ON o.id = oi.order_id WHERE oi.id = v_item;
8276
+ SELECT * INTO v_b FROM public.order_fust_bookings WHERE order_item_id = v_item FOR UPDATE;
8277
+ IF v_b IS NULL OR COALESCE(v_ppc,0) = 0 THEN RETURN jsonb_build_object('ok', false, 'reden', 'regel of boeking niet gevonden'); END IF;
8278
+ v_need := ceil(v_q::numeric / v_ppc)::int;
8279
+ IF NOT p_uitvoeren OR v_b.quantity <> 46 OR v_need <> 23 THEN
8280
+ RETURN jsonb_build_object('ok', v_b.quantity = 46 AND v_need = 23, 'proef', true, 'order', v_order, 'regel', v_item,
8281
+ 'geboekt', v_b.quantity, 'juist', v_need, 'tegenboeking', v_b.quantity - v_need);
8282
+ END IF;
8283
+ INSERT INTO public.fust_stock_mutations (user_id, fust_type_id, delta, reason, note)
8284
+ VALUES ((SELECT user_id FROM public.fust_types WHERE id = v_b.fust_type_id), public.voorraad_active_fust_id(NULL, v_b.fust_type_id),
8285
+ v_b.quantity - v_need, 'sale_cancelled', '[1119] tegenboeking ' || v_item::text);
8286
+ UPDATE public.order_fust_bookings SET quantity = v_need WHERE order_item_id = v_item AND fust_type_id = v_b.fust_type_id;
8287
+ INSERT INTO public.voorraad_correcties(correctie, uitgevoerd_door, proef, aantallen, hash_ok)
8288
+ VALUES ('voorraad_correctie_1119', COALESCE(auth.uid()::text, session_user::text), false,
8289
+ jsonb_build_object('order', v_order, 'regel', v_item, 'van', v_b.quantity, 'naar', v_need), true);
8290
+ RETURN jsonb_build_object('ok', true, 'proef', false, 'order', v_order, 'van', v_b.quantity, 'naar', v_need, 'tegenboeking', v_b.quantity - v_need);
8291
+ END;
8292
+ $function$
8293
+ ;
8294
+ REVOKE ALL ON FUNCTION voorraad_correctie_1119(boolean) FROM PUBLIC;
8295
+ REVOKE ALL ON FUNCTION voorraad_correctie_1119(boolean) FROM anon;
8296
+ GRANT EXECUTE ON FUNCTION voorraad_correctie_1119(boolean) TO service_role;
8297
+ CREATE OR REPLACE FUNCTION public.voorraad_correctie_1120(p_uitvoeren boolean DEFAULT false)
8298
+ RETURNS jsonb
8299
+ LANGUAGE plpgsql
8300
+ SECURITY DEFINER
8301
+ SET search_path TO 'public'
8302
+ AS $function$
8303
+ DECLARE
8304
+ v_plan constant jsonb := '[
8305
+ {"id":"b946f153-4d53-4be1-8a07-41d755a6b62e","verwacht":3,"juist":1,"reden":"HO-2601367: orderregel 14 per fust"},
8306
+ {"id":"5aea4066-a80f-417d-beeb-e5932449e619","verwacht":40,"juist":80,"reden":"FO-2601351: Floriday 6 per fust"},
8307
+ {"id":"bb1dd37a-f559-4e5e-93f1-c86b511d7ef9","verwacht":40,"juist":80,"reden":"FO-2601354: Floriday 6 per fust"},
8308
+ {"id":"adc0f90f-c3e9-49e9-8d7c-106418e36ee5","verwacht":20,"juist":40,"reden":"FO-2601360: Floriday 6 per fust"},
8309
+ {"id":"1dd25df8-26bb-4b47-a4c5-4d17a44ee0c0","verwacht":20,"juist":40,"reden":"FO-2601364: Floriday 6 per fust"},
8310
+ {"id":"565eb0b4-c279-4b83-8829-3409754c2799","verwacht":8,"juist":0,"reden":"FO-2601327: dubbele regel zonder koppeling"},
8311
+ {"id":"bae3ccd8-7d9d-4f85-9d64-630cca7fa991","verwacht":7,"juist":0,"reden":"FO-2601327: dubbele regel zonder koppeling"},
8312
+ {"id":"1f9afe19-c23b-4bc6-945b-51bec8647b51","verwacht":6,"juist":0,"reden":"FO-2601331: dubbele regel zonder koppeling"},
8313
+ {"id":"5bf4d2cc-a9c4-4f39-ae24-5cdc25bf627d","verwacht":16,"juist":0,"reden":"FO-2601331: dubbele regel zonder koppeling"},
8314
+ {"id":"0a4a6857-be44-4776-ada2-8554a7fbd892","verwacht":10,"juist":0,"reden":"FO-2601336: dubbele regel zonder koppeling"},
8315
+ {"id":"361d39b1-0a2f-4c6b-abdf-927e9b93325e","verwacht":9,"juist":0,"reden":"FO-2601340: dubbele regel zonder koppeling"},
8316
+ {"id":"f429a4db-37ce-4e6a-ae9a-f0947a00fd08","verwacht":18,"juist":0,"reden":"FO-2601340: dubbele regel zonder koppeling"},
8317
+ {"id":"4499a830-4d0a-42c7-90d7-8ffedf0d4e98","verwacht":8,"juist":0,"reden":"FO-2601329: dubbele order, quarantaine","quarantaine":true}
8318
+ ]'::jsonb;
8319
+ v_p jsonb; v_b record; v_id uuid; v_order text; v_q timestamptz; v_fust uuid;
8320
+ v_regels jsonb := '[]'::jsonb; v_status text; v_delta int;
8321
+ v_voor jsonb := '{}'::jsonb; v_na jsonb := '{}'::jsonb; v_n int := 0;
8322
+ BEGIN
8323
+ PERFORM public.voorraad_vereis('server', 'voorraad_correctie_1120');
8324
+ FOR v_p IN SELECT * FROM jsonb_array_elements(v_plan) LOOP
8325
+ v_id := (v_p->>'id')::uuid; v_b := NULL; v_order := NULL; v_q := NULL;
8326
+ SELECT o.order_number, oi.quarantined_at INTO v_order, v_q
8327
+ FROM public.order_items oi JOIN public.orders o ON o.id = oi.order_id WHERE oi.id = v_id;
8328
+ IF v_order IS NOT NULL THEN
8329
+ PERFORM pg_advisory_xact_lock(hashtext('voorraad_fust_order:' || v_order));
8330
+ END IF;
8331
+ SELECT * INTO v_b FROM public.order_fust_bookings WHERE order_item_id = v_id ORDER BY id LIMIT 1 FOR UPDATE;
8332
+ v_fust := CASE WHEN v_b.fust_type_id IS NULL THEN NULL ELSE public.voorraad_active_fust_id(NULL, v_b.fust_type_id) END;
8333
+ IF v_order IS NULL THEN v_status := 'overgeslagen: orderregel niet in deze database';
8334
+ ELSIF EXISTS (SELECT 1 FROM public.fust_stock_mutations WHERE note = '[1120] ' || v_id::text) THEN v_status := 'al uitgevoerd';
8335
+ ELSIF COALESCE(v_b.quantity, 0) = (v_p->>'juist')::int THEN v_status := 'al juist (geen boeking)';
8336
+ ELSIF v_b.quantity IS DISTINCT FROM (v_p->>'verwacht')::int THEN v_status := 'overgeslagen: geboekt ' || COALESCE(v_b.quantity::text,'geen') || ', verwacht ' || (v_p->>'verwacht');
8337
+ ELSIF COALESCE((v_p->>'quarantaine')::boolean, false) AND v_q IS NULL THEN v_status := 'overgeslagen: wacht op quarantaine door Verkoop';
8338
+ ELSE v_status := CASE WHEN p_uitvoeren THEN 'uitgevoerd' ELSE 'voorstel' END;
8339
+ END IF;
8340
+ v_delta := COALESCE(v_b.quantity, 0) - (v_p->>'juist')::int;
8341
+ IF v_fust IS NOT NULL AND NOT v_voor ? v_fust::text THEN
8342
+ v_voor := v_voor || jsonb_build_object(v_fust::text, (SELECT jsonb_build_object('naam', name, 'voorraad', stock) FROM public.fust_types WHERE id = v_fust));
8343
+ END IF;
8344
+ IF v_status = 'uitgevoerd' THEN
8345
+ PERFORM 1 FROM public.fust_types WHERE id = v_fust FOR UPDATE;
8346
+ INSERT INTO public.fust_stock_mutations (user_id, fust_type_id, delta, reason, note)
8347
+ VALUES ((SELECT user_id FROM public.fust_types WHERE id = v_fust), v_fust, v_delta,
8348
+ CASE WHEN v_delta > 0 THEN 'sale_cancelled' ELSE 'sale' END, '[1120] ' || v_id::text);
8349
+ UPDATE public.order_fust_bookings SET quantity = (v_p->>'juist')::int
8350
+ WHERE order_item_id = v_id AND fust_type_id = v_b.fust_type_id;
8351
+ v_n := v_n + 1;
8352
+ END IF;
8353
+ v_regels := v_regels || jsonb_build_object('order', v_order, 'regel', v_id, 'reden', v_p->>'reden',
8354
+ 'geboekt', v_b.quantity, 'juist', (v_p->>'juist')::int,
8355
+ 'tegenboeking', CASE WHEN v_status IN ('voorstel','uitgevoerd') THEN v_delta ELSE 0 END, 'status', v_status);
8356
+ END LOOP;
8357
+ SELECT COALESCE(jsonb_object_agg(id::text, jsonb_build_object('naam', name, 'voorraad', stock)), '{}'::jsonb) INTO v_na
8358
+ FROM public.fust_types WHERE v_voor ? id::text;
8359
+ IF p_uitvoeren THEN
8360
+ INSERT INTO public.voorraad_correcties(correctie, uitgevoerd_door, proef, aantallen, hash_ok)
8361
+ VALUES ('voorraad_correctie_1120', COALESCE(auth.uid()::text, session_user::text), false,
8362
+ jsonb_build_object('geboekt', v_n, 'regels', v_regels, 'voor', v_voor, 'na', v_na), true);
8363
+ END IF;
8364
+ RETURN jsonb_build_object('ok', true, 'proef', NOT p_uitvoeren, 'geboekt', v_n,
8365
+ 'regels', v_regels, 'fust_voor', v_voor, 'fust_na', v_na);
8366
+ END;
8367
+ $function$
8368
+ ;
8369
+ REVOKE ALL ON FUNCTION voorraad_correctie_1120(boolean) FROM PUBLIC;
8370
+ REVOKE ALL ON FUNCTION voorraad_correctie_1120(boolean) FROM anon;
8371
+ GRANT EXECUTE ON FUNCTION voorraad_correctie_1120(boolean) TO service_role;
8128
8372
  CREATE OR REPLACE FUNCTION public.voorraad_correctie_status(p_correctie text DEFAULT '1098'::text)
8129
8373
  RETURNS jsonb
8130
8374
  LANGUAGE plpgsql
@@ -9322,7 +9566,8 @@ DO $g$ BEGIN IF to_regclass('public.module_runtime_config') IS NULL THEN RAISE N
9322
9566
  DROP TRIGGER IF EXISTS trg_order_fust_bookings_updated_at ON public.order_fust_bookings;
9323
9567
  CREATE TRIGGER trg_order_fust_bookings_updated_at BEFORE UPDATE ON public.order_fust_bookings FOR EACH ROW EXECUTE FUNCTION set_updated_at_order_fust_bookings();
9324
9568
  DO $g$ BEGIN IF to_regclass('public.order_items') IS NULL THEN RAISE NOTICE 'Voorraad: order_items (Verkoop) ontbreekt, triggers overgeslagen; draai de schema-update opnieuw na die module'; ELSE EXECUTE $q$CREATE TRIGGER trg_order_items_autolink BEFORE INSERT ON public.order_items FOR EACH ROW EXECUTE FUNCTION trg_order_items_autolink_fn()$q$; END IF; END $g$;
9325
- DO $g$ BEGIN IF to_regclass('public.order_items') IS NULL THEN RAISE NOTICE 'Voorraad: order_items (Verkoop) ontbreekt, triggers overgeslagen; draai de schema-update opnieuw na die module'; ELSE EXECUTE $q$CREATE TRIGGER trg_order_items_fusten_aiud AFTER INSERT OR DELETE OR UPDATE OF quantity, product_id ON public.order_items FOR EACH ROW EXECUTE FUNCTION trg_order_items_fusten()$q$; END IF; END $g$;
9569
+ DO $g$ BEGIN IF to_regclass('public.order_items') IS NULL THEN RAISE NOTICE 'Voorraad: order_items (Verkoop) ontbreekt, triggers overgeslagen; draai de schema-update opnieuw na die module'; ELSE EXECUTE $q$CREATE TRIGGER trg_order_items_fusten_aid AFTER INSERT OR DELETE ON public.order_items FOR EACH ROW EXECUTE FUNCTION trg_order_items_fusten()$q$; END IF; END $g$;
9570
+ DO $g$ BEGIN IF to_regclass('public.order_items') IS NULL THEN RAISE NOTICE 'Voorraad: order_items (Verkoop) ontbreekt, triggers overgeslagen; draai de schema-update opnieuw na die module'; ELSE EXECUTE $q$CREATE TRIGGER trg_order_items_fusten_au AFTER UPDATE OF quantity, product_id, plants_per_crate, quantity_crates, vbn_package_code, packing_source, quarantined_at ON public.order_items FOR EACH ROW WHEN (((old.quantity IS DISTINCT FROM new.quantity) OR (old.product_id IS DISTINCT FROM new.product_id) OR (old.plants_per_crate IS DISTINCT FROM new.plants_per_crate) OR (old.quantity_crates IS DISTINCT FROM new.quantity_crates) OR (old.vbn_package_code IS DISTINCT FROM new.vbn_package_code) OR (old.packing_source IS DISTINCT FROM new.packing_source) OR ((old.quarantined_at IS NULL) IS DISTINCT FROM (new.quarantined_at IS NULL)))) EXECUTE FUNCTION trg_order_items_fusten()$q$; END IF; END $g$;
9326
9571
  DO $g$ BEGIN IF to_regclass('public.order_items') IS NULL THEN RAISE NOTICE 'Voorraad: order_items (Verkoop) ontbreekt, triggers overgeslagen; draai de schema-update opnieuw na die module'; ELSE EXECUTE $q$CREATE TRIGGER trg_order_items_reserve AFTER INSERT OR DELETE OR UPDATE OF quantity, product_id, order_id, quarantined_at, source_line_key ON public.order_items FOR EACH ROW EXECUTE FUNCTION trg_order_items_reserve_fn()$q$; END IF; END $g$;
9327
9572
  DO $g$ BEGIN IF to_regclass('public.orders') IS NULL THEN RAISE NOTICE 'Voorraad: orders (Verkoop) ontbreekt, triggers overgeslagen; draai de schema-update opnieuw na die module'; ELSE EXECUTE $q$CREATE TRIGGER trg_orders_cancel_release AFTER UPDATE OF status ON public.orders FOR EACH ROW EXECUTE FUNCTION trg_orders_cancel_release_fn()$q$; END IF; END $g$;
9328
9573
  DO $g$ BEGIN IF to_regclass('public.orders') IS NULL THEN RAISE NOTICE 'Voorraad: orders (Verkoop) ontbreekt, triggers overgeslagen; draai de schema-update opnieuw na die module'; ELSE EXECUTE $q$CREATE TRIGGER trg_orders_consume_on_complete AFTER UPDATE OF status ON public.orders FOR EACH ROW EXECUTE FUNCTION trg_orders_consume_on_complete_fn()$q$; END IF; END $g$;