@flowselections/logistiek-engine 1.0.242 → 1.0.244

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 (63) hide show
  1. package/db/schema/logistiek-engine.sql +9696 -34
  2. package/dist-lib/components/BeheerPage.d.ts.map +1 -1
  3. package/dist-lib/components/BeheerPage.js +2 -1
  4. package/dist-lib/components/DataGereedheidCard.d.ts +1 -0
  5. package/dist-lib/components/DataGereedheidCard.d.ts.map +1 -1
  6. package/dist-lib/components/DataGereedheidCard.js +4 -3
  7. package/dist-lib/components/GeblokkeerdeOrdersCard.d.ts.map +1 -1
  8. package/dist-lib/components/GeblokkeerdeOrdersCard.js +14 -3
  9. package/dist-lib/components/GereedheidCard.d.ts +14 -0
  10. package/dist-lib/components/GereedheidCard.d.ts.map +1 -0
  11. package/dist-lib/components/GereedheidCard.js +60 -0
  12. package/dist-lib/components/LadingdragersPage.d.ts.map +1 -1
  13. package/dist-lib/components/LadingdragersPage.js +5 -1
  14. package/dist-lib/components/MinuutrondeCard.js +1 -1
  15. package/dist-lib/components/StartlijstCard.d.ts.map +1 -1
  16. package/dist-lib/components/StartlijstCard.js +4 -1
  17. package/dist-lib/components/rule-engine/RuleFieldInput.d.ts.map +1 -1
  18. package/dist-lib/components/rule-engine/RuleFieldInput.js +1 -1
  19. package/dist-lib/hooks/useGeblokkeerdeOrders.d.ts +14 -0
  20. package/dist-lib/hooks/useGeblokkeerdeOrders.d.ts.map +1 -1
  21. package/dist-lib/hooks/useGeblokkeerdeOrders.js +40 -3
  22. package/dist-lib/index.d.ts +1 -0
  23. package/dist-lib/index.d.ts.map +1 -1
  24. package/dist-lib/index.js +1 -0
  25. package/dist-lib/integrations/supabase/types.d.ts +3350 -150
  26. package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
  27. package/dist-lib/lib/beladingRegels.d.ts +17 -0
  28. package/dist-lib/lib/beladingRegels.d.ts.map +1 -1
  29. package/dist-lib/lib/beladingRegels.js +29 -3
  30. package/dist-lib/lib/dimensionPlanner.d.ts +10 -0
  31. package/dist-lib/lib/dimensionPlanner.d.ts.map +1 -1
  32. package/dist-lib/lib/dimensionPlanner.js +20 -0
  33. package/dist-lib/lib/engineStatusTeksten.d.ts +1 -1
  34. package/dist-lib/lib/engineStatusTeksten.js +1 -1
  35. package/dist-lib/lib/flowRoute.d.ts.map +1 -1
  36. package/dist-lib/lib/flowRoute.js +2 -2
  37. package/dist-lib/lib/handlers/logistics-tick.server.d.ts +2 -0
  38. package/dist-lib/lib/handlers/logistics-tick.server.d.ts.map +1 -1
  39. package/dist-lib/lib/handlers/logistics-tick.server.js +259 -184
  40. package/dist-lib/lib/loadCarrierPlanning.server.d.ts +21 -0
  41. package/dist-lib/lib/loadCarrierPlanning.server.d.ts.map +1 -1
  42. package/dist-lib/lib/loadCarrierPlanning.server.js +162 -21
  43. package/dist-lib/lib/loadCarrierTypes.d.ts +2 -0
  44. package/dist-lib/lib/loadCarrierTypes.d.ts.map +1 -1
  45. package/dist-lib/lib/logisticsRecalcJob.server.d.ts.map +1 -1
  46. package/dist-lib/lib/logisticsRecalcJob.server.js +8 -1
  47. package/dist-lib/lib/modulePrompts.d.ts.map +1 -1
  48. package/dist-lib/lib/modulePrompts.js +57 -0
  49. package/dist-lib/lib/moduleVersion.d.ts +1 -1
  50. package/dist-lib/lib/moduleVersion.js +1 -1
  51. package/dist-lib/lib/ruleEngineSchema.d.ts +2 -0
  52. package/dist-lib/lib/ruleEngineSchema.d.ts.map +1 -1
  53. package/dist-lib/lib/ruleEngineSchema.js +70 -2
  54. package/dist-lib/lib/scheduleTasks.server.d.ts +3 -1
  55. package/dist-lib/lib/scheduleTasks.server.d.ts.map +1 -1
  56. package/dist-lib/lib/scheduleTasks.server.js +5 -2
  57. package/dist-lib/lib/statusFase.d.ts +11 -0
  58. package/dist-lib/lib/statusFase.d.ts.map +1 -0
  59. package/dist-lib/lib/statusFase.js +58 -0
  60. package/dist-lib/lib/werkers.server.d.ts +17 -0
  61. package/dist-lib/lib/werkers.server.d.ts.map +1 -0
  62. package/dist-lib/lib/werkers.server.js +31 -0
  63. package/package.json +34 -7
@@ -5,6 +5,7 @@
5
5
  // draait de pure planner per ladingdragertype en schrijft het resultaat weg
6
6
  // naar order_carts / order_cart_items.
7
7
  // ============================================================================
8
+ import { isTerminaleFase } from "./statusFase.js";
8
9
  import { planLoadUnits, LOAD_CARRIER_PLANNER_VERSION, bepaalLaagMenging, berekenFysiekMaxLagen, } from "./loadCarrierPlanner.js";
9
10
  import { traceBatch } from "./trace.server.js";
10
11
  import { zonderQuarantaine } from "./quarantaine.js";
@@ -12,7 +13,7 @@ import { zonderStandaardBelading } from "./hoofdverpakking.js";
12
13
  import { MODULE_VERSION } from "./moduleVersion.js";
13
14
  import { fallbackCarrierProfile } from "./loadCarrierTypes.js";
14
15
  import { WAAR_INVULLEN, gewichtOnbekendTekst, ladingdragerNietInBeheerTekst } from "./invoerTeksten.js";
15
- import { planByDimensions, DIMENSION_PLANNER_VERSION, maxFustenPerLaag } from "./dimensionPlanner.js";
16
+ import { planByDimensions, DIMENSION_PLANNER_VERSION, maxFustenPerLaag, maxFustenPerLaagMetOverstek, fustSleutel, STANDAARD_OVERSTEK_CM } from "./dimensionPlanner.js";
16
17
  import { buildDimensionItems, carrierDimensions } from "./dimensionData.server.js";
17
18
  import { zonderExternAfgehandeld } from "./externalFulfillment.js";
18
19
  import { vulBeladingAan, deelUitFloridayVerpakking, plantenVanRegel, beladingBeoordeling, fysiekeBelading, fustPerKarOnmogelijk, karrenOnwaarschijnlijk, verpakkingSleutel, controleerInvariant, VELD_NAAM, } from "./beladingRegels.js";
@@ -95,6 +96,72 @@ export function beladingPerRegelUitOrder(customFields) {
95
96
  }
