@flowselections/logistiek-engine 1.0.177 → 1.0.181

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. package/db/schema/logistiek-engine.sql +850 -0
  2. package/dist-lib/components/BeladingIndelingCard.d.ts.map +1 -1
  3. package/dist-lib/components/BeladingIndelingCard.js +2 -0
  4. package/dist-lib/integrations/supabase/previewAuthStorage.d.ts +11 -5
  5. package/dist-lib/integrations/supabase/previewAuthStorage.d.ts.map +1 -1
  6. package/dist-lib/integrations/supabase/previewAuthStorage.js +38 -21
  7. package/dist-lib/integrations/supabase/types.d.ts +137 -0
  8. package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
  9. package/dist-lib/lib/beladingRegels.d.ts +78 -0
  10. package/dist-lib/lib/beladingRegels.d.ts.map +1 -0
  11. package/dist-lib/lib/beladingRegels.js +143 -0
  12. package/dist-lib/lib/bulkLoadCarrierPlanning.server.js +2 -2
  13. package/dist-lib/lib/eigenTraysticker.d.ts +17 -0
  14. package/dist-lib/lib/eigenTraysticker.d.ts.map +1 -0
  15. package/dist-lib/lib/eigenTraysticker.js +32 -0
  16. package/dist-lib/lib/loadCarrierPlanning.server.d.ts +35 -3
  17. package/dist-lib/lib/loadCarrierPlanning.server.d.ts.map +1 -1
  18. package/dist-lib/lib/loadCarrierPlanning.server.js +251 -89
  19. package/dist-lib/lib/modulePrompts.d.ts.map +1 -1
  20. package/dist-lib/lib/modulePrompts.js +76 -0
  21. package/dist-lib/lib/moduleVersion.d.ts +1 -1
  22. package/dist-lib/lib/moduleVersion.js +1 -1
  23. package/dist-lib/lib/planQueue.server.d.ts.map +1 -1
  24. package/dist-lib/lib/planQueue.server.js +26 -2
  25. package/dist-lib/lib/stickerQueue.server.d.ts +1 -1
  26. package/dist-lib/lib/stickerQueue.server.d.ts.map +1 -1
  27. package/dist-lib/lib/stickerQueue.server.js +88 -0
  28. package/package.json +1 -1
@@ -12,6 +12,7 @@ import { attachIndelingToTasks } from "./loadCarrierTaskSync.server.js";
12
12
  import { trace, newTraceId, classifyError } from "./trace.server.js";
13
13
  import { FLOW_NODE } from "./flowNodes.js";
14
14
  import { completePlannerDiffRuns, skipPlannerDiffRuns } from "./plannerDiff.server.js";
