@flowselections/layout-maker 1.45.9 → 1.45.19

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 (65) hide show
  1. package/README.md +139 -139
  2. package/dist-lib/components/AfdrukKnop.d.ts +19 -19
  3. package/dist-lib/components/AfdrukKnop.js +64 -64
  4. package/dist-lib/components/NetwerkPrintersDialog.d.ts +7 -7
  5. package/dist-lib/components/NetwerkPrintersDialog.js +79 -79
  6. package/dist-lib/components/PakbonPreview.d.ts.map +1 -1
  7. package/dist-lib/components/PakbonPreview.js +2 -2
  8. package/dist-lib/components/PrintQueuePage.d.ts.map +1 -1
  9. package/dist-lib/components/PrintQueuePage.js +42 -5
  10. package/dist-lib/components/PrinterSetupWizard.js +3 -3
  11. package/dist-lib/components/PrintersRedirectPage.d.ts +1 -1
  12. package/dist-lib/components/PrintersRedirectPage.js +14 -14
  13. package/dist-lib/index.d.ts +1 -0
  14. package/dist-lib/index.d.ts.map +1 -1
  15. package/dist-lib/index.js +1 -0
  16. package/dist-lib/integrations/supabase/auth-middleware.d.ts +4489 -277
  17. package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
  18. package/dist-lib/integrations/supabase/client.server.d.ts +4489 -277
  19. package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
  20. package/dist-lib/integrations/supabase/client.server.js +1 -1
  21. package/dist-lib/integrations/supabase/moduleClient.d.ts +4489 -277
  22. package/dist-lib/integrations/supabase/moduleClient.d.ts.map +1 -1
  23. package/dist-lib/integrations/supabase/types.d.ts +4506 -253
  24. package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
  25. package/dist-lib/lib/aiLayout.functions.d.ts +4489 -277
  26. package/dist-lib/lib/aiLayout.functions.d.ts.map +1 -1
  27. package/dist-lib/lib/aiLayout.server.js +9 -9
  28. package/dist-lib/lib/browserPrint.js +1 -1
  29. package/dist-lib/lib/fieldRegistry.js +1 -0
  30. package/dist-lib/lib/layoutPreview.js +7 -7
  31. package/dist-lib/lib/logoLibrary.d.ts +2 -1
  32. package/dist-lib/lib/logoLibrary.d.ts.map +1 -1
  33. package/dist-lib/lib/logoLibrary.js +42 -2
  34. package/dist-lib/lib/orderData.server.d.ts.map +1 -1
  35. package/dist-lib/lib/orderData.server.js +13 -0
  36. package/dist-lib/lib/pakbonBuilder.d.ts +26 -0
  37. package/dist-lib/lib/pakbonBuilder.d.ts.map +1 -1
  38. package/dist-lib/lib/pakbonBuilder.js +37 -0
  39. package/dist-lib/lib/pakbonContent.d.ts.map +1 -1
  40. package/dist-lib/lib/pakbonContent.js +2 -2
  41. package/dist-lib/lib/pakbonHtml.d.ts.map +1 -1
  42. package/dist-lib/lib/pakbonHtml.js +60 -60
  43. package/dist-lib/lib/pakbonPresentation.d.ts.map +1 -1
  44. package/dist-lib/lib/pakbonPresentation.js +2 -4
  45. package/dist-lib/lib/pdfJobSaver.d.ts.map +1 -1
  46. package/dist-lib/lib/pdfJobSaver.js +31 -2
  47. package/dist-lib/lib/pdfMap.d.ts +7 -0
  48. package/dist-lib/lib/pdfMap.d.ts.map +1 -1
  49. package/dist-lib/lib/pdfMap.js +17 -0
  50. package/dist-lib/lib/printQueue.server.d.ts.map +1 -1
  51. package/dist-lib/lib/printQueue.server.js +142 -15
  52. package/dist-lib/lib/printService.functions.d.ts +47214 -52
  53. package/dist-lib/lib/printService.functions.d.ts.map +1 -1
  54. package/dist-lib/lib/printService.functions.js +69 -0
  55. package/dist-lib/lib/printService.server.d.ts +2 -0
  56. package/dist-lib/lib/printService.server.d.ts.map +1 -1
  57. package/dist-lib/lib/printService.server.js +10 -0
  58. package/dist-lib/lib/productiebonHtml.js +15 -15
  59. package/dist-lib/lib/profielHtml.d.ts.map +1 -1
  60. package/dist-lib/lib/profielHtml.js +22 -19
  61. package/dist-lib/lib/supabaseAdmin.server.d.ts +5 -5
  62. package/dist-lib/lib/supabaseAdmin.server.js +43 -43
  63. package/dist-lib/lib/testpagina.js +3 -3
  64. package/dist-lib/lib/testprint.js +4 -4
  65. package/package.json +3 -3
