@flowselections/logistiek-engine 1.0.195 → 1.0.199
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 +1042 -13
- package/dist-lib/components/BeheerPage.d.ts.map +1 -1
- package/dist-lib/components/BeheerPage.js +3 -1
- package/dist-lib/components/DataGereedheidCard.d.ts +47 -0
- package/dist-lib/components/DataGereedheidCard.d.ts.map +1 -0
- package/dist-lib/components/DataGereedheidCard.js +133 -0
- package/dist-lib/components/GeblokkeerdeOrdersCard.d.ts.map +1 -1
- package/dist-lib/components/GeblokkeerdeOrdersCard.js +3 -1
- package/dist-lib/components/GeblokkeerdeStickersCard.d.ts.map +1 -1
- package/dist-lib/components/GeblokkeerdeStickersCard.js +11 -1
- package/dist-lib/components/LadingdragerVoorstellenCard.d.ts +4 -0
- package/dist-lib/components/LadingdragerVoorstellenCard.d.ts.map +1 -0
- package/dist-lib/components/LadingdragerVoorstellenCard.js +44 -0
- package/dist-lib/components/LadingdragersPage.d.ts.map +1 -1
- package/dist-lib/components/LadingdragersPage.js +2 -1
- package/dist-lib/components/StartlijstCard.d.ts +2 -0
- package/dist-lib/components/StartlijstCard.d.ts.map +1 -0
- package/dist-lib/components/StartlijstCard.js +54 -0
- package/dist-lib/hooks/useGeblokkeerdeOrders.d.ts.map +1 -1
- package/dist-lib/hooks/useGeblokkeerdeOrders.js +2 -0
- package/dist-lib/index.d.ts +3 -0
- package/dist-lib/index.d.ts.map +1 -1
- package/dist-lib/index.js +3 -0
- package/dist-lib/integrations/supabase/types.d.ts +341 -121
- package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/types.js +1 -0
- package/dist-lib/lib/beladingRegels.d.ts +22 -0
- package/dist-lib/lib/beladingRegels.d.ts.map +1 -1
- package/dist-lib/lib/beladingRegels.js +46 -4
- package/dist-lib/lib/dimensionData.server.js +15 -12
- package/dist-lib/lib/dimensionPlanner.d.ts +11 -0
- package/dist-lib/lib/dimensionPlanner.d.ts.map +1 -1
- package/dist-lib/lib/dimensionPlanner.js +30 -2
- package/dist-lib/lib/handlers/index.d.ts +1 -0
- package/dist-lib/lib/handlers/index.d.ts.map +1 -1
- package/dist-lib/lib/handlers/index.js +1 -0
- package/dist-lib/lib/handlers/logistics-plan-now.server.d.ts +18 -0
- package/dist-lib/lib/handlers/logistics-plan-now.server.d.ts.map +1 -0
- package/dist-lib/lib/handlers/logistics-plan-now.server.js +113 -0
- package/dist-lib/lib/handlers/logistics-tick.server.d.ts +5 -0
- package/dist-lib/lib/handlers/logistics-tick.server.d.ts.map +1 -1
- package/dist-lib/lib/handlers/logistics-tick.server.js +7 -0
- package/dist-lib/lib/invoerTeksten.d.ts +4 -0
- package/dist-lib/lib/invoerTeksten.d.ts.map +1 -0
- package/dist-lib/lib/invoerTeksten.js +19 -0
- package/dist-lib/lib/layoutPrint.server.d.ts +8 -0
- package/dist-lib/lib/layoutPrint.server.d.ts.map +1 -1
- package/dist-lib/lib/layoutPrint.server.js +32 -0
- package/dist-lib/lib/loadCarrierPlanning.server.d.ts +4 -0
- package/dist-lib/lib/loadCarrierPlanning.server.d.ts.map +1 -1
- package/dist-lib/lib/loadCarrierPlanning.server.js +209 -12
- package/dist-lib/lib/moduleVersion.d.ts +1 -1
- package/dist-lib/lib/moduleVersion.js +1 -1
- package/dist-lib/lib/ruleEngineSchema.d.ts.map +1 -1
- package/dist-lib/lib/ruleEngineSchema.js +14 -0
- package/dist-lib/lib/stickerQueue.server.d.ts +12 -0
- package/dist-lib/lib/stickerQueue.server.d.ts.map +1 -1
- package/dist-lib/lib/stickerQueue.server.js +40 -2
- package/package.json +11 -1
|
@@ -9,10 +9,11 @@ import { planLoadUnits, LOAD_CARRIER_PLANNER_VERSION, } from "./loadCarrierPlann
|
|
|
9
9
|
import { traceBatch } from "./trace.server.js";
|
|
10
10
|
import { MODULE_VERSION } from "./moduleVersion.js";
|
|
11
11
|
import { fallbackCarrierProfile } from "./loadCarrierTypes.js";
|
|
12
|
-
import {
|
|
12
|
+
import { WAAR_INVULLEN, gewichtOnbekendTekst, ladingdragerNietInBeheerTekst } from "./invoerTeksten.js";
|
|
13
|
+
import { planByDimensions, DIMENSION_PLANNER_VERSION, maxFustenPerLaag } from "./dimensionPlanner.js";
|
|
13
14
|
import { buildDimensionItems, carrierDimensions } from "./dimensionData.server.js";
|
|
14
15
|
import { zonderExternAfgehandeld } from "./externalFulfillment.js";
|
|
15
|
-
import { vulBeladingAan, deelUitFloridayVerpakking, plantenVanRegel, beladingBeoordeling, fustPerKarOnmogelijk, karrenOnwaarschijnlijk, verpakkingSleutel, controleerInvariant, VELD_NAAM, } from "./beladingRegels.js";
|
|
16
|
+
import { vulBeladingAan, deelUitFloridayVerpakking, plantenVanRegel, beladingBeoordeling, fysiekeBelading, fustPerKarOnmogelijk, karrenOnwaarschijnlijk, verpakkingSleutel, controleerInvariant, VELD_NAAM, } from "./beladingRegels.js";
|
|
16
17
|
// ---------------------------------------------------------------------------
|
|
17
18
|
// Buffer
|
|
18
19
|
// ---------------------------------------------------------------------------
|
|
@@ -327,10 +328,27 @@ async function bevestigdeSleutels(sb, orderIds) {
|
|
|
327
328
|
return set;
|
|
328
329
|
}
|
|
329
330
|
async function beladingOnwaarschijnlijk(sb, inputs, rawRows, profile, profielPerRegel) {
|
|
331
|
+
// 1.0.198: fustmaten en planthoogte (inclusief pot) uit één bron: logistics_product_maten
|
|
332
|
+
// (Voorraad als die functies bestaan, anders Floricode → Floriday → handmatig). Nooit gegokt.
|
|
333
|
+
const productIds = Array.from(new Set(inputs.map((i) => i.product_id).filter((x) => !!x)));
|
|
334
|
+
const maten = new Map();
|
|
335
|
+
if (productIds.length > 0) {
|
|
336
|
+
const { data, error } = await sb.rpc("logistics_product_maten", { p_product_ids: productIds });
|
|
337
|
+
if (error)
|
|
338
|
+
throw new Error(`Indeling geblokkeerd: maten konden niet worden gelezen (${error.message}).`);
|
|
339
|
+
for (const m of (Array.isArray(data) ? data : [])) {
|
|
340
|
+
maten.set(String(m.product_id), {
|
|
341
|
+
length_mm: getal(m.fust_length_mm),
|
|
342
|
+
width_mm: getal(m.fust_width_mm),
|
|
343
|
+
height_mm: getal(m.fust_height_mm),
|
|
344
|
+
plant: getal(m.plant_height_cm),
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
}
|
|
330
348
|
const fustIds = Array.from(new Set(rawRows.map((r) => r.products?.fust_type_id).filter((x) => !!x).map(String)));
|
|
331
349
|
const fustById = new Map();
|
|
332
350
|
if (fustIds.length > 0) {
|
|
333
|
-
const { data } = await sb.from("fust_types").select("id, name, capacity_units").in("id", fustIds);
|
|
351
|
+
const { data } = await sb.from("fust_types").select("id, name, capacity_units, length_mm, width_mm, height_mm").in("id", fustIds);
|
|
334
352
|
for (const f of (Array.isArray(data) ? data : []))
|
|
335
353
|
fustById.set(String(f.id), f);
|
|
336
354
|
}
|
|
@@ -338,21 +356,62 @@ async function beladingOnwaarschijnlijk(sb, inputs, rawRows, profile, profielPer
|
|
|
338
356
|
for (const r of rawRows)
|
|
339
357
|
if (r.product_id)
|
|
340
358
|
fustPerProduct.set(String(r.product_id), r.products?.fust_type_id ?? null);
|
|
359
|
+
const codes = Array.from(new Set([profile.code, ...Array.from(profielPerRegel?.values() ?? []).map((p) => p.code)]));
|
|
360
|
+
const drager = new Map();
|
|
361
|
+
{
|
|
362
|
+
const { data } = await sb
|
|
363
|
+
.from("load_carrier_types")
|
|
364
|
+
.select("code, name, layer_length_cm, layer_width_cm, max_height_cm, max_crates_per_layer")
|
|
365
|
+
.in("code", codes);
|
|
366
|
+
for (const d of (Array.isArray(data) ? data : [])) {
|
|
367
|
+
drager.set(String(d.code), {
|
|
368
|
+
naam: String(d.name ?? d.code),
|
|
369
|
+
l: getal(d.layer_length_cm),
|
|
370
|
+
b: getal(d.layer_width_cm),
|
|
371
|
+
h: getal(d.max_height_cm),
|
|
372
|
+
maxPerLaag: getal(d.max_crates_per_layer),
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
}
|
|
341
376
|
const kandidaten = [];
|
|
377
|
+
const onmogelijk = [];
|
|
342
378
|
const waarschuwingen = [];
|
|
343
379
|
for (const i of inputs) {
|
|
344
380
|
if (i.plants <= 0)
|
|
345
381
|
continue;
|
|
346
382
|
const fustId = i.product_id ? fustPerProduct.get(String(i.product_id)) ?? null : null;
|
|
383
|
+
const fust = fustId ? fustById.get(fustId) ?? null : null;
|
|
384
|
+
const prof = profielPerRegel?.get(i.order_item_id) ?? profile;
|
|
347
385
|
const oordeel = beladingBeoordeling({
|
|
348
386
|
belading: { plants_per_crate: i.plants_per_crate, crates_per_layer: i.crates_per_layer, layers_per_cart: i.layers_per_cart },
|
|
349
|
-
fust
|
|
350
|
-
maxLagenLadingdrager:
|
|
387
|
+
fust,
|
|
388
|
+
maxLagenLadingdrager: prof.layers_per_carrier,
|
|
351
389
|
metBron: Boolean(i.packing_authoritative),
|
|
352
390
|
});
|
|
353
391
|
for (const w of oordeel.waarschuwen) {
|
|
354
392
|
waarschuwingen.push({ order_id: i.order_id ?? null, sleutel: `lagen:${verpakkingSleutel(i.product_id, i)}`, tekst: `${i.product_name}: ${w}` });
|
|
355
393
|
}
|
|
394
|
+
const d = drager.get(prof.code);
|
|
395
|
+
const mt = i.product_id ? maten.get(String(i.product_id)) : undefined;
|
|
396
|
+
const fysiek = fysiekeBelading({
|
|
397
|
+
product: i.product_name,
|
|
398
|
+
bron: i.packing_authoritative ? "Floriday-verpakking" : "product",
|
|
399
|
+
crates_per_layer: i.crates_per_layer,
|
|
400
|
+
layers_per_cart: i.layers_per_cart,
|
|
401
|
+
fust_hoogte_cm: mt?.height_mm ? mt.height_mm / 10 : null,
|
|
402
|
+
plant_hoogte_cm: mt?.plant ?? null,
|
|
403
|
+
layerspace_max: maxFustenPerLaag(d?.l, d?.b, mt?.length_mm ? mt.length_mm / 10 : null, mt?.width_mm ? mt.width_mm / 10 : null),
|
|
404
|
+
ladingdrager: d?.naam ?? prof.name,
|
|
405
|
+
ladingdrager_hoogte_cm: d?.h ?? null,
|
|
406
|
+
max_crates_per_layer: d?.maxPerLaag ?? (prof.max_crates_per_layer ?? null),
|
|
407
|
+
standaard_lagen: prof.layers_per_carrier,
|
|
408
|
+
});
|
|
409
|
+
for (const w of fysiek.waarschuwen) {
|
|
410
|
+
waarschuwingen.push({ order_id: i.order_id ?? null, sleutel: `fysiek:${verpakkingSleutel(i.product_id, i)}`, tekst: w });
|
|
411
|
+
}
|
|
412
|
+
if (fysiek.blokkeren.length > 0) {
|
|
413
|
+
onmogelijk.push({ order_id: i.order_id ?? null, product: i.product_name, sleutel: verpakkingSleutel(i.product_id, i), redenen: fysiek.blokkeren });
|
|
414
|
+
}
|
|
356
415
|
if (oordeel.blokkeren.length === 0)
|
|
357
416
|
continue;
|
|
358
417
|
kandidaten.push({
|
|
@@ -363,9 +422,13 @@ async function beladingOnwaarschijnlijk(sb, inputs, rawRows, profile, profielPer
|
|
|
363
422
|
});
|
|
364
423
|
}
|
|
365
424
|
if (kandidaten.length === 0)
|
|
366
|
-
return { blokkeren: [], waarschuwingen };
|
|
425
|
+
return { blokkeren: [], onmogelijk, waarschuwingen };
|
|
367
426
|
const ok = await bevestigdeSleutels(sb, Array.from(new Set(kandidaten.map((k) => k.order_id).filter((x) => !!x))));
|
|
368
|
-
return { blokkeren: kandidaten.filter((k) => !ok.has(`${k.order_id}|${k.sleutel}`)), waarschuwingen };
|
|
427
|
+
return { blokkeren: kandidaten.filter((k) => !ok.has(`${k.order_id}|${k.sleutel}`)), onmogelijk, waarschuwingen };
|
|
428
|
+
}
|
|
429
|
+
function getal(v) {
|
|
430
|
+
const n = Number(v);
|
|
431
|
+
return v != null && Number.isFinite(n) && n > 0 ? n : null;
|
|
369
432
|
}
|
|
370
433
|
/** Standaardladingdrager uit de werkwijze (karrenindeling.default_carrier_type); alleen terugval. */
|
|
371
434
|
async function standaardLadingdragerUitWerkwijze(sb) {
|
|
@@ -545,6 +608,8 @@ export async function loadBeladingRules(sb) {
|
|
|
545
608
|
maxCustomersPerLayer: numOrNull(k.max_customers_per_layer),
|
|
546
609
|
optimalFill: k.optimal_fill !== false,
|
|
547
610
|
layoutMethod: k.cart_layout_method === "afmetingen" ? "afmetingen" : "belading",
|
|
611
|
+
aanvullenUitLadingdrager: k.aanvullen_uit_ladingdrager === true,
|
|
612
|
+
gewichtVerplicht: k.gewicht_verplicht === true,
|
|
548
613
|
};
|
|
549
614
|
}
|
|
550
615
|
/**
|
|
@@ -1292,6 +1357,22 @@ async function replanBufferUnlocked(sb, bufferId, options = {}) {
|
|
|
1292
1357
|
// Wat al op een bevroren kar ligt, wordt niet opnieuw ingedeeld.
|
|
1293
1358
|
let frozenCartCount = 0;
|
|
1294
1359
|
let cartNumberStart = 1;
|
|
1360
|
+
// 1.0.197: karren uit eerdere buffers van deze orders blijven bestaan en tellen mee in
|
|
1361
|
+
// de nummering ("van N"); nieuwe karren nummeren door na het hoogste actieve nummer.
|
|
1362
|
+
let andereKarren = [];
|
|
1363
|
+
if (!options.dryRun) {
|
|
1364
|
+
const { data: unit, error: unitError } = await sb.rpc("logistics_order_unit_carts", { p_order_ids: orderIds });
|
|
1365
|
+
if (unitError && !/logistics_order_unit_carts/.test(unitError.message)) {
|
|
1366
|
+
throw new Error(`Karren van de eenheid konden niet worden gelezen: ${unitError.message}`);
|
|
1367
|
+
}
|
|
1368
|
+
const keep = new Set(keepCartIds);
|
|
1369
|
+
const eigenOrders = new Set(orderIds);
|
|
1370
|
+
andereKarren = (Array.isArray(unit) ? unit : [])
|
|
1371
|
+
.filter((c) => !keep.has(String(c.cart_id)) &&
|
|
1372
|
+
c.buffer_id !== bufferId &&
|
|
1373
|
+
(c.order_id == null || !eigenOrders.has(String(c.order_id))))
|
|
1374
|
+
.map((c) => ({ cart_number: Number(c.cart_number) || 0 }));
|
|
1375
|
+
}
|
|
1295
1376
|
if (isSupplement) {
|
|
1296
1377
|
const { data: keptCarts, error: keptError } = await sb
|
|
1297
1378
|
.from("order_carts")
|
|
@@ -1406,32 +1487,72 @@ async function replanBufferUnlocked(sb, bufferId, options = {}) {
|
|
|
1406
1487
|
const hoofdCode = [...telling.entries()].sort((a, b) => b[1] - a[1])[0]?.[0] ?? null;
|
|
1407
1488
|
const defaultProfile = { code: options.carrierType ?? hoofdCode ?? "" };
|
|
1408
1489
|
const profielPerRegel = new Map();
|
|
1490
|
+
// 1.0.196 (E2): een ladingdragercode die niet in beheer staat wordt nooit met een
|
|
1491
|
+
// ingebouwd profiel doorgerekend, maar blokkeert met 'ladingdrager_niet_in_beheer'.
|
|
1492
|
+
const bekendeCodes = new Set(profiles.map((p) => p.code));
|
|
1493
|
+
const nietInBeheer = new Map();
|
|
1409
1494
|
for (const i of inputs) {
|
|
1410
1495
|
const c = ladingdragerPerRegel.get(i.order_item_id);
|
|
1411
|
-
if (c)
|
|
1412
|
-
|
|
1496
|
+
if (!c)
|
|
1497
|
+
continue;
|
|
1498
|
+
if (!bekendeCodes.has(c)) {
|
|
1499
|
+
const l = nietInBeheer.get(c) ?? [];
|
|
1500
|
+
l.push(i.product_name);
|
|
1501
|
+
nietInBeheer.set(c, l);
|
|
1502
|
+
continue;
|
|
1503
|
+
}
|
|
1504
|
+
profielPerRegel.set(i.order_item_id, profileFor(profiles, c));
|
|
1505
|
+
}
|
|
1506
|
+
if (options.carrierType && !bekendeCodes.has(options.carrierType))
|
|
1507
|
+
nietInBeheer.set(options.carrierType, []);
|
|
1508
|
+
if (nietInBeheer.size > 0 && !options.dryRun) {
|
|
1509
|
+
const reason = ladingdragerNietInBeheerTekst(nietInBeheer);
|
|
1510
|
+
const { error: blockError } = await sb.rpc("logistics_plan_block", {
|
|
1511
|
+
p_order_ids: orderIds,
|
|
1512
|
+
p_reason: reason,
|
|
1513
|
+
p_details: {
|
|
1514
|
+
reason_code: "ladingdrager_niet_in_beheer",
|
|
1515
|
+
bufferId,
|
|
1516
|
+
path: "typescript",
|
|
1517
|
+
codes: [...nietInBeheer.keys()],
|
|
1518
|
+
waarInvullen: "Engine → Ladingdragers",
|
|
1519
|
+
},
|
|
1520
|
+
});
|
|
1521
|
+
if (blockError)
|
|
1522
|
+
warnings.push(`Blokkade kon niet worden vastgelegd: ${blockError.message}`);
|
|
1523
|
+
return {
|
|
1524
|
+
ok: false, bufferId, orderIds, cartsCreated: 0, replacedManualCount: 0, usedFloridayPacking: false,
|
|
1525
|
+
warnings: [...warnings, reason], skipped: "blocked", blockedReason: reason, beladingSources,
|
|
1526
|
+
};
|
|
1413
1527
|
}
|
|
1414
1528
|
// Guard: zonder beladingbron mag er niet gegokt worden. Ladingdragers zonder
|
|
1415
1529
|
// lagenmodel (los product / enkele eenheid) hebben de belading niet nodig.
|
|
1416
1530
|
const guardProfile = profileFor(profiles, options.carrierType ?? defaultProfile.code);
|
|
1417
1531
|
const needsBelading = guardProfile.capacity_model !== "single" && guardProfile.capacity_model !== "none";
|
|
1418
1532
|
const useDimensions = rules?.layoutMethod === "afmetingen";
|
|
1419
|
-
// 1.0.179
|
|
1533
|
+
// 1.0.179 → 1.0.196 (E1): ontbrekende fusten per laag en lagen alleen aanvullen
|
|
1534
|
+
// uit de ladingdrager als de werkwijze-instelling aan staat; dan zichtbaar per order.
|
|
1420
1535
|
const standaard = {
|
|
1421
1536
|
crates_per_layer: positive(guardProfile.max_crates_per_layer),
|
|
1422
1537
|
layers_per_cart: positive(guardProfile.layers_per_carrier),
|
|
1423
1538
|
};
|
|
1539
|
+
const magAanvullen = rules?.aanvullenUitLadingdrager === true;
|
|
1424
1540
|
const onvolledig = [];
|
|
1541
|
+
const aangevuldPerOrder = new Map();
|
|
1425
1542
|
for (const i of inputs) {
|
|
1426
1543
|
const mist = ontbrekendeVelden.get(i.order_item_id);
|
|
1427
1544
|
if (!mist || mist.length === 0)
|
|
1428
1545
|
continue;
|
|
1429
1546
|
const rest = [];
|
|
1430
1547
|
for (const v of mist) {
|
|
1431
|
-
const w = v === "plants_per_crate" ? null : standaard[v] ?? null;
|
|
1548
|
+
const w = !magAanvullen || v === "plants_per_crate" ? null : standaard[v] ?? null;
|
|
1432
1549
|
if (w != null) {
|
|
1433
1550
|
i[v] = w;
|
|
1434
1551
|
i.packing_authoritative = false;
|
|
1552
|
+
const oid = String(i.order_id ?? "");
|
|
1553
|
+
const l = aangevuldPerOrder.get(oid) ?? [];
|
|
1554
|
+
l.push(`${i.product_name}: ${VELD_NAAM[v]} = ${w} (uit ${guardProfile.name ?? guardProfile.code})`);
|
|
1555
|
+
aangevuldPerOrder.set(oid, l);
|
|
1435
1556
|
}
|
|
1436
1557
|
else
|
|
1437
1558
|
rest.push(v);
|
|
@@ -1439,6 +1560,45 @@ async function replanBufferUnlocked(sb, bufferId, options = {}) {
|
|
|
1439
1560
|
if (rest.length > 0 && i.plants > 0)
|
|
1440
1561
|
onvolledig.push({ item: i, velden: rest });
|
|
1441
1562
|
}
|
|
1563
|
+
for (const [oid, regels] of aangevuldPerOrder) {
|
|
1564
|
+
beladingWaarschuwingen.push({
|
|
1565
|
+
order_id: oid || null,
|
|
1566
|
+
sleutel: "aangevuld_uit_ladingdrager",
|
|
1567
|
+
tekst: `Aangevuld uit de ladingdrager (instelling staat aan): ${regels.slice(0, 5).join("; ")}. Vul de echte waarde in bij het product.`,
|
|
1568
|
+
});
|
|
1569
|
+
}
|
|
1570
|
+
// 1.0.196 (E3): gewicht per fust onbekend → kg-grens niet gecontroleerd.
|
|
1571
|
+
// 1.0.198: gewicht alleen relevant als de ladingdrager een kg-grens heeft.
|
|
1572
|
+
const heeftKgGrens = Number(guardProfile.max_weight_kg) > 0;
|
|
1573
|
+
const zonderGewicht = heeftKgGrens ? inputs.filter((i) => !(Number(i.weight_per_crate) > 0) && i.plants > 0) : [];
|
|
1574
|
+
if (zonderGewicht.length > 0 && rules?.gewichtVerplicht === true && !options.dryRun) {
|
|
1575
|
+
const reason = gewichtOnbekendTekst(zonderGewicht.map((i) => i.product_name));
|
|
1576
|
+
const { error: blockError } = await sb.rpc("logistics_plan_block", {
|
|
1577
|
+
p_order_ids: orderIds,
|
|
1578
|
+
p_reason: reason,
|
|
1579
|
+
p_details: { reason_code: "gewicht_onbekend", bufferId, path: "typescript",
|
|
1580
|
+
items: zonderGewicht.slice(0, 20).map((i) => ({ id: i.order_item_id, product: i.product_name, velden: ["weight"] })) },
|
|
1581
|
+
});
|
|
1582
|
+
if (blockError)
|
|
1583
|
+
warnings.push(`Blokkade kon niet worden vastgelegd: ${blockError.message}`);
|
|
1584
|
+
return {
|
|
1585
|
+
ok: false, bufferId, orderIds, cartsCreated: 0, replacedManualCount: 0, usedFloridayPacking: false,
|
|
1586
|
+
warnings: [...warnings, reason], skipped: "blocked", blockedReason: reason, beladingSources,
|
|
1587
|
+
};
|
|
1588
|
+
}
|
|
1589
|
+
if (zonderGewicht.length > 0) {
|
|
1590
|
+
const perOrder = new Map();
|
|
1591
|
+
for (const i of zonderGewicht) {
|
|
1592
|
+
const oid = String(i.order_id ?? "");
|
|
1593
|
+
const l = perOrder.get(oid) ?? [];
|
|
1594
|
+
if (!l.includes(i.product_name))
|
|
1595
|
+
l.push(i.product_name);
|
|
1596
|
+
perOrder.set(oid, l);
|
|
1597
|
+
}
|
|
1598
|
+
for (const [oid, namen] of perOrder) {
|
|
1599
|
+
beladingWaarschuwingen.push({ order_id: oid || null, sleutel: "gewicht_onbekend", tekst: gewichtOnbekendTekst(namen) });
|
|
1600
|
+
}
|
|
1601
|
+
}
|
|
1442
1602
|
// Planten per fust is altijd nodig (ook zonder lagenmodel): fusten = planten / planten per fust.
|
|
1443
1603
|
const blokkeerOnvolledig = onvolledig.filter((o) => o.velden.includes("plants_per_crate") || (needsBelading && !useDimensions));
|
|
1444
1604
|
if ((blokkeerOnvolledig.length > 0 || onbekendePlanten.length > 0) && !options.dryRun) {
|
|
@@ -1447,7 +1607,7 @@ async function replanBufferUnlocked(sb, bufferId, options = {}) {
|
|
|
1447
1607
|
...onbekendePlanten.slice(0, 10).map((o) => `${o.naam}: eenheid fust zonder planten per fust`),
|
|
1448
1608
|
];
|
|
1449
1609
|
const reason = `Belading onvolledig voor ${blokkeerOnvolledig.length + onbekendePlanten.length} orderregel(s): ${delen.join("; ")}. ` +
|
|
1450
|
-
|
|
1610
|
+
WAAR_INVULLEN;
|
|
1451
1611
|
console.error(`[logistiek-engine] buffer ${bufferId} geblokkeerd: ${reason}`);
|
|
1452
1612
|
const { error: blockError } = await sb.rpc("logistics_plan_block", {
|
|
1453
1613
|
p_order_ids: orderIds,
|
|
@@ -1483,6 +1643,39 @@ async function replanBufferUnlocked(sb, bufferId, options = {}) {
|
|
|
1483
1643
|
if (!options.dryRun) {
|
|
1484
1644
|
const oordeel = await beladingOnwaarschijnlijk(sb, inputs, rawRows, guardProfile, profielPerRegel);
|
|
1485
1645
|
const onwaarschijnlijk = oordeel.blokkeren;
|
|
1646
|
+
// 1.0.197: fysiek onmogelijk = altijd blokkeren, niet te bevestigen; alleen gegevens aanpassen helpt.
|
|
1647
|
+
if (oordeel.onmogelijk.length > 0) {
|
|
1648
|
+
beladingWaarschuwingen.push(...oordeel.waarschuwingen);
|
|
1649
|
+
await bewaarBeladingWaarschuwingen(sb, orderIds, beladingWaarschuwingen, warnings);
|
|
1650
|
+
const reason = `Belading fysiek onmogelijk: ${oordeel.onmogelijk.slice(0, 10).map((o) => o.redenen.join("; ")).join(" | ")}. ` +
|
|
1651
|
+
"Pas de gegevens aan in Beheer → Gegevens voor karindeling; bevestigen is hier niet mogelijk.";
|
|
1652
|
+
const { error: blockError } = await sb.rpc("logistics_plan_block", {
|
|
1653
|
+
p_order_ids: orderIds,
|
|
1654
|
+
p_reason: reason,
|
|
1655
|
+
p_details: {
|
|
1656
|
+
reason_code: "belading_onwaarschijnlijk",
|
|
1657
|
+
soort: "fysiek_onmogelijk",
|
|
1658
|
+
bevestigbaar: false,
|
|
1659
|
+
bufferId,
|
|
1660
|
+
path: "typescript",
|
|
1661
|
+
onmogelijk: oordeel.onmogelijk.slice(0, 50),
|
|
1662
|
+
},
|
|
1663
|
+
});
|
|
1664
|
+
if (blockError)
|
|
1665
|
+
warnings.push(`Blokkade kon niet worden vastgelegd: ${blockError.message}`);
|
|
1666
|
+
return {
|
|
1667
|
+
ok: false,
|
|
1668
|
+
bufferId,
|
|
1669
|
+
orderIds,
|
|
1670
|
+
cartsCreated: 0,
|
|
1671
|
+
replacedManualCount: 0,
|
|
1672
|
+
usedFloridayPacking: false,
|
|
1673
|
+
warnings: [...warnings, reason],
|
|
1674
|
+
skipped: "blocked",
|
|
1675
|
+
blockedReason: reason,
|
|
1676
|
+
beladingSources,
|
|
1677
|
+
};
|
|
1678
|
+
}
|
|
1486
1679
|
beladingWaarschuwingen.push(...oordeel.waarschuwingen);
|
|
1487
1680
|
for (const w of beladingWaarschuwingen)
|
|
1488
1681
|
warnings.push(w.tekst);
|
|
@@ -1511,6 +1704,10 @@ async function replanBufferUnlocked(sb, bufferId, options = {}) {
|
|
|
1511
1704
|
};
|
|
1512
1705
|
}
|
|
1513
1706
|
}
|
|
1707
|
+
if (andereKarren.length > 0) {
|
|
1708
|
+
frozenCartCount += andereKarren.length;
|
|
1709
|
+
cartNumberStart = Math.max(cartNumberStart, andereKarren.reduce((m, c) => Math.max(m, c.cart_number), 0) + 1);
|
|
1710
|
+
}
|
|
1514
1711
|
let plannedCarts = [];
|
|
1515
1712
|
// Laagindeling van de afmetingenmethode, per cart_number. Alleen gevuld als
|
|
1516
1713
|
// er daadwerkelijk op afmetingen is ingedeeld.
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const MODULE_VERSION = "1.0.
|
|
1
|
+
export declare const MODULE_VERSION = "1.0.199";
|
|
2
2
|
//# sourceMappingURL=moduleVersion.d.ts.map
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// Automatisch gegenereerd door prebuild:lib; niet handmatig aanpassen.
|
|
2
|
-
export const MODULE_VERSION = "1.0.
|
|
2
|
+
export const MODULE_VERSION = "1.0.199";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ruleEngineSchema.d.ts","sourceRoot":"","sources":["../../src/lib/ruleEngineSchema.ts"],"names":[],"mappings":"AAQA,MAAM,MAAM,SAAS,GACjB,QAAQ,GACR,QAAQ,GACR,QAAQ,GACR,MAAM,GACN,UAAU,GACV,MAAM,GACN,UAAU,GACV,OAAO,GACP,MAAM,GACN,QAAQ,GACR,aAAa,GACb,SAAS,GACT,SAAS,GACT,UAAU,CAAC;AAMf,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,uDAAuD;IACvD,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,EAAE,EAAE,OAAO,EAAE,CAAC;IACd,sDAAsD;IACtD,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,SAAS;IACxB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,SAAS,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,WAAW,EAAE,CAAC;IACxB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,8FAA8F;IAC9F,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,wFAAwF;IACxF,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,6DAA6D;IAC7D,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,mEAAmE;IACnE,QAAQ,CAAC,EAAE;QACT,MAAM,EAAE,MAAM,CAAC;QACf,WAAW,EAAE,MAAM,CAAC;QACpB,YAAY,EAAE,MAAM,CAAC;KACtB,CAAC;IACF,wGAAwG;IACxG,SAAS,CAAC,EAAE,cAAc,GAAG,cAAc,EAAE,CAAC;CAC/C;AAGD,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,SAAS,EAAE,CAAC;CACrB;AAED,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,SAAS,EAAE,CAAC;IACpB,kBAAkB,EAAE,OAAO,CAAC;CAC7B;AAID,eAAO,MAAM,gBAAgB,EAAE,WAAW,EAQzC,CAAC;AAEF,eAAO,MAAM,mBAAmB,EAAE,WAAW,EAK5C,CAAC;AAEF,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,UAAU,EAAE,aAAa,EAAE,CAAC;CAC7B;AAED,MAAM,MAAM,kBAAkB,GAAG,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;AAI9D,eAAO,MAAM,UAAU,EAAE,QAAQ,
|
|
1
|
+
{"version":3,"file":"ruleEngineSchema.d.ts","sourceRoot":"","sources":["../../src/lib/ruleEngineSchema.ts"],"names":[],"mappings":"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"}
|
|
@@ -220,6 +220,20 @@ export const RULE_STEPS = [
|
|
|
220
220
|
{ value: "afmetingen", label: "Indelen op basis van afmetingen" },
|
|
221
221
|
],
|
|
222
222
|
},
|
|
223
|
+
{
|
|
224
|
+
key: "aanvullen_uit_ladingdrager",
|
|
225
|
+
label: "Ontbrekende fusten per laag / lagen aanvullen uit de ladingdrager",
|
|
226
|
+
type: "toggle",
|
|
227
|
+
default: false,
|
|
228
|
+
description: "Uit (standaard): een order met een ontbrekende waarde wordt niet ingedeeld en toont wat er ontbreekt. Aan: de waarde van de ladingdrager wordt gebruikt en per order als waarschuwing getoond.",
|
|
229
|
+
},
|
|
230
|
+
{
|
|
231
|
+
key: "gewicht_verplicht",
|
|
232
|
+
label: "Gewicht per fust verplicht",
|
|
233
|
+
type: "toggle",
|
|
234
|
+
default: false,
|
|
235
|
+
description: "Uit (standaard): ontbreekt het gewicht, dan krijgt de kar een waarschuwing dat de kilo's niet zijn gecontroleerd. Aan: de order wordt dan niet ingedeeld.",
|
|
236
|
+
},
|
|
223
237
|
{
|
|
224
238
|
key: "mode",
|
|
225
239
|
label: "Hoe ontstaat een belading?",
|
|
@@ -37,6 +37,8 @@ interface Row {
|
|
|
37
37
|
}
|
|
38
38
|
/** Opslag van eigen stickers (Klanten-module, customer_product_stickers). */
|
|
39
39
|
export declare const EIGEN_STICKER_BUCKET = "customer-stickers";
|
|
40
|
+
/** Foutcodes die een sticker blokkeren (status 'geblokkeerd' + reden) in plaats van later opnieuw. */
|
|
41
|
+
export declare const STICKER_BLOKKEER_CODES: string[];
|
|
40
42
|
/** Documentsoort voor de centrale documentkeuze. */
|
|
41
43
|
export declare function documentSoort(soort: Row["soort"]): string;
|
|
42
44
|
export declare function runStickerQueue(sb: SupabaseClient, opts?: {
|
|
@@ -44,5 +46,15 @@ export declare function runStickerQueue(sb: SupabaseClient, opts?: {
|
|
|
44
46
|
}): Promise<StickerRun>;
|
|
45
47
|
/** Reden die de layoutkeuze geeft als er geen (eenduidige) Floriday-layout is. */
|
|
46
48
|
export declare function isGeenLayoutReden(reden: unknown): boolean;
|
|
49
|
+
/**
|
|
50
|
+
* 1.0.197: vraag Layouts (print_kopieen_aantal, Layouts-eigendom) hoeveel kopieën het
|
|
51
|
+
* toestaat. Ontbreekt die functie in deze Shell → geen begrenzing bekend (max null).
|
|
52
|
+
* Andere fout → later opnieuw (fail-closed: niet printen).
|
|
53
|
+
*/
|
|
54
|
+
export declare function layoutsKopieenToegestaan(sb: SupabaseClient, soort: string, kopieen: number): Promise<{
|
|
55
|
+
max: number | null;
|
|
56
|
+
fout: string | null;
|
|
57
|
+
}>;
|
|
58
|
+
export declare function kopieenBegrensdTekst(label: string, nodig: number, max: number): string;
|
|
47
59
|
export {};
|
|
48
60
|
//# sourceMappingURL=stickerQueue.server.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stickerQueue.server.d.ts","sourceRoot":"","sources":["../../src/lib/stickerQueue.server.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAuB5D,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,wEAAwE;IACxE,UAAU,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACpE;AAED,UAAU,GAAG;IACX,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,SAAS,GAAG,aAAa,GAAG,eAAe,GAAG,mBAAmB,CAAC;IACzE,OAAO,EAAE,MAAM,CAAC;IAChB,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,qBAAqB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACvC;AAKD,6EAA6E;AAC7E,eAAO,MAAM,oBAAoB,sBAAsB,CAAC;AAExD,oDAAoD;AACpD,wBAAgB,aAAa,CAAC,KAAK,EAAE,GAAG,CAAC,OAAO,CAAC,GAAG,MAAM,CAEzD;AAaD,wBAAsB,eAAe,CAAC,EAAE,EAAE,cAAc,EAAE,IAAI,GAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAO,GAAG,OAAO,CAAC,UAAU,CAAC,
|
|
1
|
+
{"version":3,"file":"stickerQueue.server.d.ts","sourceRoot":"","sources":["../../src/lib/stickerQueue.server.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAuB5D,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,wEAAwE;IACxE,UAAU,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACpE;AAED,UAAU,GAAG;IACX,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,SAAS,GAAG,aAAa,GAAG,eAAe,GAAG,mBAAmB,CAAC;IACzE,OAAO,EAAE,MAAM,CAAC;IAChB,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,qBAAqB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACvC;AAKD,6EAA6E;AAC7E,eAAO,MAAM,oBAAoB,sBAAsB,CAAC;AAExD,sGAAsG;AACtG,eAAO,MAAM,sBAAsB,UAA0F,CAAC;AAE9H,oDAAoD;AACpD,wBAAgB,aAAa,CAAC,KAAK,EAAE,GAAG,CAAC,OAAO,CAAC,GAAG,MAAM,CAEzD;AAaD,wBAAsB,eAAe,CAAC,EAAE,EAAE,cAAc,EAAE,IAAI,GAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAO,GAAG,OAAO,CAAC,UAAU,CAAC,CA4b5G;AAoGD,kFAAkF;AAClF,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAGzD;AAwGD;;;;GAIG;AACH,wBAAsB,wBAAwB,CAC5C,EAAE,EAAE,cAAc,EAClB,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,GACd,OAAO,CAAC;IAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC,CAatD;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAEtF"}
|
|
@@ -22,6 +22,8 @@ import { controleerEtiket, ETIKET_ONTBREEKT, MAAT_ONBEKEND, maatSleutel, pastOpE
|
|
|
22
22
|
const COLS = "id, soort, bron_id, order_id, order_number, cart_id, kopieen, breedte_mm, hoogte_mm, print_plek_id, fulfillment_order_id, status, attempts, sticker_type, print_job_id, herprint_reden, herprint_requested_at, eigen_sticker_id";
|
|
23
23
|
/** Opslag van eigen stickers (Klanten-module, customer_product_stickers). */
|
|
24
24
|
export const EIGEN_STICKER_BUCKET = "customer-stickers";
|
|
25
|
+
/** Foutcodes die een sticker blokkeren (status 'geblokkeerd' + reden) in plaats van later opnieuw. */
|
|
26
|
+
export const STICKER_BLOKKEER_CODES = ["GEEN_PRINTER", "GEEN_LAYOUT", "GEEN_EXTERN_DOCUMENT", "DUBBEL_ORIGINEEL_ANDERE_BRON"];
|
|
25
27
|
/** Documentsoort voor de centrale documentkeuze. */
|
|
26
28
|
export function documentSoort(soort) {
|
|
27
29
|
return soort === "eigen_sticker" ? "eigen_sticker" : soort;
|
|
@@ -382,6 +384,17 @@ export async function runStickerQueue(sb, opts = {}) {
|
|
|
382
384
|
pdfOpslag = { bucket, pad: doc["storagePath"] };
|
|
383
385
|
}
|
|
384
386
|
}
|
|
387
|
+
// 1.0.197: Layouts kan het aantal kopieën per documentsoort begrenzen. Nooit stil minder
|
|
388
|
+
// printen dan nodig: dan tegenhouden met uitleg (geweigerd = zichtbaar + opnieuw proberen).
|
|
389
|
+
const toegestaan = await layoutsKopieenToegestaan(sb, r.soort, kopieen);
|
|
390
|
+
if (toegestaan.fout) {
|
|
391
|
+
await later(toegestaan.fout);
|
|
392
|
+
continue;
|
|
393
|
+
}
|
|
394
|
+
if (toegestaan.max !== null && toegestaan.max < kopieen) {
|
|
395
|
+
await blokkeer(kopieenBegrensdTekst(label, kopieen, toegestaan.max));
|
|
396
|
+
continue;
|
|
397
|
+
}
|
|
385
398
|
const printed = await printViaLayoutGecontroleerd(sb, {
|
|
386
399
|
documenttype: r.soort,
|
|
387
400
|
documentnaam: `${label} ${r.order_number ?? ""}`.trim(),
|
|
@@ -395,6 +408,7 @@ export async function runStickerQueue(sb, opts = {}) {
|
|
|
395
408
|
idempotentieSleutel: isHerprint
|
|
396
409
|
? `${r.soort}:${r.bron_id}:${r.cart_id}:${layout.layoutId}:herprint:${r.herprint_requested_at}`
|
|
397
410
|
: `${r.soort}:${r.bron_id}:${r.cart_id}:${layout.layoutId}`,
|
|
411
|
+
zelfdeBronVerplicht: true,
|
|
398
412
|
payload: {
|
|
399
413
|
source: "logistiek-engine",
|
|
400
414
|
documenttype: r.soort,
|
|
@@ -418,7 +432,7 @@ export async function runStickerQueue(sb, opts = {}) {
|
|
|
418
432
|
},
|
|
419
433
|
});
|
|
420
434
|
if (!printed.ok) {
|
|
421
|
-
if (
|
|
435
|
+
if (STICKER_BLOKKEER_CODES.includes(printed.errorCode) || printed.errorCode.startsWith("FORMAAT")) {
|
|
422
436
|
await blokkeer(printed.error);
|
|
423
437
|
}
|
|
424
438
|
else {
|
|
@@ -644,10 +658,11 @@ async function eigenTraystickersPrinten(sb, run, limit) {
|
|
|
644
658
|
herkomst: "eigen",
|
|
645
659
|
kopieen: 1,
|
|
646
660
|
idempotentieSleutel: `eigen_traysticker:${r.bron_id}:${layout.layoutId}`,
|
|
661
|
+
zelfdeBronVerplicht: true,
|
|
647
662
|
payload: { ...pl.payload, print_plek_id: r.print_plek_id, documenttype: "traysticker" },
|
|
648
663
|
});
|
|
649
664
|
if (!printed.ok) {
|
|
650
|
-
if (["GEEN_PRINTER", "GEEN_LAYOUT"].includes(printed.errorCode))
|
|
665
|
+
if (["GEEN_PRINTER", "GEEN_LAYOUT", "DUBBEL_ORIGINEEL_ANDERE_BRON"].includes(printed.errorCode))
|
|
651
666
|
await blokkeer(printed.error);
|
|
652
667
|
else {
|
|
653
668
|
const attempts = (r.attempts ?? 0) + 1;
|
|
@@ -661,3 +676,26 @@ async function eigenTraystickersPrinten(sb, run, limit) {
|
|
|
661
676
|
await trace(sb, { ...basis, step: `Eigen traysticker in de printwachtrij (opdracht ${printed.jobId})`, status: "geslaagd" });
|
|
662
677
|
}
|
|
663
678
|
}
|
|
679
|
+
/**
|
|
680
|
+
* 1.0.197: vraag Layouts (print_kopieen_aantal, Layouts-eigendom) hoeveel kopieën het
|
|
681
|
+
* toestaat. Ontbreekt die functie in deze Shell → geen begrenzing bekend (max null).
|
|
682
|
+
* Andere fout → later opnieuw (fail-closed: niet printen).
|
|
683
|
+
*/
|
|
684
|
+
export async function layoutsKopieenToegestaan(sb, soort, kopieen) {
|
|
685
|
+
const { data, error } = await sb.rpc("print_kopieen_aantal", {
|
|
686
|
+
_type: soort,
|
|
687
|
+
_kopieen: kopieen,
|
|
688
|
+
_payload: { kopieen },
|
|
689
|
+
});
|
|
690
|
+
if (error) {
|
|
691
|
+
const code = error.code;
|
|
692
|
+
if (code === "42883" || code === "PGRST202")
|
|
693
|
+
return { max: null, fout: null };
|
|
694
|
+
return { max: null, fout: `Kopieëncontrole Layouts mislukt: ${error.message}` };
|
|
695
|
+
}
|
|
696
|
+
const n = Number(data);
|
|
697
|
+
return Number.isFinite(n) && n >= 1 ? { max: Math.floor(n), fout: null } : { max: null, fout: null };
|
|
698
|
+
}
|
|
699
|
+
export function kopieenBegrensdTekst(label, nodig, max) {
|
|
700
|
+
return `${label}: ${nodig} kopieën nodig, maar Layouts staat voor dit documenttype maximaal ${max} toe. Niet geprint om geen onvolledige set te maken. Laat Layouts de grens aanpassen en kies daarna "Opnieuw proberen".`;
|
|
701
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flowselections/logistiek-engine",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.199",
|
|
4
4
|
"flowselections": {
|
|
5
5
|
"moduleId": "logistiek-engine",
|
|
6
6
|
"pages": [
|
|
@@ -82,6 +82,15 @@
|
|
|
82
82
|
"GET",
|
|
83
83
|
"OPTIONS"
|
|
84
84
|
]
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
"routePath": "/api/public/hooks/logistics-plan-now",
|
|
88
|
+
"import": "@flowselections/logistiek-engine/hooks",
|
|
89
|
+
"handler": "handleLogisticsPlanNow",
|
|
90
|
+
"methods": [
|
|
91
|
+
"POST",
|
|
92
|
+
"OPTIONS"
|
|
93
|
+
]
|
|
85
94
|
}
|
|
86
95
|
],
|
|
87
96
|
"schema": {
|
|
@@ -142,6 +151,7 @@
|
|
|
142
151
|
},
|
|
143
152
|
"devDependencies": {
|
|
144
153
|
"@cloudflare/vite-plugin": "^1.25.5",
|
|
154
|
+
"@electric-sql/pglite": "^0.5.12",
|
|
145
155
|
"@eslint/js": "^9.32.0",
|
|
146
156
|
"@flowselections/core": "1.0.30",
|
|
147
157
|
"@lovable.dev/vite-tanstack-config": "2.26.5",
|