@flowselections/pick-schermen-kwekers 1.0.21 → 1.0.23
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/dist-lib/components/pick/PickScherm.d.ts.map +1 -1
- package/dist-lib/components/pick/PickScherm.js +56 -27
- package/dist-lib/hooks/usePickConfig.d.ts +11 -11
- package/dist-lib/hooks/usePickConfig.d.ts.map +1 -1
- package/dist-lib/hooks/usePickQueue.d.ts +5 -3
- package/dist-lib/hooks/usePickQueue.d.ts.map +1 -1
- package/dist-lib/hooks/usePickQueue.js +17 -4
- package/dist-lib/hooks/usePickReferenceData.d.ts +1 -1
- package/dist-lib/hooks/usePickReferenceData.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/auth-middleware.d.ts +4754 -230
- package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/client.d.ts +4754 -230
- package/dist-lib/integrations/supabase/client.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/client.js +1 -1
- package/dist-lib/integrations/supabase/client.server.d.ts +4754 -230
- 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/previewAuthStorage.d.ts +1 -1
- package/dist-lib/integrations/supabase/previewAuthStorage.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/previewAuthStorage.js +8 -1
- package/dist-lib/integrations/supabase/types.d.ts +4795 -203
- package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
- package/dist-lib/lib/__tests__/pickRobustness.test.d.ts +2 -0
- package/dist-lib/lib/__tests__/pickRobustness.test.d.ts.map +1 -0
- package/dist-lib/lib/__tests__/pickRobustness.test.js +68 -0
- package/dist-lib/lib/pickApi.d.ts +25 -1
- package/dist-lib/lib/pickApi.d.ts.map +1 -1
- package/dist-lib/lib/pickApi.js +56 -3
- package/dist-lib/lib/pickRetry.d.ts +13 -0
- package/dist-lib/lib/pickRetry.d.ts.map +1 -0
- package/dist-lib/lib/pickRetry.js +52 -0
- package/dist-lib/lib/pickUiText.d.ts.map +1 -1
- package/dist-lib/lib/pickUiText.js +5 -3
- package/dist-lib/lib/pickVerify.d.ts +15 -0
- package/dist-lib/lib/pickVerify.d.ts.map +1 -0
- package/dist-lib/lib/pickVerify.js +32 -0
- package/package.json +5 -4
- package/dist-lib/lib/layoutApi.d.ts +0 -23
- package/dist-lib/lib/layoutApi.d.ts.map +0 -1
- package/dist-lib/lib/layoutApi.js +0 -94
- package/dist-lib/lib/layoutMaker/documentProfiles.d.ts +0 -50
- package/dist-lib/lib/layoutMaker/documentProfiles.d.ts.map +0 -1
- package/dist-lib/lib/layoutMaker/documentProfiles.js +0 -458
- package/dist-lib/lib/layoutMaker/documentTypes.d.ts +0 -123
- package/dist-lib/lib/layoutMaker/documentTypes.d.ts.map +0 -1
- package/dist-lib/lib/layoutMaker/documentTypes.js +0 -74
- package/dist-lib/lib/layoutMaker/logoLibrary.d.ts +0 -13
- package/dist-lib/lib/layoutMaker/logoLibrary.d.ts.map +0 -1
- package/dist-lib/lib/layoutMaker/logoLibrary.js +0 -54
- package/dist-lib/lib/layoutMaker/pakbonBuilder.d.ts +0 -196
- package/dist-lib/lib/layoutMaker/pakbonBuilder.d.ts.map +0 -1
- package/dist-lib/lib/layoutMaker/pakbonBuilder.js +0 -813
- package/dist-lib/lib/layoutMaker/pakbonHtml.d.ts +0 -48
- package/dist-lib/lib/layoutMaker/pakbonHtml.d.ts.map +0 -1
- package/dist-lib/lib/layoutMaker/pakbonHtml.js +0 -703
- package/dist-lib/lib/layoutRenderer.d.ts +0 -7
- package/dist-lib/lib/layoutRenderer.d.ts.map +0 -1
- package/dist-lib/lib/layoutRenderer.js +0 -233
- package/dist-lib/lib/layoutTypes.d.ts +0 -84
- package/dist-lib/lib/layoutTypes.d.ts.map +0 -1
- package/dist-lib/lib/layoutTypes.js +0 -25
- package/dist-lib/lib/orderContext.d.ts +0 -50
- package/dist-lib/lib/orderContext.d.ts.map +0 -1
- package/dist-lib/lib/orderContext.js +0 -162
- package/dist-lib/lib/pakbonOrderInput.d.ts +0 -8
- package/dist-lib/lib/pakbonOrderInput.d.ts.map +0 -1
- package/dist-lib/lib/pakbonOrderInput.js +0 -150
|
@@ -1,703 +0,0 @@
|
|
|
1
|
-
// ============================================================================
|
|
2
|
-
// src/lib/pakbonHtml.ts — canonieke print-HTML van een pakbon
|
|
3
|
-
// ----------------------------------------------------------------------------
|
|
4
|
-
// Dit is de ENIGE plek waar een pakbon naar printbare HTML wordt vertaald.
|
|
5
|
-
// Andere modules (bijv. het pickstation) mogen GEEN eigen pakbon-sjabloon meer
|
|
6
|
-
// bevatten: zij roepen `renderPakbonDocument()` of `printPakbon()` aan met de
|
|
7
|
-
// layoutconfiguratie en de echte ordergegevens. Zo tonen het live voorbeeld en
|
|
8
|
-
// de afdruk altijd exact dezelfde secties — inclusief de orderregels.
|
|
9
|
-
// ============================================================================
|
|
10
|
-
import { DEMO_ORDER, conditieContext, groepeerIdentiekeKarren, groepeerIdentiekeLagen, karBundelTekst, fustVerzamellijst, laagBundelTekst, leesPakbonConfig, sectieZichtbaar, sorteerProducten, totalen, vulVeldcodes, } from "./pakbonBuilder";
|
|
11
|
-
import { haalPrintLogos } from "./logoLibrary";
|
|
12
|
-
// --- kleine hulpjes ---------------------------------------------------------
|
|
13
|
-
const arr = (v) => (Array.isArray(v) ? v : []);
|
|
14
|
-
const opt = (s, key, fallback) => s.opties?.[key] ?? fallback;
|
|
15
|
-
const optS = (s, key, fallback) => String(s.opties?.[key] ?? fallback);
|
|
16
|
-
const lijst = (s, key) => Array.isArray(s.opties?.[key]) ? s.opties[key].map(String) : [];
|
|
17
|
-
export function esc(v) {
|
|
18
|
-
return String(v ?? "").replace(/[&<>"]/g, (c) => ({ "&": "&", "<": "<", ">": ">", '"': """ })[c]);
|
|
19
|
-
}
|
|
20
|
-
const cel = (p, veld) => {
|
|
21
|
-
if (veld === "foto")
|
|
22
|
-
return "";
|
|
23
|
-
if (veld === "qrcode")
|
|
24
|
-
return "";
|
|
25
|
-
return String(p[veld] ?? "");
|
|
26
|
-
};
|
|
27
|
-
/**
|
|
28
|
-
* Vult ontbrekende onderdelen aan met veilige lege waarden, zodat een module
|
|
29
|
-
* die maar een deel van de gegevens heeft nooit een lege of stukke pakbon
|
|
30
|
-
* afdrukt.
|
|
31
|
-
*/
|
|
32
|
-
export function maakPakbonOrder(input) {
|
|
33
|
-
const bron = (input && typeof input === "object" ? input : {});
|
|
34
|
-
const groep = (naam, velden) => {
|
|
35
|
-
const b = (bron[naam] && typeof bron[naam] === "object" ? bron[naam] : {});
|
|
36
|
-
const out = {};
|
|
37
|
-
for (const v of velden)
|
|
38
|
-
out[v] = b[v] === undefined || b[v] === null ? "" : String(b[v]);
|
|
39
|
-
return out;
|
|
40
|
-
};
|
|
41
|
-
const karren = arr(bron["karren"]).map((k, i) => ({
|
|
42
|
-
karnummer: Number(k["karnummer"] ?? i + 1),
|
|
43
|
-
route: String(k["route"] ?? ""),
|
|
44
|
-
chauffeur: String(k["chauffeur"] ?? ""),
|
|
45
|
-
gewicht: String(k["gewicht"] ?? ""),
|
|
46
|
-
lagen: arr(k["lagen"]).map((l, j) => ({
|
|
47
|
-
laagnummer: Number(l["laagnummer"] ?? j + 1),
|
|
48
|
-
belading: String(l["belading"] ?? ""),
|
|
49
|
-
potmaat: String(l["potmaat"] ?? ""),
|
|
50
|
-
gewicht: String(l["gewicht"] ?? ""),
|
|
51
|
-
opmerking: String(l["opmerking"] ?? ""),
|
|
52
|
-
producten: arr(l["producten"]).map(normProduct),
|
|
53
|
-
fusten: arr(l["fusten"]).map((f) => ({
|
|
54
|
-
fusttype: String(f["fusttype"] ?? f["fustsoort"] ?? ""),
|
|
55
|
-
aantal: Number(f["aantal"] ?? 0) || 0,
|
|
56
|
-
hoogte: String(f["hoogte"] ?? ""),
|
|
57
|
-
belading: String(f["belading"] ?? ""),
|
|
58
|
-
barcode: String(f["barcode"] ?? ""),
|
|
59
|
-
})),
|
|
60
|
-
})),
|
|
61
|
-
}));
|
|
62
|
-
// Losse orderregels zonder karren: in één kar/laag zetten zodat elke
|
|
63
|
-
// sectie-instelling (orderregels, producten) gewoon werkt.
|
|
64
|
-
const losseRegels = arr(bron["producten"] ?? bron["regels"]).map(normProduct);
|
|
65
|
-
if (!karren.length && losseRegels.length) {
|
|
66
|
-
karren.push({
|
|
67
|
-
karnummer: 1,
|
|
68
|
-
route: "",
|
|
69
|
-
chauffeur: "",
|
|
70
|
-
gewicht: "",
|
|
71
|
-
lagen: [
|
|
72
|
-
{
|
|
73
|
-
laagnummer: 1,
|
|
74
|
-
belading: "",
|
|
75
|
-
potmaat: "",
|
|
76
|
-
gewicht: "",
|
|
77
|
-
opmerking: "",
|
|
78
|
-
producten: losseRegels,
|
|
79
|
-
fusten: [],
|
|
80
|
-
},
|
|
81
|
-
],
|
|
82
|
-
});
|
|
83
|
-
}
|
|
84
|
-
return {
|
|
85
|
-
order: groep("order", ["nummer", "datum", "leverdatum", "referentie", "opmerking"]),
|
|
86
|
-
bedrijf: groep("bedrijf", ["naam", "adres", "plaats", "telefoon", "email", "kvk", "btw"]),
|
|
87
|
-
klant: groep("klant", ["naam", "nummer", "contact", "referentie"]),
|
|
88
|
-
afleveradres: groep("afleveradres", ["naam", "adres", "plaats", "land"]),
|
|
89
|
-
factuuradres: groep("factuuradres", ["naam", "adres", "plaats", "land"]),
|
|
90
|
-
transport: groep("transport", ["transporteur", "route", "chauffeur", "track"]),
|
|
91
|
-
karren,
|
|
92
|
-
};
|
|
93
|
-
}
|
|
94
|
-
function normProduct(p) {
|
|
95
|
-
const aantal = Number(p["aantal"] ?? 0) || 0;
|
|
96
|
-
return {
|
|
97
|
-
artikelnummer: String(p["artikelnummer"] ?? p["artikelcode"] ?? ""),
|
|
98
|
-
artikelnaam: String(p["artikelnaam"] ?? p["naam"] ?? p["product_name"] ?? ""),
|
|
99
|
-
potmaat: String(p["potmaat"] ?? ""),
|
|
100
|
-
aantal,
|
|
101
|
-
belading: String(p["belading"] ?? ""),
|
|
102
|
-
barcode: String(p["barcode"] ?? ""),
|
|
103
|
-
opmerking: String(p["opmerking"] ?? ""),
|
|
104
|
-
locatie: String(p["locatie"] ?? ""),
|
|
105
|
-
gewicht: String(p["gewicht"] ?? ""),
|
|
106
|
-
productgroep: String(p["productgroep"] ?? ""),
|
|
107
|
-
kleur: String(p["kleur"] ?? ""),
|
|
108
|
-
fust_aantal: Number(p["fust_aantal"] ?? 0) || 0,
|
|
109
|
-
fustsoort: String(p["fustsoort"] ?? ""),
|
|
110
|
-
planten_per_fust: Number(p["planten_per_fust"] ?? 0) || 0,
|
|
111
|
-
};
|
|
112
|
-
}
|
|
113
|
-
// --- secties ----------------------------------------------------------------
|
|
114
|
-
function blokFrame(s, inhoud) {
|
|
115
|
-
if (!inhoud)
|
|
116
|
-
return "";
|
|
117
|
-
const o = s.opties ?? {};
|
|
118
|
-
const breedte = Math.min(Math.max(Number(o["blok_breedte"] ?? 100), 5), 100);
|
|
119
|
-
const uitlijning = String(o["blok_uitlijning"] ?? "left");
|
|
120
|
-
const marge = Number(o["blok_marge"] ?? 0);
|
|
121
|
-
const padding = Number(o["blok_padding"] ?? 0);
|
|
122
|
-
const rand = !!o["blok_rand"];
|
|
123
|
-
const radius = Number(o["blok_radius"] ?? 0);
|
|
124
|
-
const schaal = Number(o["blok_tekstschaal"] ?? 100);
|
|
125
|
-
const stijl = [
|
|
126
|
-
"display:flow-root",
|
|
127
|
-
`width:${breedte}%`,
|
|
128
|
-
uitlijning === "right" || uitlijning === "center" ? "margin-left:auto" : "",
|
|
129
|
-
uitlijning === "left" || uitlijning === "center" ? "margin-right:auto" : "",
|
|
130
|
-
`margin-top:${marge}mm`,
|
|
131
|
-
`margin-bottom:${marge}mm`,
|
|
132
|
-
padding ? `padding:${padding}mm` : "",
|
|
133
|
-
o["blok_achtergrond"] ? `background:${String(o["blok_achtergrond"])}` : "",
|
|
134
|
-
rand ? `border:0.3mm solid ${String(o["blok_randkleur"] ?? "#e5e7eb")}` : "",
|
|
135
|
-
radius ? `border-radius:${radius}mm` : "",
|
|
136
|
-
schaal !== 100 ? `font-size:${schaal}%` : "",
|
|
137
|
-
]
|
|
138
|
-
.filter(Boolean)
|
|
139
|
-
.join(";");
|
|
140
|
-
return `<div class="blok" style="${stijl}">${inhoud}</div>`;
|
|
141
|
-
}
|
|
142
|
-
function titelHtml(s) {
|
|
143
|
-
return s.titel ? `<div class="sectitel">${esc(s.titel)}</div>` : "";
|
|
144
|
-
}
|
|
145
|
-
function rij(label, waarde) {
|
|
146
|
-
if (waarde === null || waarde === undefined || waarde === "")
|
|
147
|
-
return "";
|
|
148
|
-
return `<div class="kv"><span class="k">${esc(label)}</span><span class="v">${esc(waarde)}</span></div>`;
|
|
149
|
-
}
|
|
150
|
-
function regelWeergave(producten, kolommen, weergave, toonKoppen, zebra, randen) {
|
|
151
|
-
const kols = kolommen.length ? kolommen : [];
|
|
152
|
-
if (!kols.length || !producten.length)
|
|
153
|
-
return "";
|
|
154
|
-
const kstijl = (k) => [
|
|
155
|
-
k.breedte ? `width:${k.breedte}%` : "",
|
|
156
|
-
`text-align:${k.uitlijning ?? "left"}`,
|
|
157
|
-
k.fontfamilie ? `font-family:${k.fontfamilie}` : "",
|
|
158
|
-
k.fontgrootte ? `font-size:${k.fontgrootte}pt` : "",
|
|
159
|
-
k.kleur ? `color:${k.kleur}` : "",
|
|
160
|
-
k.vet ? "font-weight:700" : "",
|
|
161
|
-
k.achtergrond ? `background:${k.achtergrond}` : "",
|
|
162
|
-
]
|
|
163
|
-
.filter(Boolean)
|
|
164
|
-
.join(";");
|
|
165
|
-
if (weergave === "tabel" || weergave === "compact") {
|
|
166
|
-
// Vaste kolombreedtes zodat elke laag/kar exact onder dezelfde koppen uitlijnt,
|
|
167
|
-
// ook wanneer de koppen maar één keer worden getoond.
|
|
168
|
-
const zonder = kols.filter((k) => !k.breedte).length;
|
|
169
|
-
const gebruikt = kols.reduce((som, k) => som + (Number(k.breedte) || 0), 0);
|
|
170
|
-
const rest = zonder > 0 ? Math.max(0, 100 - gebruikt) / zonder : 0;
|
|
171
|
-
const colgroup = `<colgroup>${kols
|
|
172
|
-
.map((k) => `<col style="width:${Number(k.breedte) || rest || 100 / kols.length}%" />`)
|
|
173
|
-
.join("")}</colgroup>`;
|
|
174
|
-
const kop = toonKoppen
|
|
175
|
-
? `<thead><tr>${kols
|
|
176
|
-
.map((k) => `<th style="${kstijl(k)}" class="${randen ? "rand" : "onder"}">${esc(k.label)}</th>`)
|
|
177
|
-
.join("")}</tr></thead>`
|
|
178
|
-
: "";
|
|
179
|
-
const body = producten
|
|
180
|
-
.map((p, i) => `<tr${zebra && i % 2 === 1 ? ' class="zebra"' : ""}>${kols
|
|
181
|
-
.map((k) => `<td style="${kstijl(k)}" class="${randen ? "rand" : "onder"}">${esc(cel(p, k.key))}</td>`)
|
|
182
|
-
.join("")}</tr>`)
|
|
183
|
-
.join("");
|
|
184
|
-
return `<table class="regels ${weergave === "compact" ? "compact" : ""}">${colgroup}${kop}<tbody>${body}</tbody></table>`;
|
|
185
|
-
}
|
|
186
|
-
const items = producten
|
|
187
|
-
.map((p) => `<div class="regelrij">${kols
|
|
188
|
-
.map((k) => `<span style="${kstijl(k)}">${esc(cel(p, k.key))}</span>`)
|
|
189
|
-
.join("")}</div>`)
|
|
190
|
-
.join("");
|
|
191
|
-
return weergave === "naast" ? `<div class="tweekolom">${items}</div>` : `<div>${items}</div>`;
|
|
192
|
-
}
|
|
193
|
-
function groepeer(order, groepering, sortering) {
|
|
194
|
-
const alle = order.karren.flatMap((k) => k.lagen.flatMap((l) => l.producten.map((p) => ({ p, kar: k, laag: l }))));
|
|
195
|
-
const maak = (titel, items) => ({
|
|
196
|
-
titel,
|
|
197
|
-
producten: sorteerProducten(items, sortering),
|
|
198
|
-
});
|
|
199
|
-
if (groepering === "kar")
|
|
200
|
-
return order.karren.map((k) => maak(`Kar ${k.karnummer}`, k.lagen.flatMap((l) => l.producten)));
|
|
201
|
-
if (groepering === "laag") {
|
|
202
|
-
const map = new Map();
|
|
203
|
-
for (const r of alle) {
|
|
204
|
-
const t = `Kar ${r.kar.karnummer} · laag ${r.laag.laagnummer}`;
|
|
205
|
-
map.set(t, [...(map.get(t) ?? []), r.p]);
|
|
206
|
-
}
|
|
207
|
-
return [...map.entries()].map(([t, items]) => maak(t, items));
|
|
208
|
-
}
|
|
209
|
-
if (groepering === "productgroep") {
|
|
210
|
-
const map = new Map();
|
|
211
|
-
for (const r of alle)
|
|
212
|
-
map.set(r.p.productgroep, [...(map.get(r.p.productgroep) ?? []), r.p]);
|
|
213
|
-
return [...map.entries()].map(([t, items]) => maak(t, items));
|
|
214
|
-
}
|
|
215
|
-
if (groepering === "afleveradres")
|
|
216
|
-
return [maak(order.afleveradres.naam, alle.map((r) => r.p))];
|
|
217
|
-
if (groepering === "klant")
|
|
218
|
-
return [maak(order.klant.naam, alle.map((r) => r.p))];
|
|
219
|
-
return [maak("—", alle.map((r) => r.p))];
|
|
220
|
-
}
|
|
221
|
-
function fustTabel(velden, fusten, titel) {
|
|
222
|
-
if (!velden.length || !fusten.length)
|
|
223
|
-
return "";
|
|
224
|
-
const label = {
|
|
225
|
-
fusttype: "Fustsoort",
|
|
226
|
-
aantal: "Aantal",
|
|
227
|
-
hoogte: "Hoogte",
|
|
228
|
-
belading: "Belading",
|
|
229
|
-
barcode: "Barcode",
|
|
230
|
-
};
|
|
231
|
-
const kop = `<tr>${velden.map((v) => `<th class="onder">${esc(label[v] ?? v)}</th>`).join("")}</tr>`;
|
|
232
|
-
const rijen = fusten
|
|
233
|
-
.map((f) => `<tr>${velden
|
|
234
|
-
.map((v) => `<td class="onder">${esc(f[v] ?? "")}</td>`)
|
|
235
|
-
.join("")}</tr>`)
|
|
236
|
-
.join("");
|
|
237
|
-
return `${titel ? `<div class="sectitel">${esc(titel)}</div>` : ""}<table class="regels">${kop}${rijen}</table>`;
|
|
238
|
-
}
|
|
239
|
-
function productenHtml(sectie, laag, toonKop = true) {
|
|
240
|
-
const velden = lijst(sectie, "velden");
|
|
241
|
-
if (!velden.length)
|
|
242
|
-
return "";
|
|
243
|
-
let producten = sorteerProducten(laag.producten, optS(sectie, "sortering", "eigen"));
|
|
244
|
-
if (opt(sectie, "groeperen", false)) {
|
|
245
|
-
const map = new Map();
|
|
246
|
-
for (const p of producten) {
|
|
247
|
-
const b = map.get(p.artikelnummer);
|
|
248
|
-
if (b) {
|
|
249
|
-
b.aantal += p.aantal;
|
|
250
|
-
b.fust_aantal += p.fust_aantal;
|
|
251
|
-
}
|
|
252
|
-
else
|
|
253
|
-
map.set(p.artikelnummer, { ...p });
|
|
254
|
-
}
|
|
255
|
-
producten = [...map.values()];
|
|
256
|
-
}
|
|
257
|
-
if (!producten.length)
|
|
258
|
-
return "";
|
|
259
|
-
const label = (v) => v === "foto" ? "Foto" : v === "qrcode" ? "QR-code" : PRODUCT_LABELS[v] ?? v;
|
|
260
|
-
const weergave = optS(sectie, "weergave", "tabel");
|
|
261
|
-
if (weergave === "lijst") {
|
|
262
|
-
return `<ul class="plijst">${producten
|
|
263
|
-
.map((p) => `<li>${velden.map((v) => esc(cel(p, v))).filter(Boolean).join(" · ")}</li>`)
|
|
264
|
-
.join("")}</ul>`;
|
|
265
|
-
}
|
|
266
|
-
const kop = toonKop && opt(sectie, "toonKoppen", true) !== false
|
|
267
|
-
? `<tr>${velden.map((v) => `<th class="onder">${esc(label(v))}</th>`).join("")}</tr>`
|
|
268
|
-
: "";
|
|
269
|
-
const rijen = producten
|
|
270
|
-
.map((p) => `<tr>${velden.map((v) => `<td class="onder">${esc(cel(p, v))}</td>`).join("")}</tr>`)
|
|
271
|
-
.join("");
|
|
272
|
-
return `<table class="regels">${kop}${rijen}</table>`;
|
|
273
|
-
}
|
|
274
|
-
const PRODUCT_LABELS = {
|
|
275
|
-
artikelnummer: "Artikelnr",
|
|
276
|
-
artikelnaam: "Artikel",
|
|
277
|
-
potmaat: "Potmaat",
|
|
278
|
-
aantal: "Aantal",
|
|
279
|
-
belading: "Belading",
|
|
280
|
-
barcode: "Barcode",
|
|
281
|
-
opmerking: "Opmerking",
|
|
282
|
-
locatie: "Locatie",
|
|
283
|
-
gewicht: "Gewicht",
|
|
284
|
-
productgroep: "Productgroep",
|
|
285
|
-
kleur: "Kleur",
|
|
286
|
-
fust_aantal: "Aantal fusten",
|
|
287
|
-
fustsoort: "Fustsoort",
|
|
288
|
-
planten_per_fust: "Planten per fust",
|
|
289
|
-
};
|
|
290
|
-
function karKop(s, kar, velden, bundelTekst = "") {
|
|
291
|
-
const scheiding = optS(s, "scheiding", "kop");
|
|
292
|
-
const tekst = optS(s, "koptekst", "KAR {kar}")
|
|
293
|
-
.replace(/\{kar\}/g, String(kar.karnummer))
|
|
294
|
-
.replace(/\{route\}/g, kar.route)
|
|
295
|
-
.replace(/\{chauffeur\}/g, kar.chauffeur)
|
|
296
|
-
.replace(/\{lagen\}/g, String(kar.lagen.length))
|
|
297
|
-
.replace(/\{producten\}/g, String(kar.lagen.reduce((n, l) => n + l.producten.length, 0)));
|
|
298
|
-
const info = [];
|
|
299
|
-
if (velden.includes("lagen"))
|
|
300
|
-
info.push(`${kar.lagen.length} lagen`);
|
|
301
|
-
if (velden.includes("producten"))
|
|
302
|
-
info.push(`${kar.lagen.reduce((n, l) => n + l.producten.reduce((m, p) => m + p.aantal, 0), 0)} producten`);
|
|
303
|
-
if (velden.includes("gewicht") && kar.gewicht)
|
|
304
|
-
info.push(kar.gewicht);
|
|
305
|
-
if (velden.includes("chauffeur") && kar.chauffeur)
|
|
306
|
-
info.push(kar.chauffeur);
|
|
307
|
-
if (velden.includes("route") && kar.route)
|
|
308
|
-
info.push(kar.route);
|
|
309
|
-
const toonNummer = velden.includes("karnummer");
|
|
310
|
-
if (!toonNummer && !info.length && !bundelTekst)
|
|
311
|
-
return "";
|
|
312
|
-
const groot = scheiding === "kop";
|
|
313
|
-
const gekleurd = scheiding === "kleur" || groot;
|
|
314
|
-
const stijl = [
|
|
315
|
-
gekleurd ? `background:${optS(s, "kleur", "#e8eef7")}` : "",
|
|
316
|
-
groot ? "font-size:117%;font-weight:700" : "font-weight:600",
|
|
317
|
-
]
|
|
318
|
-
.filter(Boolean)
|
|
319
|
-
.join(";");
|
|
320
|
-
const links = [
|
|
321
|
-
toonNummer ? esc(tekst) : "",
|
|
322
|
-
bundelTekst ? `<span class="bundel">${esc(bundelTekst)}</span>` : "",
|
|
323
|
-
]
|
|
324
|
-
.filter(Boolean)
|
|
325
|
-
.join(" ");
|
|
326
|
-
return `<div class="karkop" style="${stijl}"><span>${links}</span><span class="meta">${info
|
|
327
|
-
.map((t) => `<span>${esc(t)}</span>`)
|
|
328
|
-
.join("")}</span></div>`;
|
|
329
|
-
}
|
|
330
|
-
function laagKop(s, laag, velden, compact, bundelTekst = "") {
|
|
331
|
-
const tekst = optS(s, "koptekst", "Laag {laag}").replace(/\{laag\}/g, String(laag.laagnummer));
|
|
332
|
-
const info = [];
|
|
333
|
-
if (velden.includes("belading") && laag.belading)
|
|
334
|
-
info.push(`Belading ${laag.belading}`);
|
|
335
|
-
if (velden.includes("potmaat") && laag.potmaat)
|
|
336
|
-
info.push(laag.potmaat);
|
|
337
|
-
if (velden.includes("producten"))
|
|
338
|
-
info.push(`${laag.producten.reduce((n, p) => n + p.aantal, 0)} producten`);
|
|
339
|
-
if (velden.includes("fusten"))
|
|
340
|
-
info.push(`${laag.fusten.reduce((n, f) => n + f.aantal, 0)} fusten`);
|
|
341
|
-
if (velden.includes("gewicht") && laag.gewicht)
|
|
342
|
-
info.push(laag.gewicht);
|
|
343
|
-
if (velden.includes("opmerking") && laag.opmerking)
|
|
344
|
-
info.push(laag.opmerking);
|
|
345
|
-
if (!velden.includes("laagnummer") && !info.length && !bundelTekst)
|
|
346
|
-
return "";
|
|
347
|
-
return `<div class="laagkop${compact ? " compact" : ""}"><span>${velden.includes("laagnummer") ? esc(tekst) : ""}${bundelTekst ? ` <span class="bundel">${esc(bundelTekst)}</span>` : ""}</span><span class="meta">${info.map((t) => `<span>${esc(t)}</span>`).join("")}</span></div>`;
|
|
348
|
-
}
|
|
349
|
-
function bundelMelding(tekst) {
|
|
350
|
-
return `<div class="bundel">${esc(tekst)}</div>`;
|
|
351
|
-
}
|
|
352
|
-
function karBlok(s, kar, lagen, zoek, omkaderd, aantal = 1) {
|
|
353
|
-
// Bij samenvoegen van identieke karren vervalt het unieke karnummer.
|
|
354
|
-
const bundelt = opt(s, "bundel_identiek", true) !== false;
|
|
355
|
-
const velden = lijst(s, "velden").filter((v) => !(bundelt && (v === "karnummer" || v === "barcode" || v === "qrcode")));
|
|
356
|
-
const lagenSectie = zoek("lagen");
|
|
357
|
-
const productSectie = zoek("producten");
|
|
358
|
-
const fustSectie = zoek("fusten");
|
|
359
|
-
const lagenModus = lagenSectie ? optS(lagenSectie, "modus", "apart") : "geen";
|
|
360
|
-
let inhoud = karKop(s, kar, velden, karBundelTekst(aantal));
|
|
361
|
-
if (lagen.length && lagenSectie && lagenModus !== "geen") {
|
|
362
|
-
const lVelden = lijst(lagenSectie, "velden");
|
|
363
|
-
const toonProducten = lagenModus !== "nummer" && lagenModus !== "regel";
|
|
364
|
-
const bundelLagen = lagenModus === "gebundeld";
|
|
365
|
-
inhoud += groepeerIdentiekeLagen(lagen, bundelLagen)
|
|
366
|
-
.map(({ laag, aantal: laagAantal }, index) => {
|
|
367
|
-
let l = laagKop(lagenSectie, laag, lVelden, lagenModus === "regel", laagAantal > 1 ? laagBundelTekst(laagAantal) : "");
|
|
368
|
-
if (toonProducten && productSectie)
|
|
369
|
-
l += productenHtml(productSectie, laag, index === 0 || opt(productSectie, "kop_eenmalig", true) === false);
|
|
370
|
-
if (fustSectie && ["laag", "product"].includes(optS(fustSectie, "modus", "totaal")))
|
|
371
|
-
l += fustTabel(lijst(fustSectie, "velden"), laag.fusten, `Fusten laag ${laag.laagnummer}`);
|
|
372
|
-
return `<div class="laag">${l}</div>`;
|
|
373
|
-
})
|
|
374
|
-
.join("");
|
|
375
|
-
}
|
|
376
|
-
if (fustSectie && optS(fustSectie, "modus", "totaal") === "kar")
|
|
377
|
-
inhoud += fustTabel(lijst(fustSectie, "velden"), lagen.flatMap((l) => l.fusten), `Fusten kar ${kar.karnummer}`);
|
|
378
|
-
return `<div class="karblok${omkaderd ? " omkaderd" : ""}">${inhoud}</div>`;
|
|
379
|
-
}
|
|
380
|
-
/** Verzamellijst met alle te picken fusten per fusttype. */
|
|
381
|
-
function fustVerzamellijstHtml(order) {
|
|
382
|
-
const lijst = fustVerzamellijst(order?.karren);
|
|
383
|
-
if (!lijst.length)
|
|
384
|
-
return "";
|
|
385
|
-
const totaal = lijst.reduce((n, f) => n + f.aantal, 0);
|
|
386
|
-
const blokken = lijst
|
|
387
|
-
.map((f) => {
|
|
388
|
-
const kop = `<div class="sectitel" style="display:flex;justify-content:space-between"><span>${esc([f.fusttype, f.hoogte, f.belading].filter(Boolean).join(" · "))}</span><span>${f.aantal}× fust</span></div>`;
|
|
389
|
-
const rijen = f.producten
|
|
390
|
-
.map((pr) => `<tr><td class="onder">${esc(pr.artikelnummer)}</td><td class="onder">${esc(pr.artikelnaam)}</td><td class="onder">${esc(pr.potmaat)}</td><td class="onder">${esc(pr.kleur)}</td><td class="onder">${esc(pr.locatie)}</td><td class="onder">${esc(pr.fustsoort)}</td><td class="onder" style="text-align:right">${pr.planten_per_fust}</td><td class="onder" style="text-align:right">${pr.aantal}</td></tr>`)
|
|
391
|
-
.join("");
|
|
392
|
-
return `${kop}<table class="regels"><tr><th class="onder">Artikel</th><th class="onder">Omschrijving</th><th class="onder">Potmaat</th><th class="onder">Kleur</th><th class="onder">Locatie</th><th class="onder">Tray/fust</th><th class="onder" style="text-align:right">Pl./fust</th><th class="onder" style="text-align:right">Planten</th></tr>${rijen}</table>`;
|
|
393
|
-
})
|
|
394
|
-
.join("");
|
|
395
|
-
return `<div class="sectitel">Te verzamelen fusten — karindeling zelf bepalen</div>${blokken}<table class="regels"><tr><td style="font-weight:700">Totaal fusten</td><td style="font-weight:700;text-align:right">${totaal}</td></tr></table>`;
|
|
396
|
-
}
|
|
397
|
-
function karrenHtml(s, order, zoek) {
|
|
398
|
-
const modus = optS(s, "modus", "pagina");
|
|
399
|
-
if (modus === "geen")
|
|
400
|
-
return "";
|
|
401
|
-
if (optS(s, "pickmethode", "kar") === "fusten")
|
|
402
|
-
return blokFrame(s, fustVerzamellijstHtml(order));
|
|
403
|
-
const scheiding = optS(s, "scheiding", "kop");
|
|
404
|
-
const lagenSectie = zoek("lagen");
|
|
405
|
-
const lagenModus = lagenSectie ? optS(lagenSectie, "modus", "apart") : "geen";
|
|
406
|
-
const perPagina = modus === "pagina" || scheiding === "pagina";
|
|
407
|
-
const perLaagPagina = modus !== "nummer" && lagenModus === "pagina";
|
|
408
|
-
const bundelKarren = opt(s, "bundel_identiek", true) !== false;
|
|
409
|
-
const karGroepen = groepeerIdentiekeKarren(order.karren, bundelKarren);
|
|
410
|
-
if (modus === "naast") {
|
|
411
|
-
return blokFrame(s, `<div class="tweekolom">${karGroepen
|
|
412
|
-
.map((g) => karBlok(s, g.kar, g.kar.lagen, zoek, false, g.aantal))
|
|
413
|
-
.join("")}</div>`);
|
|
414
|
-
}
|
|
415
|
-
const delen = [];
|
|
416
|
-
karGroepen.forEach((groep, i) => {
|
|
417
|
-
const kar = groep.kar;
|
|
418
|
-
const lagen = modus === "nummer" ? [] : kar.lagen;
|
|
419
|
-
if (perLaagPagina && lagen.length) {
|
|
420
|
-
const laagGroepen = groepeerIdentiekeLagen(lagen, String(lagenModus) === "gebundeld");
|
|
421
|
-
laagGroepen.forEach((lg, j) => {
|
|
422
|
-
const breek = i > 0 || j > 0;
|
|
423
|
-
delen.push(`<div class="${breek ? "breekvoor" : ""}">${blokFrame(s, karBlok(s, kar, [lg.laag], zoek, modus === "blok" || modus === "passend", groep.aantal))}</div>`);
|
|
424
|
-
});
|
|
425
|
-
return;
|
|
426
|
-
}
|
|
427
|
-
const breek = i > 0 && perPagina;
|
|
428
|
-
const scheider = i > 0 && !perPagina ? scheidingHtml(scheiding) : "";
|
|
429
|
-
delen.push(`<div class="${breek ? "breekvoor" : ""} ${modus === "passend" ? "heelhouden" : ""}">${scheider}${blokFrame(s, karBlok(s, kar, lagen, zoek, modus === "blok" || modus === "passend", groep.aantal))}</div>`);
|
|
430
|
-
});
|
|
431
|
-
return delen.join("");
|
|
432
|
-
}
|
|
433
|
-
function scheidingHtml(modus) {
|
|
434
|
-
if (modus === "lijn")
|
|
435
|
-
return `<div class="lijn"></div>`;
|
|
436
|
-
if (modus === "balk")
|
|
437
|
-
return `<div class="balk"></div>`;
|
|
438
|
-
return "";
|
|
439
|
-
}
|
|
440
|
-
function sectieHtml(s, order, zoek, opties) {
|
|
441
|
-
switch (s.type) {
|
|
442
|
-
case "logo": {
|
|
443
|
-
const logoId = typeof s.opties?.["logoId"] === "string" ? String(s.opties["logoId"]) : null;
|
|
444
|
-
// Geen eigen logo gekozen? Dan niets tonen — het documentlogo staat al bovenaan.
|
|
445
|
-
if (!logoId)
|
|
446
|
-
return "";
|
|
447
|
-
const logos = arr(opties.logos);
|
|
448
|
-
const logo = logos.find((l) => l.id === logoId);
|
|
449
|
-
if (!logo?.viewUrl)
|
|
450
|
-
return "";
|
|
451
|
-
const align = optS(s, "uitlijning", "left");
|
|
452
|
-
return `<div style="text-align:${esc(align)}"><img src="${esc(logo.viewUrl)}" alt="${esc(logo.naam ?? "")}" style="height:${Number(opt(s, "hoogte", 18))}mm;object-fit:contain" /></div>`;
|
|
453
|
-
}
|
|
454
|
-
case "bedrijfsgegevens":
|
|
455
|
-
return (titelHtml(s) +
|
|
456
|
-
[
|
|
457
|
-
opt(s, "toonNaam", true) ? `<div class="vet">${esc(order.bedrijf.naam)}</div>` : "",
|
|
458
|
-
opt(s, "toonAdres", true)
|
|
459
|
-
? `<div>${esc([order.bedrijf.adres, order.bedrijf.plaats].filter(Boolean).join(" · "))}</div>`
|
|
460
|
-
: "",
|
|
461
|
-
opt(s, "toonContact", true)
|
|
462
|
-
? `<div>${esc([order.bedrijf.telefoon, order.bedrijf.email].filter(Boolean).join(" · "))}</div>`
|
|
463
|
-
: "",
|
|
464
|
-
opt(s, "toonKvk", false)
|
|
465
|
-
? `<div>${esc([order.bedrijf.kvk, order.bedrijf.btw].filter(Boolean).join(" · "))}</div>`
|
|
466
|
-
: "",
|
|
467
|
-
].join(""));
|
|
468
|
-
case "klantgegevens":
|
|
469
|
-
return (titelHtml(s) +
|
|
470
|
-
`<div class="vet">${esc(order.klant.naam)}</div>` +
|
|
471
|
-
(opt(s, "toonKlantnummer", true) ? rij("Klantnummer", order.klant.nummer) : "") +
|
|
472
|
-
(opt(s, "toonContact", true) ? rij("Contact", order.klant.contact) : "") +
|
|
473
|
-
(opt(s, "toonReferentie", true) ? rij("Referentie", order.klant.referentie) : ""));
|
|
474
|
-
case "afleveradres":
|
|
475
|
-
case "factuuradres": {
|
|
476
|
-
const a = s.type === "afleveradres" ? order.afleveradres : order.factuuradres;
|
|
477
|
-
return (titelHtml(s) +
|
|
478
|
-
`<div class="vet">${esc(a.naam)}</div><div>${esc(a.adres)}</div><div>${esc(a.plaats)}</div>` +
|
|
479
|
-
(opt(s, "toonLand", true) ? `<div>${esc(a.land)}</div>` : ""));
|
|
480
|
-
}
|
|
481
|
-
case "orderinformatie":
|
|
482
|
-
return (titelHtml(s) +
|
|
483
|
-
`<div class="grid2">${[
|
|
484
|
-
opt(s, "toonOrdernummer", true) ? rij("Ordernummer", order.order.nummer) : "",
|
|
485
|
-
opt(s, "toonDatum", true) ? rij("Orderdatum", order.order.datum) : "",
|
|
486
|
-
opt(s, "toonLeverdatum", true) ? rij("Leverdatum", order.order.leverdatum) : "",
|
|
487
|
-
opt(s, "toonReferentie", true) ? rij("Referentie", order.order.referentie) : "",
|
|
488
|
-
].join("")}</div>`);
|
|
489
|
-
case "transportinformatie":
|
|
490
|
-
return (titelHtml(s) +
|
|
491
|
-
`<div class="grid2">${[
|
|
492
|
-
opt(s, "toonTransporteur", true) ? rij("Transporteur", order.transport.transporteur) : "",
|
|
493
|
-
opt(s, "toonRoute", true) ? rij("Route", order.transport.route) : "",
|
|
494
|
-
opt(s, "toonChauffeur", false) ? rij("Chauffeur", order.transport.chauffeur) : "",
|
|
495
|
-
opt(s, "toonTrack", false) ? rij("Track & trace", order.transport.track) : "",
|
|
496
|
-
].join("")}</div>`);
|
|
497
|
-
case "opmerkingen":
|
|
498
|
-
if (!order.order.opmerking)
|
|
499
|
-
return "";
|
|
500
|
-
return `<div class="${opt(s, "kader", true) ? "kader" : ""}"><span class="vet">Opmerkingen: </span>${esc(order.order.opmerking)}</div>`;
|
|
501
|
-
case "orderregels": {
|
|
502
|
-
const kolommen = arr(s.kolommen);
|
|
503
|
-
const groepen = groepeer(order, optS(s, "groepering", "order"), optS(s, "sortering", "eigen"));
|
|
504
|
-
const body = groepen
|
|
505
|
-
.map((g) => {
|
|
506
|
-
const tabel = regelWeergave(g.producten, kolommen, optS(s, "weergave", "tabel"), opt(s, "toonKoppen", true) !== false, !!opt(s, "zebra", false), opt(s, "randen", true) !== false);
|
|
507
|
-
if (!tabel)
|
|
508
|
-
return "";
|
|
509
|
-
return `<div class="groep">${g.titel !== "—" ? `<div class="vet">${esc(g.titel)}</div>` : ""}${tabel}</div>`;
|
|
510
|
-
})
|
|
511
|
-
.join("");
|
|
512
|
-
return titelHtml(s) + body;
|
|
513
|
-
}
|
|
514
|
-
case "karren":
|
|
515
|
-
case "lagen":
|
|
516
|
-
case "producten":
|
|
517
|
-
return ""; // apart afgehandeld
|
|
518
|
-
case "fusten": {
|
|
519
|
-
if (optS(s, "modus", "totaal") !== "totaal")
|
|
520
|
-
return "";
|
|
521
|
-
const alle = order.karren.flatMap((k) => k.lagen.flatMap((l) => l.fusten));
|
|
522
|
-
return fustTabel(lijst(s, "velden"), alle, s.titel);
|
|
523
|
-
}
|
|
524
|
-
case "totalen": {
|
|
525
|
-
const t = totalen(order);
|
|
526
|
-
const velden = lijst(s, "velden");
|
|
527
|
-
if (!velden.length)
|
|
528
|
-
return "";
|
|
529
|
-
const rechts = optS(s, "uitlijning", "right") === "right";
|
|
530
|
-
return `<div class="totalen" style="justify-content:${rechts ? "flex-end" : "flex-start"}">${velden
|
|
531
|
-
.map((v) => `<span>${esc(t[v] ?? v)}</span>`)
|
|
532
|
-
.join("")}</div>`;
|
|
533
|
-
}
|
|
534
|
-
case "handtekening":
|
|
535
|
-
return (`<div class="handtekening">${opt(s, "label", "") ? `<div class="sectitel">${esc(opt(s, "label", ""))}</div>` : ""}` +
|
|
536
|
-
(opt(s, "toonNaam", true) ? `<div class="lijnveld">Naam</div>` : "") +
|
|
537
|
-
(opt(s, "toonDatum", true) ? `<div class="lijnveld">Datum</div>` : "") +
|
|
538
|
-
`<div class="lijnveld hoog"></div></div>`);
|
|
539
|
-
case "tekstblok":
|
|
540
|
-
return `<div class="${opt(s, "kader", false) ? "kader" : ""}" style="font-size:${Number(opt(s, "fontgrootte", 9))}pt">${esc(vulVeldcodes(optS(s, "tekst", ""), order))}</div>`;
|
|
541
|
-
case "qrcode":
|
|
542
|
-
return `<div><div class="qr" style="width:${Number(opt(s, "grootte", 25))}mm;height:${Number(opt(s, "grootte", 25))}mm"></div><div class="klein">${esc(vulVeldcodes(optS(s, "waarde", ""), order))}</div></div>`;
|
|
543
|
-
case "barcode":
|
|
544
|
-
return `<div><div class="streepjes" style="height:${Number(opt(s, "hoogte", 15))}mm"></div><div>${esc(vulVeldcodes(optS(s, "waarde", ""), order))}</div></div>`;
|
|
545
|
-
case "afbeelding": {
|
|
546
|
-
const url = optS(s, "url", "");
|
|
547
|
-
return url
|
|
548
|
-
? `<img src="${esc(url)}" alt="${esc(s.titel)}" style="height:${Number(opt(s, "hoogte", 25))}mm" />`
|
|
549
|
-
: "";
|
|
550
|
-
}
|
|
551
|
-
default:
|
|
552
|
-
return "";
|
|
553
|
-
}
|
|
554
|
-
}
|
|
555
|
-
// --- publieke API -----------------------------------------------------------
|
|
556
|
-
/** Bouwt alleen de inhoud (zonder <html>-wrapper) van de pakbon. */
|
|
557
|
-
export function renderPakbonBody(config, order, opties = {}) {
|
|
558
|
-
const cfg = leesPakbonConfig(config);
|
|
559
|
-
const ctx = conditieContext(order);
|
|
560
|
-
const secties = arr(cfg.secties).filter((s) => sectieZichtbaar(s, ctx));
|
|
561
|
-
const zoek = (t) => secties.find((s) => s.type === t);
|
|
562
|
-
const opts = { ...opties, logoId: opties.logoId ?? cfg.logo?.logoId ?? null };
|
|
563
|
-
const kop = documentLogoHtml(cfg.logo, opts);
|
|
564
|
-
const totalenSectie = zoek("totalen");
|
|
565
|
-
const totalenElkePagina = !!totalenSectie && opt(totalenSectie, "elke_pagina", false) === true;
|
|
566
|
-
const inhoud = secties
|
|
567
|
-
.filter((s) => !(s.type === "totalen" && totalenElkePagina))
|
|
568
|
-
.map((s) => s.type === "karren" ? karrenHtml(s, order, zoek) : blokFrame(s, sectieHtml(s, order, zoek, opts)))
|
|
569
|
-
.join("");
|
|
570
|
-
const voet = totalenElkePagina && totalenSectie
|
|
571
|
-
? `<div class="paginavoet">${sectieHtml(totalenSectie, order, zoek, opts)}</div>`
|
|
572
|
-
: "";
|
|
573
|
-
return kop + inhoud + voet;
|
|
574
|
-
}
|
|
575
|
-
/** Het documentlogo bovenaan de pakbon. */
|
|
576
|
-
function documentLogoHtml(logoCfg, opties) {
|
|
577
|
-
if (!logoCfg?.logoId)
|
|
578
|
-
return "";
|
|
579
|
-
const logos = arr(opties.logos);
|
|
580
|
-
const logo = logos.find((l) => l.id === logoCfg.logoId);
|
|
581
|
-
if (!logo?.viewUrl)
|
|
582
|
-
return "";
|
|
583
|
-
return `<div class="blok" style="text-align:${esc(logoCfg.uitlijning ?? "left")}"><img src="${esc(logo.viewUrl)}" alt="${esc(logo.naam ?? "")}" style="height:${Number(logoCfg.hoogte ?? 18)}mm;object-fit:contain" /></div>`;
|
|
584
|
-
}
|
|
585
|
-
/** Bouwt een volledig printbaar HTML-document van de pakbon. */
|
|
586
|
-
export function renderPakbonDocument(config, order, opties = {}) {
|
|
587
|
-
const b = opties.breedteMm ?? 210;
|
|
588
|
-
const h = opties.hoogteMm ?? 297;
|
|
589
|
-
const marge = opties.margeMm ?? 12;
|
|
590
|
-
const font = opties.lettertype ?? 'Arial, Helvetica, "Segoe UI", sans-serif';
|
|
591
|
-
const grootte = opties.lettergrootte ?? 8.5;
|
|
592
|
-
const body = renderPakbonBody(config, order, opties);
|
|
593
|
-
return `<!doctype html><html lang="nl"><head><meta charset="utf-8" />
|
|
594
|
-
<title>${esc(opties.titel ?? "Pakbon")}</title>
|
|
595
|
-
<style>
|
|
596
|
-
@page { size: ${b}mm ${h}mm; margin: ${marge}mm; }
|
|
597
|
-
* { box-sizing: border-box; }
|
|
598
|
-
html, body { margin: 0; padding: 0; }
|
|
599
|
-
body { font-family: ${font}; font-size: ${grootte}pt; color: #111827; line-height: 1.35; }
|
|
600
|
-
.blok { margin-bottom: 2mm; }
|
|
601
|
-
.sectitel { font-size: 90%; font-weight: 600; text-transform: uppercase; color: #6b7280; margin-bottom: 0.6mm; }
|
|
602
|
-
.vet { font-weight: 600; }
|
|
603
|
-
.kv { display: flex; justify-content: space-between; gap: 3mm; }
|
|
604
|
-
.kv .k { color: #6b7280; }
|
|
605
|
-
.kv .v { font-weight: 500; }
|
|
606
|
-
.grid2 { display: grid; grid-template-columns: 1fr 1fr; gap: 0 6mm; }
|
|
607
|
-
.kader { border: 0.25mm solid #d1d5db; border-radius: 1mm; padding: 1.5mm; }
|
|
608
|
-
table.regels { width: 100%; border-collapse: collapse; table-layout: fixed; }
|
|
609
|
-
table.regels th, table.regels td { overflow-wrap: anywhere; }
|
|
610
|
-
table.regels th, table.regels td { padding: 0.7mm 1mm; vertical-align: top; }
|
|
611
|
-
table.regels th { font-size: 90%; text-transform: uppercase; color: #6b7280; font-weight: 600; text-align: left; }
|
|
612
|
-
.rand { border: 0.2mm solid #d1d5db; }
|
|
613
|
-
.onder { border-bottom: 0.2mm solid #d1d5db; }
|
|
614
|
-
table.regels.compact th, table.regels.compact td { padding: 0.3mm 0.8mm; }
|
|
615
|
-
tr.zebra td { background: #f3f4f6; }
|
|
616
|
-
.plijst { margin: 0; padding-left: 4mm; }
|
|
617
|
-
.groep { margin-bottom: 1.5mm; }
|
|
618
|
-
.regelrij { display: flex; gap: 2mm; }
|
|
619
|
-
.tweekolom { display: grid; grid-template-columns: 1fr 1fr; gap: 3mm; }
|
|
620
|
-
.karblok { margin-bottom: 2mm; }
|
|
621
|
-
.karblok.omkaderd { border: 0.25mm solid #d1d5db; border-radius: 1mm; padding: 1.5mm; }
|
|
622
|
-
.karkop { display: flex; justify-content: space-between; align-items: center; padding: 0.8mm 1.5mm; border-radius: 1mm; margin-bottom: 1mm; }
|
|
623
|
-
.bundel { display: inline-block; border: 0.3mm dashed #6b7280; border-radius: 1mm; padding: 0.4mm 1.5mm; margin-bottom: 1mm; font-weight: 700; text-transform: uppercase; letter-spacing: 0.2mm; font-size: 90%; }
|
|
624
|
-
.laag { margin-bottom: 1.5mm; }
|
|
625
|
-
.laagkop { display: flex; justify-content: space-between; font-weight: 500; border-bottom: 0.2mm solid #d1d5db; padding-bottom: 0.4mm; margin-bottom: 0.6mm; }
|
|
626
|
-
.laagkop.compact { border-bottom: none; }
|
|
627
|
-
.meta { display: flex; gap: 3mm; font-size: 88%; font-weight: 400; color: #4b5563; }
|
|
628
|
-
.totalen { display: flex; flex-wrap: wrap; gap: 5mm; border-top: 0.25mm solid #d1d5db; padding-top: 1.2mm; font-weight: 500; }
|
|
629
|
-
.paginavoet { position: fixed; bottom: 0; left: 0; right: 0; background: #fff; }
|
|
630
|
-
.handtekening { margin-top: 3mm; }
|
|
631
|
-
.lijnveld { margin-top: 4mm; border-bottom: 0.25mm solid #6b7280; padding-bottom: 3mm; }
|
|
632
|
-
.lijnveld.hoog { padding-bottom: 10mm; }
|
|
633
|
-
.lijn { border-top: 0.25mm solid #d1d5db; margin: 1.5mm 0; }
|
|
634
|
-
.balk { height: 1mm; background: #374151; margin: 1.5mm 0; }
|
|
635
|
-
.qr { border: 0.25mm dashed #9ca3af; }
|
|
636
|
-
.streepjes { width: 45mm; background: repeating-linear-gradient(to right, #111827 0 2px, transparent 2px 5px); }
|
|
637
|
-
.klein { font-size: 85%; color: #6b7280; }
|
|
638
|
-
.breekvoor { break-before: page; page-break-before: always; }
|
|
639
|
-
.heelhouden { break-inside: avoid; page-break-inside: avoid; }
|
|
640
|
-
img { max-width: 100%; }
|
|
641
|
-
</style></head>
|
|
642
|
-
<body>${body}</body></html>`;
|
|
643
|
-
}
|
|
644
|
-
/** Handig voor testen: het document met de voorbeeldgegevens. */
|
|
645
|
-
export function voorbeeldPakbonOrder() {
|
|
646
|
-
return DEMO_ORDER;
|
|
647
|
-
}
|
|
648
|
-
/**
|
|
649
|
-
* Opent het printvenster met de pakbon. Andere modules hoeven hiervoor niets
|
|
650
|
-
* zelf te bouwen: layoutconfiguratie + ordergegevens erin, afdruk eruit.
|
|
651
|
-
*/
|
|
652
|
-
export function printPakbon(config, order, opties = {}) {
|
|
653
|
-
if (typeof window === "undefined")
|
|
654
|
-
return;
|
|
655
|
-
const html = renderPakbonDocument(config, order, opties);
|
|
656
|
-
const frame = document.createElement("iframe");
|
|
657
|
-
frame.setAttribute("aria-hidden", "true");
|
|
658
|
-
frame.style.cssText = "position:fixed;right:0;bottom:0;width:0;height:0;border:0";
|
|
659
|
-
document.body.appendChild(frame);
|
|
660
|
-
const doc = frame.contentDocument;
|
|
661
|
-
if (!doc) {
|
|
662
|
-
frame.remove();
|
|
663
|
-
return;
|
|
664
|
-
}
|
|
665
|
-
doc.open();
|
|
666
|
-
doc.write(html);
|
|
667
|
-
doc.close();
|
|
668
|
-
const start = () => {
|
|
669
|
-
frame.contentWindow?.focus();
|
|
670
|
-
frame.contentWindow?.print();
|
|
671
|
-
window.setTimeout(() => frame.remove(), 60000);
|
|
672
|
-
};
|
|
673
|
-
// Wachten tot afbeeldingen (logo's) geladen zijn, anders drukt de browser
|
|
674
|
-
// een pakbon af zonder logo.
|
|
675
|
-
const wachtOpAfbeeldingen = async () => {
|
|
676
|
-
const imgs = Array.from(doc.images ?? []);
|
|
677
|
-
await Promise.all(imgs.map((img) => img.complete
|
|
678
|
-
? Promise.resolve()
|
|
679
|
-
: new Promise((res) => {
|
|
680
|
-
img.onload = () => res();
|
|
681
|
-
img.onerror = () => res();
|
|
682
|
-
})));
|
|
683
|
-
start();
|
|
684
|
-
};
|
|
685
|
-
window.setTimeout(() => void wachtOpAfbeeldingen(), 200);
|
|
686
|
-
}
|
|
687
|
-
/**
|
|
688
|
-
* Zelfde als `printPakbon`, maar haalt de logo's zelf uit de centrale Logo
|
|
689
|
-
* Bibliotheek en bedt ze in. Andere modules hoeven zo niets over logo's te
|
|
690
|
-
* weten: het documentlogo verschijnt automatisch op de afdruk.
|
|
691
|
-
*/
|
|
692
|
-
export async function printPakbonMetLogos(config, order, opties = {}) {
|
|
693
|
-
let logos = opties.logos;
|
|
694
|
-
if (!Array.isArray(logos) || logos.length === 0) {
|
|
695
|
-
try {
|
|
696
|
-
logos = await haalPrintLogos();
|
|
697
|
-
}
|
|
698
|
-
catch {
|
|
699
|
-
logos = [];
|
|
700
|
-
}
|
|
701
|
-
}
|
|
702
|
-
printPakbon(config, order, { ...opties, logos });
|
|
703
|
-
}
|