96
97
  return out;
97
98
  }
99
+ /**
100
+ * Golf 1 / 12: vul alleen LEGE velden van de Floriday-regel aan uit de Floriday-cache.
101
+ * Een lege waarde overschrijft nooit een gevulde.
102
+ */
103
+ export function vulDeelAan(regel, cache) {
104
+ if (!regel && !cache)
105
+ return null;
106
+ const uit = { ...(regel ?? {}) };
107
+ for (const v of ["plants_per_crate", "crates_per_layer", "layers_per_cart"]) {
108
+ if (positive(uit[v]) == null && positive(cache?.[v]) != null)
109
+ uit[v] = positive(cache?.[v]);
110
+ }
111
+ return uit;
112
+ }
113
+ /**
114
+ * Golf 1 / 12: per Floriday-regel de verpakking uit floriday_salesorders_cache (packingConfiguration)
115
+ * gebruiken voor velden die op de order ontbreken. Fout bij lezen = zichtbare waarschuwing, geen stille terugval.
116
+ */
117
+ async function vulRegelVerpakkingUitCache(sb, orderRows, packingByOrderLine, warnings) {
118
+ const nodig = [];
119
+ for (const o of orderRows) {
120
+ const lines = Array.isArray(o.custom_fields?.floriday_lines) ? o.custom_fields.floriday_lines : [];
121
+ for (const raw of lines) {
122
+ const line = (raw ?? {});
123
+ const ti = line.trade_item_id ? String(line.trade_item_id) : null;
124
+ const so = line.sales_order_id ? String(line.sales_order_id) : null;
125
+ if (!ti || !so)
126
+ continue;
127
+ const huidig = packingByOrderLine.get(String(o.id))?.get(ti) ?? null;
128
+ const compleet = huidig && positive(huidig.plants_per_crate) != null && positive(huidig.crates_per_layer) != null && positive(huidig.layers_per_cart) != null;
129
+ if (!compleet)
130
+ nodig.push({ orderId: String(o.id), tradeItemId: ti, salesOrderId: so });
131
+ }
132
+ }
133
+ if (nodig.length === 0)
134
+ return;
135
+ const perSo = new Map();
136
+ const ids = Array.from(new Set(nodig.map((n) => n.salesOrderId)));
137
+ for (let i = 0; i < ids.length; i += 200) {
138
+ const { data, error } = await sb
139
+ .from("floriday_salesorders_cache")
140
+ .select("floriday_id, packing:data->packingConfiguration")
141
+ .in("floriday_id", ids.slice(i, i + 200));
142
+ if (error) {
143
+ const ontbreekt = /does not exist|42P01|PGRST205|schema cache/i.test(`${error.code ?? ""} ${error.message}`);
144
+ if (!ontbreekt)
145
+ warnings.push(`Floriday-verpakking per regel kon niet uit de Floriday-gegevens worden gelezen: ${error.message}`);
146
+ return;
147
+ }
148
+ for (const r of (Array.isArray(data) ? data : [])) {
149
+ const d = deelUitFloridayVerpakking(r.packing);
150
+ if (d)
151
+ perSo.set(String(r.floriday_id), d);
152
+ }
153
+ }
154
+ for (const n of nodig) {
155
+ const cache = perSo.get(n.salesOrderId);
156
+ if (!cache)
157
+ continue;
158
+ const map = packingByOrderLine.get(n.orderId) ?? new Map();
159
+ const nieuw = vulDeelAan(map.get(n.tradeItemId) ?? null, cache);
160
+ if (nieuw)
161
+ map.set(n.tradeItemId, nieuw);
162
+ packingByOrderLine.set(n.orderId, map);
163
+ }
164
+ }
98
165
  /** Waarden van de orderregel zelf (per veld; 0 of leeg = onbekend). */
99
166
  function deelVanRij(row) {
100
167
  if (!row)
@@ -335,6 +402,23 @@ export async function expandOrderItemsForPlanner(sb, rows, options = {}) {
335
402
  }
336
403
  return out;
337
404
  }
