@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.
- package/db/schema/logistiek-engine.sql +8794 -33
- package/dist-lib/components/BeheerPage.d.ts.map +1 -1
- package/dist-lib/components/BeheerPage.js +2 -1
- package/dist-lib/components/DataGereedheidCard.d.ts +1 -0
- package/dist-lib/components/DataGereedheidCard.d.ts.map +1 -1
- package/dist-lib/components/DataGereedheidCard.js +4 -3
- package/dist-lib/components/GeblokkeerdeOrdersCard.d.ts.map +1 -1
- package/dist-lib/components/GeblokkeerdeOrdersCard.js +14 -3
- package/dist-lib/components/GereedheidCard.d.ts +14 -0
- package/dist-lib/components/GereedheidCard.d.ts.map +1 -0
- package/dist-lib/components/GereedheidCard.js +60 -0
- package/dist-lib/components/LadingdragersPage.d.ts.map +1 -1
- package/dist-lib/components/LadingdragersPage.js +5 -1
- package/dist-lib/components/MinuutrondeCard.js +1 -1
- package/dist-lib/components/StandaardgegevensCard.d.ts.map +1 -1
- package/dist-lib/components/StandaardgegevensCard.js +25 -1
- package/dist-lib/components/StartlijstCard.d.ts.map +1 -1
- package/dist-lib/components/StartlijstCard.js +4 -1
- package/dist-lib/components/rule-engine/RuleFieldInput.d.ts.map +1 -1
- package/dist-lib/components/rule-engine/RuleFieldInput.js +1 -1
- package/dist-lib/hooks/useGeblokkeerdeOrders.d.ts +14 -0
- package/dist-lib/hooks/useGeblokkeerdeOrders.d.ts.map +1 -1
- package/dist-lib/hooks/useGeblokkeerdeOrders.js +40 -3
- package/dist-lib/index.d.ts +1 -0
- package/dist-lib/index.d.ts.map +1 -1
- package/dist-lib/index.js +1 -0
- package/dist-lib/integrations/supabase/types.d.ts +3014 -85
- package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
- package/dist-lib/lib/beladingRegels.d.ts +17 -0
- package/dist-lib/lib/beladingRegels.d.ts.map +1 -1
- package/dist-lib/lib/beladingRegels.js +29 -3
- package/dist-lib/lib/dimensionPlanner.d.ts +10 -0
- package/dist-lib/lib/dimensionPlanner.d.ts.map +1 -1
- package/dist-lib/lib/dimensionPlanner.js +20 -0
- package/dist-lib/lib/engineStatusTeksten.d.ts +1 -1
- package/dist-lib/lib/engineStatusTeksten.js +1 -1
- package/dist-lib/lib/flowRoute.d.ts.map +1 -1
- package/dist-lib/lib/flowRoute.js +2 -2
- package/dist-lib/lib/handlers/logistics-tick.server.d.ts +2 -0
- package/dist-lib/lib/handlers/logistics-tick.server.d.ts.map +1 -1
- package/dist-lib/lib/handlers/logistics-tick.server.js +259 -184
- package/dist-lib/lib/karindelingBewakingTeksten.d.ts +9 -0
- package/dist-lib/lib/karindelingBewakingTeksten.d.ts.map +1 -0
- package/dist-lib/lib/karindelingBewakingTeksten.js +26 -0
- package/dist-lib/lib/loadCarrierPlanning.server.d.ts +21 -0
- package/dist-lib/lib/loadCarrierPlanning.server.d.ts.map +1 -1
- package/dist-lib/lib/loadCarrierPlanning.server.js +162 -21
- package/dist-lib/lib/loadCarrierTypes.d.ts +2 -0
- package/dist-lib/lib/loadCarrierTypes.d.ts.map +1 -1
- package/dist-lib/lib/logisticsRecalcJob.server.d.ts.map +1 -1
- package/dist-lib/lib/logisticsRecalcJob.server.js +8 -1
- package/dist-lib/lib/moduleVersion.d.ts +1 -1
- package/dist-lib/lib/moduleVersion.js +1 -1
- package/dist-lib/lib/ruleEngineSchema.d.ts +2 -0
- package/dist-lib/lib/ruleEngineSchema.d.ts.map +1 -1
- package/dist-lib/lib/ruleEngineSchema.js +38 -1
- package/dist-lib/lib/scheduleTasks.server.d.ts +3 -1
- package/dist-lib/lib/scheduleTasks.server.d.ts.map +1 -1
- package/dist-lib/lib/scheduleTasks.server.js +5 -2
- package/dist-lib/lib/statusFase.d.ts +11 -0
- package/dist-lib/lib/statusFase.d.ts.map +1 -0
- package/dist-lib/lib/statusFase.js +58 -0
- package/dist-lib/lib/werkers.server.d.ts +17 -0
- package/dist-lib/lib/werkers.server.d.ts.map +1 -0
- package/dist-lib/lib/werkers.server.js +31 -0
- 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
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
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
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
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
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
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
|
-
|
|
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
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
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
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
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
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
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
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
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
|
-
|
|
240
|
-
|
|
241
|
-
push
|
|
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
|
-
|
|
250
|
-
|
|
251
|
-
|
|
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
|
-
|
|
263
|
-
//
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
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
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
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
|
-
|
|
293
|
-
|
|
294
|
-
|
|
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
|
-
|
|
329
|
-
|
|
330
|
-
|
|
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
|
-
|
|
338
|
-
|
|
339
|
-
|
|
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
|
-
|
|
347
|
-
|
|
348
|
-
|
|
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
|
-
|
|
358
|
-
|
|
359
|
-
|
|
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
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
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
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
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
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
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
|
+
}
|