@flowselections/floriday-voorraad 1.0.108 → 1.0.110

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.108",
3
+ "version": "1.0.110",
4
4
  "sideEffects": false,
5
5
  "type": "module",
6
6
  "main": "./dist-lib/index.js",
@@ -1,4 +1,4 @@
1
- -- @flowselections/floriday-voorraad 1.0.107 – volledig schema (idempotent, geen gegevens).
1
+ -- @flowselections/floriday-voorraad 1.0.109 – 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.
@@ -2504,6 +2504,7 @@ $function$
2504
2504
  ;
2505
2505
  REVOKE ALL ON FUNCTION ensure_trade_item_stock_row_key(text) FROM PUBLIC;
2506
2506
  REVOKE ALL ON FUNCTION ensure_trade_item_stock_row_key(text) FROM anon;
2507
+ GRANT EXECUTE ON FUNCTION ensure_trade_item_stock_row_key(text) TO authenticated;
2507
2508
  GRANT EXECUTE ON FUNCTION ensure_trade_item_stock_row_key(text) TO service_role;
2508
2509
  CREATE OR REPLACE FUNCTION public.ensure_voorraad_local_context()
2509
2510
  RETURNS uuid
@@ -4279,7 +4280,9 @@ BEGIN
4279
4280
  SELECT l.created_by INTO v_user FROM public.voorraad_local_context l WHERE l.id = v_local;
4280
4281
  END IF;
4281
4282
  RETURN QUERY SELECT v_local, 'lokaal'::text, v_local,
4282
- COALESCE((SELECT t.user_id FROM public.trade_item_stock t WHERE t.connection_id = v_local ORDER BY t.created_at LIMIT 1), auth.uid(), v_user);
4283
+ COALESCE((SELECT t.user_id FROM public.trade_item_stock t WHERE t.connection_id = v_local ORDER BY t.created_at LIMIT 1), auth.uid(), v_user,
4284
+ -- eigenaar: oudste voorraadrij van deze database (één database = één bedrijf), voor serveraanroepen zonder gebruiker
4285
+ (SELECT t.user_id FROM public.trade_item_stock t ORDER BY t.created_at LIMIT 1));
4283
4286
  END $function$
4284
4287
  ;
4285
4288
  REVOKE ALL ON FUNCTION get_stock_context(boolean) FROM PUBLIC;
@@ -8208,7 +8211,7 @@ CREATE OR REPLACE FUNCTION public.voorraad_install_check()
8208
8211
  STABLE SECURITY DEFINER
8209
8212
  SET search_path TO 'public'
8210
8213
  AS $function$
