@flowselections/logistiek-engine 1.0.241 → 1.0.243

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 (66) hide show
  1. package/db/schema/logistiek-engine.sql +8794 -33
  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/StandaardgegevensCard.d.ts.map +1 -1
  16. package/dist-lib/components/StandaardgegevensCard.js +25 -1
  17. package/dist-lib/components/StartlijstCard.d.ts.map +1 -1
  18. package/dist-lib/components/StartlijstCard.js +4 -1
  19. package/dist-lib/components/rule-engine/RuleFieldInput.d.ts.map +1 -1
  20. package/dist-lib/components/rule-engine/RuleFieldInput.js +1 -1
  21. package/dist-lib/hooks/useGeblokkeerdeOrders.d.ts +14 -0
  22. package/dist-lib/hooks/useGeblokkeerdeOrders.d.ts.map +1 -1
  23. package/dist-lib/hooks/useGeblokkeerdeOrders.js +40 -3
  24. package/dist-lib/index.d.ts +1 -0
  25. package/dist-lib/index.d.ts.map +1 -1
  26. package/dist-lib/index.js +1 -0
  27. package/dist-lib/integrations/supabase/types.d.ts +3014 -85
  28. package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
  29. package/dist-lib/lib/beladingRegels.d.ts +17 -0
  30. package/dist-lib/lib/beladingRegels.d.ts.map +1 -1
  31. package/dist-lib/lib/beladingRegels.js +29 -3
  32. package/dist-lib/lib/dimensionPlanner.d.ts +10 -0
  33. package/dist-lib/lib/dimensionPlanner.d.ts.map +1 -1
  34. package/dist-lib/lib/dimensionPlanner.js +20 -0
  35. package/dist-lib/lib/engineStatusTeksten.d.ts +1 -1
  36. package/dist-lib/lib/engineStatusTeksten.js +1 -1
  37. package/dist-lib/lib/flowRoute.d.ts.map +1 -1
  38. package/dist-lib/lib/flowRoute.js +2 -2
  39. package/dist-lib/lib/handlers/logistics-tick.server.d.ts +2 -0
  40. package/dist-lib/lib/handlers/logistics-tick.server.d.ts.map +1 -1
  41. package/dist-lib/lib/handlers/logistics-tick.server.js +259 -184
  42. package/dist-lib/lib/karindelingBewakingTeksten.d.ts +9 -0
  43. package/dist-lib/lib/karindelingBewakingTeksten.d.ts.map +1 -0
  44. package/dist-lib/lib/karindelingBewakingTeksten.js +26 -0
  45. package/dist-lib/lib/loadCarrierPlanning.server.d.ts +21 -0
  46. package/dist-lib/lib/loadCarrierPlanning.server.d.ts.map +1 -1
  47. package/dist-lib/lib/loadCarrierPlanning.server.js +162 -21
  48. package/dist-lib/lib/loadCarrierTypes.d.ts +2 -0
  49. package/dist-lib/lib/loadCarrierTypes.d.ts.map +1 -1
  50. package/dist-lib/lib/logisticsRecalcJob.server.d.ts.map +1 -1
  51. package/dist-lib/lib/logisticsRecalcJob.server.js +8 -1
  52. package/dist-lib/lib/moduleVersion.d.ts +1 -1
  53. package/dist-lib/lib/moduleVersion.js +1 -1
  54. package/dist-lib/lib/ruleEngineSchema.d.ts +2 -0
  55. package/dist-lib/lib/ruleEngineSchema.d.ts.map +1 -1
  56. package/dist-lib/lib/ruleEngineSchema.js +38 -1
  57. package/dist-lib/lib/scheduleTasks.server.d.ts +3 -1
  58. package/dist-lib/lib/scheduleTasks.server.d.ts.map +1 -1
  59. package/dist-lib/lib/scheduleTasks.server.js +5 -2
  60. package/dist-lib/lib/statusFase.d.ts +11 -0
  61. package/dist-lib/lib/statusFase.d.ts.map +1 -0
  62. package/dist-lib/lib/statusFase.js +58 -0
  63. package/dist-lib/lib/werkers.server.d.ts +17 -0
  64. package/dist-lib/lib/werkers.server.d.ts.map +1 -0
  65. package/dist-lib/lib/werkers.server.js +31 -0
  66. package/package.json +31 -1
@@ -10,6 +10,7 @@
10
10
  // met SUPABASE_PUBLISHABLE_KEY / SUPABASE_ANON_KEY.
11
11
  // ============================================================================
12
12
  import { MODULE_VERSION } from "../moduleVersion.js";
