@flowselections/logistiek-engine 1.0.189 → 1.0.191

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 (52) hide show
  1. package/db/schema/logistiek-engine.sql +1273 -0
  2. package/dist-lib/components/BeladingIndelingCard.d.ts.map +1 -1
  3. package/dist-lib/components/BeladingIndelingCard.js +4 -2
  4. package/dist-lib/components/GeblokkeerdeOrdersCard.d.ts.map +1 -1
  5. package/dist-lib/components/GeblokkeerdeOrdersCard.js +43 -4
  6. package/dist-lib/components/LadingdragersPage.d.ts.map +1 -1
  7. package/dist-lib/components/LadingdragersPage.js +4 -1
  8. package/dist-lib/components/OrdersZonderKarCard.d.ts +2 -0
  9. package/dist-lib/components/OrdersZonderKarCard.d.ts.map +1 -0
  10. package/dist-lib/components/OrdersZonderKarCard.js +36 -0
  11. package/dist-lib/components/RegiePage.d.ts.map +1 -1
  12. package/dist-lib/components/RegiePage.js +2 -1
  13. package/dist-lib/components/RuleEnginePage.js +8 -3
  14. package/dist-lib/hooks/useFloricodeLadingdragers.d.ts +2 -0
  15. package/dist-lib/hooks/useFloricodeLadingdragers.d.ts.map +1 -0
  16. package/dist-lib/hooks/useFloricodeLadingdragers.js +25 -0
  17. package/dist-lib/hooks/useGeblokkeerdeOrders.d.ts +1 -0
  18. package/dist-lib/hooks/useGeblokkeerdeOrders.d.ts.map +1 -1
  19. package/dist-lib/hooks/useGeblokkeerdeOrders.js +38 -1
  20. package/dist-lib/hooks/useRecalcJob.d.ts +1 -0
  21. package/dist-lib/hooks/useRecalcJob.d.ts.map +1 -1
  22. package/dist-lib/hooks/useRecalcJob.js +11 -1
  23. package/dist-lib/integrations/supabase/types.d.ts +407 -1
  24. package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
  25. package/dist-lib/lib/beladingRegels.d.ts +18 -0
  26. package/dist-lib/lib/beladingRegels.d.ts.map +1 -1
  27. package/dist-lib/lib/beladingRegels.js +28 -0
  28. package/dist-lib/lib/handlers/logistics-tick.server.d.ts.map +1 -1
  29. package/dist-lib/lib/handlers/logistics-tick.server.js +14 -0
  30. package/dist-lib/lib/loadCarrierPlanner.d.ts +3 -0
  31. package/dist-lib/lib/loadCarrierPlanner.d.ts.map +1 -1
  32. package/dist-lib/lib/loadCarrierPlanner.js +18 -13
  33. package/dist-lib/lib/loadCarrierPlanning.server.d.ts +16 -0
  34. package/dist-lib/lib/loadCarrierPlanning.server.d.ts.map +1 -1
  35. package/dist-lib/lib/loadCarrierPlanning.server.js +394 -24
  36. package/dist-lib/lib/loadCarrierTypes.d.ts +7 -0
  37. package/dist-lib/lib/loadCarrierTypes.d.ts.map +1 -1
  38. package/dist-lib/lib/loadCarrierTypes.js +17 -0
  39. package/dist-lib/lib/logisticsRecalcJob.server.d.ts +1 -0
  40. package/dist-lib/lib/logisticsRecalcJob.server.d.ts.map +1 -1
  41. package/dist-lib/lib/logisticsRecalcJob.server.js +3 -1
  42. package/dist-lib/lib/logisticsRecalculate.functions.js +2 -2
  43. package/dist-lib/lib/moduleVersion.d.ts +1 -1
  44. package/dist-lib/lib/moduleVersion.js +1 -1
  45. package/dist-lib/lib/planQueue.server.d.ts.map +1 -1
  46. package/dist-lib/lib/planQueue.server.js +6 -1
  47. package/dist-lib/lib/ruleEngineSchema.d.ts.map +1 -1
  48. package/dist-lib/lib/ruleEngineSchema.js +9 -0
  49. package/dist-lib/lib/scheduleTasks.server.d.ts +18 -0
  50. package/dist-lib/lib/scheduleTasks.server.d.ts.map +1 -0
  51. package/dist-lib/lib/scheduleTasks.server.js +65 -0
  52. package/package.json +1 -1
@@ -12,7 +12,7 @@ import { fallbackCarrierProfile } from "./loadCarrierTypes.js";
12
12
  import { planByDimensions, DIMENSION_PLANNER_VERSION } from "./dimensionPlanner.js";
13
13
  import { buildDimensionItems, carrierDimensions } from "./dimensionData.server.js";
14
14
  import { zonderExternAfgehandeld } from "./externalFulfillment.js";
15
- import { vulBeladingAan, deelUitFloridayVerpakking, plantenVanRegel, plausibiliteit, karrenOnwaarschijnlijk, verpakkingSleutel, controleerInvariant, VELD_NAAM, } from "./beladingRegels.js";
15
+ import { vulBeladingAan, deelUitFloridayVerpakking, plantenVanRegel, beladingBeoordeling, fustPerKarOnmogelijk, karrenOnwaarschijnlijk, verpakkingSleutel, controleerInvariant, VELD_NAAM, } from "./beladingRegels.js";
16
16
  // ---------------------------------------------------------------------------
17
17
  // Buffer
18
18
  // ---------------------------------------------------------------------------
@@ -241,8 +241,14 @@ export async function expandOrderItemsForPlanner(sb, rows, options = {}) {
241
241
  continue;
242
242
  }