15
+ import { opnieuwOverSeconden } from "./beladingRegels.js";
15
16
  const EMPTY = {
16
17
  claimed: 0,
17
18
  planned: 0,
@@ -103,6 +104,7 @@ export async function runPlanQueue(sb, options = {}) {
103
104
  const bufferId = group.find((g) => g.buffer_id)?.buffer_id ?? null;
104
105
  let blocked = false;
105
106
  let blockedReasonFor = null;
107
+ let bezet = false;
106
108
  await trace(sb, {
107
109
  ...basis,
108
110
  nodeId: FLOW_NODE.ontvangst,
@@ -120,7 +122,10 @@ export async function runPlanQueue(sb, options = {}) {
120
122
  if (!plannedBuffers.has(bufferId)) {
121
123
  plannedBuffers.add(bufferId);
122
124
  const plan = await replanBufferInternal(sb, bufferId);
123
- if (plan.skipped === "locked") {
125
+ if (plan.skipped === "busy") {
126
+ bezet = true;
127
+ }
128
+ else if (plan.skipped === "locked") {
124
129
  result.skippedLocked++;
125
130
  await trace(sb, {
126
131
  ...basis,
@@ -177,7 +182,10 @@ export async function runPlanQueue(sb, options = {}) {
177
182
  }
178
183
  else {
179
184
  const plan = await replanOrderInternal(sb, row.order_id);
180
- if (plan?.skipped === "locked") {
185
+ if (plan?.skipped === "busy") {
186
+ bezet = true;
187
+ }
188
+ else if (plan?.skipped === "locked") {
181
189
  result.skippedLocked++;
182
190
  await trace(sb, {
183
191
  ...basis,
@@ -230,6 +238,22 @@ export async function runPlanQueue(sb, options = {}) {
230
238
  // De blokkade zet de wachtrijregel zelf al op 'geblokkeerd'; die mag niet
231
239
  // alsnog als afgerond worden weggeschreven. Een wachtende vergelijking
232
240
  // krijgt geen "na"-stand en wordt daarom als overgeslagen afgesloten.
241
+ // 1.0.179 (punt 5): lease bezet is geen "klaar". Opdracht blijft open en komt later terug.
242
+ if (bezet) {
243
+ const sec = opnieuwOverSeconden(Number(row.attempts ?? 0));
244
+ await sb.rpc("logistics_plan_retry_later", { p_id: row.id, p_seconds: sec });
245
+ result.skippedLocked++;
246
+ await trace(sb, {
247
+ ...basis,
248
+ step: "Indeling later opnieuw",
249
+ status: "wachtend",
250
+ decision: "Opdracht blijft open",
251
+ reason: "Deze buffer werd op hetzelfde moment al ingedeeld. De order wordt over " + sec + " seconden opnieuw geprobeerd.",
252
+ errorClass: "regel",
253
+ nextStep: "Volgende planronde",
254
+ });
255
+ continue;
256
+ }
233
257
  if (blocked) {
234
258
  // De blokkade raakt soms alleen de buffer-leden zonder kar, niet de
235
259
  // order die deze ronde oppakte (FO-2601389). Sluit die claim dan hier
@@ -11,7 +11,7 @@ export interface StickerRun {
11
11
  }
12
12
  interface Row {
13
13
  id: string;
14
- soort: "sticker" | "traysticker" | "eigen_sticker";
14
+ soort: "sticker" | "traysticker" | "eigen_sticker" | "eigen_traysticker";
15
15
  bron_id: string;
16
16
  eigen_sticker_id?: string | null;
17
17
  order_id: string | null;
@@ -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;AAiB5D,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;CAClB;AAED,UAAU,GAAG;IACX,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,SAAS,GAAG,aAAa,GAAG,eAAe,CAAC;IACnD,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,CA8V5G;AAoGD,kFAAkF;AAClF,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAGzD"}
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;AAkB5D,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;CAClB;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,CAuW5G;AAoGD,kFAAkF;AAClF,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAGzD"}
@@ -17,6 +17,7 @@ import { readFloridayWriteMode } from "./floridayWriteMode.server.js";
17
17
  import { trace, newTraceId } from "./trace.server.js";
18
18
  import { printViaLayoutGecontroleerd, floridayLayout, bucketUitLink } from "./layoutPrint.server.js";
19
19
  import { getFlorydayDocument, getFlorydayFulfillmentStatus } from "./floridayDocuments.server.js";
20
+ import { controleerTrayPayload } from "./eigenTraysticker.js";
20
21
  import { controleerEtiket, parseGeladenEtiketten, pdfFormaat, stickerBackoffMinuten, stickerKopieen, waarschuwingTekst, } from "./stickerRules.js";
21
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";
22
23
  /** Opslag van eigen stickers (Klanten-module, customer_product_stickers). */
@@ -53,6 +54,12 @@ export async function runStickerQueue(sb, opts = {}) {
53
54
  if (error)
54
55
  run.errors.push(`eigen stickers: ${error.message}`);
55
56
  }
57
+ // 1c. Eigen traystickers (1.0.178): één per tray volgens de karindeling.
58
+ {
59
+ const { error } = await sb.rpc("logistics_own_traysticker_sync", { p_limit: 200 });
60
+ if (error)
61
+ run.errors.push(`eigen traystickers: ${error.message}`);
62
+ }
56
63
  // 2. Tray-stickers: vrij na geaccepteerde belading.
57
64
  {
58
65
  const { data } = await sb
@@ -144,6 +151,7 @@ export async function runStickerQueue(sb, opts = {}) {
144
151
  .from("logistics_sticker_prints")
145
152
  .select(COLS)
146
153
  .eq("status", "klaar")
154
+ .neq("soort", "eigen_traysticker")
147
155
  .or(`next_attempt_at.is.null,next_attempt_at.lte.${nu()}`)
148
156
  .order("created_at")
149
157
  .limit(limit);
@@ -369,6 +377,8 @@ export async function runStickerQueue(sb, opts = {}) {
369
377
  status: "geslaagd",
370
378
  });
371
379
  }
380
+ // 4b. Eigen traystickers (1.0.178): één opdracht per tray via layout_print_document.
381
+ await eigenTraystickersPrinten(sb, run, limit);
372
382
  // 5. Afmelden in Floriday na de goedgekeurde controlescan.
373
383
  run.handled = await afmelden(sb, run);
374
384
  return run;
@@ -508,3 +518,81 @@ async function traystickerLayoutVrijgeven(sb) {
508
518
  .eq("status", "geblokkeerd");
509
519
  return ids.length;
510
520
  }
521
+ /**
522
+ * 1.0.178: eigen traystickers (HO/MO/AO). Layout via de centrale documentkeuze
523
+ * (logistics_document_for_order -> layout_voor_document, 'traysticker').
524
+ * Ontbreekt indeling of layout: alleen deze tray geblokkeerd met uitleg.
525
+ */
526
+ async function eigenTraystickersPrinten(sb, run, limit) {
527
+ const nu = new Date().toISOString();
528
+ const { data } = await sb
529
+ .from("logistics_sticker_prints")
530
+ .select(COLS)
531
+ .eq("soort", "eigen_traysticker")
532
+ .eq("status", "klaar")
533
+ .or(`next_attempt_at.is.null,next_attempt_at.lte.${nu}`)
534
+ .order("created_at")
535
+ .limit(limit);
536
+ for (const r of (Array.isArray(data) ? data : [])) {
537
+ const zet = (velden) => sb.from("logistics_sticker_prints").update(velden).eq("id", r.id);
538
+ const basis = {
539
+ traceId: newTraceId(),
540
+ orderRef: r.order_number ?? r.order_id ?? null,
541
+ orderSource: "eigen",
542
+ cartRef: r.cart_id ?? null,
543
+ phase: "stickers",
544
+ actor: "logistiek-engine:stickerronde",
545
+ };
546
+ const blokkeer = async (reden) => {
547
+ await zet({ status: "geblokkeerd", reden });
548
+ run.blocked++;
549
+ await trace(sb, { ...basis, step: "Eigen traysticker niet geprint", status: "waarschuwing", reason: reden });
550
+ };
551
+ if (!r.order_id || !r.cart_id) {
552
+ await blokkeer("Geen order of kar bekend voor deze traysticker.");
553
+ continue;
554
+ }
555
+ if (!r.print_plek_id) {
556
+ await blokkeer("Geen printplek bekend (geen pakbon met printplek en geen pickstation in de werkwijze).");
557
+ continue;
558
+ }
559
+ const { data: raw, error } = await sb.rpc("logistics_own_traysticker_payload", { p_id: r.id });
560
+ if (error) {
561
+ run.errors.push(`eigen traysticker ${r.id}: ${error.message}`);
562
+ continue;
563
+ }
564
+ const pl = controleerTrayPayload(raw);
565
+ if (!pl.ok) {
566
+ await blokkeer(pl.reden);
567
+ continue;
568
+ }
569
+ const layout = await floridayLayout(sb, "traysticker", r.order_id);
570
+ if (!layout.layoutId) {
571
+ await blokkeer(layout.reden ?? "Maak of activeer een traystickerlayout in Layouts en printers.");
572
+ continue;
573
+ }
574
+ const printed = await printViaLayoutGecontroleerd(sb, {
575
+ documenttype: "traysticker",
576
+ documentnaam: `Traysticker ${r.order_number ?? ""} ${String(pl.payload["tray_nummer"])}/${String(pl.payload["tray_totaal"])}`.trim(),
577
+ layoutId: layout.layoutId,
578
+ printPlekId: r.print_plek_id,
579
+ herkomst: "eigen",
580
+ kopieen: 1,
581
+ idempotentieSleutel: `eigen_traysticker:${r.bron_id}:${layout.layoutId}`,
582
+ payload: { ...pl.payload, print_plek_id: r.print_plek_id, documenttype: "traysticker" },
583
+ });
584
+ if (!printed.ok) {
585
+ if (["GEEN_PRINTER", "GEEN_LAYOUT"].includes(printed.errorCode))
586
+ await blokkeer(printed.error);
587
+ else {
588
+ const attempts = (r.attempts ?? 0) + 1;
589
+ await zet({ attempts, reden: printed.error, next_attempt_at: new Date(Date.now() + stickerBackoffMinuten(attempts) * 60000).toISOString() });
590
+ run.retrying++;
591
+ }
592
+ continue;
593
+ }
594
+ await zet({ status: "in_printwachtrij", print_job_id: printed.jobId, reden: null, next_attempt_at: null });
595
+ run.printed++;
596
+ await trace(sb, { ...basis, step: `Eigen traysticker in de printwachtrij (opdracht ${printed.jobId})`, status: "geslaagd" });
597
+ }
598
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flowselections/logistiek-engine",
3
- "version": "1.0.177",
3
+ "version": "1.0.181",
4
4
  "flowselections": {
5
5
  "moduleId": "logistiek-engine",
6
6
  "pages": [