13
+ import { GEEN_WERKERS, werkersActief } from "../werkers.server.js";
13
14
  /** Herkenning in elk antwoord: de database ziet zo dat de Engine zelf antwoordde. */
14
15
  export function engineIdent(opts) {
15
16
  return { engine: "logistiek-engine", moduleVersion: MODULE_VERSION, moduleProject: opts?.moduleProject === true };
@@ -46,6 +47,8 @@ export async function handleLogisticsTick(request, supabase, opts) {
46
47
  export async function runLockedTickRound(supabase, ident) {
47
48
  // Alle databasetoegang loopt via de meegegeven client (geen gedeelde state).
48
49
  const { tickJob } = await import("../logisticsRecalcJob.server.js");
50
+ // Golf 1 stap 2: SQL-stappen die een databasewerker al doet, slaat deze ronde over.
51
+ const werkers = await werkersActief(supabase);
49
52
  // Hartslag: de schermen kunnen zo laten zien of de verwerking nog loopt.
50
53
  try {
51
54
  await supabase.rpc("logistics_tick_ping");
@@ -54,29 +57,32 @@ export async function runLockedTickRound(supabase, ident) {
54
57
  // Een ontbrekende hartslag mag de verwerking nooit tegenhouden.
55
58
  }
56
59
  // 1.0.172: ontbrekende standaardinstellingen aanvullen (bestaande waarden blijven).
57
- try {
58
- await supabase.rpc("logistics_ensure_runtime_defaults");
59
- }
60
- catch {
61
- // Niet aanvullen = fail closed (verzendbrieven wachten); ronde loopt door.
62
- }
60
+ if (!werkers.onderhoud)
61
+ try {
62
+ await supabase.rpc("logistics_ensure_runtime_defaults");
63
+ }
64
+ catch {
65
+ // Niet aanvullen = fail closed (verzendbrieven wachten); ronde loopt door.
66
+ }
63
67
  // 1.0.227: ontbrekende standaardgegevens (ladingdragers, Floriday-vertaling,
64
68
  // planinstellingen, tijdinstelling) aanvullen; schrijft niets als alles er is.
65
- try {
66
- await supabase.rpc("logistics_ensure_defaults");
67
- }
68
- catch {
69
- // Ontbreekt de functie nog (schemasync), dan alleen geen zelfherstel.
70
- }
69
+ if (!werkers.onderhoud)
70
+ try {
71
+ await supabase.rpc("logistics_ensure_defaults");
72
+ }
73
+ catch {
74
+ // Ontbreekt de functie nog (schemasync), dan alleen geen zelfherstel.
75
+ }
71
76
  // 1.0.194: "Opnieuw indelen bij wijziging" aanvullen gebeurt niet meer elke
72
77
  // ronde, maar hooguit 1x per uur in logistics_tick() (logistics_ensure_periodiek).
73
78
  // 1.0.190: open orders zonder kar opnieuw inplannen (max. 3 pogingen, daarna melding).
74
- try {
75
- await supabase.rpc("logistics_bewaak_zonder_kar");
76
- }
77
- catch {
78
- // Ontbreekt de functie nog (schemasync), dan alleen geen bewaking.
79
- }
79
+ if (!werkers.planning)
80
+ try {
81
+ await supabase.rpc("logistics_bewaak_zonder_kar");
82
+ }
83
+ catch {
84
+ // Ontbreekt de functie nog (schemasync), dan alleen geen bewaking.
85
+ }
80
86
  // Rondeslot: nooit twee tick-rondes tegelijk (minuut-cron én wek-aanroep
81
87
  // komen hier allebei binnen). Hergebruikt het bestaande planslot met
82
88
  // vervaltijd. Fail-closed: bij twijfel wordt de ronde overgeslagen; de
@@ -113,7 +119,7 @@ export async function runLockedTickRound(supabase, ident) {
113
119
  // Alleen meting; mag de ronde niet tegenhouden.
114
120
  }
115
121
  try {
116
- return await runTickRound(supabase, tickJob, ident, roundStart);
122
+ return await runTickRound(supabase, tickJob, ident, roundStart, werkers);
117
123
  }
118
124
  finally {
119
125
  try {
@@ -129,10 +135,19 @@ export async function runLockedTickRound(supabase, ident) {
129
135
  }
130
136
  /** Vaste sleutel van het rondeslot; gelijk aan de check in logistics_plan_kick(). */
131
137
  export const TICK_ROUND_LOCK_KEY = "tick:ronde";
132
- async function runTickRound(supabase, tickJob, ident, roundStart = new Date().toISOString()) {
138
+ /** Golf 1: hard budget van één HTTP-ronde (planwachtrij, karindeling, printen). */
139
+ export const RONDE_BUDGET_MS = 20000;
140
+ async function runTickRound(supabase, tickJob, ident, roundStart = new Date().toISOString(), werkers = { ...GEEN_WERKERS }) {
133
141
  const roundStartedMs = Date.now();
142
+ const VIA_WERKER = { via: "databasewerker" };
143
+ // Golf 1 (aanvulling A): hard budget voor de hele HTTP-ronde. Vóór elke stap
144
+ // wordt gekeken of er nog tijd is; wat niet past, loopt de volgende ronde.
145
+ const rondeEinde = roundStartedMs + RONDE_BUDGET_MS;
146
+ const resterend = () => rondeEinde - Date.now();
147
+ const UITGESTELD = { uitgesteld: "volgende ronde (tijdslimiet van de ronde bereikt)" };
148
+ const uitgesteld = [];
134
149
  // Meerdere batches per aanroep, met een ruime marge tot de time-out.
135
- const deadline = Date.now() + 8000;
150
+ const deadline = Math.min(Date.now() + 8000, rondeEinde);
136
151
  let last = null;
137
152
  let ticks = 0;
138
153
  while (Date.now() < deadline) {
@@ -144,42 +159,45 @@ async function runTickRound(supabase, tickJob, ident, roundStart = new Date().to
144
159
  break;
145
160
  }
146
161
  // Ophaalmomenten rollend bijwerken vanuit de klantinstellingen (vervoerders).
147
- let pickupMoments = null;
148
- try {
149
- const { data, error } = await supabase.rpc("logistics_refresh_pickup_moments", {
150
- p_weeks: 4,
151
- });
152
- pickupMoments = error ? { error: error.message } : data;
153
- }
154
- catch (e) {
155
- pickupMoments = { error: String(e?.message ?? e) };
156
- }
162
+ let pickupMoments = werkers.planning ? VIA_WERKER : null;
163
+ if (!werkers.planning)
164
+ try {
165
+ const { data, error } = await supabase.rpc("logistics_refresh_pickup_moments", {
166
+ p_weeks: 4,
167
+ });
168
+ pickupMoments = error ? { error: error.message } : data;
169
+ }
170
+ catch (e) {
171
+ pickupMoments = { error: String(e?.message ?? e) };
172
+ }
157
173
  // Wees-taken van verwijderde orders afsluiten, zodat elke module dezelfde
158
174
  // verzameling orders ziet (één waarheid).
159
- let orphans = null;
160
- try {
161
- const { data, error } = await supabase.rpc("logistics_cancel_orphan_tasks");
162
- orphans = error ? { error: error.message } : data;
163
- }
164
- catch (e) {
165
- orphans = { error: String(e?.message ?? e) };
166
- }
175
+ let orphans = werkers.planning ? VIA_WERKER : null;
176
+ if (!werkers.planning)
177
+ try {
178
+ const { data, error } = await supabase.rpc("logistics_cancel_orphan_tasks");
179
+ orphans = error ? { error: error.message } : data;
180
+ }
181
+ catch (e) {
182
+ orphans = { error: String(e?.message ?? e) };
183
+ }
167
184
  // Vangnet: recente orders die nooit in de planwachtrij kwamen alsnog
168
185
  // aanmelden, zodat ze in deze zelfde ronde nog worden ingedeeld.
169
- let reconciledOrders = null;
170
- try {
171
- const { data, error } = await supabase.rpc("logistics_reconcile_missing_plan_queue");
172
- reconciledOrders = error ? { error: error.message } : data;
173
- }
174
- catch (e) {
175
- reconciledOrders = { error: String(e?.message ?? e) };
176
- }
186
+ let reconciledOrders = werkers.planning ? VIA_WERKER : null;
187
+ if (!werkers.planning)
188
+ try {
189
+ const { data, error } = await supabase.rpc("logistics_reconcile_missing_plan_queue");
190
+ reconciledOrders = error ? { error: error.message } : data;
191
+ }
192
+ catch (e) {
193
+ reconciledOrders = { error: String(e?.message ?? e) };
194
+ }
177
195
  // Planwachtrij: nieuwe/gewijzigde orders incrementeel indelen.
178
196
  let plan = null;
179
197
  let prune = null;
180
198
  try {
181
199
  const { runPlanQueue, prunePlanQueue } = await import("../planQueue.server.js");
182
- plan = await runPlanQueue(supabase, { deadlineAt: Date.now() + 15000 });
200
+ plan = await runPlanQueue(supabase, { deadlineAt: Math.min(Date.now() + 15000, rondeEinde) });
183
201
  // M12: een wek-aanroep kan net vóór de order zichtbaar of vrij is binnenkomen
184
202
  // (rij nog vergrendeld door de aanmaakende transactie). Dan kort nawachten
185
203
  // in plaats van tot de volgende minuutronde te wachten. Begrensd: max. 3 x.
@@ -187,11 +205,13 @@ async function runTickRound(supabase, tickJob, ident, roundStart = new Date().to
187
205
  const claimed = Number(plan?.claimed ?? 0);
188
206
  if (claimed > 0)
189
207
  break;
208
+ if (resterend() < 3000)
209
+ break;
190
210
  const { data: due, error: dueError } = await supabase.rpc("logistics_plan_due_count");
191
211
  if (dueError || Number(due ?? 0) === 0)
192
212
  break;
193
213
  await new Promise((r) => setTimeout(r, 1500));
194
- plan = await runPlanQueue(supabase, { deadlineAt: Date.now() + 15000 });
214
+ plan = await runPlanQueue(supabase, { deadlineAt: Math.min(Date.now() + 15000, rondeEinde) });
195
215
  }
196
216
  prune = await prunePlanQueue(supabase);
197
217
  }
@@ -211,16 +231,17 @@ async function runTickRound(supabase, tickJob, ident, roundStart = new Date().to
211
231
  // Afgehandelde orderbuffers sluiten, dat melden aan de andere modules en
212
232
  // orders die er ná de afhandeling in kwamen weer losmaken (fail closed:
213
233
  // karren met print-, scan- of controlebewijs blijven ongemoeid).
214
- let buffers = null;
215
- try {
216
- const { data, error } = await supabase.rpc("logistics_buffer_detach_late_orders", {
217
- p_buffer: null,
218
- });
219
- buffers = error ? { error: error.message } : data;
220
- }
221
- catch (e) {
222
- buffers = { error: String(e?.message ?? e) };
223
- }
234
+ let buffers = werkers.planning ? VIA_WERKER : null;
235
+ if (!werkers.planning)
236
+ try {
237
+ const { data, error } = await supabase.rpc("logistics_buffer_detach_late_orders", {
238
+ p_buffer: null,
239
+ });
240
+ buffers = error ? { error: error.message } : data;
241
+ }
242
+ catch (e) {
243
+ buffers = { error: String(e?.message ?? e) };
244
+ }
224
245
  // 1.0.194: de pickwachtrij wordt elke minuut in logistics_tick() (SQL) gevuld
225
246
  // en direct na elke indeling door de planwachtrij; niet meer in deze HTTP-ronde.
226
247
  const queueRefresh = "in_sql_ronde";
@@ -236,165 +257,210 @@ async function runTickRound(supabase, tickJob, ident, roundStart = new Date().to
236
257
  }
237
258
  // Floriday-pushwachtrij verwerken binnen dezelfde aanroep, met eigen marge.
238
259
  let push = null;
239
- try {
240
- const { runFloridayPushQueue, maybePruneFloridayPushes } = await import("../floridayPush.server.js");
241
- push = await runFloridayPushQueue(supabase, { deadlineAt: Date.now() + 8000 });
242
- await maybePruneFloridayPushes(supabase);
243
- }
244
- catch (e) {
245
- push = { error: String(e?.message ?? e) };
260
+ if (resterend() <= 0) {
261
+ push = UITGESTELD;
262
+ uitgesteld.push("Floriday-pushwachtrij");
246
263
  }
264
+ else
265
+ try {
266
+ const { runFloridayPushQueue, maybePruneFloridayPushes } = await import("../floridayPush.server.js");
267
+ push = await runFloridayPushQueue(supabase, { deadlineAt: Math.min(Date.now() + 8000, rondeEinde) });
268
+ await maybePruneFloridayPushes(supabase);
269
+ }
270
+ catch (e) {
271
+ push = { error: String(e?.message ?? e) };
272
+ }
247
273
  // Controlepost: goedgekeurde scans afronden met de juiste afleverbrief.
248
274
  let control = null;
249
- try {
250
- const { runControlScanQueue } = await import("../controlScan.server.js");
251
- control = await runControlScanQueue(supabase, { limit: 10 });
252
- }
253
- catch (e) {
254
- control = { error: String(e?.message ?? e) };
275
+ if (resterend() <= 0) {
276
+ control = UITGESTELD;
277
+ uitgesteld.push("Controlepost");
255
278
  }
279
+ else
280
+ try {
281
+ const { runControlScanQueue } = await import("../controlScan.server.js");
282
+ control = await runControlScanQueue(supabase, { limit: 10 });
283
+ }
284
+ catch (e) {
285
+ control = { error: String(e?.message ?? e) };
286
+ }
256
287
  // Verzendbrief die meelift met een geprinte pakbon (Floriday-orders).
257
288
  // Deze verwerking is verplicht: ontbreekt zij, dan is de draaiende versie te
258
289
  // oud en moet de tick dat als fout melden in plaats van "succes".
259
290
  let shippingNotes = null;
260
291
  let shippingNotesOk = false;
261
292
  let shippingNotesBackfill = null;
262
- try {
263
- // Inhaalslag: pakbonnen die al in het pickstation staan alsnog van een
264
- // verzendbrief voorzien. Idempotent, dus veilig bij elke tick.
265
- const { data: backfill } = await supabase.rpc("logistics_ensure_shipping_notes", {
266
- p_limit: 200,
267
- });
268
- shippingNotesBackfill = backfill ?? null;
269
- // 1.0.168: verzendstatus van voorraadmails overnemen uit de outbox.
270
- try {
271
- await supabase.rpc("logistics_sync_stock_alert_email_status", { p_limit: 500 });
272
- }
273
- catch {
274
- /* mag de ronde niet blokkeren */
275
- }
276
- // Zelfherstel: een eigen verzendbrief die bij een Floriday-order hoort wordt
277
- // geannuleerd en vervangen door de officiele Floriday-aanvraag.
278
- try {
279
- await supabase.rpc("logistics_fix_wrong_shipping_notes");
280
- }
281
- catch {
282
- /* zelfherstel mag de ronde niet blokkeren */
283
- }
284
- // Vrijgeven (alleen database): wachtende aanvragen van orders met een
285
- // goedgekeurde controlescan, zodra de poort het aanmaken toestaat.
293
+ if (resterend() <= 0) {
294
+ // Tijdslimiet bereikt: geen fout, de verzendbrieven volgen in de volgende ronde.
295
+ shippingNotes = { ...UITGESTELD };
296
+ shippingNotesOk = true;
297
+ uitgesteld.push("Verzendbrieven");
298
+ }
299
+ else
286
300
  try {
287
- await supabase.rpc("logistics_shipping_note_release", { _order_number: null });
288
- }
289
- catch {
290
- /* vrijgeven mag de ronde niet blokkeren */
301
+ // Inhaalslag: pakbonnen die al in het pickstation staan alsnog van een
302
+ // verzendbrief voorzien. Idempotent, dus veilig bij elke tick.
303
+ if (werkers.pickwachtrij) {
304
+ shippingNotesBackfill = VIA_WERKER;
305
+ }
306
+ else {
307
+ const { data: backfill } = await supabase.rpc("logistics_ensure_shipping_notes", {
308
+ p_limit: 200,
309
+ });
310
+ shippingNotesBackfill = backfill ?? null;
311
+ // 1.0.168: verzendstatus van voorraadmails overnemen uit de outbox.
312
+ try {
313
+ await supabase.rpc("logistics_sync_stock_alert_email_status", { p_limit: 500 });
314
+ }
315
+ catch {
316
+ /* mag de ronde niet blokkeren */
317
+ }
318
+ // Zelfherstel: een eigen verzendbrief die bij een Floriday-order hoort wordt
319
+ // geannuleerd en vervangen door de officiele Floriday-aanvraag.
320
+ try {
321
+ await supabase.rpc("logistics_fix_wrong_shipping_notes");
322
+ }
323
+ catch {
324
+ /* zelfherstel mag de ronde niet blokkeren */
325
+ }
326
+ // Vrijgeven (alleen database): wachtende aanvragen van orders met een
327
+ // goedgekeurde controlescan, zodra de poort het aanmaken toestaat.
328
+ try {
329
+ await supabase.rpc("logistics_shipping_note_release", { _order_number: null });
330
+ }
331
+ catch {
332
+ /* vrijgeven mag de ronde niet blokkeren */
333
+ }
334
+ // 1.0.153: belading door Floriday geaccepteerd (op welke manier ook
335
+ // verstuurd)? Dan per kar een verzendbriefaanvraag klaarzetten.
336
+ try {
337
+ await supabase.rpc("logistics_shipping_note_ensure_accepted", { p_order_id: null, p_limit: 100 });
338
+ }
339
+ catch {
340
+ /* mag de ronde niet blokkeren */
341
+ }
342
+ }
343
+ const { runShippingNoteQueue } = await import("../shippingNoteQueue.server.js");
344
+ if (typeof runShippingNoteQueue !== "function") {
345
+ throw new Error("Deze versie van de Logistiek Engine bevat geen verzendbriefverwerking. Werk de module bij.");
346
+ }
347
+ const result = (await runShippingNoteQueue(supabase, { limit: 10 }));
348
+ const verwacht = ["claimed", "printed", "waiting", "retrying", "failed"];
349
+ const ontbreekt = verwacht.filter((k) => typeof result?.[k] !== "number");
350
+ if (ontbreekt.length > 0) {
351
+ throw new Error(`De verzendbriefverwerking levert geen volledige telling (mist: ${ontbreekt.join(", ")}). Werk de module bij.`);
352
+ }
353
+ shippingNotes = {
354
+ found: result["claimed"],
355
+ printed: result["printed"],
356
+ waitingForFloriday: result["waiting"],
357
+ retrying: result["retrying"],
358
+ failed: result["failed"],
359
+ printJobsCreated: result["printed"],
360
+ backfill: shippingNotesBackfill,
361
+ errors: result["errors"] ?? [],
362
+ };
363
+ shippingNotesOk = true;
291
364
  }
292
- // 1.0.153: belading door Floriday geaccepteerd (op welke manier ook
293
- // verstuurd)? Dan per kar een verzendbriefaanvraag klaarzetten.
294
- try {
295
- await supabase.rpc("logistics_shipping_note_ensure_accepted", { p_order_id: null, p_limit: 100 });
365
+ catch (e) {
366
+ shippingNotes = { error: String(e?.message ?? e) };
367
+ shippingNotesOk = false;
296
368
  }
297
- catch {
298
- /* mag de ronde niet blokkeren */
299
- }
300
- const { runShippingNoteQueue } = await import("../shippingNoteQueue.server.js");
301
- if (typeof runShippingNoteQueue !== "function") {
302
- throw new Error("Deze versie van de Logistiek Engine bevat geen verzendbriefverwerking. Werk de module bij.");
303
- }
304
- const result = (await runShippingNoteQueue(supabase, { limit: 10 }));
305
- const verwacht = ["claimed", "printed", "waiting", "retrying", "failed"];
306
- const ontbreekt = verwacht.filter((k) => typeof result?.[k] !== "number");
307
- if (ontbreekt.length > 0) {
308
- throw new Error(`De verzendbriefverwerking levert geen volledige telling (mist: ${ontbreekt.join(", ")}). Werk de module bij.`);
309
- }
310
- shippingNotes = {
311
- found: result["claimed"],
312
- printed: result["printed"],
313
- waitingForFloriday: result["waiting"],
314
- retrying: result["retrying"],
315
- failed: result["failed"],
316
- printJobsCreated: result["printed"],
317
- backfill: shippingNotesBackfill,
318
- errors: result["errors"] ?? [],
319
- };
320
- shippingNotesOk = true;
321
- }
322
- catch (e) {
323
- shippingNotes = { error: String(e?.message ?? e) };
324
- shippingNotesOk = false;
325
- }
326
369
  // Stickerronde: klant- en tray-stickers (Engine is eigenaar).
327
370
  let stickers = null;
328
- try {
329
- const { runStickerQueue } = await import("../stickerQueue.server.js");
330
- stickers = await runStickerQueue(supabase, { limit: 10 });
331
- }
332
- catch (e) {
333
- stickers = { error: String(e?.message ?? e) };
371
+ if (resterend() <= 0) {
372
+ stickers = UITGESTELD;
373
+ uitgesteld.push("Stickerronde");
334
374
  }
375
+ else
376
+ try {
377
+ const { runStickerQueue } = await import("../stickerQueue.server.js");
378
+ stickers = await runStickerQueue(supabase, { limit: 10 });
379
+ }
380
+ catch (e) {
381
+ stickers = { error: String(e?.message ?? e) };
382
+ }
335
383
  // Automatisch versturen (1.0.149): alleen Floriday-orders, alleen als de werkwijze het aanzet.
336
384
  let autoSend = null;
337
- try {
338
- const { runAutoSendQueue } = await import("../autoSend.server.js");
339
- autoSend = await runAutoSendQueue(supabase, { limit: 10 });
340
- }
341
- catch (e) {
342
- autoSend = { error: String(e?.message ?? e) };
385
+ if (resterend() <= 0) {
386
+ autoSend = UITGESTELD;
387
+ uitgesteld.push("Automatisch versturen");
343
388
  }
389
+ else
390
+ try {
391
+ const { runAutoSendQueue } = await import("../autoSend.server.js");
392
+ autoSend = await runAutoSendQueue(supabase, { limit: 10 });
393
+ }
394
+ catch (e) {
395
+ autoSend = { error: String(e?.message ?? e) };
396
+ }
344
397
  // Seintjes van de pickschermen: karplan ontbreekt. Direct oppakken.
345
398
  let cartPlanRequests = null;
346
- try {
347
- const { runCartPlanRequests } = await import("../cartPlanRequests.server.js");
348
- cartPlanRequests = await runCartPlanRequests(supabase, { limit: 20 });
349
- }
350
- catch (e) {
351
- cartPlanRequests = { error: String(e?.message ?? e) };
399
+ if (resterend() <= 0) {
400
+ cartPlanRequests = UITGESTELD;
401
+ uitgesteld.push("Seintjes pickschermen");
352
402
  }
403
+ else
404
+ try {
405
+ const { runCartPlanRequests } = await import("../cartPlanRequests.server.js");
406
+ cartPlanRequests = await runCartPlanRequests(supabase, { limit: 20 });
407
+ }
408
+ catch (e) {
409
+ cartPlanRequests = { error: String(e?.message ?? e) };
410
+ }
353
411
  // In de karwerkwijze vervangen concrete karregels de oude orderregel. Een
354
412
  // reeds geprinte orderregel blijft als historie bestaan, maar mag niet meer
355
413
  // als actieve printregel met een verouderde status op het scherm staan.
356
414
  let queueCleanup = null;
357
- try {
358
- const { retireSupersededOrderRows } = await import("../pickQueueCleanup.server.js");
359
- queueCleanup = await retireSupersededOrderRows(supabase);
360
- }
361
- catch (e) {
362
- queueCleanup = { error: String(e?.message ?? e) };
415
+ if (resterend() <= 0) {
416
+ queueCleanup = UITGESTELD;
417
+ uitgesteld.push("Oude orderregels in pickwachtrij");
363
418
  }
419
+ else
420
+ try {
421
+ const { retireSupersededOrderRows } = await import("../pickQueueCleanup.server.js");
422
+ queueCleanup = await retireSupersededOrderRows(supabase);
423
+ }
424
+ catch (e) {
425
+ queueCleanup = { error: String(e?.message ?? e) };
426
+ }
364
427
  // Blokkades van verzonden orders sluiten, niet-geleverde verlopen orders
365
428
  // archiveren en meldingen sluiten waarvan de oorzaak weg is.
366
- let housekeeping = null;
367
- try {
368
- const { data, error } = await supabase.rpc("logistics_plan_housekeeping");
369
- housekeeping = error ? { error: error.message } : data;
370
- }
371
- catch (e) {
372
- housekeeping = { error: String(e?.message ?? e) };
373
- }
429
+ let housekeeping = werkers.planning ? VIA_WERKER : null;
430
+ if (!werkers.planning)
431
+ try {
432
+ const { data, error } = await supabase.rpc("logistics_plan_housekeeping");
433
+ housekeeping = error ? { error: error.message } : data;
434
+ }
435
+ catch (e) {
436
+ housekeeping = { error: String(e?.message ?? e) };
437
+ }
374
438
  // Pakbonnen zonder kar, of van een kar die na een herindeling niet meer
375
439
  // bestaat, kunnen nooit een barcode krijgen. Die worden hier afgesloten met
376
440
  // een leesbare reden in plaats van eeuwig te blijven wachten.
377
- let orphanPrintJobs = null;
378
- try {
379
- const { data, error } = await supabase.rpc("logistics_cleanup_orphan_print_jobs");
380
- orphanPrintJobs = error ? { error: error.message } : data;
381
- }
382
- catch (e) {
383
- orphanPrintJobs = { error: String(e?.message ?? e) };
384
- }
441
+ let orphanPrintJobs = werkers.pickwachtrij ? VIA_WERKER : null;
442
+ if (!werkers.pickwachtrij)
443
+ try {
444
+ const { data, error } = await supabase.rpc("logistics_cleanup_orphan_print_jobs");
445
+ orphanPrintJobs = error ? { error: error.message } : data;
446
+ }
447
+ catch (e) {
448
+ orphanPrintJobs = { error: String(e?.message ?? e) };
449
+ }
385
450
  // Barcodeverzoeken van de Layout maker: de Engine bepaalt de kar en levert
386
451
  // de vaste barcode terug in het verzoek én in de printopdracht. Kan het
387
452
  // niet, dan wordt het verzoek geannuleerd met een reden (fail closed).
388
- let barcodes = null;
389
- try {
390
- const { data, error } = await supabase.rpc("logistics_fulfill_barcode_requests", {
391
- p_limit: 100,
392
- });
393
- barcodes = error ? { error: error.message } : data;
394
- }
395
- catch (e) {
396
- barcodes = { error: String(e?.message ?? e) };
397
- }
453
+ let barcodes = werkers.pickwachtrij ? VIA_WERKER : null;
454
+ if (!werkers.pickwachtrij)
455
+ try {
456
+ const { data, error } = await supabase.rpc("logistics_fulfill_barcode_requests", {
457
+ p_limit: 100,
458
+ });
459
+ barcodes = error ? { error: error.message } : data;
460
+ }
461
+ catch (e) {
462
+ barcodes = { error: String(e?.message ?? e) };
463
+ }
398
464
  // 1.0.194: laadtoestand draait in logistics_tick() (SQL), alleen bij wijziging.
399
465
  const planState = "in_sql_ronde";
400
466
  // 1.0.194 (B1): kwam er tijdens deze ronde een wek-aanroep binnen, dan nog
@@ -405,9 +471,14 @@ async function runTickRound(supabase, tickJob, ident, roundStart = new Date().to
405
471
  if (pendingError) {
406
472
  kickPending = { error: pendingError.message };
407
473
  }
474
+ else if (pending === true && resterend() < 2000) {
475
+ // Geen tijd meer: de wek-aanroep wordt in de volgende ronde opgepakt.
476
+ kickPending = { extraRonde: false, ...UITGESTELD };
477
+ uitgesteld.push("Extra indeelronde na wek-aanroep");
478
+ }
408
479
  else if (pending === true) {
409
480
  const { runPlanQueue } = await import("../planQueue.server.js");
410
- kickPending = { extraRonde: await runPlanQueue(supabase, { deadlineAt: Date.now() + 10000 }) };
481
+ kickPending = { extraRonde: await runPlanQueue(supabase, { deadlineAt: Math.min(Date.now() + 10000, rondeEinde) }) };
411
482
  }
412
483
  else {
413
484
  kickPending = { extraRonde: false };
@@ -433,6 +504,8 @@ async function runTickRound(supabase, tickJob, ident, roundStart = new Date().to
433
504
  stickerSyncFouten,
434
505
  kickPending,
435
506
  shippingNotesOk,
507
+ werkers,
508
+ uitgesteld,
436
509
  },
437
510
  });
438
511
  }
@@ -442,6 +515,8 @@ async function runTickRound(supabase, tickJob, ident, roundStart = new Date().to
442
515
  return Response.json({
443
516
  ...ident,
444
517
  ok: shippingNotesOk,
518
+ werkers,
519
+ uitgesteld,
445
520
  stickerSyncFouten,
446
521
  kickPending,
447
522
  planState,
@@ -0,0 +1,9 @@
1
+ export type KarindelingBewaking = {
2
+ achterstand?: number;
3
+ productmatenMs?: number | null;
4
+ tijdelijkOpnieuw?: number;
5
+ herstelTijdelijk?: number | null;
6
+ };
7
+ export declare function karindelingMeldingen(info: KarindelingBewaking): string[];
8
+ export declare function karindelingInfo(info: KarindelingBewaking): string[];
9
+ //# sourceMappingURL=karindelingBewakingTeksten.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"karindelingBewakingTeksten.d.ts","sourceRoot":"","sources":["../../src/lib/karindelingBewakingTeksten.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,mBAAmB,GAAG;IAChC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAClC,CAAC;AAEF,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,mBAAmB,GAAG,MAAM,EAAE,CAYxE;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,mBAAmB,GAAG,MAAM,EAAE,CAWnE"}
@@ -0,0 +1,26 @@
1
+ // Meldingen in gewone taal voor de bewaking van de karindeling in Engine-status (alleen beheer).
2
+ export function karindelingMeldingen(info) {
3
+ const uit = [];
4
+ const achter = Number(info.achterstand ?? 0);
5
+ if (achter > 0) {
6
+ uit.push(`De karindeling loopt achter: ${achter} ${achter === 1 ? "order wacht" : "orders wachten"} langer dan 10 minuten.`);
7
+ }
8
+ const ms = Number(info.productmatenMs ?? 0);
9
+ if (ms > 3000) {
10
+ const s = (ms / 1000).toFixed(1).replace(".", ",");
11
+ uit.push(`Het lezen van productmaten duurt te lang (${s} s).`);
12
+ }
13
+ return uit;
14
+ }
15
+ export function karindelingInfo(info) {
16
+ const uit = [];
17
+ const opnieuw = Number(info.tijdelijkOpnieuw ?? 0);
18
+ if (opnieuw > 0) {
19
+ uit.push(`${opnieuw} ${opnieuw === 1 ? "order wordt" : "orders worden"} na een tijdslimiet automatisch opnieuw ingedeeld.`);
20
+ }
21
+ const herstel = Number(info.herstelTijdelijk ?? 0);
22
+ if (herstel > 0) {
23
+ uit.push(`Bij de update ${herstel === 1 ? "is 1 order" : `zijn ${herstel} orders`} die door een tijdslimiet vaststond${herstel === 1 ? "" : "en"} opnieuw aangeboden.`);
24
+ }
25
+ return uit;
26
+ }