@flowselections/logistiek-engine 1.0.13 → 1.0.15
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/README.md +24 -24
- package/dist-lib/components/FloridayPushCard.d.ts.map +1 -1
- package/dist-lib/components/FloridayPushCard.js +7 -2
- package/dist-lib/components/PrintjobsCard.d.ts.map +1 -1
- package/dist-lib/components/PrintjobsCard.js +3 -1
- package/dist-lib/hooks/useFloridayPushes.d.ts +7 -0
- package/dist-lib/hooks/useFloridayPushes.d.ts.map +1 -1
- package/dist-lib/hooks/useFloridayPushes.js +11 -0
- package/dist-lib/hooks/usePrintjobs.d.ts +1 -0
- package/dist-lib/hooks/usePrintjobs.d.ts.map +1 -1
- package/dist-lib/hooks/usePrintjobs.js +17 -1
- package/dist-lib/integrations/supabase/auth-middleware.d.ts +336 -0
- package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/client.d.ts +1516 -159
- package/dist-lib/integrations/supabase/client.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/client.server.d.ts +336 -0
- package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/moduleClient.d.ts +336 -0
- package/dist-lib/integrations/supabase/moduleClient.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/previewAuthStorage.d.ts +6 -0
- package/dist-lib/integrations/supabase/previewAuthStorage.d.ts.map +1 -0
- package/dist-lib/integrations/supabase/previewAuthStorage.js +92 -0
- package/dist-lib/integrations/supabase/types.d.ts +343 -0
- package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
- package/dist-lib/lib/bulkLoadCarrierPlanning.server.d.ts +2 -0
- package/dist-lib/lib/bulkLoadCarrierPlanning.server.d.ts.map +1 -1
- package/dist-lib/lib/bulkLoadCarrierPlanning.server.js +11 -0
- package/dist-lib/lib/floridayDocuments.functions.d.ts +1008 -0
- package/dist-lib/lib/floridayDocuments.functions.d.ts.map +1 -1
- package/dist-lib/lib/floridayPush.functions.d.ts +336 -0
- package/dist-lib/lib/floridayPush.functions.d.ts.map +1 -1
- package/dist-lib/lib/floridayPush.server.d.ts +12 -0
- package/dist-lib/lib/floridayPush.server.d.ts.map +1 -1
- package/dist-lib/lib/floridayPush.server.js +41 -9
- package/dist-lib/lib/loadCarrierPlanning.functions.d.ts +1685 -30
- package/dist-lib/lib/loadCarrierPlanning.functions.d.ts.map +1 -1
- package/dist-lib/lib/loadCarrierPlanning.server.d.ts +27 -2
- package/dist-lib/lib/loadCarrierPlanning.server.d.ts.map +1 -1
- package/dist-lib/lib/loadCarrierPlanning.server.js +162 -30
- package/dist-lib/lib/loadCarrierTaskSync.server.js +1 -1
- package/dist-lib/lib/logisticsRecalcJob.functions.d.ts +672 -0
- package/dist-lib/lib/logisticsRecalcJob.functions.d.ts.map +1 -1
- package/dist-lib/lib/logisticsRecalculate.functions.d.ts +336 -0
- package/dist-lib/lib/logisticsRecalculate.functions.d.ts.map +1 -1
- package/dist-lib/lib/modulePrompts.d.ts.map +1 -1
- package/dist-lib/lib/modulePrompts.js +1009 -970
- package/dist-lib/lib/planQueue.server.d.ts +2 -0
- package/dist-lib/lib/planQueue.server.d.ts.map +1 -1
- package/dist-lib/lib/planQueue.server.js +22 -4
- package/package.json +1 -1
|
@@ -12,7 +12,27 @@ export declare function buildBufferKey(order: {
|
|
|
12
12
|
location: string;
|
|
13
13
|
};
|
|
14
14
|
export declare function resolveBufferForOrderInternal(sb: SupabaseClient, orderId: string): Promise<string | null>;
|
|
15
|
-
|
|
15
|
+
interface Belading {
|
|
16
|
+
plants_per_crate: number;
|
|
17
|
+
crates_per_layer: number;
|
|
18
|
+
layers_per_cart: number;
|
|
19
|
+
}
|
|
20
|
+
/** Waar de belading van een orderregel vandaan komt. */
|
|
21
|
+
export type BeladingSource = "floriday_packing" | "order_items" | "product" | "geen";
|
|
22
|
+
/**
|
|
23
|
+
* Zet een Floriday-verpakking om naar beladingwaarden.
|
|
24
|
+
* pieces_per_package -> planten per fust
|
|
25
|
+
* packages_per_layer -> fusten per laag
|
|
26
|
+
* layers_per_load_carrier -> lagen per ladingdrager
|
|
27
|
+
*/
|
|
28
|
+
export declare function beladingFromFloridayPacking(packing: unknown): Belading | null;
|
|
29
|
+
export interface ExpandOptions {
|
|
30
|
+
/** Beladingwaarden per order uit custom_fields.floriday_packing. */
|
|
31
|
+
packingByOrder?: Map<string, Belading>;
|
|
32
|
+
/** Wordt gevuld met de gebruikte bron per orderregel-sleutel. */
|
|
33
|
+
sources?: Map<string, BeladingSource>;
|
|
34
|
+
}
|
|
35
|
+
export declare function expandOrderItemsForPlanner(sb: SupabaseClient, rows: Array<Record<string, any>>, options?: ExpandOptions): Promise<PlannerInputItem[]>;
|
|
16
36
|
export declare function loadCarrierProfiles(sb: SupabaseClient): Promise<LoadCarrierTypeRow[]>;
|
|
17
37
|
export interface BeladingRules {
|
|
18
38
|
maxOrders: number | null;
|
|
@@ -33,7 +53,11 @@ export interface ReplanResult {
|
|
|
33
53
|
replacedManualCount: number;
|
|
34
54
|
usedFloridayPacking: boolean;
|
|
35
55
|
warnings: string[];
|
|
36
|
-
skipped?: "locked";
|
|
56
|
+
skipped?: "locked" | "blocked";
|
|
57
|
+
/** Leesbare reden waarom er niets is ingedeeld. */
|
|
58
|
+
blockedReason?: string;
|
|
59
|
+
/** Gebruikte beladingbronnen in deze buffer, met aantal orderregels per bron. */
|
|
60
|
+
beladingSources?: Record<string, number>;
|
|
37
61
|
carts?: PlannedLoadUnit[];
|
|
38
62
|
}
|
|
39
63
|
export interface ReplanOptions {
|
|
@@ -44,4 +68,5 @@ export interface ReplanOptions {
|
|
|
44
68
|
}
|
|
45
69
|
export declare function replanBufferInternal(sb: SupabaseClient, bufferId: string, options?: ReplanOptions): Promise<ReplanResult>;
|
|
46
70
|
export declare function replanOrderInternal(sb: SupabaseClient, orderId: string, options?: ReplanOptions): Promise<ReplanResult | null>;
|
|
71
|
+
export {};
|
|
47
72
|
//# sourceMappingURL=loadCarrierPlanning.server.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loadCarrierPlanning.server.d.ts","sourceRoot":"","sources":["../../src/lib/loadCarrierPlanning.server.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAGL,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACtB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAA0B,KAAK,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAiCrF,wBAAgB,cAAc,CAC5B,KAAK,EAAE;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,aAAa,CAAC,EAAE,GAAG,CAAA;CAAE,EACtG,IAAI,EAAE,MAAM,GACX;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAqCvD;AAID,wBAAsB,6BAA6B,CAAC,EAAE,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAmE/G;
|
|
1
|
+
{"version":3,"file":"loadCarrierPlanning.server.d.ts","sourceRoot":"","sources":["../../src/lib/loadCarrierPlanning.server.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAGL,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACtB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAA0B,KAAK,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAiCrF,wBAAgB,cAAc,CAC5B,KAAK,EAAE;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,aAAa,CAAC,EAAE,GAAG,CAAA;CAAE,EACtG,IAAI,EAAE,MAAM,GACX;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAqCvD;AAID,wBAAsB,6BAA6B,CAAC,EAAE,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAmE/G;AAMD,UAAU,QAAQ;IAChB,gBAAgB,EAAE,MAAM,CAAC;IACzB,gBAAgB,EAAE,MAAM,CAAC;IACzB,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,wDAAwD;AACxD,MAAM,MAAM,cAAc,GAAG,kBAAkB,GAAG,aAAa,GAAG,SAAS,GAAG,MAAM,CAAC;AAOrF;;;;;GAKG;AACH,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,OAAO,GAAG,QAAQ,GAAG,IAAI,CAQ7E;AAiCD,MAAM,WAAW,aAAa;IAC5B,oEAAoE;IACpE,cAAc,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IACvC,iEAAiE;IACjE,OAAO,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;CACvC;AAED,wBAAsB,0BAA0B,CAC9C,EAAE,EAAE,cAAc,EAClB,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,EAChC,OAAO,GAAE,aAAkB,GAC1B,OAAO,CAAC,gBAAgB,EAAE,CAAC,CA6H7B;AAMD,wBAAsB,mBAAmB,CAAC,EAAE,EAAE,cAAc,GAAG,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAO3F;AAUD,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,WAAW,EAAE,OAAO,CAAC;CACtB;AAOD,wFAAwF;AACxF,wBAAsB,iBAAiB,CAAC,EAAE,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAsBzF;AAED,2FAA2F;AAC3F,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,cAAc,EAAE,KAAK,EAAE,aAAa,GAAG,IAAI,GAAG,cAAc,CAaxG;AAWD,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,OAAO,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,mBAAmB,EAAE,OAAO,CAAC;IAC7B,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,OAAO,CAAC,EAAE,QAAQ,GAAG,SAAS,CAAC;IAC/B,mDAAmD;IACnD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,iFAAiF;IACjF,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzC,KAAK,CAAC,EAAE,eAAe,EAAE,CAAC;CAC3B;AAGD,MAAM,WAAW,aAAa;IAC5B,4CAA4C;IAC5C,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,sEAAsE;IACtE,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AAED,wBAAsB,oBAAoB,CACxC,EAAE,EAAE,cAAc,EAClB,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,aAAkB,GAC1B,OAAO,CAAC,YAAY,CAAC,CAyUvB;AAGD,wBAAsB,mBAAmB,CACvC,EAAE,EAAE,cAAc,EAClB,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,aAAkB,GAC1B,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAI9B"}
|
|
@@ -128,10 +128,53 @@ export async function resolveBufferForOrderInternal(sb, orderId) {
|
|
|
128
128
|
}
|
|
129
129
|
return bufferId;
|
|
130
130
|
}
|
|
131
|
-
|
|
132
|
-
|
|
131
|
+
const positive = (v) => {
|
|
132
|
+
const n = Number(v);
|
|
133
|
+
return Number.isFinite(n) && n > 0 ? Math.round(n) : null;
|
|
134
|
+
};
|
|
135
|
+
/**
|
|
136
|
+
* Zet een Floriday-verpakking om naar beladingwaarden.
|
|
137
|
+
* pieces_per_package -> planten per fust
|
|
138
|
+
* packages_per_layer -> fusten per laag
|
|
139
|
+
* layers_per_load_carrier -> lagen per ladingdrager
|
|
140
|
+
*/
|
|
141
|
+
export function beladingFromFloridayPacking(packing) {
|
|
142
|
+
if (!packing || typeof packing !== "object")
|
|
143
|
+
return null;
|
|
144
|
+
const p = packing;
|
|
145
|
+
const ppc = positive(p.pieces_per_package);
|
|
146
|
+
const cpl = positive(p.packages_per_layer);
|
|
147
|
+
const lpc = positive(p.layers_per_load_carrier);
|
|
148
|
+
if (ppc == null || cpl == null || lpc == null)
|
|
149
|
+
return null;
|
|
150
|
+
return { plants_per_crate: ppc, crates_per_layer: cpl, layers_per_cart: lpc };
|
|
133
151
|
}
|
|
134
|
-
|
|
152
|
+
/** Beladingbronnen in volgorde: Floriday-verpakking > orderregel > product > geen. */
|
|
153
|
+
function resolveBelading(row, packing) {
|
|
154
|
+
if (packing)
|
|
155
|
+
return { belading: packing, source: "floriday_packing" };
|
|
156
|
+
const fromRow = positive(row.plants_per_crate) != null || positive(row.crates_per_layer) != null || positive(row.layers_per_cart) != null
|
|
157
|
+
? {
|
|
158
|
+
plants_per_crate: positive(row.plants_per_crate) ?? 1,
|
|
159
|
+
crates_per_layer: positive(row.crates_per_layer) ?? 1,
|
|
160
|
+
layers_per_cart: positive(row.layers_per_cart) ?? 1,
|
|
161
|
+
}
|
|
162
|
+
: null;
|
|
163
|
+
if (fromRow)
|
|
164
|
+
return { belading: fromRow, source: "order_items" };
|
|
165
|
+
const p = row.products ?? null;
|
|
166
|
+
const fromProduct = p && (positive(p.plants_per_crate) != null || positive(p.crates_per_layer) != null || positive(p.layers_per_cart) != null)
|
|
167
|
+
? {
|
|
168
|
+
plants_per_crate: positive(p.plants_per_crate) ?? 1,
|
|
169
|
+
crates_per_layer: positive(p.crates_per_layer) ?? 1,
|
|
170
|
+
layers_per_cart: positive(p.layers_per_cart) ?? 1,
|
|
171
|
+
}
|
|
172
|
+
: null;
|
|
173
|
+
if (fromProduct)
|
|
174
|
+
return { belading: fromProduct, source: "product" };
|
|
175
|
+
return { belading: { plants_per_crate: 1, crates_per_layer: 1, layers_per_cart: 1 }, source: "geen" };
|
|
176
|
+
}
|
|
177
|
+
export async function expandOrderItemsForPlanner(sb, rows, options = {}) {
|
|
135
178
|
const out = [];
|
|
136
179
|
const groupIds = rows
|
|
137
180
|
.map((r) => r.product_id)
|
|
@@ -142,15 +185,13 @@ export async function expandOrderItemsForPlanner(sb, rows) {
|
|
|
142
185
|
groupSet = new Set((groups ?? []).map((g) => g.id));
|
|
143
186
|
}
|
|
144
187
|
for (const row of rows) {
|
|
145
|
-
const
|
|
146
|
-
|
|
147
|
-
crates_per_layer: Math.max(1, Number(row.crates_per_layer ?? row.products?.crates_per_layer ?? 1)),
|
|
148
|
-
layers_per_cart: Math.max(1, Number(row.layers_per_cart ?? row.products?.layers_per_cart ?? 1)),
|
|
149
|
-
};
|
|
188
|
+
const packing = row.order_id ? options.packingByOrder?.get(String(row.order_id)) ?? null : null;
|
|
189
|
+
const { belading: base, source } = resolveBelading(row, packing);
|
|
150
190
|
const plants = Math.max(0, Number(row.quantity ?? 0));
|
|
151
191
|
const isGroup = (row.product_id && groupSet.has(row.product_id)) ||
|
|
152
192
|
String(row.product_name ?? "").startsWith("[Groep]");
|
|
153
193
|
if (!isGroup) {
|
|
194
|
+
options.sources?.set(String(row.id), source);
|
|
154
195
|
out.push({
|
|
155
196
|
order_item_id: row.id,
|
|
156
197
|
order_id: row.order_id ?? null,
|
|
@@ -170,6 +211,7 @@ export async function expandOrderItemsForPlanner(sb, rows) {
|
|
|
170
211
|
.order("sort_order", { ascending: true });
|
|
171
212
|
const memberList = Array.isArray(members) ? members : [];
|
|
172
213
|
if (memberList.length === 0) {
|
|
214
|
+
options.sources?.set(String(row.id), source);
|
|
173
215
|
out.push({
|
|
174
216
|
order_item_id: row.id,
|
|
175
217
|
order_id: row.order_id ?? null,
|
|
@@ -195,22 +237,22 @@ export async function expandOrderItemsForPlanner(sb, rows) {
|
|
|
195
237
|
}
|
|
196
238
|
const resolved = memberList.map((m) => {
|
|
197
239
|
const p = prodByTrade.get(String(m.trade_item_id));
|
|
198
|
-
|
|
240
|
+
// Een Floriday-verpakking geldt voor de hele orderregel, dus ook voor de leden.
|
|
241
|
+
if (packing)
|
|
242
|
+
return { m, p, belading: { ...base }, source: "floriday_packing" };
|
|
243
|
+
const known = p &&
|
|
244
|
+
(positive(p.plants_per_crate) != null ||
|
|
245
|
+
positive(p.crates_per_layer) != null ||
|
|
246
|
+
positive(p.layers_per_cart) != null);
|
|
247
|
+
const belading = known
|
|
199
248
|
? {
|
|
200
|
-
plants_per_crate:
|
|
201
|
-
crates_per_layer:
|
|
202
|
-
layers_per_cart:
|
|
249
|
+
plants_per_crate: positive(p.plants_per_crate) ?? 1,
|
|
250
|
+
crates_per_layer: positive(p.crates_per_layer) ?? 1,
|
|
251
|
+
layers_per_cart: positive(p.layers_per_cart) ?? 1,
|
|
203
252
|
}
|
|
204
|
-
: {
|
|
205
|
-
return { m, p, belading };
|
|
253
|
+
: { ...base };
|
|
254
|
+
return { m, p, belading, source: (known ? "product" : source) };
|
|
206
255
|
});
|
|
207
|
-
// Fallback: sibling met bekende belading, anders de groepsregel zelf.
|
|
208
|
-
const known = resolved.find((r) => !isDefaultBelading(r.belading))?.belading;
|
|
209
|
-
for (const r of resolved) {
|
|
210
|
-
if (isDefaultBelading(r.belading)) {
|
|
211
|
-
r.belading = known ?? (isDefaultBelading(base) ? r.belading : base);
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
256
|
const weightSum = resolved.reduce((s, r) => s + Number(r.m.quantity ?? 1), 0) || resolved.length;
|
|
215
257
|
const shares = [];
|
|
216
258
|
let assigned = 0;
|
|
@@ -229,6 +271,7 @@ export async function expandOrderItemsForPlanner(sb, rows) {
|
|
|
229
271
|
resolved.forEach((r, idx) => {
|
|
230
272
|
if (shares[idx] <= 0)
|
|
231
273
|
return;
|
|
274
|
+
options.sources?.set(`${row.id}::${r.m.trade_item_id}`, r.source);
|
|
232
275
|
out.push({
|
|
233
276
|
order_item_id: `${row.id}::${r.m.trade_item_id}`,
|
|
234
277
|
order_id: row.order_id ?? null,
|
|
@@ -335,11 +378,23 @@ export async function replanBufferInternal(sb, bufferId, options = {}) {
|
|
|
335
378
|
...it,
|
|
336
379
|
customer_id: customerByOrder.get(it.order_id) ?? null,
|
|
337
380
|
}));
|
|
338
|
-
|
|
381
|
+
// Floriday-verpakking per order is de eerste beladingbron.
|
|
382
|
+
const packingByOrder = new Map();
|
|
383
|
+
for (const o of orderRows) {
|
|
384
|
+
const belading = beladingFromFloridayPacking(o.custom_fields?.floriday_packing);
|
|
385
|
+
if (belading)
|
|
386
|
+
packingByOrder.set(String(o.id), belading);
|
|
387
|
+
}
|
|
388
|
+
const sources = new Map();
|
|
389
|
+
const inputs = await expandOrderItemsForPlanner(sb, rawRows, { packingByOrder, sources });
|
|
339
390
|
const itemToOrder = new Map();
|
|
340
391
|
for (const i of inputs)
|
|
341
392
|
if (i.order_id)
|
|
342
393
|
itemToOrder.set(i.order_item_id, i.order_id);
|
|
394
|
+
const beladingSources = {};
|
|
395
|
+
for (const s of sources.values())
|
|
396
|
+
beladingSources[s] = (beladingSources[s] ?? 0) + 1;
|
|
397
|
+
console.log(`[logistiek-engine] buffer ${bufferId}: beladingbronnen ${JSON.stringify(beladingSources)}`);
|
|
343
398
|
// Bestaande beladingen tellen en wissen.
|
|
344
399
|
const { data: existingCarts } = await sb
|
|
345
400
|
.from("order_carts")
|
|
@@ -355,6 +410,41 @@ export async function replanBufferInternal(sb, bufferId, options = {}) {
|
|
|
355
410
|
const profiles = await loadCarrierProfiles(sb);
|
|
356
411
|
const rules = await loadBeladingRules(sb);
|
|
357
412
|
const defaultProfile = profiles.find((p) => p.is_default) ?? profileFor(profiles, "trolley");
|
|
413
|
+
// Guard: zonder beladingbron mag er niet gegokt worden. Ladingdragers zonder
|
|
414
|
+
// lagenmodel (los product / enkele eenheid) hebben de belading niet nodig.
|
|
415
|
+
const guardProfile = profileFor(profiles, options.carrierType ?? defaultProfile.code);
|
|
416
|
+
const needsBelading = guardProfile.capacity_model !== "single" && guardProfile.capacity_model !== "none";
|
|
417
|
+
const unknownItems = inputs.filter((i) => sources.get(i.order_item_id) === "geen" && i.plants > 0);
|
|
418
|
+
if (needsBelading && unknownItems.length > 0 && !options.dryRun) {
|
|
419
|
+
const reason = `Belading onbekend voor ${unknownItems.length} orderregel(s): geen Floriday-verpakking, ` +
|
|
420
|
+
`geen waarden op de orderregel en geen productinstelling. Er is niets ingedeeld.`;
|
|
421
|
+
console.error(`[logistiek-engine] buffer ${bufferId} geblokkeerd: ${reason}`);
|
|
422
|
+
const { error: blockError } = await sb.rpc("logistics_plan_block", {
|
|
423
|
+
p_order_ids: orderIds,
|
|
424
|
+
p_reason: reason,
|
|
425
|
+
p_details: {
|
|
426
|
+
bufferId,
|
|
427
|
+
path: "typescript",
|
|
428
|
+
carrier: guardProfile.code,
|
|
429
|
+
beladingSources,
|
|
430
|
+
items: unknownItems.slice(0, 20).map((i) => ({ id: i.order_item_id, product: i.product_name })),
|
|
431
|
+
},
|
|
432
|
+
});
|
|
433
|
+
if (blockError)
|
|
434
|
+
warnings.push(`Blokkade kon niet worden vastgelegd: ${blockError.message}`);
|
|
435
|
+
return {
|
|
436
|
+
ok: false,
|
|
437
|
+
bufferId,
|
|
438
|
+
orderIds,
|
|
439
|
+
cartsCreated: 0,
|
|
440
|
+
replacedManualCount: 0,
|
|
441
|
+
usedFloridayPacking: false,
|
|
442
|
+
warnings: [...warnings, reason],
|
|
443
|
+
skipped: "blocked",
|
|
444
|
+
blockedReason: reason,
|
|
445
|
+
beladingSources,
|
|
446
|
+
};
|
|
447
|
+
}
|
|
358
448
|
let plannedCarts = [];
|
|
359
449
|
let usedFloridayPacking = false;
|
|
360
450
|
const solo = orderRows.length === 1 ? orderRows[0] : null;
|
|
@@ -407,11 +497,39 @@ export async function replanBufferInternal(sb, bufferId, options = {}) {
|
|
|
407
497
|
c.cart_number = idx + 1;
|
|
408
498
|
});
|
|
409
499
|
}
|
|
410
|
-
// Veiligheidsgrens: een onrealistisch grote
|
|
411
|
-
//
|
|
500
|
+
// Veiligheidsgrens: een onrealistisch grote uitkomst wordt niet afgekapt maar
|
|
501
|
+
// geblokkeerd. Een half plan laat planten stilzwijgend uit de pickstroom vallen.
|
|
412
502
|
if (plannedCarts.length > MAX_CARTS_PER_BUFFER) {
|
|
413
|
-
|
|
414
|
-
|
|
503
|
+
const reason = `Deze orderbuffer levert ${plannedCarts.length} ladingdragers op, meer dan de grens van ` +
|
|
504
|
+
`${MAX_CARTS_PER_BUFFER}. Er is niets ingedeeld; controleer de aantallen of de belading van de producten.`;
|
|
505
|
+
console.error(`[logistiek-engine] buffer ${bufferId} geblokkeerd: ${reason}`);
|
|
506
|
+
if (!options.dryRun) {
|
|
507
|
+
const { error: blockError } = await sb.rpc("logistics_plan_block", {
|
|
508
|
+
p_order_ids: orderIds,
|
|
509
|
+
p_reason: reason,
|
|
510
|
+
p_details: {
|
|
511
|
+
bufferId,
|
|
512
|
+
path: "typescript",
|
|
513
|
+
carts: plannedCarts.length,
|
|
514
|
+
limit: MAX_CARTS_PER_BUFFER,
|
|
515
|
+
beladingSources,
|
|
516
|
+
},
|
|
517
|
+
});
|
|
518
|
+
if (blockError)
|
|
519
|
+
warnings.push(`Blokkade kon niet worden vastgelegd: ${blockError.message}`);
|
|
520
|
+
}
|
|
521
|
+
return {
|
|
522
|
+
ok: false,
|
|
523
|
+
bufferId,
|
|
524
|
+
orderIds,
|
|
525
|
+
cartsCreated: 0,
|
|
526
|
+
replacedManualCount,
|
|
527
|
+
usedFloridayPacking,
|
|
528
|
+
warnings: [...warnings, reason],
|
|
529
|
+
skipped: "blocked",
|
|
530
|
+
blockedReason: reason,
|
|
531
|
+
beladingSources,
|
|
532
|
+
};
|
|
415
533
|
}
|
|
416
534
|
if (options.dryRun) {
|
|
417
535
|
return {
|
|
@@ -460,8 +578,10 @@ export async function replanBufferInternal(sb, bufferId, options = {}) {
|
|
|
460
578
|
.insert(cartRows)
|
|
461
579
|
.select("id, cart_number");
|
|
462
580
|
if (error || !Array.isArray(insertedCarts)) {
|
|
463
|
-
|
|
464
|
-
|
|
581
|
+
const message = `Ladingdragers konden niet worden opgeslagen: ${error?.message ?? "onbekende fout"}`;
|
|
582
|
+
console.error(`[logistiek-engine] buffer ${bufferId}: ${message}`);
|
|
583
|
+
await sb.from("order_carts").delete().eq("buffer_id", bufferId);
|
|
584
|
+
throw new Error(message);
|
|
465
585
|
}
|
|
466
586
|
created += insertedCarts.length;
|
|
467
587
|
const idByNumber = new Map(insertedCarts.map((c) => [Number(c.cart_number), String(c.id)]));
|
|
@@ -490,8 +610,19 @@ export async function replanBufferInternal(sb, bufferId, options = {}) {
|
|
|
490
610
|
}
|
|
491
611
|
for (let s = 0; s < itemRows.length; s += 500) {
|
|
492
612
|
const slice = itemRows.slice(s, s + 500);
|
|
493
|
-
if (slice.length
|
|
494
|
-
|
|
613
|
+
if (slice.length === 0)
|
|
614
|
+
continue;
|
|
615
|
+
const { error: itemError } = await sb.from("order_cart_items").insert(slice);
|
|
616
|
+
if (itemError) {
|
|
617
|
+
// Ladingdragers zonder inhoud zijn een ongeldige uitkomst: alles terugdraaien
|
|
618
|
+
// en de planrun laten falen, zodat de wachtrij het opnieuw of geblokkeerd oppakt.
|
|
619
|
+
const message = `Inhoud van de ladingdragers kon niet worden opgeslagen: ${itemError.message}` +
|
|
620
|
+
(itemError.details ? ` — ${itemError.details}` : "") +
|
|
621
|
+
(itemError.hint ? ` (${itemError.hint})` : "");
|
|
622
|
+
console.error(`[logistiek-engine] buffer ${bufferId}: ${message}`);
|
|
623
|
+
await sb.from("order_carts").delete().eq("buffer_id", bufferId);
|
|
624
|
+
throw new Error(message);
|
|
625
|
+
}
|
|
495
626
|
}
|
|
496
627
|
}
|
|
497
628
|
return {
|
|
@@ -502,6 +633,7 @@ export async function replanBufferInternal(sb, bufferId, options = {}) {
|
|
|
502
633
|
replacedManualCount,
|
|
503
634
|
usedFloridayPacking,
|
|
504
635
|
warnings,
|
|
636
|
+
beladingSources,
|
|
505
637
|
carts: plannedCarts,
|
|
506
638
|
};
|
|
507
639
|
}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
// ============================================================================
|
|
7
7
|
const OPEN_STATUSES = ["planned", "ready", "in_progress"];
|
|
8
8
|
export async function attachIndelingToTasks(sb, result) {
|
|
9
|
-
if (!result || result.skipped
|
|
9
|
+
if (!result || result.skipped)
|
|
10
10
|
return 0;
|
|
11
11
|
const carts = Array.isArray(result.carts) ? result.carts : [];
|
|
12
12
|
if (result.orderIds.length === 0)
|