8211
- DECLARE v_f text[] := ARRAY['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'];
8214
+ DECLARE v_f text[] := ARRAY['voorraad_reverse_order_mutations','voorraad_withdraw_stock_row','ensure_voorraad_local_context','get_stock_context','voorraad_stock_context_status','voorraad_fcsq_local_guard','_floriday_queue_cleanup_batch','_migrate_rewrite_stock_key','_stock_import_caller','apply_fust_stock_mutation','apply_stock_consumption','apply_stock_reservation','autolink_product_to_floriday','backfill_open_order_reservations','calculate_floriday_desired_stock','calculate_structure_available','canonicalize_trade_item_stock_key','check_product_structure','claim_floriday_base_supply','claim_floriday_stock_queue','cleanup_floriday_stock_queue','cleanup_voorraad_module_history','complete_floriday_base_supply','complete_floriday_stock_queue','complete_floriday_stock_queue_outcome','consume_bom_for_production','consume_order_item','drain_inventory_recompute_queue','enqueue_floriday_continuous_stock','enqueue_floriday_continuous_stock_after_warehouse_preference','enqueue_structure_recompute','ensure_inventory_recompute_drain','ensure_trade_item_stock_row','ensure_trade_item_stock_row_key','fcsq_block_disabled','fcsq_fallback_supersede','fcsq_wake_base_supply','find_trade_item_key','floriday_adopt_connection','floriday_autoheal_connections','floriday_capture_stock_snapshot','floriday_cleanup_stock_queue_v2','floriday_compact_stock_snapshots','floriday_default_owner','floriday_desired_stock_regression_check','floriday_ensure_inventory_settings','floriday_erp_leading_preview','floriday_fill_publish_horizon','floriday_follow_apply','floriday_ingest_sales_orders','floriday_leading_summary','floriday_make_erp_leading','floriday_norm_offer_forms','floriday_orphan_settings_count','floriday_publish_horizon_weeks','floriday_queue_erp_guard','floriday_queue_first_positive','floriday_recompute_inventory','floriday_relink_products','floriday_requeue_dead_letters','floriday_settings_change_recompute','floriday_settings_change_recompute_stmt','floriday_stock_change_recompute','floriday_stock_queue_supersede','floriday_takeover_apply','floriday_takeover_fetch_state','floriday_takeover_store_forms','floriday_takeover_undo','fust_negative_stock_report','fust_types_duplicate_guard','get_active_floriday_context','get_group_allocation_keys','get_group_head_key','get_product_stock','get_product_structure_totals','get_product_structure_totals_batch','inventory_inline_budget_ok','merge_products','normalize_order_source','process_inventory_recompute_queue','produce_designed_product','product_active_group_id','product_id_from_ref','product_is_set','product_label','product_member_group_ids','product_own_available','product_packings_derive','product_packings_replace','product_packings_sync_from_floriday','product_primary_pieces','product_set_depth_above','product_set_depth_below','product_structure_ancestors','product_structure_max_depth','product_structure_path','product_tray_stickers_touch','product_type','promote_trade_item_key','recompute_all_reserved_stock','recompute_order_status','recompute_reserved_for_order','recompute_reserved_stock','requeue_skipped_stock_rows','reset_stalled_floriday_stock_queue','resolve_group_allocation','resolve_group_member_stock_key','resolve_product_id_for_item','resolve_stock_trade_item_key','reverse_production','run_floriday_base_supply_process','run_floriday_continuous_stock_sync','run_floriday_follow','run_floriday_stock_queue_process','run_floriday_stock_reconcile','run_logistics_tasks_dispatch','self_heal_floriday_links','set_module_runtime_config_updated_at','set_product_bom_tenant','set_product_stock','set_updated_at_order_fust_bookings','stock_import_cleanup','stock_import_create','stock_import_process_chunk','stock_import_stop','stock_import_undo_chunk','sync_order_item_fusten','sync_products_quantity_from_stock','touch_fust_types_updated_at','trade_item_drafts_readonly','trade_item_prices_touch_updated_at','trade_item_stock_duplicates','trg_cart_numbers_sync_order_status_fn','trg_cart_session_consume_fn','trg_check_product_structure','trg_floriday_cache_relink_fn','trg_floriday_trade_items_autolink_fn','trg_order_items_autolink_fn','trg_order_items_fusten','trg_order_items_reserve_fn','trg_orders_cancel_release_fn','trg_orders_consume_on_complete_fn','trg_orders_default_phase_fn','trg_orders_external_fulfilled_fn','trg_orders_recompute_reserved_fn','trg_pgi_assign_sort_order','trg_pgi_order_taken_check','trg_process_recompute_on_commit','trg_product_packings_derive_fn','trg_products_autolink_fn','trg_products_floriday_link_enqueue','trg_products_seed_stock_fn','trg_requeue_no_warehouse_cache','trg_requeue_no_warehouse_connection','trg_requeue_no_warehouse_item_pref','trg_requeue_skipped_no_price','trg_stock_structure_recompute_ins','trg_stock_structure_recompute_upd','trg_structure_change_recompute','trg_trade_item_drafts_autolink_fn','trg_trade_item_stock_auto_bom','trg_trade_item_stock_auto_bom_reverse','trg_trade_item_stock_log_change','trg_trade_item_stock_reserve_fn','trg_trade_item_stock_zero_watch','trg_trade_items_cache_autolink_fn','update_updated_at_column','voorraad_alert_floriday_not_updated','voorraad_can_write','voorraad_cleanup_old_rows','voorraad_consume_external_fulfilled','voorraad_correctie_1098','voorraad_correctie_status','voorraad_external_fulfilled_ids','voorraad_floriday_leads_key','voorraad_fust_dimensions','voorraad_hook_base_url','voorraad_hook_on','voorraad_hook_post','voorraad_hook_switch_jobs','voorraad_install_check','voorraad_is_server','voorraad_mark_floriday_sold_before_switch','voorraad_production_enabled','voorraad_schema_ddl','voorraad_touch_updated_at','voorraad_vereis','voorraad_week_is_past'];
8212
8215
  v_t text[] := ARRAY['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'];