405
+ /** Golf 1 / 11: bevestigde aantallen per laag (sleutel product|fust|ladingdrager). */
406
+ async function laagBevestigingen(sb, productIds) {
407
+ const uit = new Map();
408
+ for (let s = 0; s < productIds.length; s += 500) {
409
+ const { data, error } = await sb
410
+ .from("logistics_laag_bevestigingen")
411
+ .select("product_id, fust_sleutel, ladingdrager_code, fusten_per_laag")
412
+ .in("product_id", productIds.slice(s, s + 500));
413
+ if (error)
414
+ throw new Error(`Indeling geblokkeerd: bevestigingen van fusten per laag konden niet worden gelezen (${error.message}).`);
415
+ for (const r of (Array.isArray(data) ? data : [])) {
416
+ const k = `${r.product_id}|${r.fust_sleutel}|${r.ladingdrager_code}`;
417
+ uit.set(k, Math.max(uit.get(k) ?? 0, Number(r.fusten_per_laag) || 0));
418
+ }
419
+ }
420
+ return uit;
421
+ }
338
422
  async function bevestigdeSleutels(sb, orderIds) {
339
423
  const set = new Set();
340
424
  if (orderIds.length === 0)
@@ -349,7 +433,7 @@ async function bevestigdeSleutels(sb, orderIds) {
349
433
  set.add(`${r.order_id}|${r.verpakking_sleutel}`);
350
434
  return set;
351
435
  }
352
- async function beladingOnwaarschijnlijk(sb, inputs, rawRows, profile, profielPerRegel) {
436
+ async function beladingOnwaarschijnlijk(sb, inputs, rawRows, profile, profielPerRegel, blokkeerFactor = null) {
353
437
  // 1.0.198: fustmaten en planthoogte (inclusief pot) uit één bron: logistics_product_maten
354
438
  // (Voorraad als die functies bestaan, anders Floricode → Floriday → handmatig). Nooit gegokt.
355
439
  const productIds = Array.from(new Set(inputs.map((i) => i.product_id).filter((x) => !!x)));
@@ -382,20 +466,22 @@ async function beladingOnwaarschijnlijk(sb, inputs, rawRows, profile, profielPer
382
466
  const codes = Array.from(new Set([profile.code, ...Array.from(profielPerRegel?.values() ?? []).map((p) => p.code)]));
383
467
  const drager = new Map();
384
468
  {
385
- const { data } = await sb
386
- .from("load_carrier_types")
387
- .select("code, name, layer_length_cm, layer_width_cm, max_height_cm, max_crates_per_layer")
388
- .in("code", codes);
469
+ // Golf 1 / 11: alle kolommen (ook overstek_cm); ontbreekt het overstek, dan de standaard van 2 cm.
470
+ const { data } = await sb.from("load_carrier_types").select("*").in("code", codes);
389
471
  for (const d of (Array.isArray(data) ? data : [])) {
472
+ const o = Number(d.overstek_cm);
390
473
  drager.set(String(d.code), {
391
474
  naam: String(d.name ?? d.code),
392
475
  l: getal(d.layer_length_cm),
393
476
  b: getal(d.layer_width_cm),
394
477
  h: getal(d.max_height_cm),
395
478
  maxPerLaag: getal(d.max_crates_per_layer),
479
+ overstek: d.overstek_cm != null && Number.isFinite(o) && o >= 0 ? o : STANDAARD_OVERSTEK_CM,
396
480
  });
397
481
  }
398
482
  }
483
+ const bevestigdPerLaag = await laagBevestigingen(sb, productIds);
484
+ const vragen = [];
399
485
  const kandidaten = [];
400
486
  const onmogelijk = [];
401
487
  const waarschuwingen = [];
@@ -416,6 +502,8 @@ async function beladingOnwaarschijnlijk(sb, inputs, rawRows, profile, profielPer
416
502
  }
417
503
  const d = drager.get(prof.code);
418
504
  const mt = i.product_id ? maten.get(String(i.product_id)) : undefined;
505
+ const fs = fustSleutel(mt?.length_mm, mt?.width_mm);
506
+ const bevestigd = i.product_id && fs ? bevestigdPerLaag.get(`${i.product_id}|${fs}|${prof.code}`) ?? null : null;
419
507
  const fysiek = fysiekeBelading({
420
508
  product: i.product_name,
421
509
  bron: i.packing_authoritative ? "Floriday-verpakking" : "product",
@@ -424,13 +512,24 @@ async function beladingOnwaarschijnlijk(sb, inputs, rawRows, profile, profielPer
424
512
  fust_hoogte_cm: mt?.height_mm ? mt.height_mm / 10 : null,
425
513
  plant_hoogte_cm: mt?.plant ?? null,
426
514
  plant_hoogte_bron: mt?.plantBron ?? null,
427
- layerspace_max: maxFustenPerLaag(d?.l, d?.b, mt?.length_mm ? mt.length_mm / 10 : null, mt?.width_mm ? mt.width_mm / 10 : null),
515
+ layerspace_max: maxFustenPerLaagMetOverstek(d?.l, d?.b, mt?.length_mm ? mt.length_mm / 10 : null, mt?.width_mm ? mt.width_mm / 10 : null, d?.overstek ?? STANDAARD_OVERSTEK_CM),
428
516
  layerspace_strikt: maxFustenPerLaag(d?.l, d?.b, mt?.length_mm ? mt.length_mm / 10 : null, mt?.width_mm ? mt.width_mm / 10 : null, 0),
429
517
  ladingdrager: d?.naam ?? prof.name,
430
518
  ladingdrager_hoogte_cm: d?.h ?? null,
431
519
  max_crates_per_layer: d?.maxPerLaag ?? (prof.max_crates_per_layer ?? null),
432
520
  standaard_lagen: prof.layers_per_carrier,
521
+ blokkeer_factor: blokkeerFactor,
522
+ laag_bevestigd: bevestigd,
433
523
  });
524
+ for (const v of fysiek.vragen) {
525
+ if (!i.product_id || !fs)
526
+ continue;
527
+ vragen.push({
528
+ order_id: i.order_id ?? null, product_id: String(i.product_id), product: i.product_name,
529
+ fust_sleutel: fs, ladingdrager_code: prof.code, ladingdrager: d?.naam ?? prof.name,
530
+ opgegeven: v.opgegeven, berekend: v.berekend, tekst: `${i.product_name}: ${v.tekst}`,
531
+ });
532
+ }
434
533
  // 1.0.213: fysieke grens aan de lagen per kar voor de planner (alleen met bekende maten).
435
534
  i.fysiek_max_lagen = berekenFysiekMaxLagen(d?.h ?? null, mt?.height_mm ? mt.height_mm / 10 : null, mt?.plant ?? null);
436
535
  for (const w of fysiek.waarschuwen) {
@@ -449,9 +548,9 @@ async function beladingOnwaarschijnlijk(sb, inputs, rawRows, profile, profielPer
449
548
  });
450
549
  }
451
550
  if (kandidaten.length === 0)
452
- return { blokkeren: [], onmogelijk, waarschuwingen };
551
+ return { blokkeren: [], onmogelijk, waarschuwingen, vragen };
453
552
  const ok = await bevestigdeSleutels(sb, Array.from(new Set(kandidaten.map((k) => k.order_id).filter((x) => !!x))));
454
- return { blokkeren: kandidaten.filter((k) => !ok.has(`${k.order_id}|${k.sleutel}`)), onmogelijk, waarschuwingen };
553
+ return { blokkeren: kandidaten.filter((k) => !ok.has(`${k.order_id}|${k.sleutel}`)), onmogelijk, waarschuwingen, vragen };
455
554
  }
456
555
  function getal(v) {
457
556
  const n = Number(v);
@@ -566,6 +665,14 @@ async function bepaalLadingdragers(sb, inputs, rawRows, profiles, handmatig, sta
566
665
  function profileFor(profiles, code) {
567
666
  return profiles.find((p) => p.code === code) ?? fallbackCarrierProfile(code);
568
667
  }
668
+ /** Golf 1 / 11: blokkeergrens uit de werkwijze, tussen 1 en 5 (standaard 1,5). Zelfde als public.logistics_laag_blokkeer_factor. */
669
+ export function leesBlokkeerFactor(k) {
670
+ const raw = k?.laag_blokkeer_factor;
671
+ const n = typeof raw === "string" ? Number(raw.replace(",", ".")) : Number(raw);
672
+ if (raw == null || raw === "" || !Number.isFinite(n))
673
+ return 1.5;
674
+ return Math.min(5, Math.max(1, n));
675
+ }
569
676
  const numOrNull = (v) => {
570
677
  const n = typeof v === "string" ? Number(v) : v;
571
678
  return Number.isFinite(n) && n > 0 ? Math.round(n) : null;
@@ -595,6 +702,7 @@ export async function loadBeladingRules(sb) {
595
702
  layoutMethod: k.cart_layout_method === "afmetingen" ? "afmetingen" : "belading",
596
703
  aanvullenUitLadingdrager: k.aanvullen_uit_ladingdrager === true,
597
704
  gewichtVerplicht: k.gewicht_verplicht === true,
705
+ laagBlokkeerFactor: leesBlokkeerFactor(k),
598
706
  };
599
707
  }
600
708
  /**
@@ -819,15 +927,6 @@ export function dimensionCartsToPlannedUnits(carts) {
819
927
  * opgeblazen aantallen zonder echte grote orders te blokkeren.
820
928
  */
821
929
  const MAX_CARTS_PER_BUFFER = 1000;
822
- /** Statussen waarbij een order niet meer ingedeeld hoeft te worden. */
823
- const TERMINALE_STATUSSEN = new Set([
824
- "verzonden",
825
- "afgehandeld",
826
- "geannuleerd",
827
- "cancelled",
828
- "geleverd",
829
- "afgerond",
830
- ]);
831
930
  /** Reservering of al afgehandelde order: niet plannen, ook niet blokkeren. */
832
931
  function isReservationOrTerminal(order) {
833
932
  if (!order)
@@ -837,8 +936,8 @@ function isReservationOrTerminal(order) {
837
936
  // 1.0.190 (F): geannuleerde orders horen nooit in een buffer of op een kar.
838
937
  if (order.cancelled_at)
839
938
  return true;
840
- const status = String(order.status ?? "").trim().toLowerCase();
841
- return TERMINALE_STATUSSEN.has(status);
939
+ // Golf 1: vaste fase uit de statuslijst (zelfde indeling als logistics_status_fase).
940
+ return isTerminaleFase(order.status);
842
941
  }
843
942
  /**
844
943
  * Duidelijk teveel identieke bestelregels wijst op een synchronisatie die
@@ -1226,6 +1325,8 @@ async function replanBufferUnlocked(sb, bufferId, options = {}) {
1226
1325
  if (perRegel.size > 0)
1227
1326
  packingByOrderLine.set(String(o.id), perRegel);
1228
1327
  }
1328
+ // Golf 1 / 12: ontbrekende velden per Floriday-regel eerst uit de Floriday-cache (nooit gevuld overschrijven).
1329
+ await vulRegelVerpakkingUitCache(sb, orderRows, packingByOrderLine, warnings);
1229
1330
  const sources = new Map();
1230
1331
  const setComponents = new Map();
1231
1332
  const expandNotes = [];
@@ -1630,7 +1731,7 @@ async function replanBufferUnlocked(sb, bufferId, options = {}) {
1630
1731
  // 1.0.179 (punt 3): plausibiliteit, alle ordersoorten. Eén bevestiging per order en verpakking.
1631
1732
  // 1.0.213: ook in droge modus rekenen (alleen lezen); schrijven alleen buiten droge modus.
1632
1733
  {
1633
- const oordeel = await beladingOnwaarschijnlijk(sb, inputs, rawRows, guardProfile, profielPerRegel);
1734
+ const oordeel = await beladingOnwaarschijnlijk(sb, inputs, rawRows, guardProfile, profielPerRegel, rules?.laagBlokkeerFactor ?? 1.5);
1634
1735
  const onwaarschijnlijk = oordeel.blokkeren;
1635
1736
  // 1.0.197: fysiek onmogelijk = altijd blokkeren, niet te bevestigen; alleen gegevens aanpassen helpt.
1636
1737
  if (oordeel.onmogelijk.length > 0) {
@@ -1671,6 +1772,46 @@ async function replanBufferUnlocked(sb, bufferId, options = {}) {
1671
1772
  warnings.push(w.tekst);
1672
1773
  if (!options.dryRun)
1673
1774
  await bewaarBeladingWaarschuwingen(sb, orderIds, beladingWaarschuwingen, warnings);
1775
+ // Golf 1 / 11: wijkt de bron af van de berekening (binnen de blokkeergrens), dan eerst één vraag
1776
+ // per product + fust + ladingdrager: "Floriday zegt 6 per laag, de berekening 5. Klopt 6?".
1777
+ if (oordeel.vragen.length > 0) {
1778
+ const uniek = new Map();
1779
+ for (const v of oordeel.vragen)
1780
+ uniek.set(`${v.product_id}|${v.fust_sleutel}|${v.ladingdrager_code}`, v);
1781
+ const lijst = Array.from(uniek.values());
1782
+ const reason = `Vraag over de belading: ${lijst.slice(0, 10).map((v) => v.tekst).join(" | ")}` +
1783
+ (onwaarschijnlijk.length > 0
1784
+ ? ` | Belading onwaarschijnlijk: ${onwaarschijnlijk.slice(0, 10).map((o) => `${o.product}: ${o.redenen.join("; ")}`).join(" | ")}`
1785
+ : "") +
1786
+ ". Bevestig dit bij Vastgelopen orders; daarna wordt de order vanzelf ingedeeld.";
1787
+ const { error: blockError } = options.dryRun ? { error: null } : await sb.rpc("logistics_plan_block", {
1788
+ p_order_ids: orderIds,
1789
+ p_reason: reason,
1790
+ p_details: {
1791
+ reason_code: "belading_onwaarschijnlijk",
1792
+ soort: "laag_vraag",
1793
+ bevestigbaar: true,
1794
+ bufferId,
1795
+ path: "typescript",
1796
+ laag_vragen: oordeel.vragen.slice(0, 50),
1797
+ bevestigen: onwaarschijnlijk.slice(0, 50),
1798
+ },
1799
+ });
1800
+ if (blockError)
1801
+ warnings.push(`Blokkade kon niet worden vastgelegd: ${blockError.message}`);
1802
+ return {
1803
+ ok: false,
1804
+ bufferId,
1805
+ orderIds,
1806
+ cartsCreated: 0,
1807
+ replacedManualCount: 0,
1808
+ usedFloridayPacking: false,
1809
+ warnings: [...warnings, reason],
1810
+ skipped: "blocked",
1811
+ blockedReason: reason,
1812
+ beladingSources,
1813
+ };
1814
+ }
1674
1815
  if (onwaarschijnlijk.length > 0) {
1675
1816
  const reason = `Belading onwaarschijnlijk: ${onwaarschijnlijk.slice(0, 10).map((o) => `${o.product}: ${o.redenen.join("; ")}`).join(" | ")}. ` +
1676
1817
  "Kantoor moet dit bevestigen voordat er wordt ingedeeld.";
@@ -5,6 +5,8 @@ export interface LoadCarrierTypeRow extends CarrierProfile {
5
5
  layer_length_cm: number;
6
6
  layer_width_cm: number;
7
7
  max_height_cm: number | null;
8
+ /** Golf 1 / 11: overstek in cm bij het berekenen van fusten per laag (standaard 2). */
9
+ overstek_cm?: number | null;
8
10
  max_orders: number | null;
9
11
  max_customers: number | null;
10
12
  is_active: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"loadCarrierTypes.d.ts","sourceRoot":"","sources":["../../src/lib/loadCarrierTypes.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAE1E,MAAM,MAAM,eAAe,GACvB,SAAS,GACT,kBAAkB,GAClB,mBAAmB,GACnB,QAAQ,GACR,aAAa,GACb,OAAO,CAAC;AAEZ,MAAM,WAAW,kBAAmB,SAAQ,cAAc;IACxD,EAAE,EAAE,MAAM,CAAC;IACX,eAAe,EAAE,MAAM,CAAC;IACxB,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,EAAE,OAAO,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,eAAO,MAAM,qBAAqB,EAAE,MAAM,CAAC,aAAa,EAAE,MAAM,CAK/D,CAAC;AAEF,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAOjD,CAAC;AAEF,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAEpE;AAED,kEAAkE;AAClE,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,CAmBnE;AAED;;;GAGG;AACH,eAAO,MAAM,uBAAuB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAO1D,CAAC;AAEF,iFAAiF;AACjF,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAG/G"}
1
+ {"version":3,"file":"loadCarrierTypes.d.ts","sourceRoot":"","sources":["../../src/lib/loadCarrierTypes.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAE1E,MAAM,MAAM,eAAe,GACvB,SAAS,GACT,kBAAkB,GAClB,mBAAmB,GACnB,QAAQ,GACR,aAAa,GACb,OAAO,CAAC;AAEZ,MAAM,WAAW,kBAAmB,SAAQ,cAAc;IACxD,EAAE,EAAE,MAAM,CAAC;IACX,eAAe,EAAE,MAAM,CAAC;IACxB,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,uFAAuF;IACvF,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,EAAE,OAAO,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,eAAO,MAAM,qBAAqB,EAAE,MAAM,CAAC,aAAa,EAAE,MAAM,CAK/D,CAAC;AAEF,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAOjD,CAAC;AAEF,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAEpE;AAED,kEAAkE;AAClE,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,CAmBnE;AAED;;;GAGG;AACH,eAAO,MAAM,uBAAuB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAO1D,CAAC;AAEF,iFAAiF;AACjF,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAG/G"}
@@ -1 +1 @@
1
- {"version":3,"file":"logisticsRecalcJob.server.d.ts","sourceRoot":"","sources":["../../src/lib/logisticsRecalcJob.server.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAM5D,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,SAAS,GAAG,MAAM,GAAG,OAAO,CAAC;IACrC,KAAK,EAAE,SAAS,GAAG,QAAQ,GAAG,UAAU,CAAC;IACzC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,KAAK,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC5D,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,wBAAsB,aAAa,CAAC,EAAE,EAAE,cAAc,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CAGjF;AAED,wBAAsB,YAAY,CAAC,EAAE,EAAE,cAAc,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CAOhF;AAED,8DAA8D;AAC9D,wBAAsB,QAAQ,CAC5B,EAAE,EAAE,cAAc,EAClB,SAAS,EAAE,MAAM,GAAG,IAAI,EACxB,UAAU,EAAE,MAAM,GAAG,IAAI,GACxB,OAAO,CAAC,SAAS,CAAC,CAsBpB;AAED,6EAA6E;AAC7E,wBAAsB,OAAO,CAAC,EAAE,EAAE,cAAc,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CA2D3E;AAED,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAIlE,iFAAiF;AACjF,wBAAsB,aAAa,CAAC,EAAE,EAAE,cAAc,EAAE,IAAI,SAAK;UAiBpC,MAAM;QAAM,OAAO;YAAU,MAAM;KAO/D"}
1
+ {"version":3,"file":"logisticsRecalcJob.server.d.ts","sourceRoot":"","sources":["../../src/lib/logisticsRecalcJob.server.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAM5D,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,SAAS,GAAG,MAAM,GAAG,OAAO,CAAC;IACrC,KAAK,EAAE,SAAS,GAAG,QAAQ,GAAG,UAAU,CAAC;IACzC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,KAAK,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC5D,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,wBAAsB,aAAa,CAAC,EAAE,EAAE,cAAc,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CAGjF;AAED,wBAAsB,YAAY,CAAC,EAAE,EAAE,cAAc,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CAOhF;AAED,8DAA8D;AAC9D,wBAAsB,QAAQ,CAC5B,EAAE,EAAE,cAAc,EAClB,SAAS,EAAE,MAAM,GAAG,IAAI,EACxB,UAAU,EAAE,MAAM,GAAG,IAAI,GACxB,OAAO,CAAC,SAAS,CAAC,CAsBpB;AAED,6EAA6E;AAC7E,wBAAsB,OAAO,CAAC,EAAE,EAAE,cAAc,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CA2D3E;AAED,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAIlE,iFAAiF;AACjF,wBAAsB,aAAa,CAAC,EAAE,EAAE,cAAc,EAAE,IAAI,SAAK;UAiBpC,MAAM;QAAM,OAAO;YAAU,MAAM;KAc/D"}
@@ -124,8 +124,15 @@ export async function runFinalSteps(sb, days = 21) {
124
124
  },
125
125
  ];
126
126
  const steps = [];
127
- steps.push(await scheduleTasksPerOrder(sb));
127
+ const { werkersActief } = await import("./werkers.server.js");
128
+ const werkers = await werkersActief(sb);
129
+ steps.push(await scheduleTasksPerOrder(sb, Date.now, werkers.taken ? { totaalBudgetMs: 0 } : {}));
128
130
  for (const call of calls) {
131
+ // De pickwachtrij-werker houdt de pickwachtrij al elke minuut bij.
132
+ if (werkers.pickwachtrij && call.fn === "logistics_sync_pick_queue") {
133
+ steps.push({ step: call.step, ok: true });
134
+ continue;
135
+ }
129
136
  const { error } = await sb.rpc(call.fn, call.args);
130
137
  steps.push({ step: call.step, ok: !error, ...(error ? { error: voorraadFoutTekst(error) } : {}) });
131
138
  }
@@ -1 +1 @@
1
- {"version":3,"file":"modulePrompts.d.ts","sourceRoot":"","sources":["../../src/lib/modulePrompts.ts"],"names":[],"mappings":"AAWA;;;;GAIG;AACH,eAAO,MAAM,oBAAoB;;;;;;;;;;;;CAKvB,CAAC;AAEX,MAAM,MAAM,eAAe,GAAG,MAAM,OAAO,oBAAoB,CAAC;AAKhE,MAAM,WAAW,oBAAoB;IACnC,6DAA6D;IAC7D,MAAM,EAAE,MAAM,CAAC;IACf,mCAAmC;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,yDAAyD;IACzD,IAAI,EAAE,MAAM,CAAC;IACb,6CAA6C;IAC7C,OAAO,EAAE,MAAM,CAAC;IAChB,qEAAqE;IACrE,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,iFAAiF;IACjF,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,+BAA+B;IAC/B,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,eAAe,CAAC;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,oBAAoB,EAAE,CAAC;CACnC;AAsgND,eAAO,MAAM,cAAc,EAAE,YAAY,EAUL,CAAC;AAErC,gEAAgE;AAChE,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,YAAY,EACpB,QAAQ,EAAE,oBAAoB,GAC7B,MAAM,CAmBR;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CAE9D;AAED,oEAAoE;AACpE,wBAAgB,qBAAqB,IAAI,MAAM,CAiB9C;AAED,wBAAgB,oBAAoB,CAClC,SAAS,EAAE,eAAe,EAC1B,cAAc,CAAC,EAAE,MAAM,GACtB,MAAM,CAIR"}
1
+ {"version":3,"file":"modulePrompts.d.ts","sourceRoot":"","sources":["../../src/lib/modulePrompts.ts"],"names":[],"mappings":"AAWA;;;;GAIG;AACH,eAAO,MAAM,oBAAoB;;;;;;;;;;;;CAKvB,CAAC;AAEX,MAAM,MAAM,eAAe,GAAG,MAAM,OAAO,oBAAoB,CAAC;AAKhE,MAAM,WAAW,oBAAoB;IACnC,6DAA6D;IAC7D,MAAM,EAAE,MAAM,CAAC;IACf,mCAAmC;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,yDAAyD;IACzD,IAAI,EAAE,MAAM,CAAC;IACb,6CAA6C;IAC7C,OAAO,EAAE,MAAM,CAAC;IAChB,qEAAqE;IACrE,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,iFAAiF;IACjF,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,+BAA+B;IAC/B,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,eAAe,CAAC;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,oBAAoB,EAAE,CAAC;CACnC;AAikND,eAAO,MAAM,cAAc,EAAE,YAAY,EAUL,CAAC;AAErC,gEAAgE;AAChE,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,YAAY,EACpB,QAAQ,EAAE,oBAAoB,GAC7B,MAAM,CAmBR;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CAE9D;AAED,oEAAoE;AACpE,wBAAgB,qBAAqB,IAAI,MAAM,CAiB9C;AAED,wBAAgB,oBAAoB,CAClC,SAAS,EAAE,eAAe,EAC1B,cAAc,CAAC,EAAE,MAAM,GACtB,MAAM,CAIR"}
@@ -4743,6 +4743,63 @@ CONTROLELIJST
4743
4743
  [ ] De knop staat alleen bij karren met een al geprinte klantsticker.
4744
4744
  [ ] Een verkeerd station of een verkeerde kar geeft een duidelijke melding.
4745
4745
  [ ] Een herprint van de pakbon print geen stickers opnieuw.
4746
+ [ ] Meld terug met je pakketversie.`,
4747
+ },
4748
+ {
4749
+ number: 50,
4750
+ title: "Vooruit printen: vraag bij een deelkar",
4751
+ date: "2026-11-11",
4752
+ summary: "Bij een bewust gekozen volgende pickdag is een deelkar direct printbaar; het pickscherm stelt eerst één vraag.",
4753
+ body: `WAAROM
4754
+ Via Meer → Volgende dag kiest de teamleider bewust een pickdag vooruit. Een kar
4755
+ die nog niet vol is, wachtte dan toch op mogelijke aanvulling ("Deelkar wacht
4756
+ tot 12:15 …"). De Engine laat zo'n kar nu direct printen. Het pickscherm moet
4757
+ alleen nog één vraag stellen.
4758
+
4759
+ WAT DE ENGINE NU LEVERT (pick_queue.payload per kar)
4760
+ - printbaar: true bij een vooruit gekozen pickdag, ook in latere rondes.
4761
+ - vooruitGekozen: de gekozen pickdag (JJJJ-MM-DD), of leeg.
4762
+ - bevestigVraag: "Deze kar is nog niet vol. Toch nu printen?" als de kar nog
4763
+ niet vol is en zonder die keuze nog had moeten wachten; anders leeg.
4764
+ - logistics_cart_release_reason(kar) geeft bij een vooruit gekozen pickdag geen
4765
+ wachtreden meer.
4766
+
4767
+ WAT JIJ BOUWT
4768
+ - Staat bevestigVraag gevuld: toon bij Printen eerst precies die vraag met de
4769
+ knoppen "Toch printen" en "Annuleren". Alleen na "Toch printen" printen.
4770
+ - Is bevestigVraag leeg: gewoon printen zoals nu.
4771
+ - Reken niet zelf of een kar vol is en zet printbaar nooit zelf terug.
4772
+
4773
+ CONTROLELIJST
4774
+ [ ] Een halfvolle kar op een vooruit gekozen pickdag is printbaar na "Toch printen".
4775
+ [ ] Na "Annuleren" wordt er niets geprint.
4776
+ [ ] Een volle kar print zonder vraag.
4777
+ [ ] Meld terug met je pakketversie.`,
4778
+ },
4779
+ {
4780
+ number: 51,
4781
+ title: "Pickhistorie in kleine porties opruimen",
4782
+ date: "2026-11-11",
4783
+ summary: "pick_cleanup_history duurt 21–26 s; ruim per ronde in porties op en lever het opruimen van de achterstand als handmatige stap.",
4784
+ body: `WAAROM
4785
+ De taak pick-history-cleanup (pick_cleanup_history) duurt 21–26 s, boven het
4786
+ budget van 20 s. De pickhistorie is in dev ±5,0 GB (±665.000 rijen). De tabel
4787
+ en de taak zijn van Pickstations; de Engine past ze niet aan.
4788
+
4789
+ WAT JIJ BOUWT
4790
+ - pick_cleanup_history ruimt per ronde hooguit 5.000 rijen op of stopt na 10 s,
4791
+ wat eerst komt. De volgende ronde gaat verder.
4792
+ - Een index (IF NOT EXISTS) op de kolom waarop wordt opgeruimd (de datum),
4793
+ zodat elke portie snel is.
4794
+ - Lever als handmatige SQL-stap (niet in een migratie):
4795
+ 1. het eenmalig opruimen van de oude achterstand, in porties;
4796
+ 2. apart: VACUUM FULL op de pickhistorie, met de uitleg dat dit de tabel kort
4797
+ blokkeert en alleen buiten werktijd mag.
4798
+ - Fouten loggen; nooit stil overslaan.
4799
+
4800
+ CONTROLELIJST
4801
+ [ ] Elke ronde van pick-history-cleanup duurt minder dan 10 s.
4802
+ [ ] De achterstand wordt over meerdere rondes kleiner.
4746
4803
  [ ] Meld terug met je pakketversie.`,
4747
4804
  },
4748
4805
  ],
@@ -1,2 +1,2 @@
1
- export declare const MODULE_VERSION = "1.0.242";
1
+ export declare const MODULE_VERSION = "1.0.244";
2
2
  //# sourceMappingURL=moduleVersion.d.ts.map
@@ -1,2 +1,2 @@
1
1
  // Automatisch gegenereerd door prebuild:lib; niet handmatig aanpassen.
2
- export const MODULE_VERSION = "1.0.242";
2
+ export const MODULE_VERSION = "1.0.244";
@@ -20,6 +20,8 @@ export interface RuleField {
20
20
  options?: FieldOption[];
21
21
  min?: number;
22
22
  max?: number;
23
+ /** Stapgrootte bij type "number" (bijv. 0.1 voor een factor). */
24
+ step?: number;
23
25
  unit?: string;
24
26
  default?: unknown;
25
27
  /** Alleen bij type "layout": welke documenttypes uit de Layout maker gekozen mogen worden. */
@@ -1 +1 @@
1
- {"version":3,"file":"ruleEngineSchema.d.ts","sourceRoot":"","sources":["../../src/lib/ruleEngineSchema.ts"],"names":[],"mappings":"AAQA,MAAM,MAAM,SAAS,GACjB,QAAQ,GACR,QAAQ,GACR,QAAQ,GACR,MAAM,GACN,UAAU,GACV,MAAM,GACN,UAAU,GACV,OAAO,GACP,MAAM,GACN,QAAQ,GACR,aAAa,GACb,SAAS,GACT,SAAS,GACT,UAAU,CAAC;AAMf,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,uDAAuD;IACvD,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,EAAE,EAAE,OAAO,EAAE,CAAC;IACd,sDAAsD;IACtD,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,SAAS;IACxB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,SAAS,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,WAAW,EAAE,CAAC;IACxB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,8FAA8F;IAC9F,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,wFAAwF;IACxF,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,6DAA6D;IAC7D,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,mEAAmE;IACnE,QAAQ,CAAC,EAAE;QACT,MAAM,EAAE,MAAM,CAAC;QACf,WAAW,EAAE,MAAM,CAAC;QACpB,YAAY,EAAE,MAAM,CAAC;KACtB,CAAC;IACF,wGAAwG;IACxG,SAAS,CAAC,EAAE,cAAc,GAAG,cAAc,EAAE,CAAC;CAC/C;AAGD,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,SAAS,EAAE,CAAC;CACrB;AAED,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,SAAS,EAAE,CAAC;IACpB,kBAAkB,EAAE,OAAO,CAAC;CAC7B;AAID,eAAO,MAAM,gBAAgB,EAAE,WAAW,EAQzC,CAAC;AAEF,eAAO,MAAM,mBAAmB,EAAE,WAAW,EAK5C,CAAC;AAEF,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,UAAU,EAAE,aAAa,EAAE,CAAC;CAC7B;AAED,MAAM,MAAM,kBAAkB,GAAG,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;AAI9D,eAAO,MAAM,UAAU,EAAE,QAAQ,EAooChC,CAAC;AAKF,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,QAAQ,GAAG,YAAY,CAgBhE;AAED,wBAAgB,eAAe,IAAI,kBAAkB,CAMpD;AAED,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,OAAO,GAAG,kBAAkB,CAalE;AAED,wBAAgB,YAAY,CAC1B,KAAK,EAAE,SAAS,EAChB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,WAAW,CAAC,EAAE,kBAAkB,WAcjC;AAYD,wBAAgB,WAAW,CAAC,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,OAAO,UAE3D;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,UAEhD;AAED,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,MAAM,UAEnD"}
1
+ {"version":3,"file":"ruleEngineSchema.d.ts","sourceRoot":"","sources":["../../src/lib/ruleEngineSchema.ts"],"names":[],"mappings":"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"}
@@ -114,6 +114,18 @@ export const RULE_STEPS = [
114
114
  { value: "order_type", label: "Zelfde ordertype" },
115
115
  ],
116
116
  },
117
+ {
118
+ key: "buffer_sluiten",
119
+ label: "Buffer en karindeling sluiten",
120
+ type: "select",
121
+ default: "verzendmoment",
122
+ options: [
123
+ { value: "verzendmoment", label: "Op het verzendmoment" },
124
+ { value: "werkuren", label: "Vaste werkuren v\u00f3\u00f3r ophalen" },
125
+ ],
126
+ description: "Op het verzendmoment: tot de transportaanmelding verstuurd wordt, kunnen nog orders en niet-geprinte karren mee. Geprinte of gepickte karren liggen altijd vast. Zonder aanmeldmoment (bijvoorbeeld eigen vervoer) gelden de werkuren.",
127
+ dependsOn: { key: "buffer_enabled", in: [true] },
128
+ },
117
129
  {
118
130
  key: "buffer_close_reference",
119
131
  label: "Buffer sluit ten opzichte van",
@@ -204,6 +216,20 @@ export const RULE_STEPS = [
204
216
  description: "Bepaal exact hoe een belading (kar, pallet, doos of container) ontstaat en wordt samengesteld.",
205
217
  supportsConditions: true,
206
218
  groups: [
219
+ {
220
+ id: "tekening",
221
+ label: "Kar-tekening",
222
+ description: "Kleuren van de klanten in de tekening van een kar of pallet. Blokken van andere orders op een gedeelde drager zijn altijd grijs.",
223
+ fields: [
224
+ {
225
+ key: "klantkleuren",
226
+ label: "Klantkleuren",
227
+ type: "text",
228
+ default: "--chart-1, --chart-2, --chart-3, --chart-4, --chart-5",
229
+ description: "Namen van themakleuren, gescheiden door een komma (bijvoorbeeld --chart-1). Zo passen de kleuren bij licht, donker en de huisstijl. Leeg = standaardlijst.",
230
+ },
231
+ ],
232
+ },
207
233
  {
208
234
  id: "verzendmethode",
209
235
  label: "Verzendmethodes (Floriday)",
@@ -259,6 +285,16 @@ export const RULE_STEPS = [
259
285
  default: false,
260
286
  description: "Uit (standaard): ontbreekt het gewicht, dan krijgt de kar een waarschuwing dat de kilo's niet zijn gecontroleerd. Aan: de order wordt dan niet ingedeeld.",
261
287
  },
288
+ {
289
+ key: "laag_blokkeer_factor",
290
+ label: "Grens voor te veel fusten per laag (× de berekening)",
291
+ type: "number",
292
+ default: 1.5,
293
+ min: 1,
294
+ max: 5,
295
+ step: 0.1,
296
+ description: "Geeft Floriday of het artikel meer fusten per laag op dan volgens fustmaat en laagmaat (met overstek) past, dan vraagt het systeem één keer per product, fust en ladingdrager of dat klopt. Alleen boven deze grens blijft de order geblokkeerd. Standaard 1,5.",
297
+ },
262
298
  {
263
299
  key: "mode",
264
300
  label: "Hoe ontstaat een belading?",
@@ -1060,7 +1096,25 @@ export const RULE_STEPS = [
1060
1096
  type: "number",
1061
1097
  min: 1,
1062
1098
  max: 3650,
1063
- description: "Na deze termijn worden logboekregels van orders opgeruimd. Laat leeg om niets op te ruimen.",
1099
+ description: "Na deze termijn worden logboekregels van orders opgeruimd. Het logboek is per week opgedeeld; een verlopen week wordt in zijn geheel weggegooid.",
1100
+ },
1101
+ {
1102
+ key: "trace_error_retention_days",
1103
+ default: 90,
1104
+ label: "Foutregels in het logboek bewaren (dagen)",
1105
+ type: "number",
1106
+ min: 1,
1107
+ max: 365,
1108
+ description: "Logboekregels met een fout blijven langer bewaard, zodat je ze later nog kunt terugzoeken.",
1109
+ },
1110
+ {
1111
+ key: "worker_budget_seconds",
1112
+ default: 20,
1113
+ label: "Maximale duur van een achtergrondronde (seconden)",
1114
+ type: "number",
1115
+ min: 5,
1116
+ max: 50,
1117
+ description: "Elke minuut werken vier achtergrondrondes (planning, pickwachtrij, taken en onderhoud) de open orders bij. Na deze tijd stopt een ronde; wat nog niet af is, gaat mee met de volgende ronde.",
1064
1118
  },
1065
1119
  {
1066
1120
  key: "pick_history_retention_days",
@@ -1080,6 +1134,15 @@ export const RULE_STEPS = [
1080
1134
  max: 3650,
1081
1135
  description: "Alleen barcodes van karren die niet meer bestaan en nooit zijn geprint, gescand of gecontroleerd. Laat leeg om niets op te ruimen.",
1082
1136
  },
1137
+ {
1138
+ key: "archive_after_days",
1139
+ default: 14,
1140
+ label: "Niet geleverde orders archiveren na (dagen)",
1141
+ type: "number",
1142
+ min: 3,
1143
+ max: 90,
1144
+ description: "Een order die niet ingedeeld kon worden en waarvan de leverdag zo lang voorbij is, gaat naar het archief van de wachtrij. Planners krijgen daarvan een melding; met \"Opnieuw aanbieden\" zet je hem terug.",
1145
+ },
1083
1146
  ],
1084
1147
  },
1085
1148
  ],
@@ -1121,8 +1184,13 @@ export function normalizeSettings(raw) {
1121
1184
  const incoming = source[step.id];
1122
1185
  if (!incoming || typeof incoming !== "object")
1123
1186
  continue;
1187
+ const incomingFields = { ...(incoming.fields ?? {}) };
1188
+ // Bestaande werkwijzen zonder keuze houden de werkurenregel; alleen nieuwe werkwijzen krijgen het verzendmoment.
1189
+ if (step.id === "order_verwerking" && incomingFields.buffer_sluiten === undefined) {
1190
+ incomingFields.buffer_sluiten = "werkuren";
1191
+ }
1124
1192
  base[step.id] = {
1125
- fields: { ...base[step.id].fields, ...(incoming.fields ?? {}) },
1193
+ fields: { ...base[step.id].fields, ...incomingFields },
1126
1194
  conditions: Array.isArray(incoming.conditions) ? incoming.conditions : [],
1127
1195
  };
1128
1196
  }
@@ -20,5 +20,7 @@ export type InplanStap = {
20
20
  error?: string;
21
21
  voortgang?: string;
22
22
  };
23
- export declare function scheduleTasksPerOrder(sb: SupabaseClient, nu?: () => number): Promise<InplanStap>;
23
+ export declare function scheduleTasksPerOrder(sb: SupabaseClient, nu?: () => number, opties?: {
24
+ totaalBudgetMs?: number;
25
+ }): Promise<InplanStap>;
24
26
  //# sourceMappingURL=scheduleTasks.server.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"scheduleTasks.server.d.ts","sourceRoot":"","sources":["../../src/lib/scheduleTasks.server.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAG5D;;;;;;;;;;GAUG;AACH,eAAO,MAAM,aAAa,KAAK,CAAC;AAChC,eAAO,MAAM,gBAAgB,QAAS,CAAC;AACvC,eAAO,MAAM,gBAAgB,SAAU,CAAC;AAUxC,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAEzD;AAED,MAAM,MAAM,UAAU,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAE3F,wBAAsB,qBAAqB,CAAC,EAAE,EAAE,cAAc,EAAE,EAAE,GAAE,MAAM,MAAiB,GAAG,OAAO,CAAC,UAAU,CAAC,CAoChH"}
1
+ {"version":3,"file":"scheduleTasks.server.d.ts","sourceRoot":"","sources":["../../src/lib/scheduleTasks.server.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAG5D;;;;;;;;;;GAUG;AACH,eAAO,MAAM,aAAa,KAAK,CAAC;AAChC,eAAO,MAAM,gBAAgB,QAAS,CAAC;AACvC,eAAO,MAAM,gBAAgB,SAAU,CAAC;AAUxC,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAEzD;AAED,MAAM,MAAM,UAAU,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAE3F,wBAAsB,qBAAqB,CACzC,EAAE,EAAE,cAAc,EAClB,EAAE,GAAE,MAAM,MAAiB,EAC3B,MAAM,GAAE;IAAE,cAAc,CAAC,EAAE,MAAM,CAAA;CAAO,GACvC,OAAO,CAAC,UAAU,CAAC,CAuCrB"}
@@ -16,9 +16,12 @@ export const TOTAAL_BUDGET_MS = 110000;
16
16
  export function isOntbrekendeFunctie(msg) {
17
17
  return /could not find the function|does not exist|PGRST202/i.test(msg);
18
18
  }
19
- export async function scheduleTasksPerOrder(sb, nu = Date.now) {
19
+ export async function scheduleTasksPerOrder(sb, nu = Date.now, opties = {}) {
20
20
  const step = "Taken opnieuw inplannen";
21
21
  const start = nu();
22
+ // Golf 1: draait de taken-werker in de database, dan maakt de shell alleen de
23
+ // eerste portie en loopt de rest daar verder (logistics_schedule_tasks_vervolg).
24
+ const totaalBudget = Math.max(0, opties.totaalBudgetMs ?? TOTAAL_BUDGET_MS);
22
25
  try {
23
26
  let runId = null;
24
27
  let laatste = {};
@@ -42,7 +45,7 @@ export async function scheduleTasksPerOrder(sb, nu = Date.now) {
42
45
  }
43
46
  if (!laatste.resterend || laatste.resterend <= 0)
44
47
  break;
45
- if (nu() - start > TOTAAL_BUDGET_MS) {
48
+ if (nu() - start > totaalBudget) {
46
49
  return {
47
50
  step,
48
51
  ok: true,
@@ -0,0 +1,11 @@
1
+ export type StatusFase = "concept" | "open" | "te_picken" | "gepickt" | "verzonden" | "gereed_voor_facturatie" | "gefactureerd" | "geannuleerd" | "gereserveerd" | "afgehandeld_floriday" | "onbekend";
2
+ export declare const STATUS_FASEN_STANDAARD: Readonly<Record<string, StatusFase>>;
3
+ /** Zelfde regel als logistics_status_fase(): hoofdletters en spaties rondom tellen niet mee. */
4
+ export declare function statusFase(status: string | null | undefined): StatusFase;
5
+ /** Fases waarin de order klaar is met de logistiek: niet meer indelen. */
6
+ export declare const AFGERONDE_FASEN: ReadonlySet<StatusFase>;
7
+ /** Order hoeft niet (meer) ingedeeld te worden: afgerond of geannuleerd. */
8
+ export declare function isTerminaleFase(status: string | null | undefined): boolean;
9
+ /** Order is logistiek afgerond (niet geannuleerd). */
10
+ export declare function isAfgerondeFase(status: string | null | undefined): boolean;
11
+ //# sourceMappingURL=statusFase.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"statusFase.d.ts","sourceRoot":"","sources":["../../src/lib/statusFase.ts"],"names":[],"mappings":"AAQA,MAAM,MAAM,UAAU,GAClB,SAAS,GACT,MAAM,GACN,WAAW,GACX,SAAS,GACT,WAAW,GACX,wBAAwB,GACxB,cAAc,GACd,aAAa,GACb,cAAc,GACd,sBAAsB,GACtB,UAAU,CAAC;AAEf,eAAO,MAAM,sBAAsB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CA2BvE,CAAC;AAEF,gGAAgG;AAChG,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,UAAU,CAIxE;AAED,0EAA0E;AAC1E,eAAO,MAAM,eAAe,EAAE,WAAW,CAAC,UAAU,CAKlD,CAAC;AAEH,4EAA4E;AAC5E,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAG1E;AAED,sDAAsD;AACtD,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAE1E"}