243
243
  if (kind === "onbekend") {
244
- // Regel zonder artikel: niet raden (ook niet via de naam), indeling blokkeert met reden.
245
- throw new Error(`Indeling geblokkeerd: regel "${row.product_name ?? "onbekend"}" heeft geen artikel; groepen worden alleen via het groepshoofd herkend.`);
244
+ // Regel zonder artikel: niet raden (ook niet via de naam). 1.0.190: alleen deze order wacht.
245
+ const reden = `Regel "${row.product_name ?? "onbekend"}" heeft geen artikel: kies een artikel bij deze orderregel. ` +
246
+ "Alleen deze order wacht; de rest van de buffer wordt gewoon ingedeeld.";
247
+ if (options.orderBlokkades && orderKey) {
248
+ options.orderBlokkades.set(orderKey, reden);
249
+ continue;
250
+ }
251
+ throw new Error(`Indeling geblokkeerd: ${reden}`);
246
252
  }
247
253
  if (kind === "fout") {
248
254
  throw new Error(`Indeling geblokkeerd voor ${row.product_name ?? "onbekend artikel"}: ${lines[0]?.note ?? "fout in de samenstelling"}.`);
@@ -273,7 +279,13 @@ export async function expandOrderItemsForPlanner(sb, rows, options = {}) {
273
279
  continue;
274
280
  const p = l.product_id ? prodById.get(String(l.product_id)) : null;
275
281
  if (!p) {
276
- throw new Error(`Indeling geblokkeerd voor ${row.product_name ?? "groep"}: groepslid ${l.trade_item_key ?? "onbekend"} heeft geen artikel.`);
282
+ const reden = `Groepslid ${l.trade_item_key ?? "onbekend"} van ${row.product_name ?? "groep"} heeft geen artikel: kies een artikel. ` +
283
+ "Alleen deze order wacht.";
284
+ if (options.orderBlokkades && orderKey) {
285
+ options.orderBlokkades.set(orderKey, reden);
286
+ continue;
287
+ }
288
+ throw new Error(`Indeling geblokkeerd: ${reden}`);
277
289
  }
278
290
  const eigenLijn = lineMap?.get(String(p.floriday_trade_item_id ?? "")) ?? lineMap?.get(String(p.id)) ?? null;
279
291
  const lid = source === "handmatig"
@@ -314,7 +326,7 @@ async function bevestigdeSleutels(sb, orderIds) {
314
326
  set.add(`${r.order_id}|${r.verpakking_sleutel}`);
315
327
  return set;
316
328
  }
317
- async function beladingOnwaarschijnlijk(sb, inputs, rawRows, profile) {
329
+ async function beladingOnwaarschijnlijk(sb, inputs, rawRows, profile, profielPerRegel) {
318
330
  const fustIds = Array.from(new Set(rawRows.map((r) => r.products?.fust_type_id).filter((x) => !!x).map(String)));
319
331
  const fustById = new Map();
320
332
  if (fustIds.length > 0) {
@@ -327,28 +339,54 @@ async function beladingOnwaarschijnlijk(sb, inputs, rawRows, profile) {
327
339
  if (r.product_id)
328
340
  fustPerProduct.set(String(r.product_id), r.products?.fust_type_id ?? null);
329
341
  const kandidaten = [];
342
+ const waarschuwingen = [];
330
343
  for (const i of inputs) {
331
344
  if (i.plants <= 0)
332
345
  continue;
333
346
  const fustId = i.product_id ? fustPerProduct.get(String(i.product_id)) ?? null : null;
334
- const redenen = plausibiliteit({
347
+ const oordeel = beladingBeoordeling({
335
348
  belading: { plants_per_crate: i.plants_per_crate, crates_per_layer: i.crates_per_layer, layers_per_cart: i.layers_per_cart },
336
349
  fust: fustId ? fustById.get(fustId) ?? null : null,
337
- maxLagenLadingdrager: i.packing_authoritative ? null : profile.layers_per_carrier,
350
+ maxLagenLadingdrager: (profielPerRegel?.get(i.order_item_id) ?? profile).layers_per_carrier,
351
+ metBron: Boolean(i.packing_authoritative),
338
352
  });
339
- if (redenen.length === 0)
353
+ for (const w of oordeel.waarschuwen) {
354
+ waarschuwingen.push({ order_id: i.order_id ?? null, sleutel: `lagen:${verpakkingSleutel(i.product_id, i)}`, tekst: `${i.product_name}: ${w}` });
355
+ }
356
+ if (oordeel.blokkeren.length === 0)
340
357
  continue;
341
358
  kandidaten.push({
342
359
  order_id: i.order_id ?? null,
343
360
  product: i.product_name,
344
361
  sleutel: verpakkingSleutel(i.product_id, i),
345
- redenen,
362
+ redenen: oordeel.blokkeren,
346
363
  });
347
364
  }
348
365
  if (kandidaten.length === 0)
349
- return [];
366
+ return { blokkeren: [], waarschuwingen };
350
367
  const ok = await bevestigdeSleutels(sb, Array.from(new Set(kandidaten.map((k) => k.order_id).filter((x) => !!x))));
351
- return kandidaten.filter((k) => !ok.has(`${k.order_id}|${k.sleutel}`));
368
+ return { blokkeren: kandidaten.filter((k) => !ok.has(`${k.order_id}|${k.sleutel}`)), waarschuwingen };
369
+ }
370
+ /** Standaardladingdrager uit de werkwijze (karrenindeling.default_carrier_type); alleen terugval. */
371
+ async function standaardLadingdragerUitWerkwijze(sb) {
372
+ const { data } = await sb
373
+ .from("logistics_rule_configs")
374
+ .select("settings")
375
+ .eq("is_active", true)
376
+ .order("updated_at", { ascending: false })
377
+ .limit(1);
378
+ const st = Array.isArray(data) && data.length > 0 ? data[0]?.settings : null;
379
+ const v = st?.karrenindeling?.fields?.default_carrier_type ?? st?.karrenindeling?.default_carrier_type;
380
+ return typeof v === "string" && v.trim() ? v.trim() : null;
381
+ }
382
+ /** Actuele beladingwaarschuwingen per order vastleggen (vervangt de vorige van die orders). */
383
+ async function bewaarBeladingWaarschuwingen(sb, orderIds, items, warnings) {
384
+ const { error } = await sb.rpc("logistics_belading_waarschuwingen_zet", {
385
+ p_order_ids: orderIds,
386
+ p_items: items.filter((w) => w.order_id),
387
+ });
388
+ if (error)
389
+ warnings.push(`Beladingwaarschuwingen konden niet worden vastgelegd: ${error.message}`);
352
390
  }
353
391
  /** Invariant: elk fust van elke ingedeelde regel staat op precies één kar en de planten kloppen. */
354
392
  export function invariantFouten(inputs, carts) {
@@ -369,6 +407,114 @@ export async function loadCarrierProfiles(sb) {
369
407
  .order("sort_order", { ascending: true });
370
408
  return Array.isArray(data) ? data : [];
371
409
  }
410
+ /**
411
+ * 1.0.190 (E): Floriday-ladingdrager (packaging.loadCarrier) naar ladingdragertype.
412
+ * Vaste referentiedata, geen omgevingswaarde. Onbekende waarde = geen keuze.
413
+ */
414
+ export const FLORIDAY_LADINGDRAGER = {
415
+ DANISH_TROLLEY: "danish_container",
416
+ DANISH_TROLLEY_NON_CC_RFID: "danish_container",
417
+ DANISH_TROLLEY_WATER_TRAY: "danish_container",
418
+ // Floriday-authenticatie 1.0.117: AUCTION_TROLLEY = "Stapelwagen (veilingkar)".
419
+ AUCTION_TROLLEY: "trolley",
420
+ EURO_TROLLEY: "euro_rolcontainer",
421
+ HT_EURO_PALLET: "euro_pallet",
422
+ EURO_PALLET: "euro_pallet",
423
+ PALLET: "pallet",
424
+ BLOCK_PALLET: "pallet",
425
+ DISPOSABLE_PALLET: "pallet",
426
+ EURO_ROLL_CONTAINER: "euro_rolcontainer",
427
+ NONE: "loose",
428
+ };
429
+ export function ladingdragerUitVerpakking(waarde, actieveCodes) {
430
+ const w = String(waarde ?? "").trim();
431
+ if (!w)
432
+ return null;
433
+ if (actieveCodes.has(w))
434
+ return w;
435
+ const c = FLORIDAY_LADINGDRAGER[w.toUpperCase()];
436
+ return c && actieveCodes.has(c) ? c : null;
437
+ }
438
+ /**
439
+ * Ladingdrager per planregel: handmatige keuze → orderregel → primaire verpakking
440
+ * van het product. Geen standaardtype: zonder bron blijft de regel leeg (blokkeren).
441
+ */
442
+ async function bepaalLadingdragers(sb, inputs, rawRows, profiles, handmatig, standaardGebruikt, standaardCode) {
443
+ const codes = new Set(profiles.map((p) => String(p.code)));
444
+ const uit = new Map();
445
+ const perRegel = new Map();
446
+ for (const r of rawRows)
447
+ perRegel.set(String(r.id), ladingdragerUitVerpakking(r.load_carrier_type, codes));
448
+ const productIds = Array.from(new Set(inputs.map((i) => i.product_id).filter((x) => !!x)));
449
+ const perProduct = new Map();
450
+ if (productIds.length > 0) {
451
+ const { data, error } = await sb
452
+ .from("product_packings")
453
+ .select("product_id, load_carrier, is_primary, sort_order")
454
+ .in("product_id", productIds);
455
+ if (error)
456
+ throw new Error(`Verpakkingen konden niet worden gelezen: ${error.message}`);
457
+ const rijen = (Array.isArray(data) ? data : []).sort((a, b) => Number(Boolean(b.is_primary)) - Number(Boolean(a.is_primary)) || Number(a.sort_order ?? 0) - Number(b.sort_order ?? 0));
458
+ for (const r of rijen) {
459
+ const pid = String(r.product_id);
460
+ if (perProduct.get(pid))
461
+ continue;
462
+ perProduct.set(pid, ladingdragerUitVerpakking(r.load_carrier, codes));
463
+ }
464
+ }
465
+ // 1.0.190: eerst de Floriday-verpakking van het artikel
466
+ // (floriday_trade_items_cache.data.packingConfigurations[].loadCarrier, primaire eerst).
467
+ const perFloriday = new Map();
468
+ if (productIds.length > 0) {
469
+ const { data: prods } = await sb
470
+ .from("products")
471
+ .select("id, floriday_trade_item_id")
472
+ .in("id", productIds);
473
+ const tiPerProduct = new Map();
474
+ for (const p of (Array.isArray(prods) ? prods : [])) {
475
+ if (p?.floriday_trade_item_id)
476
+ tiPerProduct.set(String(p.id), String(p.floriday_trade_item_id));
477
+ }
478
+ const tiIds = Array.from(new Set(tiPerProduct.values()));
479
+ if (tiIds.length > 0) {
480
+ const { data: tis } = await sb
481
+ .from("floriday_trade_items_cache")
482
+ .select("floriday_id, data, is_deleted")
483
+ .in("floriday_id", tiIds);
484
+ const lcPerTi = new Map();
485
+ for (const t of (Array.isArray(tis) ? tis : [])) {
486
+ if (t?.is_deleted)
487
+ continue;
488
+ const confs = Array.isArray(t?.data?.packingConfigurations) ? t.data.packingConfigurations : [];
489
+ const sorted = [...confs].sort((a, b) => Number(Boolean(b?.primary)) - Number(Boolean(a?.primary)));
490
+ let code = null;
491
+ for (const c of sorted) {
492
+ code = ladingdragerUitVerpakking(c?.loadCarrier, codes);
493
+ if (code)
494
+ break;
495
+ }
496
+ lcPerTi.set(String(t.floriday_id), code);
497
+ }
498
+ for (const [pid, ti] of tiPerProduct)
499
+ perFloriday.set(pid, lcPerTi.get(ti) ?? null);
500
+ }
501
+ }
502
+ const handmatigeCode = handmatig && codes.has(handmatig) ? handmatig : null;
503
+ const terugval = standaardCode && codes.has(standaardCode) ? standaardCode : null;
504
+ for (const i of inputs) {
505
+ const base = i.order_item_id.includes("::") ? i.order_item_id.split("::")[0] : i.order_item_id;
506
+ const pid = i.product_id ? String(i.product_id) : null;
507
+ const uitFloriday = pid ? perFloriday.get(pid) || null : null;
508
+ const uitProduct = pid ? perProduct.get(pid) || null : null;
509
+ let code = handmatigeCode || perRegel.get(base) || uitFloriday || uitProduct || null;
510
+ if (!code && terugval) {
511
+ code = terugval;
512
+ standaardGebruikt?.add(i.order_item_id);
513
+ }
514
+ uit.set(i.order_item_id, code);
515
+ }
516
+ return uit;
517
+ }
372
518
  function profileFor(profiles, code) {
373
519
  return profiles.find((p) => p.code === code) ?? fallbackCarrierProfile(code);
374
520
  }
@@ -637,6 +783,9 @@ function isReservationOrTerminal(order) {
637
783
  return true;
638
784
  if (order.is_reservation === true)
639
785
  return true;
786
+ // 1.0.190 (F): geannuleerde orders horen nooit in een buffer of op een kar.
787
+ if (order.cancelled_at)
788
+ return true;
640
789
  const status = String(order.status ?? "").trim().toLowerCase();
641
790
  return TERMINALE_STATUSSEN.has(status);
642
791
  }
@@ -824,13 +973,14 @@ export async function replanBufferInternal(sb, bufferId, options = {}) {
824
973
  export const BUFFER_BUSY_MESSAGE = "Deze orderbuffer wordt op dit moment al bijgewerkt; probeer het zo opnieuw.";
825
974
  async function replanBufferUnlocked(sb, bufferId, options = {}) {
826
975
  const warnings = [];
976
+ const beladingWaarschuwingen = [];
827
977
  // Bevroren karren blijven staan; al het overige van de héle buffer wordt
828
978
  // samen ingedeeld, zodat orders van de buffer op dezelfde karren komen.
829
979
  const keepCartIds = (options.keepCartIds ?? []).map(String).filter(Boolean);
830
980
  const isSupplement = keepCartIds.length > 0;
831
981
  const { data: orders } = await sb
832
982
  .from("orders")
833
- .select("id, order_number, customer_id, custom_fields, plan_locked_at, status, is_reservation")
983
+ .select("id, order_number, customer_id, custom_fields, plan_locked_at, status, is_reservation, cancelled_at")
834
984
  .eq("buffer_id", bufferId)
835
985
  .is("deleted_at", null);
836
986
  const allOrderRows = Array.isArray(orders) ? orders : [];
@@ -938,7 +1088,7 @@ async function replanBufferUnlocked(sb, bufferId, options = {}) {
938
1088
  const { data: rawItems } = await sb
939
1089
  .from("order_items")
940
1090
  .select("id, order_id, product_id, product_name, quantity, unit, plants_per_crate, crates_per_layer, layers_per_cart, " +
941
- "packing_locked_at, packing_locked_by, source_line_key, " +
1091
+ "packing_locked_at, packing_locked_by, source_line_key, load_carrier_type, " +
942
1092
  "products(plants_per_crate, crates_per_layer, layers_per_cart, weight, floriday_trade_item_id, fust_type_id)")
943
1093
  .in("order_id", idList)
944
1094
  // Regels in quarantaine (Verkoop-herstel) nooit inplannen.
@@ -1028,6 +1178,7 @@ async function replanBufferUnlocked(sb, bufferId, options = {}) {
1028
1178
  const expandNotes = [];
1029
1179
  const ontbrekendeVelden = new Map();
1030
1180
  const onbekendePlanten = [];
1181
+ const orderBlokkades = new Map();
1031
1182
  let inputs = await expandOrderItemsForPlanner(sb, rawRows, {
1032
1183
  packingByOrder,
1033
1184
  packingByOrderLine,
@@ -1036,7 +1187,92 @@ async function replanBufferUnlocked(sb, bufferId, options = {}) {
1036
1187
  notes: expandNotes,
1037
1188
  ontbrekend: ontbrekendeVelden,
1038
1189
  onbekendePlanten,
1190
+ orderBlokkades,
1039
1191
  });
1192
+ // 1.0.190 (G): regel zonder artikel blokkeert alleen die order, nooit de hele buffer.
1193
+ if (orderBlokkades.size > 0) {
1194
+ const geblokkeerd = new Set(orderBlokkades.keys());
1195
+ inputs = inputs.filter((i) => !i.order_id || !geblokkeerd.has(String(i.order_id)));
1196
+ if (!options.dryRun) {
1197
+ for (const [oid, reden] of orderBlokkades) {
1198
+ const { error: blockError } = await sb.rpc("logistics_plan_block", {
1199
+ p_order_ids: [oid],
1200
+ p_reason: reden,
1201
+ p_details: { reason_code: "artikel_ontbreekt", bufferId, path: "typescript" },
1202
+ });
1203
+ if (blockError)
1204
+ warnings.push(`Blokkade kon niet worden vastgelegd: ${blockError.message}`);
1205
+ }
1206
+ }
1207
+ warnings.push(...orderBlokkades.values());
1208
+ }
1209
+ // 1.0.190 (E): ladingdrager per regel uit de verpakking (loadCarrier), nooit een stille standaard.
1210
+ const profilesVroeg = await loadCarrierProfiles(sb);
1211
+ const standaardGebruikt = new Set();
1212
+ const standaardType = profilesVroeg.find((p) => p.is_default)?.code ??
1213
+ (await standaardLadingdragerUitWerkwijze(sb));
1214
+ const ladingdragerPerRegel = await bepaalLadingdragers(sb, inputs, rawRows, profilesVroeg, options.carrierType ?? null, standaardGebruikt, standaardType ?? null);
1215
+ if (standaardGebruikt.size > 0) {
1216
+ const perOrder = new Map();
1217
+ for (const i of inputs) {
1218
+ if (!standaardGebruikt.has(i.order_item_id))
1219
+ continue;
1220
+ const l = perOrder.get(String(i.order_id ?? "")) ?? [];
1221
+ l.push(i.product_name);
1222
+ perOrder.set(String(i.order_id ?? ""), l);
1223
+ }
1224
+ for (const [oid, namen] of perOrder) {
1225
+ beladingWaarschuwingen.push({
1226
+ order_id: oid || null,
1227
+ sleutel: "ladingdrager_standaard",
1228
+ tekst: `Ladingdrager niet bekend uit verpakking, standaard gebruikt (${namen.slice(0, 5).join(", ")}).`,
1229
+ });
1230
+ }
1231
+ }
1232
+ {
1233
+ const onbekend = new Map();
1234
+ for (const i of inputs) {
1235
+ if (ladingdragerPerRegel.get(i.order_item_id))
1236
+ continue;
1237
+ const oid = String(i.order_id ?? "");
1238
+ if (!oid)
1239
+ continue;
1240
+ const l = onbekend.get(oid) ?? [];
1241
+ l.push(i.product_name);
1242
+ onbekend.set(oid, l);
1243
+ }
1244
+ if (onbekend.size > 0) {
1245
+ inputs = inputs.filter((i) => !onbekend.has(String(i.order_id ?? "")));
1246
+ for (const [oid, namen] of onbekend) {
1247
+ const reden = `Ladingdrager onbekend voor ${namen.slice(0, 5).join(", ")}: de verpakking noemt geen ladingdrager ` +
1248
+ "en er is er geen gekozen. Alleen deze order wacht.";
1249
+ warnings.push(reden);
1250
+ if (!options.dryRun) {
1251
+ const { error: blockError } = await sb.rpc("logistics_plan_block", {
1252
+ p_order_ids: [oid],
1253
+ p_reason: reden,
1254
+ p_details: { reason_code: "ladingdrager_onbekend", bufferId, path: "typescript" },
1255
+ });
1256
+ if (blockError)
1257
+ warnings.push(`Blokkade kon niet worden vastgelegd: ${blockError.message}`);
1258
+ }
1259
+ }
1260
+ }
1261
+ }
1262
+ if (inputs.length === 0 && (orderBlokkades.size > 0 || [...ladingdragerPerRegel.values()].some((v) => !v))) {
1263
+ const reden = "Alle orders van deze buffer wachten op een artikel of ladingdrager; bestaande karren blijven staan.";
1264
+ return {
1265
+ ok: false,
1266
+ bufferId,
1267
+ orderIds,
1268
+ cartsCreated: 0,
1269
+ replacedManualCount: 0,
1270
+ usedFloridayPacking: false,
1271
+ warnings: [...warnings, reden],
1272
+ skipped: "blocked",
1273
+ blockedReason: reden,
1274
+ };
1275
+ }
1040
1276
  // Groepsregel naast ledenregels: niet ingedeeld, wel zichtbaar gemeld bij de order.
1041
1277
  for (const n of expandNotes) {
1042
1278
  if (!n.order_id)
@@ -1160,7 +1396,21 @@ async function replanBufferUnlocked(sb, bufferId, options = {}) {
1160
1396
  const neverMix = new Set((overrides ?? []).filter((o) => o.never_mix && o.product_id).map((o) => String(o.product_id)));
1161
1397
  const profiles = await loadCarrierProfiles(sb);
1162
1398
  const rules = await loadBeladingRules(sb);
1163
- const defaultProfile = profiles.find((p) => p.is_default) ?? profileFor(profiles, "trolley");
1399
+ // 1.0.190 (E): geen standaard-'trolley' meer. Hoofdladingdrager = meest voorkomende van de regels.
1400
+ const telling = new Map();
1401
+ for (const i of inputs) {
1402
+ const c = ladingdragerPerRegel.get(i.order_item_id);
1403
+ if (c)
1404
+ telling.set(c, (telling.get(c) ?? 0) + 1);
1405
+ }
1406
+ const hoofdCode = [...telling.entries()].sort((a, b) => b[1] - a[1])[0]?.[0] ?? null;
1407
+ const defaultProfile = { code: options.carrierType ?? hoofdCode ?? "" };
1408
+ const profielPerRegel = new Map();
1409
+ for (const i of inputs) {
1410
+ const c = ladingdragerPerRegel.get(i.order_item_id);
1411
+ if (c)
1412
+ profielPerRegel.set(i.order_item_id, profileFor(profiles, c));
1413
+ }
1164
1414
  // Guard: zonder beladingbron mag er niet gegokt worden. Ladingdragers zonder
1165
1415
  // lagenmodel (los product / enkele eenheid) hebben de belading niet nodig.
1166
1416
  const guardProfile = profileFor(profiles, options.carrierType ?? defaultProfile.code);
@@ -1231,7 +1481,12 @@ async function replanBufferUnlocked(sb, bufferId, options = {}) {
1231
1481
  }
1232
1482
  // 1.0.179 (punt 3): plausibiliteit, alle ordersoorten. Eén bevestiging per order en verpakking.
1233
1483
  if (!options.dryRun) {
1234
- const onwaarschijnlijk = await beladingOnwaarschijnlijk(sb, inputs, rawRows, guardProfile);
1484
+ const oordeel = await beladingOnwaarschijnlijk(sb, inputs, rawRows, guardProfile, profielPerRegel);
1485
+ const onwaarschijnlijk = oordeel.blokkeren;
1486
+ beladingWaarschuwingen.push(...oordeel.waarschuwingen);
1487
+ for (const w of beladingWaarschuwingen)
1488
+ warnings.push(w.tekst);
1489
+ await bewaarBeladingWaarschuwingen(sb, orderIds, beladingWaarschuwingen, warnings);
1235
1490
  if (onwaarschijnlijk.length > 0) {
1236
1491
  const reason = `Belading onwaarschijnlijk: ${onwaarschijnlijk.slice(0, 10).map((o) => `${o.product}: ${o.redenen.join("; ")}`).join(" | ")}. ` +
1237
1492
  "Kantoor moet dit bevestigen voordat er wordt ingedeeld.";
@@ -1360,7 +1615,7 @@ async function replanBufferUnlocked(sb, bufferId, options = {}) {
1360
1615
  // Ladingdrager per orderregel bepalen en per type een ronde draaien.
1361
1616
  const groups = new Map();
1362
1617
  for (const input of inputs) {
1363
- const code = options.carrierType ?? defaultProfile.code;
1618
+ const code = ladingdragerPerRegel.get(input.order_item_id) ?? defaultProfile.code;
1364
1619
  const list = groups.get(code) ?? [];
1365
1620
  list.push(input);
1366
1621
  groups.set(code, list);
@@ -1437,17 +1692,31 @@ async function replanBufferUnlocked(sb, bufferId, options = {}) {
1437
1692
  // 1.0.179 (punt 11): invariant; bij een verschil wordt niets weggeschreven en niets geprint.
1438
1693
  if (!options.dryRun) {
1439
1694
  const perOrder = new Map();
1695
+ const fustTeVeel = new Map();
1440
1696
  for (const c of plannedCarts) {
1441
1697
  for (const oid of new Set(c.items.map((it) => it.order_id).filter((x) => !!x))) {
1442
1698
  perOrder.set(oid, (perOrder.get(oid) ?? 0) + 1);
1443
1699
  }
1444
1700
  }
1445
- const teVeel = Array.from(perOrder.entries()).filter(([, n]) => karrenOnwaarschijnlijk(n) != null);
1701
+ // 1.0.190: meer dan 200 fusten op één kar is onmogelijk; per order blokkeren.
1702
+ for (const c of plannedCarts) {
1703
+ const fusten = c.items.reduce((t, it) => t + Number(it.crates ?? 0), 0);
1704
+ const tekst = fustPerKarOnmogelijk(fusten);
1705
+ if (!tekst)
1706
+ continue;
1707
+ for (const oid of new Set(c.items.map((it) => it.order_id).filter((x) => !!x))) {
1708
+ perOrder.set(oid, Math.max(perOrder.get(oid) ?? 0, 0));
1709
+ fustTeVeel.set(oid, tekst);
1710
+ }
1711
+ }
1712
+ const teVeel = Array.from(perOrder.entries()).filter(([oid, n]) => karrenOnwaarschijnlijk(n) != null || fustTeVeel.has(oid));
1446
1713
  if (teVeel.length > 0) {
1447
1714
  const ok = await bevestigdeSleutels(sb, teVeel.map(([oid]) => oid));
1448
- const open = teVeel.filter(([oid, n]) => !ok.has(`${oid}|karren:${n}`));
1715
+ const sleutelVan = (oid, n) => (fustTeVeel.has(oid) ? "fust_per_kar" : `karren:${n}`);
1716
+ const tekstVan = (oid, n) => fustTeVeel.get(oid) ?? karrenOnwaarschijnlijk(n) ?? "";
1717
+ const open = teVeel.filter(([oid, n]) => !ok.has(`${oid}|${sleutelVan(oid, n)}`));
1449
1718
  if (open.length > 0) {
1450
- const reason = `Belading onwaarschijnlijk: ${open.map(([oid, n]) => `order ${orderRows.find((o) => String(o.id) === oid)?.order_number ?? oid}: ${karrenOnwaarschijnlijk(n)}`).join("; ")}. ` +
1719
+ const reason = `Belading onwaarschijnlijk: ${open.map(([oid, n]) => `order ${orderRows.find((o) => String(o.id) === oid)?.order_number ?? oid}: ${tekstVan(oid, n)}`).join("; ")}. ` +
1451
1720
  "Kantoor moet dit bevestigen voordat er wordt ingedeeld.";
1452
1721
  await sb.rpc("logistics_plan_block", {
1453
1722
  p_order_ids: orderIds,
@@ -1456,7 +1725,7 @@ async function replanBufferUnlocked(sb, bufferId, options = {}) {
1456
1725
  reason_code: "belading_onwaarschijnlijk",
1457
1726
  bufferId,
1458
1727
  path: "typescript",
1459
- bevestigen: open.map(([oid, n]) => ({ order_id: oid, sleutel: `karren:${n}`, redenen: [karrenOnwaarschijnlijk(n)] })),
1728
+ bevestigen: open.map(([oid, n]) => ({ order_id: oid, sleutel: sleutelVan(oid, n), redenen: [tekstVan(oid, n)] })),
1460
1729
  },
1461
1730
  });
1462
1731
  return {
@@ -1596,6 +1865,7 @@ async function replanBufferUnlocked(sb, bufferId, options = {}) {
1596
1865
  getal(r["crates_per_layer"]),
1597
1866
  getal(r["layers_per_cart"]),
1598
1867
  getal(r["layer_fraction"]),
1868
+ getal(r["lagen"]),
1599
1869
  getal(r["layer_share_pct"]),
1600
1870
  getal(r["layer_index"]),
1601
1871
  getal(r["crates_in_layer"]),
@@ -1653,7 +1923,11 @@ async function replanBufferUnlocked(sb, bufferId, options = {}) {
1653
1923
  plants_per_crate: i.plants_per_crate,
1654
1924
  crates_per_layer: i.crates_per_layer,
1655
1925
  layers_per_cart: i.layers_per_cart,
1656
- layer_fraction: i.layer_fraction,
1926
+ // 1.0.190: layer_fraction = vulling per laag (0–1); lagen = fusten ÷ fusten per laag.
1927
+ layer_fraction: Math.min(1, Math.max(0, Number(i.layer_fraction ?? 0))),
1928
+ lagen: Number(i.crates_per_layer) > 0
1929
+ ? Math.round((Number(i.crates) / Number(i.crates_per_layer)) * 1000) / 1000
1930
+ : null,
1657
1931
  layer_share_pct: i.layer_share_pct,
1658
1932
  layer_index: i.layer_index ?? null,
1659
1933
  crates_in_layer: i.crates_in_layer ?? null,
@@ -1684,7 +1958,7 @@ async function replanBufferUnlocked(sb, bufferId, options = {}) {
1684
1958
  const { data: rijen, error: itemsError } = await sb
1685
1959
  .from("order_cart_items")
1686
1960
  .select("cart_id, order_item_id, product_id, crates, boxes, weight_kg, plants_per_crate, " +
1687
- "crates_per_layer, layers_per_cart, layer_fraction, layer_share_pct, layer_index, crates_in_layer")
1961
+ "crates_per_layer, layers_per_cart, layer_fraction, lagen, layer_share_pct, layer_index, crates_in_layer")
1688
1962
  .in("cart_id", slice);
1689
1963
  if (itemsError) {
1690
1964
  throw new Error(`Inhoud van bestaande karren kon niet worden gelezen: ${itemsError.message}`);
@@ -1714,9 +1988,76 @@ async function replanBufferUnlocked(sb, bufferId, options = {}) {
1714
1988
  else
1715
1989
  nieuw.push(g);
1716
1990
  }
1717
- const teVerwijderen = [];
1991
+ const teVerwijderenAlle = [];
1718
1992
  for (const lijst of beschikbaarPerSig.values())
1719
- teVerwijderen.push(...lijst);
1993
+ teVerwijderenAlle.push(...lijst);
1994
+ // 1.0.190 (B): een order die in deze ronde niet is ingedeeld (wacht op artikel,
1995
+ // ladingdrager of bevestiging) houdt zijn bestaande karren. Alleen karren waarvan
1996
+ // alle inhoud bij wél ingedeelde orders hoort, mogen vervangen worden.
1997
+ const ingedeeld = new Set(inputs.map((i) => String(i.order_id ?? "")).filter(Boolean));
1998
+ const orderVanRegel = new Map();
1999
+ for (const r of rawRows)
2000
+ orderVanRegel.set(String(r.id), String(r.order_id ?? ""));
2001
+ const karOrder = new Map();
2002
+ for (const c of vergelijkbaar)
2003
+ karOrder.set(String(c.id), c.order_id ? String(c.order_id) : null);
2004
+ // 1.0.190 (FO-2601368): regels van orders die deze buffer al hebben verlaten
2005
+ // ("verhuizers") staan niet in rawRows. Hun kar mag pas weg zodra die order in
2006
+ // zijn nieuwe buffer een kar heeft; anders zou hij zonder indeling achterblijven.
2007
+ const onbekendeRegels = new Set();
2008
+ for (const id of teVerwijderenAlle) {
2009
+ for (const it of huidigeItems.get(id) ?? []) {
2010
+ const rid = String(it.order_item_id ?? "");
2011
+ if (rid && !orderVanRegel.has(rid))
2012
+ onbekendeRegels.add(rid);
2013
+ }
2014
+ }
2015
+ const verhuizerZonderKar = new Set();
2016
+ if (onbekendeRegels.size > 0) {
2017
+ const ids = [...onbekendeRegels];
2018
+ const verhuizers = new Set();
2019
+ for (let s = 0; s < ids.length; s += 200) {
2020
+ const { data: rr, error: rrErr } = await sb.from("order_items").select("id, order_id").is("quarantined_at", null).in("id", ids.slice(s, s + 200));
2021
+ if (rrErr)
2022
+ throw new Error(`Orderregels van eerdere bufferleden konden niet worden gelezen: ${rrErr.message}`);
2023
+ for (const r of Array.isArray(rr) ? rr : []) {
2024
+ orderVanRegel.set(String(r.id), String(r.order_id ?? ""));
2025
+ if (r.order_id)
2026
+ verhuizers.add(String(r.order_id));
2027
+ }
2028
+ }
2029
+ const scopeIds = new Set(huidigeIds);
2030
+ for (const oid of verhuizers) {
2031
+ const { data: elders } = await sb
2032
+ .from("order_cart_items")
2033
+ .select("cart_id, order_items!inner(order_id), order_carts!inner(id, archived_at)")
2034
+ .eq("order_items.order_id", oid)
2035
+ .is("order_carts.archived_at", null)
2036
+ .limit(50);
2037
+ const heeftElders = (Array.isArray(elders) ? elders : []).some((e) => !scopeIds.has(String(e.cart_id)));
2038
+ if (!heeftElders)
2039
+ verhuizerZonderKar.add(oid);
2040
+ }
2041
+ }
2042
+ const teVerwijderen = teVerwijderenAlle.filter((id) => {
2043
+ const eigen = karOrder.get(id);
2044
+ if (eigen && !ingedeeld.has(eigen))
2045
+ return false;
2046
+ for (const it of huidigeItems.get(id) ?? []) {
2047
+ const oid = orderVanRegel.get(String(it.order_item_id ?? ""));
2048
+ if (oid && verhuizerZonderKar.has(oid))
2049
+ return false;
2050
+ if (oid && !ingedeeld.has(oid) && !onbekendeRegels.has(String(it.order_item_id ?? "")))
2051
+ return false;
2052
+ }
2053
+ return true;
2054
+ });
2055
+ if (verhuizerZonderKar.size > 0) {
2056
+ warnings.push(`${verhuizerZonderKar.size} order(s) zijn naar een andere buffer verhuisd en houden hun oude kar tot de nieuwe indeling klaar is.`);
2057
+ }
2058
+ if (teVerwijderen.length < teVerwijderenAlle.length) {
2059
+ warnings.push(`${teVerwijderenAlle.length - teVerwijderen.length} bestaande kar(ren) blijven staan omdat hun order nu wacht; de indeling wordt vervangen zodra de order weer ingedeeld kan worden.`);
2060
+ }
1720
2061
  let created = behouden.size;
1721
2062
  const idByPlanIndex = new Map(behouden);
1722
2063
  // A3: verouderde karren weg en nieuwe karren erbij in één transactie. Mislukt dit,
@@ -1739,6 +2080,35 @@ async function replanBufferUnlocked(sb, bufferId, options = {}) {
1739
2080
  }
1740
2081
  created += mapping.length;
1741
2082
  }
2083
+ // 1.0.190 (FO-2601368): staan ingedeelde orders van deze buffer nog op een kar
2084
+ // van hun vorige buffer, laat die vorige buffer dan opnieuw indelen; daar gaat
2085
+ // de oude kar pas weg nu de nieuwe klaar is.
2086
+ if (ingedeeld.size > 0) {
2087
+ try {
2088
+ const nieuweIds = new Set([...idByPlanIndex.values(), ...keepCartIds.map(String)]);
2089
+ const { data: oud } = await sb
2090
+ .from("order_cart_items")
2091
+ .select("cart_id, order_items!inner(order_id), order_carts!inner(buffer_id, archived_at)")
2092
+ .in("order_items.order_id", [...ingedeeld])
2093
+ .is("order_carts.archived_at", null)
2094
+ .limit(1000);
2095
+ const oudeBuffers = new Set();
2096
+ for (const r of Array.isArray(oud) ? oud : []) {
2097
+ const b = r.order_carts?.buffer_id;
2098
+ if (!nieuweIds.has(String(r.cart_id)) && b && String(b) !== String(bufferId))
2099
+ oudeBuffers.add(String(b));
2100
+ }
2101
+ for (const b of oudeBuffers) {
2102
+ const { data: lid } = await sb.from("orders").select("id").eq("buffer_id", b).is("deleted_at", null).limit(1);
2103
+ const peer = Array.isArray(lid) && lid[0] ? String(lid[0].id) : null;
2104
+ if (peer)
2105
+ await sb.rpc("logistics_plan_enqueue", { p_order_id: peer, p_reason: "vorige buffer opruimen na verhuizing" });
2106
+ }
2107
+ }
2108
+ catch (e) {
2109
+ console.error(`[logistiek-engine] buffer ${bufferId}: vorige buffer opruimen niet ingepland`, e);
2110
+ }
2111
+ }
1742
2112
  if (nieuw.length === 0) {
1743
2113
  console.log(`[logistiek-engine] buffer ${bufferId}: indeling ongewijzigd, ${behouden.size} kar(ren) behouden, ` +
1744
2114
  `${teVerwijderen.length} verouderde kar(ren) verwijderd.`);
@@ -16,4 +16,11 @@ export declare const CARRIER_LABELS: Record<string, string>;
16
16
  export declare function carrierLabel(code: string, fallback?: string): string;
17
17
  /** Fallbackprofiel als een type nog niet in de database staat. */
18
18
  export declare function fallbackCarrierProfile(code: string): CarrierProfile;
19
+ /**
20
+ * 1.0.190: Floriday-code per eigen ladingdragertype. Wordt alleen gebruikt om de
21
+ * naam uit floricode_load_carriers() te tonen; koppelen gebeurt altijd op code.
22
+ */
23
+ export declare const FLORIDAY_CODE_VOOR_TYPE: Record<string, string>;
24
+ /** Naam op het scherm: eerst de Floricode-naam (op code), anders eigen label. */
25
+ export declare function ladingdragerNaam(code: string, floricode: Map<string, string> | null, fallback?: string): string;
19
26
  //# sourceMappingURL=loadCarrierTypes.d.ts.map
@@ -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"}
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"}
@@ -39,3 +39,20 @@ export function fallbackCarrierProfile(code) {
39
39
  ...(map[code] ?? {}),
40
40
  };
41
41
  }
42
+ /**
43
+ * 1.0.190: Floriday-code per eigen ladingdragertype. Wordt alleen gebruikt om de
44
+ * naam uit floricode_load_carriers() te tonen; koppelen gebeurt altijd op code.
45
+ */
46
+ export const FLORIDAY_CODE_VOOR_TYPE = {
47
+ danish_container: "DANISH_TROLLEY",
48
+ trolley: "AUCTION_TROLLEY",
49
+ euro_rolcontainer: "EURO_TROLLEY",
50
+ pallet: "PALLET",
51
+ euro_pallet: "EURO_PALLET",
52
+ loose: "NONE",
53
+ };
54
+ /** Naam op het scherm: eerst de Floricode-naam (op code), anders eigen label. */
55
+ export function ladingdragerNaam(code, floricode, fallback) {
56
+ const f = FLORIDAY_CODE_VOOR_TYPE[code] ?? code;
57
+ return floricode?.get(f) ?? floricode?.get(code) ?? carrierLabel(code, fallback);
58
+ }
@@ -24,6 +24,7 @@ export declare function getLatestJob(sb: SupabaseClient): Promise<RecalcJob | nu
24
24
  export declare function startJob(sb: SupabaseClient, startedBy: string | null, strategyId: string | null): Promise<RecalcJob>;
25
25
  /** Verwerkt één batch van de lopende job. Geeft de bijgewerkte job terug. */
26
26
  export declare function tickJob(sb: SupabaseClient): Promise<RecalcJob | null>;
27
+ export { scheduleTasksPerOrder } from "./scheduleTasks.server.js";
27
28
  /** Draait de afsluitende herberekenstappen (planning, tellingen, capaciteit). */
28
29
  export declare function runFinalSteps(sb: SupabaseClient, days?: number): Promise<{
29
30
  step: string;
@@ -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,iFAAiF;AACjF,wBAAsB,aAAa,CAAC,EAAE,EAAE,cAAc,EAAE,IAAI,SAAK;UAkBpC,MAAM;QAAM,OAAO;YAAU,MAAM;KAM/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;AAGlE,iFAAiF;AACjF,wBAAsB,aAAa,CAAC,EAAE,EAAE,cAAc,EAAE,IAAI,SAAK;UAiBpC,MAAM;QAAM,OAAO;YAAU,MAAM;KAO/D"}