@@ -295,6 +295,23 @@ export async function bewaarInPdfMap(printerId, bestandsnaam, blob) {
295
295
  return false;
296
296
  }
297
297
  }
298
+ /**
299
+ * Verwijdert een zojuist weggeschreven bestand weer, bijvoorbeeld wanneer de
300
+ * opdracht tijdens het schrijven is geannuleerd. Lukt het niet, dan blijft het
301
+ * bestand staan en geeft de functie false terug.
302
+ */
303
+ export async function verwijderUitPdfMap(printerId, bestandsnaam) {
304
+ const handle = await leesHandle(printerId);
305
+ if (!handle?.removeEntry)
306
+ return false;
307
+ try {
308
+ await handle.removeEntry(bestandsnaam);
309
+ return true;
310
+ }
311
+ catch {
312
+ return false;
313
+ }
314
+ }
298
315
  /** Controleert toestemming zonder een bestandsactie te starten. */
299
316
  export async function pdfMapToestemming(printerId) {
300
317
  const handle = await leesHandle(printerId);
@@ -1 +1 @@
1
- {"version":3,"file":"printQueue.server.d.ts","sourceRoot":"","sources":["../../src/lib/printQueue.server.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AA6B5D,KAAK,GAAG,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AAE/B,mFAAmF;AACnF,MAAM,MAAM,OAAO,GAAG,cAAc,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;AAEpD;;;;GAIG;AACH,wBAAsB,gBAAgB,CAAC,EAAE,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAW1E;AAUD,2EAA2E;AAC3E,qBAAa,gBAAiB,SAAQ,KAAK;IAChC,IAAI,SAAsB;CACpC;AAED,+EAA+E;AAC/E,qBAAa,iBAAkB,SAAQ,KAAK;IACjC,IAAI,SAAuB;CACrC;AAED,oFAAoF;AACpF,qBAAa,kBAAmB,SAAQ,gBAAgB;IAC7C,IAAI,SAAwB;CACtC;AAED;;;;;GAKG;AACH,MAAM,MAAM,WAAW,GAAG;IAAE,MAAM,EAAE,GAAG,GAAG,IAAI,CAAC;IAAC,WAAW,EAAE,OAAO,CAAA;CAAE,CAAC;AAEvE,yEAAyE;AACzE,MAAM,MAAM,aAAa,GAAG,YAAY,GAAG,cAAc,GAAG,aAAa,GAAG,aAAa,CAAC;AAE1F,6EAA6E;AAC7E,MAAM,MAAM,YAAY,GAAG;IAAE,IAAI,EAAE,aAAa,CAAA;CAAE,CAAC;AAcnD,4EAA4E;AAC5E,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,CAI7D;AASD,sEAAsE;AACtE,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,GAAG,GAAG,MAAM,CAQnD;AA2PD;;;GAGG;AACH,wBAAsB,cAAc,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CA+BnF;AAkUD;;;;GAIG;AACH,wBAAsB,QAAQ,CAC5B,EAAE,EAAE,OAAO,EACX,GAAG,EAAE,GAAG,EACR,KAAK,EAAE,WAAW,EAClB,QAAQ,CAAC,EAAE,YAAY,GACtB,OAAO,CAAC,MAAM,CAAC,CAiXjB;AAGD;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAY1D;AAicD,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,+EAA+E;IAC/E,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AA2BD,oFAAoF;AACpF,wBAAsB,eAAe,CAAC,EAAE,EAAE,OAAO,EAAE,GAAG,SAAI,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAwFtF;AAED;;;;GAIG;AACH,wBAAsB,mBAAmB,CACvC,EAAE,EAAE,OAAO,EACX,GAAG,EAAE,GAAG,GACP,OAAO,CAAC;IAAE,KAAK,EAAE,WAAW,CAAC;IAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC,CA4B1D"}
1
+ {"version":3,"file":"printQueue.server.d.ts","sourceRoot":"","sources":["../../src/lib/printQueue.server.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAkC5D,KAAK,GAAG,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AAE/B,mFAAmF;AACnF,MAAM,MAAM,OAAO,GAAG,cAAc,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;AAEpD;;;;GAIG;AACH,wBAAsB,gBAAgB,CAAC,EAAE,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAW1E;AAoDD,2EAA2E;AAC3E,qBAAa,gBAAiB,SAAQ,KAAK;IAChC,IAAI,SAAsB;CACpC;AAED,+EAA+E;AAC/E,qBAAa,iBAAkB,SAAQ,KAAK;IACjC,IAAI,SAAuB;CACrC;AAED,oFAAoF;AACpF,qBAAa,kBAAmB,SAAQ,gBAAgB;IAC7C,IAAI,SAAwB;CACtC;AAED;;;;;GAKG;AACH,MAAM,MAAM,WAAW,GAAG;IAAE,MAAM,EAAE,GAAG,GAAG,IAAI,CAAC;IAAC,WAAW,EAAE,OAAO,CAAA;CAAE,CAAC;AAEvE,yEAAyE;AACzE,MAAM,MAAM,aAAa,GAAG,YAAY,GAAG,cAAc,GAAG,aAAa,GAAG,aAAa,CAAC;AAE1F,6EAA6E;AAC7E,MAAM,MAAM,YAAY,GAAG;IAAE,IAAI,EAAE,aAAa,CAAA;CAAE,CAAC;AAcnD,4EAA4E;AAC5E,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,CAI7D;AASD,sEAAsE;AACtE,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,GAAG,GAAG,MAAM,CAQnD;AA2PD;;;GAGG;AACH,wBAAsB,cAAc,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CA+BnF;AAkUD;;;;GAIG;AACH,wBAAsB,QAAQ,CAC5B,EAAE,EAAE,OAAO,EACX,GAAG,EAAE,GAAG,EACR,KAAK,EAAE,WAAW,EAClB,QAAQ,CAAC,EAAE,YAAY,GACtB,OAAO,CAAC,MAAM,CAAC,CAuZjB;AAGD;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAY1D;AAwfD,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,+EAA+E;IAC/E,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AA2BD,oFAAoF;AACpF,wBAAsB,eAAe,CAAC,EAAE,EAAE,OAAO,EAAE,GAAG,SAAI,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAwFtF;AAED;;;;GAIG;AACH,wBAAsB,mBAAmB,CACvC,EAAE,EAAE,OAAO,EACX,GAAG,EAAE,GAAG,GACP,OAAO,CAAC;IAAE,KAAK,EAAE,WAAW,CAAC;IAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC,CA4B1D"}
@@ -6,7 +6,7 @@
6
6
  import { maakPakbonOrder, renderPakbonDocument, voorbeeldPakbonOrder, } from "./pakbonHtml.js";
7
7
  import { bouwPakbonScanInhoud, bewaarPakbonScanInhoud } from "./pakbonScan.js";
8
8
  import { bouwPakbonBarcodeVerzoek, geleverdeBarcodeVoorJob, vraagPakbonBarcodeAan, } from "./pakbonBarcodeVerzoek.js";
9
- import { deelkarRegels, leesAfleverbriefConfig, pakbonLayoutVariant } from "./pakbonBuilder.js";
9
+ import { bepaalVolleKarStand, deelkarRegels, leesAfleverbriefConfig, pakbonLayoutVariant, } from "./pakbonBuilder.js";
10
10
  import { renderProductiebonDocument } from "./productiebonHtml.js";
11
11
  import { heeftOrderInhoud, laadOrderVanuitDatabase, laadPakbonData } from "./orderData.server.js";
12
12
  import { profielHeeftInhoud, renderProfielDocument } from "./profielHtml.js";
@@ -43,6 +43,45 @@ async function log(db, jobId, gebeurtenis, detail) {
43
43
  ...(detail ? { detail: String(detail).slice(0, 1000) } : {}),
44
44
  });
45
45
  }
46
+ /**
47
+ * Bepaalt de positie van een volle kar uit de actuele taken van dezelfde
48
+ * printsessie. Dit vangt Engine-series op waarin elke afzonderlijke taak ten
49
+ * onrechte K1/cartIndex 1 bevat. Oude geannuleerde of mislukte taken tellen
50
+ * bewust niet mee.
51
+ */
52
+ async function volleKarReeksIndex(db, job) {
53
+ const payload = (job.payload && typeof job.payload === "object" ? job.payload : {});
54
+ const data = (payload.data && typeof payload.data === "object" ? payload.data : {});
55
+ const sessieId = String(payload.session_id ?? data.sessionId ?? data.session_id ?? "").trim();
56
+ const orderId = String(data.orderId ?? data.order_id ?? payload.order_id ?? "").trim();
57
+ const huidigeCartId = String(data.cart?.cartId ?? data.cart?.cart_id ?? payload.cart?.cartId ?? payload.cart?.cart_id ?? payload.cart_id ?? "").trim();
58
+ if (!sessieId || !orderId || !huidigeCartId)
59
+ return undefined;
60
+ const { data: rijen, error } = await db
61
+ .from("print_jobs")
62
+ .select("created_at, status, payload")
63
+ .eq("documenttype", "pakbon")
64
+ .eq("payload->>session_id", sessieId)
65
+ .order("created_at", { ascending: true });
66
+ if (error)
67
+ return undefined;
68
+ const geldigeCartIds = [];
69
+ for (const rij of arr(rijen)) {
70
+ if (["geannuleerd", "mislukt"].includes(String(rij.status ?? "")))
71
+ continue;
72
+ const p = (rij.payload && typeof rij.payload === "object" ? rij.payload : {});
73
+ const d = (p.data && typeof p.data === "object" ? p.data : {});
74
+ const variant = String(p.layout_variant ?? d.layoutVariant ?? d.layout_variant ?? "");
75
+ const isVolleKar = variant === "full_cart" || d.cart?.isFullCart === true || p.cart?.isFullCart === true;
76
+ const rijOrderId = String(d.orderId ?? d.order_id ?? p.order_id ?? "").trim();
77
+ const cartId = String(d.cart?.cartId ?? d.cart?.cart_id ?? p.cart?.cartId ?? p.cart?.cart_id ?? p.cart_id ?? "").trim();
78
+ if (!isVolleKar || rijOrderId !== orderId || !cartId || geldigeCartIds.includes(cartId))
79
+ continue;
80
+ geldigeCartIds.push(cartId);
81
+ }
82
+ const positie = geldigeCartIds.indexOf(huidigeCartId) + 1;
83
+ return positie > 0 ? positie : undefined;
84
+ }
46
85
  /** Fout waarbij opnieuw proberen zinloos is (er valt niets te printen). */
47
86
  export class GeenDocumentFout extends Error {
48
87
  constructor() {
@@ -880,6 +919,34 @@ export async function bouwHtml(db, job, keuze, uitkomst) {
880
919
  throw new EngineContractFout("De gekozen kar bevat geen productregels; de pakbon zou zonder producten worden afgedrukt.");
881
920
  }
882
921
  if (gekozenKar) {
922
+ // Bepaal de reeks één keer canoniek. Daarna krijgen zowel de karkop als
923
+ // de PDF uitsluitend deze herstelde waarden en niet het tegenstrijdige
924
+ // cartLabel dat de Engine soms meestuurt.
925
+ const data = (p.data && typeof p.data === "object" ? p.data : {});
926
+ const variant = pakbonLayoutVariant(engineVariant);
927
+ const ruweIndex = Number(engineCart?.cart_index ?? gekozenKar.cart_index ?? 0) || undefined;
928
+ const ruweTotaal = Number(engineCart?.cart_total ?? gekozenKar.cart_total ?? 0) || undefined;
929
+ let karIndex = ruweIndex;
930
+ let karTotaal = ruweTotaal;
931
+ let karLabel = karIndex && karTotaal ? `Kar ${karIndex} van ${karTotaal}` : undefined;
932
+ if (variant === "full_cart") {
933
+ try {
934
+ const reeksIndex = await volleKarReeksIndex(db, job);
935
+ const stand = bepaalVolleKarStand({
936
+ documentnaam: job.documentnaam ?? p.document_number,
937
+ volleKarren: data.fullCarts ?? data.full_carts,
938
+ cartIndex: ruweIndex,
939
+ cartTotal: ruweTotaal,
940
+ reeksIndex,
941
+ });
942
+ karIndex = stand?.index;
943
+ karTotaal = stand?.totaal;
944
+ karLabel = stand?.label;
945
+ }
946
+ catch (e) {
947
+ throw new EngineContractFout(e instanceof Error ? e.message : "De volle-karstand in de Engine-opdracht is ongeldig.");
948
+ }
949
+ }
883
950
  order = {
884
951
  ...order,
885
952
  cart: {
@@ -887,11 +954,20 @@ export async function bouwHtml(db, job, keuze, uitkomst) {
887
954
  ...engineCart,
888
955
  barcode,
889
956
  cart_id: cartId || gekozenKar.cart_id,
890
- cart_label: String(engineCart?.cart_label ?? gekozenKar.cart_label ?? "").trim() || undefined,
891
- cart_index: Number(engineCart?.cart_index ?? gekozenKar.cart_index ?? 0) || undefined,
892
- cart_total: Number(engineCart?.cart_total ?? gekozenKar.cart_total ?? 0) || undefined,
957
+ cart_label: karLabel,
958
+ cart_index: karIndex,
959
+ cart_total: karTotaal,
893
960
  },
894
- karren: [{ ...gekozenKar, barcode }],
961
+ karren: [
962
+ {
963
+ ...gekozenKar,
964
+ barcode,
965
+ ...(karLabel ? { cart_label: karLabel } : {}),
966
+ ...(karIndex ? { cart_index: karIndex } : {}),
967
+ ...(karTotaal ? { cart_total: karTotaal } : {}),
968
+ ...(karIndex ? { karnummer: karIndex } : {}),
969
+ },
970
+ ],
895
971
  };
896
972
  }
897
973
  }
@@ -1041,7 +1117,8 @@ async function wachtOpLayout(db, job, reden) {
1041
1117
  voltooid_op: null,
1042
1118
  updated_at: new Date().toISOString(),
1043
1119
  })
1044
- .eq("id", job.id);
1120
+ .eq("id", job.id)
1121
+ .neq("status", "geannuleerd");
1045
1122
  await log(db, job.id, "wacht_op_engine_layout", reden);
1046
1123
  }
1047
1124
  async function mislukt(db, job, reden, terugNaarWachtend) {
@@ -1061,7 +1138,8 @@ async function mislukt(db, job, reden, terugNaarWachtend) {
1061
1138
  voltooid_op: opnieuw ? null : new Date().toISOString(),
1062
1139
  updated_at: new Date().toISOString(),
1063
1140
  })
1064
- .eq("id", job.id);
1141
+ .eq("id", job.id)
1142
+ .neq("status", "geannuleerd");
1065
1143
  await log(db, job.id, opnieuw ? "opnieuw_proberen" : "mislukt", reden);
1066
1144
  }