8213
8216
  v_p text[] := ARRAY['Authenticated can add learnings@voorraad_publish_learnings','Authenticated can delete voorraad custom fields@voorraad_custom_fields','Authenticated can insert vbn required characteristics@floriday_vbn_required_characteristics','Authenticated can insert voorraad custom fields@voorraad_custom_fields','Authenticated can read continuous stock cache@floriday_continuous_stock_cache','Authenticated can read learnings@voorraad_publish_learnings','Authenticated can read vbn required characteristics@floriday_vbn_required_characteristics','Authenticated can read voorraad custom fields@voorraad_custom_fields','Authenticated can update learnings@voorraad_publish_learnings','Authenticated can update vbn required characteristics@floriday_vbn_required_characteristics','Authenticated can update voorraad custom fields@voorraad_custom_fields','Authenticated delete trade_item_warehouse_preferences@trade_item_warehouse_preferences','Authenticated insert trade_item_warehouse_preferences@trade_item_warehouse_preferences','Authenticated select trade_item_warehouse_preferences@trade_item_warehouse_preferences','Authenticated update trade_item_warehouse_preferences@trade_item_warehouse_preferences','Eigen momentopnamen lezen@inventory_stock_snapshots','Eigen voorraadinstellingen beheren@floriday_inventory_settings','Eigenaar leest importporties@stock_import_chunks','Eigenaar leest importregels@stock_import_rows','Fust mutations delete@fust_stock_mutations','Fust mutations insert@fust_stock_mutations','Fust mutations read@fust_stock_mutations','Fust mutations update@fust_stock_mutations','Fust types delete@fust_types','Fust types insert@fust_types','Fust types read@fust_types','Fust types update@fust_types','Ingelogde gebruikers kunnen bufferstatus lezen@voorraad_buffer_state','Ingelogde gebruikers kunnen de eventlog lezen@voorraad_logistics_event_log','Ingelogde gebruikers kunnen fustboekingen lezen@order_fust_bookings','Ingelogde gebruikers lezen drempels@inventory_recompute_limits','Ingelogde gebruikers lezen opruimlogboek@structure_cleanup_log','Ingelogde gebruikers lezen opruimruns@floriday_queue_cleanup_runs','Ingelogden beheren traystickers@product_tray_stickers','Ingelogden lezen herberekenwachtrij@inventory_recompute_queue','Ingelogden lezen voorraadmeldingen@stock_integrity_alerts','Ingelogden markeren voorraadmeldingen als gezien@stock_integrity_alerts','Own queue rows select@floriday_continuous_stock_queue','Owner delete trade_item_drafts@trade_item_drafts','Owner insert trade_item_drafts@trade_item_drafts','Owner select trade_item_drafts@trade_item_drafts','Owner update trade_item_drafts@trade_item_drafts','Signed-in users read reconcile runs@floriday_reconcile_runs','Stock delete@trade_item_stock','Stock insert@trade_item_stock','Stock read@trade_item_stock','Stock update@trade_item_stock','Synchronisatieposities lezen@floriday_sync_cursors','Users can insert own publish attempts@voorraad_publish_attempts','Users can read own publish attempts@voorraad_publish_attempts','Users can read own stock mutations@stock_mutation_log','Users manage own import runs@stock_import_runs','Users manage own supply lines@trade_item_supply_lines','Users manage own trade item prices@trade_item_prices','Users manage their own publish jobs@voorraad_publish_jobs','Users manage their own publish steps@voorraad_publish_steps','Verwerkte orders lezen@floriday_sales_order_ingest','Voorraadschrijvers lezen overnameregels@floriday_takeover_items','Voorraadschrijvers lezen overnames@floriday_takeover_runs','authenticated can insert merge log@product_merge_log','authenticated can read merge log@product_merge_log','service role only@voorraad_media_upload_failures','product_packings_read@product_packings','product_packings_write@product_packings'];
