@flowselections/layout-maker 1.32.0 → 1.36.7
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/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/PakbonBuilderPage.d.ts.map +1 -1
- package/dist-lib/components/PakbonBuilderPage.js +3 -1
- package/dist-lib/components/PakbonSectiesPanel.d.ts.map +1 -1
- package/dist-lib/components/PakbonSectiesPanel.js +6 -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 +25 -2
- 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 +6 -0
- package/dist-lib/index.d.ts.map +1 -1
- package/dist-lib/index.js +11 -1
- package/dist-lib/integrations/supabase/auth-middleware.d.ts +242 -1
- package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/client.server.d.ts +242 -1
- package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/types.d.ts +244 -1
- package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
- package/dist-lib/lib/aiLayout.functions.d.ts +242 -1
- package/dist-lib/lib/aiLayout.functions.d.ts.map +1 -1
- package/dist-lib/lib/htmlPdf.server.d.ts.map +1 -1
- package/dist-lib/lib/htmlPdf.server.js +43 -6
- package/dist-lib/lib/orderData.server.d.ts.map +1 -1
- package/dist-lib/lib/orderData.server.js +117 -10
- 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.map +1 -1
- package/dist-lib/lib/printQueue.server.js +99 -26
- package/dist-lib/lib/printService.functions.d.ts +968 -4
- package/dist-lib/lib/printService.functions.d.ts.map +1 -1
- 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 +1 -1
|
@@ -428,14 +428,34 @@ class Tekenaar {
|
|
|
428
428
|
function regelHoogte(s) {
|
|
429
429
|
return s.grootte * 1.35;
|
|
430
430
|
}
|
|
431
|
-
/** Breekt tekst af op de beschikbare breedte. */
|
|
431
|
+
/** Breekt tekst af op de beschikbare breedte; te lange woorden worden hard gesplitst. */
|
|
432
432
|
function breekAf(tk, tekst, s, breedte) {
|
|
433
433
|
const woorden = veiligeTekst(tekst).split(/\s+/).filter((w) => w.length);
|
|
434
434
|
if (!woorden.length)
|
|
435
435
|
return [];
|
|
436
436
|
const regels = [];
|
|
437
437
|
let huidig = "";
|
|
438
|
+
const duw = () => {
|
|
439
|
+
if (huidig)
|
|
440
|
+
regels.push(huidig);
|
|
441
|
+
huidig = "";
|
|
442
|
+
};
|
|
438
443
|
for (const w of woorden) {
|
|
444
|
+
// Woord past sowieso niet: hak het op zodat het nooit de kolom uitloopt.
|
|
445
|
+
if (tk.breedteVan(w, s) > breedte) {
|
|
446
|
+
duw();
|
|
447
|
+
let deel = "";
|
|
448
|
+
for (const teken of w) {
|
|
449
|
+
if (deel && tk.breedteVan(deel + teken, s) > breedte) {
|
|
450
|
+
regels.push(deel);
|
|
451
|
+
deel = teken;
|
|
452
|
+
}
|
|
453
|
+
else
|
|
454
|
+
deel += teken;
|
|
455
|
+
}
|
|
456
|
+
huidig = deel;
|
|
457
|
+
continue;
|
|
458
|
+
}
|
|
439
459
|
const kandidaat = huidig ? `${huidig} ${w}` : w;
|
|
440
460
|
if (tk.breedteVan(kandidaat, s) <= breedte || !huidig)
|
|
441
461
|
huidig = kandidaat;
|
|
@@ -444,10 +464,25 @@ function breekAf(tk, tekst, s, breedte) {
|
|
|
444
464
|
huidig = w;
|
|
445
465
|
}
|
|
446
466
|
}
|
|
447
|
-
|
|
448
|
-
regels.push(huidig);
|
|
467
|
+
duw();
|
|
449
468
|
return regels;
|
|
450
469
|
}
|
|
470
|
+
/**
|
|
471
|
+
* Verkleint de letter zo min mogelijk tot het langste woord binnen de breedte
|
|
472
|
+
* past. Zo blijft tekst maximaal leesbaar zonder ooit over te lopen.
|
|
473
|
+
*/
|
|
474
|
+
function passendeStijl(tk, tekst, s, breedte) {
|
|
475
|
+
const woorden = veiligeTekst(tekst).split(/\s+/).filter((w) => w.length);
|
|
476
|
+
if (!woorden.length || breedte <= 0)
|
|
477
|
+
return s;
|
|
478
|
+
const langste = woorden.reduce((a, b) => (tk.breedteVan(b, s) > tk.breedteVan(a, s) ? b : a));
|
|
479
|
+
const nodig = tk.breedteVan(langste, s);
|
|
480
|
+
if (nodig <= breedte)
|
|
481
|
+
return s;
|
|
482
|
+
const min = Math.max(4.5, s.grootte * 0.6);
|
|
483
|
+
const geschaald = Math.max(min, Math.floor((s.grootte * breedte) / nodig * 10) / 10);
|
|
484
|
+
return geschaald < s.grootte ? { ...s, grootte: geschaald } : s;
|
|
485
|
+
}
|
|
451
486
|
function tekenRegel(tk, regel, x, breedte, y, s) {
|
|
452
487
|
const t = metTransform(veiligeTekst(regel), s);
|
|
453
488
|
const w = tk.breedteVan(t, s);
|
|
@@ -770,9 +805,11 @@ async function tekenTabel(ctx, tabel, vak, s) {
|
|
|
770
805
|
const eigenCelRanden = rijen.some((r) => r.kinderen.some((c) => (c.tag === "td" || c.tag === "th") && !!celRandOnder(c)));
|
|
771
806
|
const tekenRij = (rij, herhaald = false) => {
|
|
772
807
|
const cellen = rij.kinderen.filter((c) => c.tag === "td" || c.tag === "th");
|
|
773
|
-
const
|
|
774
|
-
const
|
|
775
|
-
const
|
|
808
|
+
const celBreedte = (i) => Math.max(4, (breedtes[i] ?? 20) - padX * 2);
|
|
809
|
+
const celTekst = cellen.map((c) => plattTekst(c));
|
|
810
|
+
const cellenStijl = cellen.map((c, i) => passendeStijl(tk, celTekst[i], stijlVan(c, s), celBreedte(i)));
|
|
811
|
+
const teksten = cellen.map((_, i) => breekAf(tk, celTekst[i], cellenStijl[i], celBreedte(i)));
|
|
812
|
+
const hoogte = Math.max(regelHoogte(s), ...teksten.map((t, i) => Math.max(1, t.length) * regelHoogte(cellenStijl[i] ?? s))) + padY * 2;
|
|
776
813
|
if (hoogte > tk.ruimte) {
|
|
777
814
|
tk.nieuwePagina();
|
|
778
815
|
if (kopRij && !herhaald && rij !== kopRij)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"orderData.server.d.ts","sourceRoot":"","sources":["../../src/lib/orderData.server.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAEnD,KAAK,GAAG,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AAE/B,MAAM,MAAM,WAAW,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAC;AAwBtD,mEAAmE;AACnE,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAKvD;AAED,+EAA+E;AAC/E,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,GAAG;IACrD,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB,CAgCA;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG;IAC7C,KAAK,EAAE,WAAW,CAAC;IACnB,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;IAClB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B,CAMA;AAED;;;GAGG;AACH,wBAAsB,cAAc,CAClC,EAAE,EAAE,OAAO,EACX,MAAM,EAAE;IAAE,KAAK,EAAE,WAAW,CAAC;IAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GAC7E,OAAO,CAAC,GAAG,GAAG,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"orderData.server.d.ts","sourceRoot":"","sources":["../../src/lib/orderData.server.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAEnD,KAAK,GAAG,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AAE/B,MAAM,MAAM,WAAW,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAC;AAwBtD,mEAAmE;AACnE,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAKvD;AAED,+EAA+E;AAC/E,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,GAAG;IACrD,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB,CAgCA;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG;IAC7C,KAAK,EAAE,WAAW,CAAC;IACnB,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;IAClB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B,CAMA;AAED;;;GAGG;AACH,wBAAsB,cAAc,CAClC,EAAE,EAAE,OAAO,EACX,MAAM,EAAE;IAAE,KAAK,EAAE,WAAW,CAAC;IAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GAC7E,OAAO,CAAC,GAAG,GAAG,IAAI,CAAC,CAwXrB;AA+DD;;;GAGG;AACH,wBAAsB,uBAAuB,CAAC,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,GAAG,GAAG,IAAI,CAAC,CAGhG"}
|
|
@@ -205,24 +205,127 @@ export async function laadPakbonData(db, opties) {
|
|
|
205
205
|
if (productIds.length) {
|
|
206
206
|
const data = await veilig(() => db
|
|
207
207
|
.from("products")
|
|
208
|
-
.select("id, product, article_number, barcode, location, unit, weight, custom_fields")
|
|
208
|
+
.select("id, product, article_number, barcode, location, unit, weight, custom_fields, floriday_trade_item_id, trade_item_key")
|
|
209
209
|
.in("id", productIds));
|
|
210
210
|
for (const p of arr(data))
|
|
211
211
|
producten.set(String(p["id"]), p);
|
|
212
212
|
}
|
|
213
|
+
// --- verrijking vanuit Floriday-artikelcache ------------------------------
|
|
214
|
+
// `products.unit` is vrijwel altijd "stuks" en zegt niets over het fust.
|
|
215
|
+
// De echte fustsoort, potmaat en kleur staan in de Floriday-cache:
|
|
216
|
+
// - fustsoort: primaire verpakkingsconfiguratie → vbnPackageCode, met de
|
|
217
|
+
// gebruikersnaam uit `fust_types`.
|
|
218
|
+
// - potmaat: kenmerk S01 (Potmaat).
|
|
219
|
+
// - kleur: kenmerk B01 (Hoofdkleur 1).
|
|
220
|
+
const floridayIds = uniek(Array.from(producten.values()).map((p) => {
|
|
221
|
+
const extra = (p["custom_fields"] ?? {});
|
|
222
|
+
return String(p["floriday_trade_item_id"] ?? p["trade_item_key"] ?? extra["floriday_id"] ?? "");
|
|
223
|
+
}));
|
|
224
|
+
const floridayInfo = new Map();
|
|
225
|
+
if (floridayIds.length) {
|
|
226
|
+
const items = arr(await veilig(() => db
|
|
227
|
+
.from("floriday_trade_items_cache")
|
|
228
|
+
.select("floriday_id, data")
|
|
229
|
+
.in("floriday_id", floridayIds)));
|
|
230
|
+
for (const it of items) {
|
|
231
|
+
const d = (it["data"] ?? {});
|
|
232
|
+
const configs = arr(d["packingConfigurations"]);
|
|
233
|
+
const primair = configs.find((c) => c["primary"]) ?? configs[0] ?? {};
|
|
234
|
+
const fustCode = String((primair["package"] ?? {})["vbnPackageCode"] ?? "");
|
|
235
|
+
const kenmerken = arr(d["characteristics"]);
|
|
236
|
+
const b01 = kenmerken.find((k) => String(k["vbnCode"]) === "B01");
|
|
237
|
+
const s01 = kenmerken.find((k) => String(k["vbnCode"]).toUpperCase() === "S01");
|
|
238
|
+
const kleurCode = b01 ? String(b01["vbnValueCode"] ?? "") : "";
|
|
239
|
+
const potmaatCode = s01 ? String(s01["vbnValueCode"] ?? "") : "";
|
|
240
|
+
floridayInfo.set(String(it["floriday_id"]), { fustCode, kleurCode, potmaatCode });
|
|
241
|
+
}
|
|
242
|
+
// VBN-verpakkincode → de ingestelde fustnaam. `fust_types` is hiervoor de
|
|
243
|
+
// bron van waarheid; `crate_types` bevat niet alle VBN-codes.
|
|
244
|
+
const fustCodes = uniek(Array.from(floridayInfo.values()).map((i) => i.fustCode));
|
|
245
|
+
const fustNamen = new Map();
|
|
246
|
+
if (fustCodes.length) {
|
|
247
|
+
const fusten = arr(await veilig(() => db.from("fust_types").select("vbn_code, name").in("vbn_code", fustCodes)));
|
|
248
|
+
for (const fust of fusten) {
|
|
249
|
+
const code = String(fust["vbn_code"] ?? "");
|
|
250
|
+
const naam = String(fust["name"] ?? "").trim();
|
|
251
|
+
if (code && naam && !fustNamen.has(code))
|
|
252
|
+
fustNamen.set(code, naam);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
// Floriday-kenmerkcodes → leesbare labels uit de VBN-codelijst.
|
|
256
|
+
const kenmerkSleutels = uniek(Array.from(floridayInfo.values()).flatMap((i) => [
|
|
257
|
+
i.kleurCode ? `B01:${i.kleurCode}` : "",
|
|
258
|
+
i.potmaatCode ? `S01:${i.potmaatCode}` : "",
|
|
259
|
+
]));
|
|
260
|
+
const kenmerkNamen = new Map();
|
|
261
|
+
if (kenmerkSleutels.length) {
|
|
262
|
+
const waarden = arr(await veilig(() => db
|
|
263
|
+
.from("floriday_code_lists_cache")
|
|
264
|
+
.select("code, label")
|
|
265
|
+
.eq("list_name", "vbn-characteristic-value")
|
|
266
|
+
.in("code", kenmerkSleutels)));
|
|
267
|
+
for (const w of waarden) {
|
|
268
|
+
const code = String(w["code"] ?? "");
|
|
269
|
+
const label = String(w["label"] ?? "").trim();
|
|
270
|
+
if (code && label && !kenmerkNamen.has(code))
|
|
271
|
+
kenmerkNamen.set(code, label);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
// Schrijf de verrijking terug in de custom_fields van het product.
|
|
275
|
+
for (const p of producten.values()) {
|
|
276
|
+
const extra = (p["custom_fields"] ?? {});
|
|
277
|
+
const floridayId = String(p["floriday_trade_item_id"] ?? p["trade_item_key"] ?? extra["floriday_id"] ?? "");
|
|
278
|
+
const info = floridayInfo.get(floridayId);
|
|
279
|
+
if (!info)
|
|
280
|
+
continue;
|
|
281
|
+
if (info.fustCode) {
|
|
282
|
+
const naam = fustNamen.get(info.fustCode);
|
|
283
|
+
extra["__fustsoort"] = naam ?? `VBN ${info.fustCode}`;
|
|
284
|
+
}
|
|
285
|
+
if (info.kleurCode) {
|
|
286
|
+
extra["__kleur"] = kenmerkNamen.get(`B01:${info.kleurCode}`) ?? info.kleurCode;
|
|
287
|
+
}
|
|
288
|
+
if (info.potmaatCode) {
|
|
289
|
+
extra["__potmaat"] = kenmerkNamen.get(`S01:${info.potmaatCode}`) ?? info.potmaatCode;
|
|
290
|
+
}
|
|
291
|
+
p["custom_fields"] = extra;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
213
294
|
const maakProduct = (bron, aantalPlanten, fusten, perFust) => {
|
|
214
295
|
const p = producten.get(String(bron["product_id"] ?? "")) ?? {};
|
|
215
296
|
const extra = (p["custom_fields"] ?? {});
|
|
297
|
+
const unit = String(p["unit"] ?? "").trim();
|
|
216
298
|
return {
|
|
217
299
|
artikelnummer: String(p["article_number"] ?? ""),
|
|
218
300
|
artikelnaam: taalTekst(bron["product_name"]) || taalTekst(p["product"]),
|
|
219
|
-
potmaat: String(extra["potmaat"] ??
|
|
301
|
+
potmaat: String(extra["potmaat"] ??
|
|
302
|
+
extra["Potmaat"] ??
|
|
303
|
+
extra["pot_size"] ??
|
|
304
|
+
extra["potSize"] ??
|
|
305
|
+
extra["potmaat_cm"] ??
|
|
306
|
+
extra["maat"] ??
|
|
307
|
+
extra["__potmaat"] ??
|
|
308
|
+
"Niet ingesteld"),
|
|
309
|
+
kleur: String(extra["kleur"] ??
|
|
310
|
+
extra["Kleur"] ??
|
|
311
|
+
extra["colour"] ??
|
|
312
|
+
extra["color"] ??
|
|
313
|
+
extra["__kleur"] ??
|
|
314
|
+
"Niet ingesteld"),
|
|
220
315
|
aantal: aantalPlanten,
|
|
221
316
|
barcode: String(p["barcode"] ?? ""),
|
|
222
|
-
locatie: String(p["location"]
|
|
317
|
+
locatie: String(p["location"] ||
|
|
318
|
+
extra["locatie"] ||
|
|
319
|
+
extra["location"] ||
|
|
320
|
+
extra["warehouse_location"] ||
|
|
321
|
+
"Niet ingesteld"),
|
|
223
322
|
gewicht: bron["weight_kg"] ? `${num(bron["weight_kg"]).toFixed(1)} kg` : "",
|
|
224
323
|
fust_aantal: fusten,
|
|
225
|
-
|
|
324
|
+
// "stuks" is een aantalseenheid, geen fustsoort — die slaan we over.
|
|
325
|
+
fustsoort: String(extra["fustsoort"] ??
|
|
326
|
+
extra["Fustsoort"] ??
|
|
327
|
+
extra["__fustsoort"] ??
|
|
328
|
+
(unit && unit.toLowerCase() !== "stuks" ? unit : "Niet ingesteld")),
|
|
226
329
|
planten_per_fust: perFust,
|
|
227
330
|
};
|
|
228
331
|
};
|
|
@@ -256,12 +359,16 @@ export async function laadPakbonData(db, opties) {
|
|
|
256
359
|
gewicht: kar["weight_kg"] ? `${num(kar["weight_kg"]).toFixed(1)} kg` : "",
|
|
257
360
|
gedeeld: kar["__gedeeld"] === true,
|
|
258
361
|
opmerking: String(kar["notes"] ?? ""),
|
|
259
|
-
lagen: lagen.slice(0, Math.max(maxLagen, lagen.length)).map((laag, j) =>
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
362
|
+
lagen: lagen.slice(0, Math.max(maxLagen, lagen.length)).map((laag, j) => {
|
|
363
|
+
const potmaten = uniek(laag.producten.map((product) => product["potmaat"]));
|
|
364
|
+
return {
|
|
365
|
+
laagnummer: j + 1,
|
|
366
|
+
belading: `${Math.round(Math.min(laag.ruimte, 1) * 100)}%`,
|
|
367
|
+
potmaat: potmaten.length === 1 ? potmaten[0] : "",
|
|
368
|
+
producten: laag.producten,
|
|
369
|
+
fusten: vatFustenSamen(laag.producten),
|
|
370
|
+
};
|
|
371
|
+
}),
|
|
265
372
|
};
|
|
266
373
|
});
|
|
267
374
|
// --- losse orderregels (zonder karindeling) -------------------------------
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
type PdfServiceState = {
|
|
2
|
+
gestart: boolean;
|
|
3
|
+
bezig: boolean;
|
|
4
|
+
interval: number | null;
|
|
5
|
+
gemeldePrinters: Set<string>;
|
|
6
|
+
};
|
|
7
|
+
declare global {
|
|
8
|
+
interface Window {
|
|
9
|
+
__layoutMakerPdfService?: PdfServiceState;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
/** Start één permanente lokale PDF-afhandelaar voor de volledige shell. */
|
|
13
|
+
export declare function startPdfMapService(): void;
|
|
14
|
+
/**
|
|
15
|
+
* Wacht na een printactie op de server-PDF en schrijft hem vervolgens direct
|
|
16
|
+
* naar de map die op deze computer voor de virtuele printer is gekozen.
|
|
17
|
+
*/
|
|
18
|
+
export declare function bewaarPdfJobZodraKlaar(jobId: string, printerId: string): Promise<boolean>;
|
|
19
|
+
export {};
|
|
20
|
+
//# sourceMappingURL=pdfJobSaver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pdfJobSaver.d.ts","sourceRoot":"","sources":["../../src/lib/pdfJobSaver.ts"],"names":[],"mappings":"AA2BA,KAAK,eAAe,GAAG;IACrB,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,OAAO,CAAC;IACf,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,eAAe,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CAC9B,CAAC;AAEF,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,MAAM;QACd,uBAAuB,CAAC,EAAE,eAAe,CAAC;KAC3C;CACF;AA8HD,2EAA2E;AAC3E,wBAAgB,kBAAkB,IAAI,IAAI,CAyBzC;AAED;;;GAGG;AACH,wBAAsB,sBAAsB,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAyB/F"}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { supabase } from "./supabaseClient.js";
|
|
2
|
+
import { toast } from "@flowselections/core";
|
|
3
|
+
import { bewaarInPdfMap, herstelPdfMapToestemming, heeftPdfMap, luisterNaarPdfMapWijziging, pdfMapToestemming, } from "./pdfMap.js";
|
|
4
|
+
const BEWAARD_SLEUTEL = "layout-maker-pdf-bewaard";
|
|
5
|
+
const WACHT_INTERVAL_MS = 2000;
|
|
6
|
+
const MAX_WACHTTIJD_MS = 5 * 60 * 1000;
|
|
7
|
+
const SERVICE_INTERVAL_MS = 5000;
|
|
8
|
+
async function noteerLokaleStatus(jobId, status, fout = null) {
|
|
9
|
+
await supabase
|
|
10
|
+
.from("print_jobs")
|
|
11
|
+
.update({
|
|
12
|
+
lokale_pdf_status: status,
|
|
13
|
+
lokale_pdf_fout: fout,
|
|
14
|
+
...(status === "opgeslagen"
|
|
15
|
+
? { status: "voltooid", lokaal_opgeslagen_op: new Date().toISOString() }
|
|
16
|
+
: {}),
|
|
17
|
+
})
|
|
18
|
+
.eq("id", jobId);
|
|
19
|
+
}
|
|
20
|
+
function veiligeBestandsnaam(naam) {
|
|
21
|
+
return naam.replace(/[\\/:*?"<>|]/g, "-").replace(/\s+/g, " ").trim() || "document";
|
|
22
|
+
}
|
|
23
|
+
function pdfBlob(base64) {
|
|
24
|
+
const bytes = Uint8Array.from(atob(base64), (teken) => teken.charCodeAt(0));
|
|
25
|
+
return new Blob([bytes], { type: "application/pdf" });
|
|
26
|
+
}
|
|
27
|
+
function markeerBewaard(jobId) {
|
|
28
|
+
try {
|
|
29
|
+
const ruw = localStorage.getItem(BEWAARD_SLEUTEL);
|
|
30
|
+
const gelezen = ruw ? JSON.parse(ruw) : [];
|
|
31
|
+
const ids = new Set(Array.isArray(gelezen) ? gelezen.map(String) : []);
|
|
32
|
+
ids.add(jobId);
|
|
33
|
+
localStorage.setItem(BEWAARD_SLEUTEL, JSON.stringify(Array.from(ids).slice(-500)));
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
// Opslaan in de gekozen map is al gelukt; lokale historie is alleen deduplicatie.
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
async function schrijfPdfJob(job) {
|
|
40
|
+
if (!job.printer_id || !job.resultaat_pdf_base64)
|
|
41
|
+
return "overgeslagen";
|
|
42
|
+
const toestemming = await pdfMapToestemming(job.printer_id);
|
|
43
|
+
if (toestemming === "ontbreekt") {
|
|
44
|
+
await noteerLokaleStatus(job.id, "map_ontbreekt", "Kies op deze Mac eerst een PDF-map.");
|
|
45
|
+
return "overgeslagen";
|
|
46
|
+
}
|
|
47
|
+
if (toestemming !== "granted") {
|
|
48
|
+
await noteerLokaleStatus(job.id, "toestemming_nodig", "Klik op 'Geef maptoestemming' om deze Mac opnieuw toegang tot de PDF-map te geven.");
|
|
49
|
+
return "overgeslagen";
|
|
50
|
+
}
|
|
51
|
+
const naam = `${veiligeBestandsnaam(job.documentnaam ?? "document")}-${job.id.slice(0, 8)}.pdf`;
|
|
52
|
+
const gelukt = await bewaarInPdfMap(job.printer_id, naam, pdfBlob(job.resultaat_pdf_base64));
|
|
53
|
+
if (!gelukt) {
|
|
54
|
+
await noteerLokaleStatus(job.id, "schrijffout", "De PDF kon niet naar de gekozen map worden geschreven.");
|
|
55
|
+
return "overgeslagen";
|
|
56
|
+
}
|
|
57
|
+
markeerBewaard(job.id);
|
|
58
|
+
await noteerLokaleStatus(job.id, "opgeslagen");
|
|
59
|
+
return "opgeslagen";
|
|
60
|
+
}
|
|
61
|
+
async function verwerkGereedstaandePdfs(state) {
|
|
62
|
+
if (state.bezig)
|
|
63
|
+
return;
|
|
64
|
+
state.bezig = true;
|
|
65
|
+
try {
|
|
66
|
+
const { data, error } = await supabase
|
|
67
|
+
.from("print_jobs")
|
|
68
|
+
.select("id, status, printer_id, documentnaam, resultaat_pdf_base64, lokale_pdf_status")
|
|
69
|
+
.in("lokale_pdf_status", ["gereed", "map_ontbreekt", "toestemming_nodig", "schrijffout", "timeout"])
|
|
70
|
+
.not("resultaat_pdf_base64", "is", null)
|
|
71
|
+
.order("voltooid_op", { ascending: true })
|
|
72
|
+
.limit(100);
|
|
73
|
+
if (error || !Array.isArray(data))
|
|
74
|
+
return;
|
|
75
|
+
let opgeslagen = 0;
|
|
76
|
+
for (const job of data) {
|
|
77
|
+
if (await schrijfPdfJob(job) === "opgeslagen") {
|
|
78
|
+
opgeslagen += 1;
|
|
79
|
+
if (job.printer_id)
|
|
80
|
+
state.gemeldePrinters.delete(job.printer_id);
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
if (!job.printer_id || state.gemeldePrinters.has(job.printer_id))
|
|
84
|
+
continue;
|
|
85
|
+
const toestemming = await pdfMapToestemming(job.printer_id);
|
|
86
|
+
if (toestemming === "prompt" || toestemming === "denied") {
|
|
87
|
+
state.gemeldePrinters.add(job.printer_id);
|
|
88
|
+
toast.error("De PDF-map heeft opnieuw toestemming nodig", {
|
|
89
|
+
description: "Geef deze Mac opnieuw toegang; gereedstaande PDF's worden daarna direct opgeslagen.",
|
|
90
|
+
action: {
|
|
91
|
+
label: "Geef maptoestemming",
|
|
92
|
+
onClick: () => {
|
|
93
|
+
void herstelPdfMapToestemming(job.printer_id).then((resultaat) => {
|
|
94
|
+
if (resultaat === "granted") {
|
|
95
|
+
state.gemeldePrinters.delete(job.printer_id);
|
|
96
|
+
void verwerkGereedstaandePdfs(state);
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
toast.error("Maptoestemming is niet verleend");
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
duration: 15000,
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
if (opgeslagen > 0) {
|
|
109
|
+
toast.success(opgeslagen === 1
|
|
110
|
+
? "PDF opgeslagen in de gekozen map op deze Mac"
|
|
111
|
+
: `${opgeslagen} PDF's opgeslagen in de gekozen map op deze Mac`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
finally {
|
|
115
|
+
state.bezig = false;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
/** Start één permanente lokale PDF-afhandelaar voor de volledige shell. */
|
|
119
|
+
export function startPdfMapService() {
|
|
120
|
+
if (typeof window === "undefined")
|
|
121
|
+
return;
|
|
122
|
+
const bestaand = window.__layoutMakerPdfService;
|
|
123
|
+
if (bestaand?.gestart) {
|
|
124
|
+
void verwerkGereedstaandePdfs(bestaand);
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
const state = {
|
|
128
|
+
gestart: true,
|
|
129
|
+
bezig: false,
|
|
130
|
+
interval: null,
|
|
131
|
+
gemeldePrinters: new Set(),
|
|
132
|
+
};
|
|
133
|
+
window.__layoutMakerPdfService = state;
|
|
134
|
+
const controleer = () => void verwerkGereedstaandePdfs(state);
|
|
135
|
+
state.interval = window.setInterval(controleer, SERVICE_INTERVAL_MS);
|
|
136
|
+
window.addEventListener("focus", controleer);
|
|
137
|
+
window.addEventListener("online", controleer);
|
|
138
|
+
document.addEventListener("visibilitychange", () => {
|
|
139
|
+
if (document.visibilityState === "visible")
|
|
140
|
+
controleer();
|
|
141
|
+
});
|
|
142
|
+
luisterNaarPdfMapWijziging(controleer);
|
|
143
|
+
controleer();
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Wacht na een printactie op de server-PDF en schrijft hem vervolgens direct
|
|
147
|
+
* naar de map die op deze computer voor de virtuele printer is gekozen.
|
|
148
|
+
*/
|
|
149
|
+
export async function bewaarPdfJobZodraKlaar(jobId, printerId) {
|
|
150
|
+
if (!(await heeftPdfMap(printerId))) {
|
|
151
|
+
await noteerLokaleStatus(jobId, "map_ontbreekt", "Kies op deze computer eerst een PDF-map.");
|
|
152
|
+
return false;
|
|
153
|
+
}
|
|
154
|
+
const deadline = Date.now() + MAX_WACHTTIJD_MS;
|
|
155
|
+
while (Date.now() < deadline) {
|
|
156
|
+
const { data, error } = await supabase
|
|
157
|
+
.from("print_jobs")
|
|
158
|
+
.select("id, status, documentnaam, resultaat_pdf_base64, lokale_pdf_status")
|
|
159
|
+
.eq("id", jobId)
|
|
160
|
+
.maybeSingle();
|
|
161
|
+
if (error)
|
|
162
|
+
return false;
|
|
163
|
+
const job = data;
|
|
164
|
+
if (job?.status === "mislukt" || job?.status === "geannuleerd")
|
|
165
|
+
return false;
|
|
166
|
+
if (job?.resultaat_pdf_base64) {
|
|
167
|
+
return (await schrijfPdfJob({ ...job, printer_id: printerId })) === "opgeslagen";
|
|
168
|
+
}
|
|
169
|
+
await new Promise((resolve) => window.setTimeout(resolve, WACHT_INTERVAL_MS));
|
|
170
|
+
}
|
|
171
|
+
await noteerLokaleStatus(jobId, "timeout", "De PDF was niet binnen vijf minuten gereed.");
|
|
172
|
+
return false;
|
|
173
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
export declare function mapKiezenMogelijk(): boolean;
|
|
2
|
+
export declare function luisterNaarPdfMapWijziging(handler: () => void): () => void;
|
|
3
|
+
export type PdfMapKeuze = {
|
|
4
|
+
status: "gekozen";
|
|
5
|
+
naam: string;
|
|
6
|
+
} | {
|
|
7
|
+
status: "geannuleerd";
|
|
8
|
+
} | {
|
|
9
|
+
status: "niet-ondersteund";
|
|
10
|
+
} | {
|
|
11
|
+
status: "iframe-geblokkeerd";
|
|
12
|
+
} | {
|
|
13
|
+
status: "fout";
|
|
14
|
+
melding: string;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Browsers blokkeren showDirectoryPicker in een cross-origin iframe (zoals de
|
|
18
|
+
* Lovable-preview). De aanroeper kan dan aanbieden om de app in een eigen
|
|
19
|
+
* tabblad te openen, waar de verkenner wel werkt.
|
|
20
|
+
*/
|
|
21
|
+
export declare function draaitInIframe(): boolean;
|
|
22
|
+
/** Opent de huidige pagina in een eigen tabblad (buiten het preview-iframe). */
|
|
23
|
+
export declare function openInEigenTabblad(): void;
|
|
24
|
+
/**
|
|
25
|
+
* Opent de mappenverkenner van het besturingssysteem zodat de gebruiker een
|
|
26
|
+
* map kan selecteren. Start in Downloads zodat het pad snel te vinden is.
|
|
27
|
+
* Geeft terug wat er is gebeurd zodat de aanroeper gericht kan reageren.
|
|
28
|
+
*/
|
|
29
|
+
export declare function kiesPdfMap(printerId: string): Promise<PdfMapKeuze>;
|
|
30
|
+
/** Is er voor deze printer een map gekozen op deze computer? */
|
|
31
|
+
export declare function heeftPdfMap(printerId: string): Promise<boolean>;
|
|
32
|
+
/** Naam van de gekozen map (voor meldingen), of null. */
|
|
33
|
+
export declare function pdfMapNaam(printerId: string): Promise<string | null>;
|
|
34
|
+
/**
|
|
35
|
+
* Probeert het bestand rechtstreeks in de gekozen map te zetten. Lukt dat niet
|
|
36
|
+
* (andere computer, geen toestemming, browser zonder ondersteuning), dan geeft
|
|
37
|
+
* de functie false terug en kan de aanroeper gewoon downloaden.
|
|
38
|
+
*/
|
|
39
|
+
export declare function bewaarInPdfMap(printerId: string, bestandsnaam: string, blob: Blob): Promise<boolean>;
|
|
40
|
+
/** Controleert toestemming zonder een bestandsactie te starten. */
|
|
41
|
+
export declare function pdfMapToestemming(printerId: string): Promise<"granted" | "prompt" | "denied" | "ontbreekt">;
|
|
42
|
+
/**
|
|
43
|
+
* Vraagt vanuit een expliciete gebruikersklik opnieuw schrijftoestemming voor
|
|
44
|
+
* een eerder gekozen map. Achtergrondtaken mogen deze browserprompt niet zelf
|
|
45
|
+
* openen, omdat browsers daarvoor een actieve gebruikershandeling vereisen.
|
|
46
|
+
*/
|
|
47
|
+
export declare function herstelPdfMapToestemming(printerId: string): Promise<"granted" | "denied" | "ontbreekt">;
|
|
48
|
+
//# sourceMappingURL=pdfMap.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pdfMap.d.ts","sourceRoot":"","sources":["../../src/lib/pdfMap.ts"],"names":[],"mappings":"AAsBA,wBAAgB,iBAAiB,IAAI,OAAO,CAE3C;AA6BD,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAI1E;AAgDD,MAAM,MAAM,WAAW,GACnB;IAAE,MAAM,EAAE,SAAS,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACnC;IAAE,MAAM,EAAE,aAAa,CAAA;CAAE,GACzB;IAAE,MAAM,EAAE,kBAAkB,CAAA;CAAE,GAC9B;IAAE,MAAM,EAAE,oBAAoB,CAAA;CAAE,GAChC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAExC;;;;GAIG;AACH,wBAAgB,cAAc,IAAI,OAAO,CAMxC;AAED,gFAAgF;AAChF,wBAAgB,kBAAkB,IAAI,IAAI,CAEzC;AAED;;;;GAIG;AACH,wBAAsB,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAqCxE;AAED,gEAAgE;AAChE,wBAAsB,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAErE;AAED,yDAAyD;AACzD,wBAAsB,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAG1E;AAED;;;;GAIG;AACH,wBAAsB,cAAc,CAClC,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,MAAM,EACpB,IAAI,EAAE,IAAI,GACT,OAAO,CAAC,OAAO,CAAC,CAmBlB;AAED,mEAAmE;AACnE,wBAAsB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,QAAQ,GAAG,QAAQ,GAAG,WAAW,CAAC,CAWjH;AAED;;;;GAIG;AACH,wBAAsB,wBAAwB,CAC5C,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,SAAS,GAAG,QAAQ,GAAG,WAAW,CAAC,CAe7C"}
|