1067
1145
  function pdfBytes(base64) {
@@ -1082,6 +1160,11 @@ async function bewaarPdfArtefact(db, jobId, base64) {
1082
1160
  throw new PdfRenderFout("mislukt", `Het PDF-bestand kon niet veilig worden opgeslagen: ${error.message}`);
1083
1161
  return { pad, sha256, bytes: inhoud.byteLength };
1084
1162
  }
1163
+ /** Actuele annulering wint altijd van de status uit de eerder opgehaalde claim. */
1164
+ async function isJobGeannuleerd(db, jobId) {
1165
+ const { data } = await db.from("print_jobs").select("status").eq("id", jobId).maybeSingle();
1166
+ return data?.status === "geannuleerd";
1167
+ }
1085
1168
  /**
1086
1169
  * Kleine faseklok: meet per opdracht hoe lang claimen, layout kiezen, printer
1087
1170
  * kiezen, renderen en versturen duren. Eén logregel per opdracht, zodat de
@@ -1126,6 +1209,8 @@ async function metPool(items, max, fn) {
1126
1209
  async function verwerkJob(db, job) {
1127
1210
  const klok = new Faseklok(job);
1128
1211
  try {
1212
+ if (await isJobGeannuleerd(db, String(job.id)))
1213
+ return "wachtend";
1129
1214
  // Dubbel printen voorkomen: deze opdracht staat al bij de printer.
1130
1215
  if (job.extern_job_id) {
1131
1216
  await log(db, job.id, "overgeslagen", "Stond al bij de printer.");
@@ -1165,6 +1250,12 @@ async function verwerkJob(db, job) {
1165
1250
  `Printer '${printer?.naam ?? "onbekend"}' is niet aan PrintNode gekoppeld.`, true);
1166
1251
  return "mislukt";
1167
1252
  }
1253
+ // Een gebruiker kan annuleren terwijl deze opdracht wordt gerenderd. Lees
1254
+ // de actuele status vóór elke onomkeerbare aflevering, niet de oude claim.
1255
+ if (await isJobGeannuleerd(db, String(job.id))) {
1256
+ await log(db, job.id, "annulering_bevestigd", "Niet afgeleverd: opdracht was geannuleerd.");
1257
+ return "wachtend";
1258
+ }
1168
1259
  // Uitkomst van de opmaakkeuze; wordt na verzending vastgelegd naast de
1169
1260
  // aangeleverde invoer, zodat achteraf zichtbaar is wat er gekozen is.
1170
1261
  const uitkomst = { bron: "extern_pdf" };
@@ -1189,6 +1280,10 @@ async function verwerkJob(db, job) {
1189
1280
  });
1190
1281
  }
1191
1282
  klok.markeer("rendered");
1283
+ if (await isJobGeannuleerd(db, String(job.id))) {
1284
+ await log(db, job.id, "annulering_bevestigd", "Niet afgeleverd: tijdens het opmaken geannuleerd.");
1285
+ return "wachtend";
1286
+ }
1192
1287
  // Virtuele PDF-printer: niets gaat naar een fysieke printer; de opgemaakte
1193
1288
  // PDF wordt op de opdracht bewaard en is daarna te downloaden in de
1194
1289
  // wachtrij. De opdracht is meteen voltooid.
@@ -1196,10 +1291,15 @@ async function verwerkJob(db, job) {
1196
1291
  // Bewaar de PDF één keer als binair artefact. De wachtrij en Realtime
1197
1292
  // vervoeren daarna alleen metadata; de werkplek downloadt precies dit bestand.
1198
1293
  const artefact = await bewaarPdfArtefact(db, String(job.id), pdfBase64);
1294
+ if (await isJobGeannuleerd(db, String(job.id))) {
1295
+ await db.storage.from("print-artifacts").remove([artefact.pad]);
1296
+ await log(db, job.id, "annulering_bevestigd", "PDF-artefact na annulering verwijderd.");
1297
+ return "wachtend";
1298
+ }
1199
1299
  // De opdracht blijft bewust op 'bezig' staan: hij is pas voltooid als de
1200
1300
  // werkplek het bestand echt in de PDF-map heeft gezet. De lokale service
1201
1301
  // zet daarna 'opgeslagen' + 'voltooid', of 'mislukt' met een leesbare zin.
1202
- const { error: statusFout } = await db
1302
+ const { data: opgeslagenJob, error: statusFout } = await db
1203
1303
  .from("print_jobs")
1204
1304
  .update({
1205
1305
  printer_id: printer.id,
@@ -1222,11 +1322,19 @@ async function verwerkJob(db, job) {
1222
1322
  volgende_poging: null,
1223
1323
  updated_at: new Date().toISOString(),
1224
1324
  })
1225
- .eq("id", job.id);
1325
+ .eq("id", job.id)
1326
+ .neq("status", "geannuleerd")
1327
+ .select("id")
1328
+ .maybeSingle();
1226
1329
  if (statusFout) {
1227
1330
  await mislukt(db, job, `Opdracht afronden mislukt: ${statusFout.message}`, true);
1228
1331
  return "mislukt";
1229
1332
  }
1333
+ if (!opgeslagenJob) {
1334
+ await db.storage.from("print-artifacts").remove([artefact.pad]);
1335
+ await log(db, job.id, "annulering_bevestigd", "PDF-artefact na gelijktijdige annulering verwijderd.");
1336
+ return "wachtend";
1337
+ }
1230
1338
  klok.markeer("pdf_ready");
1231
1339
  await log(db, job.id, "pdf_gereed", `PDF gereed voor lokale opslag via ${printer.naam ?? "PDF-printer"}. ${klok.samenvatting()}`);
1232
1340
  return "verstuurd";
@@ -1243,7 +1351,13 @@ async function verwerkJob(db, job) {
1243
1351
  kopieen: Number(job.kopieen ?? 1),
1244
1352
  opties: { paper: papier.paper, rotate: papier.rotate, fitToPage: false },
1245
1353
  });
1246
- await db
1354
+ if (await isJobGeannuleerd(db, String(job.id))) {
1355
+ const { annuleerExternePrintOpdracht } = await import("./printService.server.js");
1356
+ await annuleerExternePrintOpdracht(externJobId).catch(() => undefined);
1357
+ await log(db, job.id, "annulering_bevestigd", "Externe printertaak na annulering verwijderd.");
1358
+ return "wachtend";
1359
+ }
1360
+ const { data: gekoppeldeJob } = await db
1247
1361
  .from("print_jobs")
1248
1362
  .update({
1249
1363
  printer_id: printer.id,
@@ -1259,7 +1373,16 @@ async function verwerkJob(db, job) {
1259
1373
  volgende_poging: null,
1260
1374
  updated_at: new Date().toISOString(),
1261
1375
  })
1262
- .eq("id", job.id);
1376
+ .eq("id", job.id)
1377
+ .neq("status", "geannuleerd")
1378
+ .select("id")
1379
+ .maybeSingle();
1380
+ if (!gekoppeldeJob) {
1381
+ const { annuleerExternePrintOpdracht } = await import("./printService.server.js");
1382
+ await annuleerExternePrintOpdracht(externJobId).catch(() => undefined);
1383
+ await log(db, job.id, "annulering_bevestigd", "Externe printertaak na gelijktijdige annulering verwijderd.");
1384
+ return "wachtend";
1385
+ }
1263
1386
  klok.markeer("submitted");
1264
1387
  await log(db, job.id, "verstuurd", `Naar printer ${printer.naam ?? ""}. ${papier.uitleg} ${klok.samenvatting()}`);
1265
1388
  return "verstuurd";
@@ -1311,7 +1434,8 @@ async function synchroniseerStatussen(db) {
1311
1434
  foutmelding: status === "mislukt" ? "De printer meldde een fout." : null,
1312
1435
  updated_at: new Date().toISOString(),
1313
1436
  })
1314
- .eq("id", job.id);
1437
+ .eq("id", job.id)
1438
+ .neq("status", "geannuleerd");
1315
1439
  await log(db, job.id, status);
1316
1440
  return 1;
1317
1441
  }
@@ -1328,7 +1452,8 @@ async function synchroniseerStatussen(db) {
1328
1452
  foutmelding: `Status niet bevestigd: ${reden}`.slice(0, 500),
1329
1453
  updated_at: new Date().toISOString(),
1330
1454
  })
1331
- .eq("id", job.id);
1455
+ .eq("id", job.id)
1456
+ .neq("status", "geannuleerd");
1332
1457
  await log(db, job.id, "status_onbekend", reden);
1333
1458
  console.warn(`[printwachtrij] status ophalen mislukt voor job ${job.id}: ${reden}`);
1334
1459
  return 0;
@@ -1367,7 +1492,8 @@ async function vervalControle(db) {
1367
1492
  voltooid_op: new Date().toISOString(),
1368
1493
  updated_at: new Date().toISOString(),
1369
1494
  })
1370
- .eq("id", job.id);
1495
+ .eq("id", job.id)
1496
+ .neq("status", "geannuleerd");
1371
1497
  await log(db, job.id, "vervallen", reden);
1372
1498
  aantal++;
1373
1499
  }
@@ -1395,7 +1521,8 @@ async function vervalControle(db) {
1395
1521
  voltooid_op: opnieuw ? null : new Date().toISOString(),
1396
1522
  updated_at: new Date().toISOString(),
1397
1523
  })
1398
- .eq("id", job.id);
1524
+ .eq("id", job.id)
1525
+ .neq("status", "geannuleerd");
1399
1526
  await log(db, job.id, opnieuw ? "opnieuw_proberen" : "mislukt", "Vastgelopen ronde hersteld.");
1400
1527
  aantal++;
1401
1528
  }