8214
8217
  v_open text[] := ARRAY['voorraad_is_server','voorraad_vereis'];
@@ -8319,6 +8322,101 @@ GRANT EXECUTE ON FUNCTION voorraad_production_enabled() TO service_role;
8319
8322
  REVOKE EXECUTE ON FUNCTION voorraad_production_enabled() FROM anon;
8320
8323
  GRANT EXECUTE ON FUNCTION voorraad_production_enabled() TO authenticated;
8321
8324
  GRANT EXECUTE ON FUNCTION voorraad_production_enabled() TO service_role;
8325
+ CREATE OR REPLACE FUNCTION public.voorraad_reverse_order_mutations(p_order_id uuid, p_reason text)
8326
+ RETURNS jsonb
8327
+ LANGUAGE plpgsql
8328
+ SECURITY DEFINER
8329
+ SET search_path TO 'public'
8330
+ AS $function$
8331
+ DECLARE
8332
+ v_ctx uuid;
8333
+ v_r record;
8334
+ v_s record;
8335
+ v_keys text[] := ARRAY[]::text[];
8336
+ v_rows int := 0;
8337
+ v_after int;
8338
+ v_k text;
8339
+ BEGIN
8340
+ PERFORM public.voorraad_vereis('ingelogd', 'voorraad_reverse_order_mutations');
8341
+ IF p_order_id IS NULL THEN
8342
+ RAISE EXCEPTION 'Geen order opgegeven.' USING ERRCODE = '22023';
8343
+ END IF;
8344
+ IF p_reason IS NULL OR btrim(p_reason) = '' THEN
8345
+ RAISE EXCEPTION 'Geef een reden op voor het terugdraaien.' USING ERRCODE = '22023';
8346
+ END IF;
8347
+
8348
+ -- Vaste sleutel per order: twee aanroepen voor dezelfde order wachten op elkaar.
8349
+ PERFORM pg_advisory_xact_lock(hashtext('voorraad_reverse:' || p_order_id::text));
8350
+
8351
+ SELECT c.connection_id INTO v_ctx FROM public.get_stock_context(false) c;
8352
+
8353
+ -- Voorraadrijen altijd in vaste volgorde (id) vergrendelen: geen deadlock tussen orders.
8354
+ PERFORM 1
8355
+ FROM public.trade_item_stock s
8356
+ WHERE (s.connection_id, s.trade_item_key) IN (
8357
+ SELECT COALESCE(l.connection_id, v_ctx), l.trade_item_key
8358
+ FROM public.stock_mutation_log l
8359
+ WHERE l.order_id = p_order_id AND l.trade_item_key IS NOT NULL)
8360
+ ORDER BY s.id
8361
+ FOR UPDATE OF s;
8362
+
8363
+ PERFORM set_config('stock.change_source', 'ORDER_REVERSAL', true);
8364
+
8365
+ -- Netto som van ALLE fysieke mutaties van deze order, inclusief eerdere [reversal]-regels.
8366
+ FOR v_r IN
8367
+ SELECT l.trade_item_key AS k,
8368
+ COALESCE(l.connection_id, v_ctx) AS conn,
8369
+ SUM(COALESCE(l.physical_after - l.physical_before, 0)) AS net
8370
+ FROM public.stock_mutation_log l
8371
+ WHERE l.order_id = p_order_id AND l.trade_item_key IS NOT NULL
8372
+ GROUP BY 1, 2
8373
+ ORDER BY 1, 2
8374
+ LOOP
8375
+ IF v_r.net = 0 OR v_r.conn IS NULL THEN CONTINUE; END IF;
8376
+
8377
+ SELECT s.id, s.physical_stock, s.reserved_stock INTO v_s
8378
+ FROM public.trade_item_stock s
8379
+ WHERE s.connection_id = v_r.conn AND s.trade_item_key = v_r.k;
8380
+ IF NOT FOUND THEN CONTINUE; END IF;
8381
+
8382
+ v_after := COALESCE(v_s.physical_stock, 0) - v_r.net::int;
8383
+
8384
+ -- Logtrigger niet nog een regel laten schrijven: deze functie logt zelf.
8385
+ PERFORM set_config('stock.log_handled',
8386
+ COALESCE(current_setting('stock.log_handled', true), '') || ';' || v_r.conn || '|' || v_r.k || '|' || v_after || '|' || COALESCE(v_s.reserved_stock, 0) || ';', true);
8387
+
8388
+ UPDATE public.trade_item_stock
8389
+ SET physical_stock = v_after
8390
+ WHERE id = v_s.id;
8391
+
8392
+ INSERT INTO public.stock_mutation_log
8393
+ (trade_item_key, connection_id, order_id, mutation_type, qty_delta,
8394
+ physical_before, physical_after, reserved_before, reserved_after, reason, created_by)
8395
+ VALUES
8396
+ (v_r.k, v_r.conn, p_order_id, 'manual_correction', -v_r.net,
8397
+ COALESCE(v_s.physical_stock, 0), v_after, COALESCE(v_s.reserved_stock, 0), COALESCE(v_s.reserved_stock, 0),
8398
+ '[reversal] ' || p_reason, auth.uid());
8399
+
8400
+ v_rows := v_rows + 1;
8401
+ IF NOT v_r.k = ANY (v_keys) THEN v_keys := v_keys || v_r.k; END IF;
8402
+ END LOOP;
8403
+
8404
+ -- Gereserveerd heeft één bron: opnieuw berekenen per geraakte sleutel.
8405
+ FOR v_r IN SELECT DISTINCT l.trade_item_key AS k FROM public.stock_mutation_log l
8406
+ WHERE l.order_id = p_order_id AND l.trade_item_key IS NOT NULL ORDER BY 1
8407
+ LOOP
8408
+ PERFORM public.recompute_reserved_stock(v_r.k);
8409
+ IF NOT v_r.k = ANY (v_keys) THEN v_k := v_r.k; END IF;
8410
+ END LOOP;
8411
+
8412
+ RETURN jsonb_build_object('ok', true, 'keys', to_jsonb(v_keys), 'rows', v_rows);
8413
+ END;
8414
+ $function$
8415
+ ;
8416
+ REVOKE ALL ON FUNCTION voorraad_reverse_order_mutations(uuid,text) FROM PUBLIC;
8417
+ REVOKE ALL ON FUNCTION voorraad_reverse_order_mutations(uuid,text) FROM anon;
8418
+ GRANT EXECUTE ON FUNCTION voorraad_reverse_order_mutations(uuid,text) TO authenticated;
8419
+ GRANT EXECUTE ON FUNCTION voorraad_reverse_order_mutations(uuid,text) TO service_role;
8322
8420
  CREATE OR REPLACE FUNCTION public.voorraad_schema_ddl(p_tables text[], p_functions text[])
