@flowselections/floriday-voorraad 1.0.92 → 1.0.94

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"floriday-follow.d.ts","sourceRoot":"","sources":["../../src/server-hooks/floriday-follow.ts"],"names":[],"mappings":"AA4CA,4DAA4D;AAC5D,wBAAsB,oBAAoB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,CAE9E"}
1
+ {"version":3,"file":"floriday-follow.d.ts","sourceRoot":"","sources":["../../src/server-hooks/floriday-follow.ts"],"names":[],"mappings":"AAwDA,4DAA4D;AAC5D,wBAAsB,oBAAoB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,CAE9E"}
@@ -23,10 +23,20 @@ async function handle() {
23
23
  const wanted = new Set((follow ?? []).map((r) => String(r.floriday_trade_item_id ?? "").toLowerCase()).filter(Boolean));
24
24
  if (wanted.size === 0)
25
25
  return json({ idle: true });
26
+ // Reden van een wachtende ronde zichtbaar op elk Floriday-leidend artikel (alleen eigen kolommen, match op koppeling).
27
+ const markWaiting = async (reason) => {
28
+ await admin
29
+ .from("floriday_inventory_settings")
30
+ .update({ follow_floriday_at: new Date().toISOString(), follow_floriday_result: reason.slice(0, 300) })
31
+ .eq("connection_id", conn.id)
32
+ .eq("erp_leading", false);
33
+ };
26
34
  const { buildTakeoverPreview } = await import("../lib/floriday-takeover-core.js");
27
35
  const preview = await buildTakeoverPreview(admin, conn);
28
- if (preview.fetchStatus.tradeItems !== "compleet")
36
+ if (preview.fetchStatus.tradeItems !== "compleet") {
37
+ await markWaiting("Wacht: artikelen worden nog uit Floriday opgehaald");
29
38
  return json({ waiting: true, reason: "Artikelen worden nog opgehaald" });
39
+ }
30
40
  const items = preview.rows
31
41
  .filter((r) => r.productId && wanted.has(r.tradeItemId))
32
42
  .map((r) => ({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flowselections/floriday-voorraad",
3
- "version": "1.0.92",
3
+ "version": "1.0.94",
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.91 – volledig schema (idempotent, geen gegevens).
1
+ -- @flowselections/floriday-voorraad 1.0.93 – 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.
@@ -2142,7 +2142,6 @@ AS $function$
2142
2142
  DECLARE a record; n integer := 0; v_ins integer; v_norm text := public.normalize_order_source(p_source);
2143
2143
  BEGIN
2144
2144
  IF p_order_item_id IS NULL THEN RETURN 0; END IF;
2145
- -- Oude route had al afgeboekt (voor de markering bestond)
2146
2145
  IF EXISTS (SELECT 1 FROM public.stock_mutation_log WHERE order_item_id = p_order_item_id AND mutation_type = 'consumption')
2147
2146
  AND NOT EXISTS (SELECT 1 FROM public.stock_mutation_log WHERE order_item_id = p_order_item_id AND mutation_type = 'consumption_done') THEN
2148
2147
  RETURN 0;
@@ -2158,9 +2157,17 @@ BEGIN
2158
2157
  INSERT INTO public.stock_mutation_log(trade_item_key, order_id, order_item_id, order_source, mutation_type, qty_delta, reason, created_by)
2159
2158
  VALUES (a.trade_item_key, p_order_id, p_order_item_id, v_norm, 'consumption_blocked', COALESCE(a.qty,0),
2160
2159
  'toewijzing Engine: ' || COALESCE(a.note, 'fout'), auth.uid());
2160
+ ELSIF COALESCE(a.qty,0) > 0 AND v_norm = 'FO' AND public.voorraad_floriday_leads_key(a.trade_item_key) THEN
2161
+ -- Floriday leidt: Floriday heeft deze verkoop al van het eigen aanbod afgetrokken.
2162
+ INSERT INTO public.stock_mutation_log(trade_item_key, order_id, order_item_id, order_source, mutation_type, qty_delta, reason, created_by)
2163
+ VALUES (a.trade_item_key, p_order_id, p_order_item_id, v_norm, 'consumption_skipped', a.qty,
2164
+ 'Niet afgeboekt: Floriday leidt dit artikel en heeft de verkoop al verwerkt — ' || COALESCE(p_reason,''), auth.uid());
2161
2165
  ELSIF COALESCE(a.qty,0) > 0 THEN
2162
2166
  PERFORM public.apply_stock_consumption(a.trade_item_key, a.qty, p_order_id, p_order_item_id, p_source,
2163
2167
  COALESCE(p_reason,'afgeboekt') || ' [' || a.source || ']');
2168
+ IF v_norm <> 'FO' AND public.voorraad_floriday_leads_key(a.trade_item_key) THEN
2169
+ PERFORM public.voorraad_alert_floriday_not_updated(a.trade_item_key, a.qty, p_order_id, p_order_item_id);
2170
+ END IF;
2164
2171
  n := n + 1;
2165
2172
  END IF;
2166
2173
  END LOOP;
@@ -2987,6 +2994,7 @@ BEGIN
2987
2994
  WHERE t.trade_item_key IN (v_tid, 'product:'||v_pid::text, v_pid::text)
2988
2995
  ORDER BY (t.trade_item_key = v_tid) DESC, t.updated_at DESC NULLS LAST LIMIT 1;
2989
2996
  v_stock := NULLIF(v_it->>'stock','')::numeric;
2997
+ IF v_stock IS NULL THEN v_res := 'Geen Floriday-voorraad gevonden (geen doorlopende voorraad en geen weekaanbod voor deze week)'; END IF;
2990
2998
  IF v_stock IS NOT NULL AND v_stock >= 0 AND v_stock IS DISTINCT FROM v_cur_stock THEN
2991
2999
  IF v_uid IS NULL THEN RAISE EXCEPTION 'Geen eigenaar bij de voorraadsturing'; END IF;
2992
3000
  PERFORM set_config('request.jwt.claims', json_build_object('sub', v_uid, 'role', 'authenticated')::text, true);
@@ -3266,6 +3274,9 @@ BEGIN
3266
3274
  UPDATE public.floriday_inventory_settings s
3267
3275
  SET erp_leading = true, erp_leading_at = now(), updated_by = auth.uid(), updated_at = now()
3268
3276
  WHERE s.connection_id = p_connection_id AND s.product_id = v_pid;
3277
+ -- Floriday-orders van voor de overstap zitten al in de Floriday-stand: nooit nog eens afboeken.
3278
+ PERFORM public.voorraad_mark_floriday_sold_before_switch(v_pid);
3279
+ PERFORM public.recompute_reserved_stock((SELECT COALESCE(NULLIF(btrim(p.floriday_trade_item_id),''), p.trade_item_key) FROM public.products p WHERE p.id = v_pid));
3269
3280
  PERFORM public.floriday_recompute_inventory(p_connection_id, v_pid, true);
3270
3281
  v_n := v_n + 1;
3271
3282
  END LOOP;
@@ -4803,6 +4814,17 @@ BEGIN
4803
4814
  GROUP BY r.order_item_id, r.trade_item_key, r.source, r.group_id;
4804
4815
  DELETE FROM public.logistics_reservation_allocations WHERE computed_at < now() - interval '90 days';
4805
4816
 
4817
+ -- Floriday-leidend: Floriday-orders reserveren niet (Floriday heeft de verkoop al afgetrokken).
4818
+ DELETE FROM _reserved_demand WHERE true;
4819
+ INSERT INTO _reserved_demand
4820
+ SELECT a.trade_item_key, SUM(a.qty)
4821
+ FROM _reserved_alloc a
4822
+ LEFT JOIN logistics_active.order_items oi ON oi.id = a.order_item_id
4823
+ LEFT JOIN public.orders o ON o.id = oi.order_id
4824
+ WHERE NOT (public.normalize_order_source(o.order_source) = 'FO'
4825
+ AND public.voorraad_floriday_leads_key(a.trade_item_key))
4826
+ GROUP BY a.trade_item_key;
4827
+
4806
4828
  PERFORM set_config('stock.change_source', 'RESERVATION_RECOMPUTE', true);
4807
4829
  UPDATE public.trade_item_stock s
4808
4830
  SET reserved_stock = CEIL(COALESCE(d.qty, 0))::integer,
@@ -4934,6 +4956,7 @@ DECLARE
4934
4956
  v_sum integer;
4935
4957
  v_products uuid[];
4936
4958
  v_pid uuid;
4959
+ v_leads boolean;
4937
4960
  BEGIN
4938
4961
  IF p_trade_item_key IS NULL OR btrim(p_trade_item_key) = '' THEN RETURN; END IF;
4939
4962
  IF current_setting('app.recomputing_reserved', true) = 'on' THEN RETURN; END IF;
@@ -4988,6 +5011,7 @@ BEGIN
4988
5011
  RETURN;
4989
5012
  END IF;
4990
5013
 
5014
+ v_leads := public.voorraad_floriday_leads_key(v_key);
4991
5015
  SELECT CEIL(COALESCE(SUM(COALESCE(oi.quantity, 0)), 0))::integer INTO v_sum
4992
5016
  FROM logistics_active.order_items oi
4993
5017
  JOIN public.orders o ON o.id = oi.order_id
@@ -4995,6 +5019,7 @@ BEGIN
4995
5019
  WHERE COALESCE(NULLIF(btrim(COALESCE(p.floriday_trade_item_id, '')), ''),
4996
5020
  NULLIF(btrim(COALESCE(p.trade_item_key, '')), ''), p.id::text) = v_key
4997
5021
  AND o.status = 'Nog te picken' AND NOT public.logistics_reserved_order_excluded(o.id)
5022
+ AND NOT (public.normalize_order_source(o.order_source) = 'FO' AND v_leads)
4998
5023
  AND NOT EXISTS (SELECT 1 FROM public.product_bom_items b WHERE b.parent_product_id = p.id)
4999
5024
  AND NOT EXISTS (SELECT 1 FROM public.product_groups pg
5000
5025
  WHERE pg.is_active = true AND (pg.id = p.id OR pg.head_product_id = p.id))
@@ -7284,6 +7309,30 @@ REVOKE ALL ON FUNCTION update_updated_at_column() FROM PUBLIC;
7284
7309
  GRANT EXECUTE ON FUNCTION update_updated_at_column() TO anon;
7285
7310
  GRANT EXECUTE ON FUNCTION update_updated_at_column() TO authenticated;
7286
7311
  GRANT EXECUTE ON FUNCTION update_updated_at_column() TO service_role;
7312
+ 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)
7313
+ RETURNS void
7314
+ LANGUAGE plpgsql
7315
+ SECURITY DEFINER
7316
+ SET search_path TO 'public'
7317
+ AS $function$
7318
+ DECLARE v_code text; v_name text; v_nr text; v_tx bigint := -(txid_current() + 1125899906842624);
7319
+ BEGIN
7320
+ SELECT p.article_number, p.product INTO v_code, v_name FROM public.products p
7321
+ WHERE lower(p.floriday_trade_item_id) = lower(p_key) OR p.trade_item_key = p_key OR 'product:' || p.id::text = p_key OR p.id::text = p_key
7322
+ LIMIT 1;
7323
+ BEGIN EXECUTE 'SELECT order_number::text FROM public.orders WHERE id = $1' INTO v_nr USING p_order_id;
7324
+ EXCEPTION WHEN others THEN v_nr := NULL; END;
7325
+ INSERT INTO public.stock_integrity_alerts(tx_id, alert_kind, item_count, items)
7326
+ VALUES (v_tx, 'FLORIDAY_NOT_UPDATED', 1, jsonb_build_array(jsonb_build_object(
7327
+ 'trade_item_key', p_key, 'article_code', v_code, 'name', v_name, 'qty', p_qty,
7328
+ 'order_id', p_order_id, 'order_item_id', p_order_item_id, 'order_number', v_nr, 'physical_before', NULL, 'change_source', 'ORDER_DELIVERY')))
7329
+ ON CONFLICT (tx_id) DO UPDATE SET item_count = public.stock_integrity_alerts.item_count + 1,
7330
+ items = public.stock_integrity_alerts.items || EXCLUDED.items, updated_at = now();
7331
+ EXCEPTION WHEN others THEN RAISE WARNING '[floriday-not-updated-alert] %', SQLERRM;
7332
+ END $function$
7333
+ ;
7334
+ REVOKE ALL ON FUNCTION voorraad_alert_floriday_not_updated(text,numeric,uuid,uuid) FROM PUBLIC;
7335
+ GRANT EXECUTE ON FUNCTION voorraad_alert_floriday_not_updated(text,numeric,uuid,uuid) TO service_role;
7287
7336
  CREATE OR REPLACE FUNCTION public.voorraad_can_write()
7288
7337
  RETURNS boolean
7289
7338
  LANGUAGE sql
@@ -7380,6 +7429,23 @@ GRANT EXECUTE ON FUNCTION voorraad_external_fulfilled_ids(jsonb) TO PUBLIC;
7380
7429
  GRANT EXECUTE ON FUNCTION voorraad_external_fulfilled_ids(jsonb) TO anon;
7381
7430
  GRANT EXECUTE ON FUNCTION voorraad_external_fulfilled_ids(jsonb) TO authenticated;
7382
7431
  GRANT EXECUTE ON FUNCTION voorraad_external_fulfilled_ids(jsonb) TO service_role;
7432
+ CREATE OR REPLACE FUNCTION public.voorraad_floriday_leads_key(p_key text)
7433
+ RETURNS boolean
7434
+ LANGUAGE sql
7435
+ STABLE SECURITY DEFINER
7436
+ SET search_path TO 'public'
7437
+ AS $function$
7438
+ SELECT CASE WHEN p_key IS NULL OR btrim(p_key) = '' THEN false ELSE EXISTS (
7439
+ SELECT 1 FROM public.products p
7440
+ JOIN public.floriday_inventory_settings s ON s.product_id = p.id AND s.erp_leading = false
7441
+ WHERE (lower(p.floriday_trade_item_id) = lower(btrim(p_key)) OR p.trade_item_key = btrim(p_key)
7442
+ OR p.id::text = btrim(p_key) OR 'product:' || p.id::text = btrim(p_key))
7443
+ AND s.connection_id = COALESCE((SELECT c.connection_id FROM public.get_active_floriday_context() c), s.connection_id)
7444
+ ) END
7445
+ $function$
7446
+ ;
7447
+ REVOKE ALL ON FUNCTION voorraad_floriday_leads_key(text) FROM PUBLIC;
7448
+ GRANT EXECUTE ON FUNCTION voorraad_floriday_leads_key(text) TO service_role;
7383
7449
  CREATE OR REPLACE FUNCTION public.voorraad_hook_base_url()
7384
7450
  RETURNS text
7385
7451
  LANGUAGE plpgsql
@@ -7485,6 +7551,35 @@ END $function$
7485
7551
  ;
7486
7552
  REVOKE ALL ON FUNCTION voorraad_hook_switch_jobs() FROM PUBLIC;
7487
7553
  GRANT EXECUTE ON FUNCTION voorraad_hook_switch_jobs() TO service_role;
7554
+ CREATE OR REPLACE FUNCTION public.voorraad_mark_floriday_sold_before_switch(p_product_id uuid)
7555
+ RETURNS integer
7556
+ LANGUAGE plpgsql
7557
+ SECURITY DEFINER
7558
+ SET search_path TO 'public'
7559
+ AS $function$
7560
+ DECLARE r record; n int := 0; v_key text;
7561
+ BEGIN
7562
+ SELECT COALESCE(NULLIF(btrim(p.floriday_trade_item_id),''), p.trade_item_key, 'product:'||p.id::text) INTO v_key
7563
+ FROM public.products p WHERE p.id = p_product_id;
7564
+ FOR r IN
7565
+ SELECT oi.id, oi.order_id
7566
+ FROM logistics_active.order_items oi JOIN public.orders o ON o.id = oi.order_id
7567
+ WHERE oi.product_id = p_product_id
7568
+ AND public.normalize_order_source(o.order_source) = 'FO'
7569
+ AND NOT EXISTS (SELECT 1 FROM public.stock_mutation_log m WHERE m.order_item_id = oi.id AND m.mutation_type = 'consumption')
7570
+ LOOP
7571
+ INSERT INTO public.stock_mutation_log(order_id, order_item_id, order_source, mutation_type, qty_delta, reason, created_by)
7572
+ VALUES (r.order_id, r.id, 'FO', 'consumption_done', 0, 'Verwerkt in Floriday-stand', auth.uid())
7573
+ ON CONFLICT (order_item_id) WHERE mutation_type = 'consumption_done' AND order_item_id IS NOT NULL DO NOTHING;
7574
+ INSERT INTO public.stock_mutation_log(trade_item_key, order_id, order_item_id, order_source, mutation_type, qty_delta, reason, created_by)
7575
+ VALUES (v_key, r.order_id, r.id, 'FO', 'consumption', 0, 'Verwerkt in Floriday-stand bij overstap naar eigen voorraad leidend (niet opnieuw afgeboekt)', auth.uid());
7576
+ n := n + 1;
7577
+ END LOOP;
7578
+ RETURN n;
7579
+ END $function$
7580
+ ;
7581
+ REVOKE ALL ON FUNCTION voorraad_mark_floriday_sold_before_switch(uuid) FROM PUBLIC;
7582
+ GRANT EXECUTE ON FUNCTION voorraad_mark_floriday_sold_before_switch(uuid) TO service_role;
7488
7583
  CREATE OR REPLACE FUNCTION public.voorraad_production_enabled()
7489
7584
  RETURNS boolean
7490
7585
  LANGUAGE sql
@@ -7717,7 +7812,7 @@ ALTER TABLE public.stock_import_rows ADD CONSTRAINT stock_import_rows_status_che
7717
7812
  ALTER TABLE public.stock_import_runs DROP CONSTRAINT IF EXISTS stock_import_runs_status_check;
7718
7813
  ALTER TABLE public.stock_import_runs ADD CONSTRAINT stock_import_runs_status_check CHECK ((status = ANY (ARRAY['ontvangen'::text, 'bezig'::text, 'gestopt'::text, 'voltooid'::text, 'ongedaan'::text, 'deels_ongedaan'::text])));
7719
7814
  ALTER TABLE public.stock_integrity_alerts DROP CONSTRAINT IF EXISTS stock_integrity_alerts_kind_check;
7720
- ALTER TABLE public.stock_integrity_alerts ADD CONSTRAINT stock_integrity_alerts_kind_check CHECK ((alert_kind = ANY (ARRAY['ZERO_BATCH'::text, 'NEGATIVE_STOCK'::text])));
7815
+ ALTER TABLE public.stock_integrity_alerts ADD CONSTRAINT stock_integrity_alerts_kind_check CHECK ((alert_kind = ANY (ARRAY['ZERO_BATCH'::text, 'NEGATIVE_STOCK'::text, 'FLORIDAY_NOT_UPDATED'::text])));
7721
7816
  ALTER TABLE public.stock_mutation_log DROP CONSTRAINT IF EXISTS stock_mutation_log_mutation_type_check;
7722
7817
  ALTER TABLE public.stock_mutation_log ADD CONSTRAINT stock_mutation_log_mutation_type_check CHECK ((mutation_type = ANY (ARRAY['reservation'::text, 'reservation_release'::text, 'consumption'::text, 'stock_check'::text, 'reservation_skipped'::text, 'consumption_skipped'::text, 'consumption_blocked'::text, 'production_consume'::text, 'production_output'::text, 'production_reverse'::text, 'manual_correction'::text, 'order_sync'::text, 'import'::text, 'production'::text, 'stock_update'::text, 'reservation_recompute'::text, 'unknown'::text, 'consumption_done'::text, 'new_article'::text])));
7723
7818
  ALTER TABLE public.trade_item_drafts DROP CONSTRAINT IF EXISTS trade_item_drafts_link_status_chk;