@flowselections/layout-maker 1.32.0 → 1.38.4
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 +139 -139
- package/dist-lib/components/AfdrukKnop.d.ts +19 -19
- package/dist-lib/components/ComputersLijst.d.ts.map +1 -1
- package/dist-lib/components/ComputersLijst.js +5 -5
- package/dist-lib/components/DocumentenPage.d.ts.map +1 -1
- package/dist-lib/components/DocumentenPage.js +3 -2
- package/dist-lib/components/LayoutPrinterSelect.d.ts +8 -0
- package/dist-lib/components/LayoutPrinterSelect.d.ts.map +1 -0
- package/dist-lib/components/LayoutPrinterSelect.js +23 -0
- package/dist-lib/components/LayoutsPage.d.ts.map +1 -1
- package/dist-lib/components/LayoutsPage.js +16 -3
- package/dist-lib/components/NetwerkPrintersDialog.d.ts +7 -7
- package/dist-lib/components/PaginaDocument.d.ts +4 -1
- package/dist-lib/components/PaginaDocument.d.ts.map +1 -1
- package/dist-lib/components/PaginaDocument.js +10 -5
- package/dist-lib/components/PakbonBuilderPage.d.ts.map +1 -1
- package/dist-lib/components/PakbonBuilderPage.js +6 -2
- package/dist-lib/components/PakbonPreview.d.ts +4 -2
- package/dist-lib/components/PakbonPreview.d.ts.map +1 -1
- package/dist-lib/components/PakbonPreview.js +55 -12
- package/dist-lib/components/PakbonSectiesPanel.d.ts +2 -2
- package/dist-lib/components/PakbonSectiesPanel.d.ts.map +1 -1
- package/dist-lib/components/PakbonSectiesPanel.js +7 -2
- package/dist-lib/components/PdfMapAutoSaver.d.ts +6 -0
- package/dist-lib/components/PdfMapAutoSaver.d.ts.map +1 -0
- package/dist-lib/components/PdfMapAutoSaver.js +12 -0
- package/dist-lib/components/PdfMapGlobaleBanner.d.ts +12 -0
- package/dist-lib/components/PdfMapGlobaleBanner.d.ts.map +1 -0
- package/dist-lib/components/PdfMapGlobaleBanner.js +30 -0
- package/dist-lib/components/PdfMapToestemmingBanner.d.ts +7 -0
- package/dist-lib/components/PdfMapToestemmingBanner.d.ts.map +1 -0
- package/dist-lib/components/PdfMapToestemmingBanner.js +139 -0
- package/dist-lib/components/PrintQueuePage.d.ts.map +1 -1
- package/dist-lib/components/PrintQueuePage.js +42 -3
- package/dist-lib/components/PrintenPage.d.ts.map +1 -1
- package/dist-lib/components/PrintenPage.js +3 -2
- package/dist-lib/components/PrinterSetupWizard.d.ts.map +1 -1
- package/dist-lib/components/PrinterSetupWizard.js +28 -5
- package/dist-lib/components/PrintersRedirectPage.d.ts +1 -1
- package/dist-lib/components/PrintersRedirectPage.js +14 -14
- package/dist-lib/components/PrintplekDetailPage.d.ts.map +1 -1
- package/dist-lib/components/PrintplekDetailPage.js +30 -4
- package/dist-lib/components/PrintplekWizard.d.ts.map +1 -1
- package/dist-lib/components/PrintplekWizard.js +81 -3
- package/dist-lib/components/ProductiebonBuilderPage.d.ts.map +1 -1
- package/dist-lib/components/ProductiebonBuilderPage.js +3 -1
- package/dist-lib/index.d.ts +10 -2
- package/dist-lib/index.d.ts.map +1 -1
- package/dist-lib/index.js +13 -2
- package/dist-lib/integrations/supabase/auth-middleware.d.ts +1396 -727
- package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/client.server.d.ts +1396 -727
- package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/client.server.js +1 -1
- package/dist-lib/integrations/supabase/moduleClient.d.ts +24008 -0
- package/dist-lib/integrations/supabase/moduleClient.d.ts.map +1 -0
- package/dist-lib/integrations/supabase/moduleClient.js +29 -0
- package/dist-lib/integrations/supabase/types.d.ts +1403 -750
- package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
- package/dist-lib/lib/aiLayout.functions.d.ts +1396 -727
- package/dist-lib/lib/aiLayout.functions.d.ts.map +1 -1
- package/dist-lib/lib/aiLayout.server.js +9 -9
- package/dist-lib/lib/browserPrint.js +1 -1
- package/dist-lib/lib/code128.d.ts +5 -0
- package/dist-lib/lib/code128.d.ts.map +1 -0
- package/dist-lib/lib/code128.js +51 -0
- package/dist-lib/lib/htmlPdf.server.d.ts.map +1 -1
- package/dist-lib/lib/htmlPdf.server.js +101 -6
- package/dist-lib/lib/layoutPreview.d.ts.map +1 -1
- package/dist-lib/lib/layoutPreview.js +11 -8
- package/dist-lib/lib/orderData.server.d.ts.map +1 -1
- package/dist-lib/lib/orderData.server.js +123 -10
- package/dist-lib/lib/pakbonBuilder.d.ts +28 -2
- package/dist-lib/lib/pakbonBuilder.d.ts.map +1 -1
- package/dist-lib/lib/pakbonBuilder.js +69 -8
- package/dist-lib/lib/pakbonHtml.d.ts +14 -2
- package/dist-lib/lib/pakbonHtml.d.ts.map +1 -1
- package/dist-lib/lib/pakbonHtml.js +155 -63
- package/dist-lib/lib/pakbonScan.d.ts +44 -0
- package/dist-lib/lib/pakbonScan.d.ts.map +1 -0
- package/dist-lib/lib/pakbonScan.js +105 -0
- package/dist-lib/lib/pdfJobSaver.d.ts +20 -0
- package/dist-lib/lib/pdfJobSaver.d.ts.map +1 -0
- package/dist-lib/lib/pdfJobSaver.js +173 -0
- package/dist-lib/lib/pdfMap.d.ts +48 -0
- package/dist-lib/lib/pdfMap.d.ts.map +1 -0
- package/dist-lib/lib/pdfMap.js +219 -0
- package/dist-lib/lib/pdfPrinter.d.ts +9 -0
- package/dist-lib/lib/pdfPrinter.d.ts.map +1 -0
- package/dist-lib/lib/pdfPrinter.js +34 -0
- package/dist-lib/lib/previewPdf.d.ts.map +1 -1
- package/dist-lib/lib/previewPdf.js +41 -9
- package/dist-lib/lib/printApi.d.ts.map +1 -1
- package/dist-lib/lib/printApi.js +9 -1
- package/dist-lib/lib/printQueue.server.d.ts +7 -0
- package/dist-lib/lib/printQueue.server.d.ts.map +1 -1
- package/dist-lib/lib/printQueue.server.js +346 -52
- package/dist-lib/lib/printService.functions.d.ts +5365 -2689
- package/dist-lib/lib/printService.functions.d.ts.map +1 -1
- package/dist-lib/lib/productiebonHtml.js +15 -15
- package/dist-lib/lib/profielHtml.js +18 -18
- package/dist-lib/lib/supabaseAdmin.server.d.ts +5 -5
- package/dist-lib/lib/supabaseAdmin.server.js +43 -43
- package/dist-lib/lib/supabaseClient.d.ts +1 -3
- package/dist-lib/lib/supabaseClient.d.ts.map +1 -1
- package/dist-lib/lib/supabaseClient.js +4 -8
- package/dist-lib/lib/testpagina.js +3 -3
- package/dist-lib/lib/testprint.js +4 -4
- package/dist-lib/lib/useDocumentData.d.ts +10 -0
- package/dist-lib/lib/useDocumentData.d.ts.map +1 -1
- package/dist-lib/lib/useDocumentData.js +19 -0
- package/dist-lib/lib/voorbeeldPrint.d.ts +4 -0
- package/dist-lib/lib/voorbeeldPrint.d.ts.map +1 -1
- package/dist-lib/lib/voorbeeldPrint.js +13 -2
- package/package.json +151 -151
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
// de juiste printer. Draait alleen op de server (cron / interne aanroep).
|
|
5
5
|
// ============================================================================
|
|
6
6
|
import { maakPakbonOrder, renderPakbonDocument, voorbeeldPakbonOrder, } from "./pakbonHtml.js";
|
|
7
|
+
import { bouwPakbonScanInhoud, bewaarPakbonScanInhoud } from "./pakbonScan.js";
|
|
8
|
+
import { pakbonLayoutVariant } from "./pakbonBuilder.js";
|
|
7
9
|
import { renderProductiebonDocument } from "./productiebonHtml.js";
|
|
8
10
|
import { heeftOrderInhoud, laadOrderVanuitDatabase } from "./orderData.server.js";
|
|
9
11
|
import { profielHeeftInhoud, renderProfielDocument } from "./profielHtml.js";
|
|
@@ -47,6 +49,13 @@ export class GeenDocumentFout extends Error {
|
|
|
47
49
|
this.name = "GeenDocumentFout";
|
|
48
50
|
}
|
|
49
51
|
}
|
|
52
|
+
/** De Engine heeft de taak gepland, maar de layout is nog niet aangeleverd. */
|
|
53
|
+
export class WachtOpLayoutFout extends Error {
|
|
54
|
+
constructor() {
|
|
55
|
+
super(...arguments);
|
|
56
|
+
this.name = "WachtOpLayoutFout";
|
|
57
|
+
}
|
|
58
|
+
}
|
|
50
59
|
/**
|
|
51
60
|
* Oudere/andere benamingen voor hetzelfde document. De Logistiek Engine noemt
|
|
52
61
|
* de afleverbrief bijvoorbeeld nog "verzendbrief"; hier vertalen we dat naar
|
|
@@ -65,6 +74,85 @@ export function normaliseerDocumenttype(type) {
|
|
|
65
74
|
return "document";
|
|
66
75
|
return DOCUMENTTYPE_ALIASSEN[t] ?? t;
|
|
67
76
|
}
|
|
77
|
+
// ---------------------------------------------------------------------------
|
|
78
|
+
// Contract met de Logistics Engine.
|
|
79
|
+
// De Engine bepaalt documentsoort, variant, layout en printplek; deze module
|
|
80
|
+
// rendert en print alleen. Alles hieronder leest die keuzes uit de opdracht en
|
|
81
|
+
// overruled ze nooit.
|
|
82
|
+
// ---------------------------------------------------------------------------
|
|
83
|
+
/** Het layout-id zoals de Engine het aanlevert (kolom of payload). */
|
|
84
|
+
export function leesEngineLayoutId(job) {
|
|
85
|
+
const p = (job.payload ?? {});
|
|
86
|
+
const d = (p.data ?? {});
|
|
87
|
+
for (const k of [job.layout_id, p.layout_id, p.layoutId, d.layout_id, d.layoutId]) {
|
|
88
|
+
const v = String(k ?? "").trim();
|
|
89
|
+
if (v)
|
|
90
|
+
return v;
|
|
91
|
+
}
|
|
92
|
+
return "";
|
|
93
|
+
}
|
|
94
|
+
/** Documentsoort zoals de Engine hem noemt (`document_code`). */
|
|
95
|
+
function leesDocumentCode(job) {
|
|
96
|
+
const p = (job.payload ?? {});
|
|
97
|
+
const d = (p.data ?? {});
|
|
98
|
+
for (const k of [job.document_code, p.document_code, p.documentCode, d.document_code]) {
|
|
99
|
+
const v = String(k ?? "").trim();
|
|
100
|
+
if (v)
|
|
101
|
+
return v;
|
|
102
|
+
}
|
|
103
|
+
return "";
|
|
104
|
+
}
|
|
105
|
+
/** Is dit een opdracht die door de Logistics Engine is gepland? */
|
|
106
|
+
function isEngineOpdracht(job) {
|
|
107
|
+
const p = (job.payload ?? {});
|
|
108
|
+
if (leesDocumentCode(job))
|
|
109
|
+
return true;
|
|
110
|
+
const bron = String(p.bron_module ?? p.owner_module ?? p.source_module ?? "").toLowerCase();
|
|
111
|
+
return bron.includes("logistic") || bron.includes("engine");
|
|
112
|
+
}
|
|
113
|
+
/** Handmatig opnieuw printen? */
|
|
114
|
+
function isHerprint(job) {
|
|
115
|
+
const p = (job.payload ?? {});
|
|
116
|
+
const d = (p.data ?? {});
|
|
117
|
+
return job.is_herprint === true || p.is_herprint === true || d.is_herprint === true;
|
|
118
|
+
}
|
|
119
|
+
/** De ordersleutel waarop een herprint bij de oorspronkelijke print hoort. */
|
|
120
|
+
function leesOrderRef(job) {
|
|
121
|
+
const p = (job.payload ?? {});
|
|
122
|
+
const d = (p.data ?? {});
|
|
123
|
+
for (const k of [job.order_ref, p.order_ref, p.orderRef, d.order_ref, d.orderRef, job.order_id]) {
|
|
124
|
+
const v = String(k ?? "").trim();
|
|
125
|
+
if (v)
|
|
126
|
+
return v;
|
|
127
|
+
}
|
|
128
|
+
return "";
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Een herprint gebruikt dezelfde layout als de oorspronkelijke print. Stuurt de
|
|
132
|
+
* Engine geen layout mee, dan zoeken we die op bij de laatste geslaagde print
|
|
133
|
+
* van hetzelfde document van dezelfde order.
|
|
134
|
+
*/
|
|
135
|
+
async function zoekHerprintLayoutId(db, job) {
|
|
136
|
+
const orderRef = leesOrderRef(job);
|
|
137
|
+
if (!orderRef)
|
|
138
|
+
return "";
|
|
139
|
+
const type = normaliseerDocumenttype(leesDocumentCode(job) || job.documenttype);
|
|
140
|
+
const { data } = await db
|
|
141
|
+
.from("print_jobs")
|
|
142
|
+
.select("gebruikte_layout_id, documenttype, created_at")
|
|
143
|
+
.eq("order_ref", orderRef)
|
|
144
|
+
.not("gebruikte_layout_id", "is", null)
|
|
145
|
+
.order("created_at", { ascending: false })
|
|
146
|
+
.limit(10);
|
|
147
|
+
for (const rij of arr(data)) {
|
|
148
|
+
if (normaliseerDocumenttype(rij.documenttype) !== type)
|
|
149
|
+
continue;
|
|
150
|
+
const id = String(rij.gebruikte_layout_id ?? "").trim();
|
|
151
|
+
if (id)
|
|
152
|
+
return id;
|
|
153
|
+
}
|
|
154
|
+
return "";
|
|
155
|
+
}
|
|
68
156
|
/**
|
|
69
157
|
* Zoekt de layout die bij de opdracht hoort.
|
|
70
158
|
* Staat er een layout_id op de opdracht (bijv. gezet door de Logistiek
|
|
@@ -94,15 +182,19 @@ function magOntwerpWinnen(layout) {
|
|
|
94
182
|
return String(layout.bron ?? "") !== "floriday";
|
|
95
183
|
}
|
|
96
184
|
async function zoekLayout(db, job) {
|
|
185
|
+
const engineJob = isEngineOpdracht(job);
|
|
97
186
|
const heeftEigenOpmaak = typeof job.render_html === "string" && job.render_html.trim().length > 40;
|
|
98
|
-
|
|
99
|
-
// koppeling: is er opmaak meegestuurd (bijv. een voorbeeldafdruk uit de
|
|
100
|
-
// ontwerper), dan is die opmaak leidend.
|
|
101
|
-
const uitPayload = heeftEigenOpmaak
|
|
187
|
+
const uitPayload = engineJob
|
|
102
188
|
? null
|
|
103
189
|
: (job.payload?.layoutId ?? job.payload?.data?.layoutId ?? job.payload?.layout_id ?? null);
|
|
104
|
-
|
|
105
|
-
|
|
190
|
+
let id = leesEngineLayoutId(job) || job.layout_id || uitPayload;
|
|
191
|
+
if (engineJob && !id && isHerprint(job)) {
|
|
192
|
+
id = await zoekHerprintLayoutId(db, job);
|
|
193
|
+
}
|
|
194
|
+
if (engineJob && !id) {
|
|
195
|
+
throw new WachtOpLayoutFout("Wacht op layout_id van de Logistics Engine. Deze taak wordt niet met een vervangende layout uitgevoerd.");
|
|
196
|
+
}
|
|
197
|
+
const voorbeeld = !engineJob && heeftEigenOpmaak && isVoorbeeldAfdruk(job);
|
|
106
198
|
if (id) {
|
|
107
199
|
const { data } = await db
|
|
108
200
|
.from("document_layouts")
|
|
@@ -113,19 +205,24 @@ async function zoekLayout(db, job) {
|
|
|
113
205
|
throw new GeenDocumentFout("De layout die aan deze opdracht hangt bestaat niet meer. Koppel een geldige layout.");
|
|
114
206
|
}
|
|
115
207
|
const layout = data;
|
|
116
|
-
return {
|
|
208
|
+
return {
|
|
209
|
+
layout,
|
|
210
|
+
// Een Engine-layout is altijd leidend, ook als er render_html is meegestuurd.
|
|
211
|
+
ontwerpWint: engineJob || (!voorbeeld && magOntwerpWinnen(layout)),
|
|
212
|
+
};
|
|
117
213
|
}
|
|
214
|
+
const documenttype = normaliseerDocumenttype(leesDocumentCode(job) || job.documenttype);
|
|
118
215
|
const { data } = await db
|
|
119
216
|
.from("document_layouts")
|
|
120
217
|
.select("*")
|
|
121
|
-
.eq("documenttype",
|
|
218
|
+
.eq("documenttype", documenttype)
|
|
122
219
|
.eq("actief", true)
|
|
123
220
|
.order("is_standaard", { ascending: false })
|
|
124
221
|
.order("updated_at", { ascending: false })
|
|
125
222
|
.limit(1);
|
|
126
223
|
const gevonden = (arr(data)[0] ?? null);
|
|
127
224
|
if (!gevonden && !job.render_html) {
|
|
128
|
-
throw new GeenDocumentFout(`Geen layout gevonden voor documenttype '${
|
|
225
|
+
throw new GeenDocumentFout(`Geen layout gevonden voor documenttype '${documenttype}'. ` +
|
|
129
226
|
"Maak een layout aan en zet die op actief.");
|
|
130
227
|
}
|
|
131
228
|
return { layout: gevonden, ontwerpWint: !voorbeeld && magOntwerpWinnen(gevonden) };
|
|
@@ -148,7 +245,7 @@ async function zoekBronregel(db, job) {
|
|
|
148
245
|
const regel = data;
|
|
149
246
|
if (!regel || regel.bron === "auto")
|
|
150
247
|
return null;
|
|
151
|
-
if (regel.bron === "eigen_layout" && regel.layout_id && !job.layout_id) {
|
|
248
|
+
if (regel.bron === "eigen_layout" && regel.layout_id && !job.layout_id && !isEngineOpdracht(job)) {
|
|
152
249
|
job.layout_id = regel.layout_id;
|
|
153
250
|
}
|
|
154
251
|
return regel.bron;
|
|
@@ -159,6 +256,8 @@ async function zoekBronregel(db, job) {
|
|
|
159
256
|
*/
|
|
160
257
|
async function isFloridayLayoutKeuze(db, job) {
|
|
161
258
|
const payload = (job.payload ?? {});
|
|
259
|
+
if (isEngineOpdracht(job))
|
|
260
|
+
return false;
|
|
162
261
|
const id = job.layout_id ?? payload?.["layoutId"] ?? payload?.["layout_id"] ?? null;
|
|
163
262
|
if (!id)
|
|
164
263
|
return false;
|
|
@@ -324,18 +423,40 @@ async function zoekPrinter(db, job, layout) {
|
|
|
324
423
|
return null;
|
|
325
424
|
const { data } = await db
|
|
326
425
|
.from("printers")
|
|
327
|
-
.select("id, naam, extern_printer_id, actief, papierformaat, ondersteunde_formaten, ondersteunde_laden, formaten_bijgewerkt_op")
|
|
426
|
+
.select("id, naam, extern_printer_id, actief, papierformaat, ondersteunde_formaten, ondersteunde_laden, formaten_bijgewerkt_op, afdrukmethode")
|
|
328
427
|
.eq("id", id)
|
|
329
428
|
.maybeSingle();
|
|
330
429
|
return data && data.actief !== false ? data : null;
|
|
331
430
|
};
|
|
332
|
-
const type = normaliseerDocumenttype(job.documenttype);
|
|
333
|
-
//
|
|
334
|
-
|
|
431
|
+
const type = normaliseerDocumenttype(leesDocumentCode(job) || job.documenttype);
|
|
432
|
+
// Bij Engine-opdrachten is print_plek_id leidend. Een eventueel oud
|
|
433
|
+
// printer_id mag die keuze niet overschrijven.
|
|
434
|
+
let printer = isEngineOpdracht(job) && leesPrintPlekId(job)
|
|
435
|
+
? null
|
|
436
|
+
: await pak(job.printer_id);
|
|
335
437
|
if (printer)
|
|
336
438
|
return { printer, reden: "" };
|
|
337
439
|
const werkplek = String(job.payload?.werkplek ?? "").trim();
|
|
338
440
|
const pickstation = leesPickstation(job);
|
|
441
|
+
const explicietePlek = leesPrintPlekId(job);
|
|
442
|
+
// Een Engine-printplek is een harde keuze. Nooit terugvallen op computer,
|
|
443
|
+
// layout of bedrijfsregels wanneer de Engine een print_plek_id meegaf.
|
|
444
|
+
if (explicietePlek) {
|
|
445
|
+
const { data: pp } = await db
|
|
446
|
+
.from("document_printplek_printers")
|
|
447
|
+
.select("printer_id")
|
|
448
|
+
.eq("print_plek_id", explicietePlek)
|
|
449
|
+
.eq("documenttype", type)
|
|
450
|
+
.maybeSingle();
|
|
451
|
+
printer = await pak(pp?.printer_id);
|
|
452
|
+
if (printer)
|
|
453
|
+
return { printer, reden: "" };
|
|
454
|
+
const { data: plek } = await db.from("print_plekken").select("naam").eq("id", explicietePlek).maybeSingle();
|
|
455
|
+
return {
|
|
456
|
+
printer: null,
|
|
457
|
+
reden: `Printplek '${String(plek?.naam ?? explicietePlek)}' heeft geen printer voor documenttype '${type}'.`,
|
|
458
|
+
};
|
|
459
|
+
}
|
|
339
460
|
// 2. Regel van de printplek waar de opdracht vandaan komt.
|
|
340
461
|
const plekId = await zoekPrintPlekId(db, job);
|
|
341
462
|
if (plekId) {
|
|
@@ -414,24 +535,31 @@ async function zoekPrinter(db, job, layout) {
|
|
|
414
535
|
reden: `Documenttype '${type}' heeft geen standaardprinter. Stel dit in bij Printen > Standaardregels.`,
|
|
415
536
|
};
|
|
416
537
|
}
|
|
538
|
+
/** Verzamelt elk logo-id ergens in de layoutconfiguratie (op elke diepte). */
|
|
539
|
+
function verzamelLogoIds(waarde, uit, diepte = 0) {
|
|
540
|
+
if (diepte > 8 || waarde === null || typeof waarde !== "object")
|
|
541
|
+
return;
|
|
542
|
+
if (Array.isArray(waarde)) {
|
|
543
|
+
for (const item of waarde)
|
|
544
|
+
verzamelLogoIds(item, uit, diepte + 1);
|
|
545
|
+
return;
|
|
546
|
+
}
|
|
547
|
+
for (const [sleutel, val] of Object.entries(waarde)) {
|
|
548
|
+
if ((sleutel === "logoId" || sleutel === "logo_id") && typeof val === "string" && val) {
|
|
549
|
+
uit.add(val);
|
|
550
|
+
}
|
|
551
|
+
else {
|
|
552
|
+
verzamelLogoIds(val, uit, diepte + 1);
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
}
|
|
417
556
|
/** Haalt de logo's op die in de layout gebruikt worden, met tijdelijke URL. */
|
|
418
557
|
async function logosVoor(db, layout) {
|
|
419
558
|
const config = layout?.config ?? null;
|
|
420
559
|
const ids = new Set();
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
// Ook logo's die als blok in de ontwerper staan meenemen.
|
|
425
|
-
for (const sectie of Array.isArray(config?.sections) ? config.sections : []) {
|
|
426
|
-
const id = sectie?.opties?.logoId ?? sectie?.logoId;
|
|
427
|
-
if (id)
|
|
428
|
-
ids.add(String(id));
|
|
429
|
-
}
|
|
430
|
-
for (const blok of Array.isArray(config?.blokken) ? config.blokken : []) {
|
|
431
|
-
const id = blok?.opties?.logoId ?? blok?.logoId;
|
|
432
|
-
if (id)
|
|
433
|
-
ids.add(String(id));
|
|
434
|
-
}
|
|
560
|
+
// Logo's staan op verschillende plekken in de configuratie: als documentlogo,
|
|
561
|
+
// als blok in de ontwerper, of binnen `pakbon.secties` / `productiebon.blokken`.
|
|
562
|
+
verzamelLogoIds(config, ids);
|
|
435
563
|
if (!ids.size)
|
|
436
564
|
return [];
|
|
437
565
|
const { data } = await db
|
|
@@ -510,6 +638,13 @@ export async function bouwHtml(db, job, keuze, uitkomst) {
|
|
|
510
638
|
: null;
|
|
511
639
|
if ((pakbonSecties && pakbonSecties.length) || String(layout.documenttype) === "pakbon") {
|
|
512
640
|
const orderBron = p.order ?? p.data?.order ?? p.data ?? p;
|
|
641
|
+
const pakbonMethode = Array.isArray(pakbonSecties)
|
|
642
|
+
? String(pakbonSecties.find((s) => s?.type === "pickmethode")?.opties?.methode ?? "kar")
|
|
643
|
+
: "kar";
|
|
644
|
+
const engineVariant = job.layout_variant ?? p.layout_variant ?? p.layoutVariant ?? p.data?.layout_variant ?? null;
|
|
645
|
+
if (isEngineOpdracht(job) && pakbonMethode === "beide" && !["full_cart", "partial_order"].includes(String(engineVariant ?? ""))) {
|
|
646
|
+
throw new WachtOpLayoutFout("Wacht op layout_variant van de Logistics Engine: kies full_cart of partial_order.");
|
|
647
|
+
}
|
|
513
648
|
const uitDb = heeftOrderInhoud(orderBron) ? null : await laadOrderVanuitDatabase(db, p);
|
|
514
649
|
// Een voorbeeldafdruk zonder ordergegevens krijgt een voorbeeldorder, zodat
|
|
515
650
|
// je ziet hoe het document er in de praktijk uitziet.
|
|
@@ -517,14 +652,82 @@ export async function bouwHtml(db, job, keuze, uitkomst) {
|
|
|
517
652
|
(p.voorbeeld === true && !heeftOrderInhoud(orderBron)
|
|
518
653
|
? voorbeeldPakbonOrder()
|
|
519
654
|
: orderBron);
|
|
520
|
-
|
|
521
|
-
|
|
655
|
+
// Barcode en kargegevens van de Engine mogen niet verloren gaan wanneer
|
|
656
|
+
// de ordergegevens uit een subobject komen.
|
|
657
|
+
const engineBarcode = p.barcode ?? p.data?.cart?.barcode ?? null;
|
|
658
|
+
const engineCart = p.data?.cart ?? null;
|
|
659
|
+
const bronRecord = (bron && typeof bron === "object" ? bron : {});
|
|
660
|
+
// Verwijder order-/payloadbarcodes: bij Engine-opdrachten zijn alleen de
|
|
661
|
+
// twee contractvelden hierboven toegestaan als bron voor de afleverbriefscan.
|
|
662
|
+
const { barcode: _bronBarcode, cart: _bronCart, ...bronZonderEngineBarcode } = bronRecord;
|
|
663
|
+
let order = maakPakbonOrder({
|
|
664
|
+
...bronZonderEngineBarcode,
|
|
665
|
+
...(engineBarcode ? { barcode: engineBarcode } : {}),
|
|
666
|
+
...(engineCart ? { cart: engineCart } : {}),
|
|
667
|
+
});
|
|
668
|
+
if (isEngineOpdracht(job)) {
|
|
669
|
+
const barcode = String(engineBarcode ?? "").trim();
|
|
670
|
+
if (!barcode) {
|
|
671
|
+
throw new WachtOpLayoutFout("Wacht op barcode van de Logistics Engine: iedere kar moet een eigen pakbonbarcode krijgen.");
|
|
672
|
+
}
|
|
673
|
+
const cartId = String(engineCart?.cart_id ?? "").trim();
|
|
674
|
+
const karren = Array.isArray(order.karren) ? order.karren : [];
|
|
675
|
+
const passendeKarren = cartId
|
|
676
|
+
? karren.filter((kar) => String(kar.cart_id ?? "").trim() === cartId)
|
|
677
|
+
: karren;
|
|
678
|
+
// Eén Engine-opdracht staat voor precies één kar en dus één pakbon. Een
|
|
679
|
+
// opdracht met meerdere karren mag niet één barcode aan meerdere karren
|
|
680
|
+
// koppelen; de Engine moet daarvoor afzonderlijke jobs aanmaken.
|
|
681
|
+
if (passendeKarren.length > 1 || (!cartId && karren.length > 1)) {
|
|
682
|
+
throw new WachtOpLayoutFout("Wacht op afzonderlijke kar-opdrachten van de Logistics Engine: maak per kar één print_job met een eigen barcode.");
|
|
683
|
+
}
|
|
684
|
+
if (cartId && passendeKarren.length === 0 && karren.length > 1) {
|
|
685
|
+
throw new WachtOpLayoutFout("De door de Logistics Engine gekozen kar is niet gevonden in de pakbongegevens.");
|
|
686
|
+
}
|
|
687
|
+
const gekozenKar = passendeKarren[0] ?? (karren.length === 1 ? karren[0] : null);
|
|
688
|
+
if (gekozenKar) {
|
|
689
|
+
order = {
|
|
690
|
+
...order,
|
|
691
|
+
cart: {
|
|
692
|
+
...order.cart,
|
|
693
|
+
...engineCart,
|
|
694
|
+
barcode,
|
|
695
|
+
cart_id: cartId || gekozenKar.cart_id,
|
|
696
|
+
cart_label: String(engineCart?.cart_label ?? gekozenKar.cart_label ?? "").trim() || undefined,
|
|
697
|
+
cart_index: Number(engineCart?.cart_index ?? gekozenKar.cart_index ?? 0) || undefined,
|
|
698
|
+
cart_total: Number(engineCart?.cart_total ?? gekozenKar.cart_total ?? 0) || undefined,
|
|
699
|
+
},
|
|
700
|
+
karren: [{ ...gekozenKar, barcode }],
|
|
701
|
+
};
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
const html = renderPakbonDocument(layout.config, order, {
|
|
522
705
|
breedteMm,
|
|
523
706
|
hoogteMm,
|
|
524
707
|
margeMm: Number(layout.marge_boven ?? 12),
|
|
525
708
|
titel: job.documentnaam ?? "Document",
|
|
709
|
+
layoutVariant: engineVariant,
|
|
526
710
|
logos: await logosVoor(db, layout),
|
|
527
711
|
});
|
|
712
|
+
// De Engine bepaalt de barcode. De module registreert uitsluitend de
|
|
713
|
+
// gerenderde inhoud onder die bestaande waarde voor de scanner. Als de
|
|
714
|
+
// registratie tijdelijk niet kan, mag dat een afdruk nooit blokkeren.
|
|
715
|
+
const scanInhoud = bouwPakbonScanInhoud(order, {
|
|
716
|
+
barcode: engineBarcode,
|
|
717
|
+
layoutVariant: pakbonLayoutVariant(engineVariant),
|
|
718
|
+
layoutId: String(layout.id ?? "") || null,
|
|
719
|
+
printJobId: String(job.id ?? "") || null,
|
|
720
|
+
isHerprint: isHerprint(job),
|
|
721
|
+
});
|
|
722
|
+
if (scanInhoud) {
|
|
723
|
+
try {
|
|
724
|
+
await bewaarPakbonScanInhoud(db, scanInhoud);
|
|
725
|
+
}
|
|
726
|
+
catch (e) {
|
|
727
|
+
await log(db, job.id, "barcode_inhoud_niet_opgeslagen", e instanceof Error ? e.message : "Registratie van barcode-inhoud mislukt");
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
return html;
|
|
528
731
|
}
|
|
529
732
|
// 2) Productiebon-layouts (blokken onder `productiebon.blokken`).
|
|
530
733
|
if (Array.isArray(cfg.productiebon?.blokken) ||
|
|
@@ -616,6 +819,20 @@ function plattePayload(payload) {
|
|
|
616
819
|
loop(payload, "", 0);
|
|
617
820
|
return uit;
|
|
618
821
|
}
|
|
822
|
+
async function wachtOpLayout(db, job, reden) {
|
|
823
|
+
await db
|
|
824
|
+
.from("print_jobs")
|
|
825
|
+
.update({
|
|
826
|
+
status: "wachtend",
|
|
827
|
+
foutmelding: reden.slice(0, 500),
|
|
828
|
+
volgende_poging: new Date(Date.now() + 5 * 60 * 1000).toISOString(),
|
|
829
|
+
opgepakt_op: null,
|
|
830
|
+
voltooid_op: null,
|
|
831
|
+
updated_at: new Date().toISOString(),
|
|
832
|
+
})
|
|
833
|
+
.eq("id", job.id);
|
|
834
|
+
await log(db, job.id, "wacht_op_engine_layout", reden);
|
|
835
|
+
}
|
|
619
836
|
async function mislukt(db, job, reden, terugNaarWachtend) {
|
|
620
837
|
const pogingen = Number(job.pogingen ?? 0);
|
|
621
838
|
const opnieuw = terugNaarWachtend && pogingen < MAX_POGINGEN;
|
|
@@ -643,19 +860,20 @@ async function verwerkJob(db, job) {
|
|
|
643
860
|
// 1) Bronregel: bepaalt of het meegestuurde document wordt gebruikt of dat
|
|
644
861
|
// er altijd een eigen ontwerp wordt opgemaakt.
|
|
645
862
|
let bron = await zoekBronregel(db, job);
|
|
646
|
-
// 1b)
|
|
647
|
-
//
|
|
648
|
-
if (await isFloridayLayoutKeuze(db, job)) {
|
|
863
|
+
// 1b) Legacy Floriday-keuze; Engine-opdrachten volgen uitsluitend hun
|
|
864
|
+
// eigen layout_id en worden nooit teruggezet naar een externe PDF.
|
|
865
|
+
if (!isEngineOpdracht(job) && await isFloridayLayoutKeuze(db, job)) {
|
|
649
866
|
bron = "extern_pdf";
|
|
650
867
|
job.layout_id = null;
|
|
651
868
|
}
|
|
652
|
-
if (bron === "eigen_layout") {
|
|
869
|
+
if (bron === "eigen_layout" || isEngineOpdracht(job)) {
|
|
653
870
|
job.pdf_base64 = null;
|
|
654
871
|
job.pdf_url = null;
|
|
655
872
|
}
|
|
656
|
-
// 2) Kant-en-klare PDF
|
|
657
|
-
|
|
658
|
-
|
|
873
|
+
// 2) Kant-en-klare PDF gaat alleen buiten het Engine-contract ongewijzigd
|
|
874
|
+
// door. De Engine bepaalt altijd de layout, ook als er een PDF aanwezig is.
|
|
875
|
+
const klaar = isEngineOpdracht(job) ? null : await kantEnKlarePdf(db, job);
|
|
876
|
+
if (!klaar && bron === "extern_pdf" && !isEngineOpdracht(job)) {
|
|
659
877
|
await mislukt(db, job, "Floriday-document ontbreekt: voor dit document is ingesteld dat het " +
|
|
660
878
|
"aangeleverde document wordt gebruikt, maar er is geen PDF meegestuurd.", true);
|
|
661
879
|
return "mislukt";
|
|
@@ -664,7 +882,9 @@ async function verwerkJob(db, job) {
|
|
|
664
882
|
const layout = keuze.layout;
|
|
665
883
|
const printerKeuze = await zoekPrinter(db, job, layout);
|
|
666
884
|
const printer = printerKeuze.printer;
|
|
667
|
-
|
|
885
|
+
// Virtuele PDF-printer: geen PrintNode nodig; de PDF wordt bewaard.
|
|
886
|
+
const isPdfPrinter = String(printer?.afdrukmethode ?? "") === "pdf";
|
|
887
|
+
if (!printer || (!isPdfPrinter && !printer.extern_printer_id)) {
|
|
668
888
|
await mislukt(db, job, printerKeuze.reden ||
|
|
669
889
|
`Printer '${printer?.naam ?? "onbekend"}' is niet aan PrintNode gekoppeld.`, true);
|
|
670
890
|
return "mislukt";
|
|
@@ -679,18 +899,69 @@ async function verwerkJob(db, job) {
|
|
|
679
899
|
throw new GeenDocumentFout("Het document was leeg: er zijn geen gegevens gevonden om af te drukken. " +
|
|
680
900
|
"Controleer of de layout blokken bevat en of de order gegevens heeft.");
|
|
681
901
|
}
|
|
682
|
-
|
|
902
|
+
// Een voorbeeldafdruk koppelt bewust geen layout (het meegestuurde
|
|
903
|
+
// ontwerp is leidend), maar geeft wél het papierformaat van de bewerkte
|
|
904
|
+
// layout mee. Zonder dit zou het voorbeeld altijd op A4 uitkomen.
|
|
905
|
+
const vf = (job.payload ?? {})["voorbeeld_formaat"];
|
|
906
|
+
const liggend = String(layout?.orientatie ?? vf?.["orientatie"] ?? "portrait") === "landscape";
|
|
683
907
|
pdfBase64 = await htmlNaarPdfServer({
|
|
684
908
|
html,
|
|
685
|
-
breedteMm: Number(layout?.breedte_mm ?? 210),
|
|
686
|
-
hoogteMm: Number(layout?.hoogte_mm ?? 297),
|
|
687
|
-
margeMm: Number(layout?.marge_boven ?? 10),
|
|
909
|
+
breedteMm: Number(layout?.breedte_mm ?? vf?.["breedte_mm"] ?? 210),
|
|
910
|
+
hoogteMm: Number(layout?.hoogte_mm ?? vf?.["hoogte_mm"] ?? 297),
|
|
911
|
+
margeMm: Number(layout?.marge_boven ?? vf?.["marge_boven"] ?? 10),
|
|
688
912
|
landschap: liggend,
|
|
689
913
|
});
|
|
690
914
|
}
|
|
915
|
+
// Virtuele PDF-printer: niets gaat naar een fysieke printer; de opgemaakte
|
|
916
|
+
// PDF wordt op de opdracht bewaard en is daarna te downloaden in de
|
|
917
|
+
// wachtrij. De opdracht is meteen voltooid.
|
|
918
|
+
if (isPdfPrinter) {
|
|
919
|
+
// In twee stappen: eerst de status (klein en altijd betrouwbaar), daarna
|
|
920
|
+
// pas de PDF zelf. Een grote PDF kan door de databaselimiet geweigerd
|
|
921
|
+
// worden; de opdracht mag daar nooit door blijven hangen op 'bezig'.
|
|
922
|
+
const { error: statusFout } = await db
|
|
923
|
+
.from("print_jobs")
|
|
924
|
+
.update({
|
|
925
|
+
printer_id: printer.id,
|
|
926
|
+
gebruikte_layout_id: uitkomst.bron === "extern_pdf" ? null : (layout?.id ?? null),
|
|
927
|
+
gebruikte_bron: uitkomst.bron,
|
|
928
|
+
status: "voltooid",
|
|
929
|
+
lokale_pdf_status: "gereed",
|
|
930
|
+
lokale_pdf_fout: null,
|
|
931
|
+
lokaal_opgeslagen_op: null,
|
|
932
|
+
foutmelding: null,
|
|
933
|
+
volgende_poging: null,
|
|
934
|
+
voltooid_op: new Date().toISOString(),
|
|
935
|
+
updated_at: new Date().toISOString(),
|
|
936
|
+
})
|
|
937
|
+
.eq("id", job.id);
|
|
938
|
+
if (statusFout) {
|
|
939
|
+
await mislukt(db, job, `Opdracht afronden mislukt: ${statusFout.message}`, true);
|
|
940
|
+
return "mislukt";
|
|
941
|
+
}
|
|
942
|
+
const { error: pdfFout } = await db
|
|
943
|
+
.from("print_jobs")
|
|
944
|
+
.update({ resultaat_pdf_base64: pdfBase64 })
|
|
945
|
+
.eq("id", job.id);
|
|
946
|
+
if (pdfFout) {
|
|
947
|
+
await db
|
|
948
|
+
.from("print_jobs")
|
|
949
|
+
.update({
|
|
950
|
+
lokale_pdf_status: "fout",
|
|
951
|
+
lokale_pdf_fout: `PDF kon niet bewaard worden: ${pdfFout.message}`.slice(0, 500),
|
|
952
|
+
updated_at: new Date().toISOString(),
|
|
953
|
+
})
|
|
954
|
+
.eq("id", job.id);
|
|
955
|
+
await log(db, job.id, "mislukt", `PDF bewaren mislukt: ${pdfFout.message}`);
|
|
956
|
+
return "verstuurd";
|
|
957
|
+
}
|
|
958
|
+
await log(db, job.id, "pdf_gereed", `PDF gereed voor lokale opslag via ${printer.naam ?? "PDF-printer"}.`);
|
|
959
|
+
return "verstuurd";
|
|
960
|
+
}
|
|
691
961
|
const formaten = await zorgVoorFormaten(db, printer);
|
|
692
|
-
const
|
|
693
|
-
const
|
|
962
|
+
const vfPapier = (job.payload ?? {})["voorbeeld_formaat"];
|
|
963
|
+
const breedteMm = Number(layout?.breedte_mm ?? vfPapier?.["breedte_mm"] ?? standaardMaat(printer).breedte);
|
|
964
|
+
const hoogteMm = Number(layout?.hoogte_mm ?? vfPapier?.["hoogte_mm"] ?? standaardMaat(printer).hoogte);
|
|
694
965
|
const papier = kiesPapier(formaten, breedteMm, hoogteMm);
|
|
695
966
|
const externJobId = await stuurPdfNaarPrinter({
|
|
696
967
|
externPrinterId: String(printer.extern_printer_id),
|
|
@@ -717,6 +988,10 @@ async function verwerkJob(db, job) {
|
|
|
717
988
|
return "verstuurd";
|
|
718
989
|
}
|
|
719
990
|
catch (e) {
|
|
991
|
+
if (e instanceof WachtOpLayoutFout) {
|
|
992
|
+
await wachtOpLayout(db, job, e.message);
|
|
993
|
+
return "wachtend";
|
|
994
|
+
}
|
|
720
995
|
// Ontbreekt er een document of layout? Dan heeft opnieuw proberen geen zin.
|
|
721
996
|
const blijvend = e instanceof GeenDocumentFout;
|
|
722
997
|
const tijdelijk = !blijvend && (e instanceof PdfRenderFout || e?.name === "PrintToegangFout");
|
|
@@ -812,6 +1087,7 @@ async function vervalControle(db) {
|
|
|
812
1087
|
.select("id, pogingen")
|
|
813
1088
|
.eq("status", "bezig")
|
|
814
1089
|
.is("extern_job_id", null)
|
|
1090
|
+
.neq("lokale_pdf_status", "gereed")
|
|
815
1091
|
.or(`opgepakt_op.lt.${grensClaim},and(opgepakt_op.is.null,updated_at.lt.${grensClaim})`)
|
|
816
1092
|
.limit(50);
|
|
817
1093
|
for (const job of arr(vastgelopen)) {
|
|
@@ -844,16 +1120,31 @@ export async function verwerkWachtrij(db, max = 5) {
|
|
|
844
1120
|
catch (e) {
|
|
845
1121
|
console.warn(`[printwachtrij] vervalcontrole mislukt: ${e instanceof Error ? e.message : "onbekend"}`);
|
|
846
1122
|
}
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
const
|
|
1123
|
+
// PDF-opdrachten hebben een eigen claimroute. Daardoor kunnen oudere
|
|
1124
|
+
// gepubliceerde moduleversies, die een PDF-printer nog als PrintNode-printer
|
|
1125
|
+
// behandelen, deze opdrachten niet meer vóór de actuele runner oppakken.
|
|
1126
|
+
const { data: pdfData, error: pdfError } = await db.rpc("claim_pdf_print_jobs", { _max: max });
|
|
1127
|
+
if (pdfError)
|
|
1128
|
+
throw new Error(pdfError.message);
|
|
1129
|
+
const pdfJobs = arr(pdfData);
|
|
1130
|
+
const resterend = Math.max(0, max - pdfJobs.length);
|
|
1131
|
+
let gewoneJobs = [];
|
|
1132
|
+
if (resterend > 0) {
|
|
1133
|
+
const { data, error } = await db.rpc("claim_cloud_print_jobs", { _max: resterend });
|
|
1134
|
+
if (error)
|
|
1135
|
+
throw new Error(error.message);
|
|
1136
|
+
gewoneJobs = arr(data);
|
|
1137
|
+
}
|
|
1138
|
+
const jobs = [...pdfJobs, ...gewoneJobs];
|
|
851
1139
|
let verstuurd = 0;
|
|
852
1140
|
let mislukteJobs = 0;
|
|
1141
|
+
let wachtend = 0;
|
|
853
1142
|
for (const job of jobs) {
|
|
854
1143
|
const uitkomst = await verwerkJob(db, job);
|
|
855
1144
|
if (uitkomst === "verstuurd")
|
|
856
1145
|
verstuurd++;
|
|
1146
|
+
else if (uitkomst === "wachtend")
|
|
1147
|
+
wachtend++;
|
|
857
1148
|
else
|
|
858
1149
|
mislukteJobs++;
|
|
859
1150
|
}
|
|
@@ -861,6 +1152,7 @@ export async function verwerkWachtrij(db, max = 5) {
|
|
|
861
1152
|
opgepakt: jobs.length,
|
|
862
1153
|
verstuurd,
|
|
863
1154
|
mislukt: mislukteJobs,
|
|
1155
|
+
wachtend,
|
|
864
1156
|
bijgewerkt: await synchroniseerStatussen(db),
|
|
865
1157
|
};
|
|
866
1158
|
}
|
|
@@ -871,16 +1163,18 @@ export async function verwerkWachtrij(db, max = 5) {
|
|
|
871
1163
|
*/
|
|
872
1164
|
export async function bepaalDocumentKeuze(db, job) {
|
|
873
1165
|
let bron = await zoekBronregel(db, job);
|
|
874
|
-
|
|
1166
|
+
// Engine-opdrachten houden hun eigen layout_id; nooit vervangen door een
|
|
1167
|
+
// Floriday-PDF-keuze.
|
|
1168
|
+
if (!isEngineOpdracht(job) && (await isFloridayLayoutKeuze(db, job))) {
|
|
875
1169
|
bron = "extern_pdf";
|
|
876
1170
|
job.layout_id = null;
|
|
877
1171
|
}
|
|
878
|
-
if (bron === "eigen_layout") {
|
|
1172
|
+
if (bron === "eigen_layout" || isEngineOpdracht(job)) {
|
|
879
1173
|
job.pdf_base64 = null;
|
|
880
1174
|
job.pdf_url = null;
|
|
881
1175
|
}
|
|
882
|
-
const klaar = await kantEnKlarePdf(db, job);
|
|
883
|
-
if (!klaar && bron === "extern_pdf") {
|
|
1176
|
+
const klaar = isEngineOpdracht(job) ? null : await kantEnKlarePdf(db, job);
|
|
1177
|
+
if (!klaar && bron === "extern_pdf" && !isEngineOpdracht(job)) {
|
|
884
1178
|
throw new GeenDocumentFout("Voor dit document is ingesteld dat het aangeleverde document wordt " +
|
|
885
1179
|
"gebruikt, maar er is geen PDF meegestuurd.");
|
|
886
1180
|
}
|