8323
8421
  RETURNS text
8324
8422
  LANGUAGE plpgsql
@@ -8506,6 +8604,64 @@ REVOKE ALL ON FUNCTION voorraad_week_is_past(integer,integer) FROM PUBLIC;
8506
8604
  REVOKE ALL ON FUNCTION voorraad_week_is_past(integer,integer) FROM anon;
8507
8605
  GRANT EXECUTE ON FUNCTION voorraad_week_is_past(integer,integer) TO authenticated;
8508
8606
  GRANT EXECUTE ON FUNCTION voorraad_week_is_past(integer,integer) TO service_role;
8607
+ CREATE OR REPLACE FUNCTION public.voorraad_withdraw_stock_row(p_key text, p_reason text)
8608
+ RETURNS jsonb
8609
+ LANGUAGE plpgsql
8610
+ SECURITY DEFINER
8611
+ SET search_path TO 'public'
8612
+ AS $function$
8613
+ DECLARE
8614
+ v_c record;
8615
+ v_s public.trade_item_stock%ROWTYPE;
8616
+ BEGIN
8617
+ PERFORM public.voorraad_vereis('ingelogd', 'voorraad_withdraw_stock_row');
8618
+ IF p_key IS NULL OR btrim(p_key) = '' THEN
8619
+ RAISE EXCEPTION 'Geen artikelsleutel opgegeven.' USING ERRCODE = '22023';
8620
+ END IF;
8621
+ IF p_reason IS NULL OR btrim(p_reason) = '' THEN
8622
+ RAISE EXCEPTION 'Geef een reden op voor het intrekken.' USING ERRCODE = '22023';
8623
+ END IF;
8624
+
8625
+ SELECT * INTO v_c FROM public.get_stock_context(false);
8626
+ IF v_c.connection_id IS NULL THEN
8627
+ RETURN jsonb_build_object('ok', true, 'withdrawn', false, 'reason', 'geen voorraadcontext');
8628
+ END IF;
8629
+
8630
+ SELECT * INTO v_s FROM public.trade_item_stock
8631
+ WHERE connection_id = v_c.connection_id AND trade_item_key = p_key
8632
+ FOR UPDATE;
8633
+ IF NOT FOUND THEN
8634
+ RETURN jsonb_build_object('ok', true, 'withdrawn', false, 'reason', 'geen registratie');
8635
+ END IF;
8636
+
8637
+ IF COALESCE(v_s.physical_stock, 0) <> 0 OR COALESCE(v_s.reserved_stock, 0) <> 0 OR COALESCE(v_s.incoming_stock, 0) <> 0 THEN
8638
+ RAISE EXCEPTION 'Registratie % kan niet worden ingetrokken: er is nog voorraad (liggend %, gereserveerd %, inkomend %).',
8639
+ p_key, COALESCE(v_s.physical_stock, 0), COALESCE(v_s.reserved_stock, 0), COALESCE(v_s.incoming_stock, 0)
8640
+ USING ERRCODE = 'VR005';
8641
+ END IF;
8642
+
8643
+ -- Volledige rij vóór het wissen vastleggen.
8644
+ INSERT INTO public.stock_mutation_log
8645
+ (trade_item_key, connection_id, mutation_type, qty_delta,
8646
+ physical_before, physical_after, reserved_before, reserved_after, reason, created_by)
8647
+ VALUES
8648
+ (p_key, v_c.connection_id, 'manual_correction', 0, 0, 0, 0, 0,
8649
+ '[registration_withdrawn] ' || p_reason
8650
+ || ' | context=' || v_c.context_kind || ':' || v_c.connection_id
8651
+ || ' | bron=' || COALESCE(NULLIF(btrim(current_setting('stock.change_source', true)), ''), 'onbekend')
8652
+ || ' | rij=' || row_to_json(v_s)::text,
8653
+ auth.uid());
8654
+
8655
+ DELETE FROM public.trade_item_stock WHERE id = v_s.id;
8656
+
8657
+ RETURN jsonb_build_object('ok', true, 'withdrawn', true, 'key', p_key, 'context', v_c.context_kind);
8658
+ END;
8659
+ $function$
8660
+ ;
8661
+ REVOKE ALL ON FUNCTION voorraad_withdraw_stock_row(text,text) FROM PUBLIC;
8662
+ REVOKE ALL ON FUNCTION voorraad_withdraw_stock_row(text,text) FROM anon;
8663
+ GRANT EXECUTE ON FUNCTION voorraad_withdraw_stock_row(text,text) TO authenticated;
8664
+ GRANT EXECUTE ON FUNCTION voorraad_withdraw_stock_row(text,text) TO service_role;
8509
8665
 
8510
8666
  -- controles en sleutels
8511
8667
  DO $c$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'floriday_continuous_stock_cache_pkey' AND conrelid = 'public.floriday_continuous_stock_cache'::regclass) THEN ALTER TABLE public.floriday_continuous_stock_cache ADD CONSTRAINT floriday_continuous_stock_cache_pkey PRIMARY KEY (connection_id, trade_item_id); END IF; END $c$;