@flowselections/layout-maker 1.36.7 → 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/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 +4 -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 +3 -2
- package/dist-lib/components/PrinterSetupWizard.js +3 -3
- package/dist-lib/components/PrintersRedirectPage.d.ts +1 -1
- package/dist-lib/components/PrintersRedirectPage.js +14 -14
- package/dist-lib/index.d.ts +4 -2
- package/dist-lib/index.d.ts.map +1 -1
- package/dist-lib/index.js +3 -2
- package/dist-lib/integrations/supabase/auth-middleware.d.ts +1240 -812
- package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/client.server.d.ts +1240 -812
- 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 +1231 -821
- package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
- package/dist-lib/lib/aiLayout.functions.d.ts +1240 -812
- 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 +58 -0
- 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 +6 -0
- 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/printQueue.server.d.ts +7 -0
- package/dist-lib/lib/printQueue.server.d.ts.map +1 -1
- package/dist-lib/lib/printQueue.server.js +247 -26
- package/dist-lib/lib/printService.functions.d.ts +4445 -2733
- 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/package.json +151 -151
|
@@ -7,8 +7,9 @@
|
|
|
7
7
|
// layoutconfiguratie en de echte ordergegevens. Zo tonen het live voorbeeld en
|
|
8
8
|
// de afdruk altijd exact dezelfde secties — inclusief de orderregels.
|
|
9
9
|
// ============================================================================
|
|
10
|
-
import { DEMO_ORDER, conditieContext, groepeerIdentiekeKarren, groepeerIdentiekeLagen, karBundelTekst, fustVerzamellijst, pickMethode, productVeldBreedtes,
|
|
10
|
+
import { DEMO_ORDER, conditieContext, groepeerIdentiekeKarren, groepeerIdentiekeLagen, karBundelTekst, fustVerzamellijst, pickMethode, pakbonLayoutVariant, productVeldBreedtes, sectieOnderdruktVoorVariant, laagBundelTekst, leesPakbonConfig, sectieZichtbaar, sorteerProducten, totalen, vulVeldcodes, } from "./pakbonBuilder.js";
|
|
11
11
|
import { haalPrintLogos } from "./logoLibrary.js";
|
|
12
|
+
import { code128BarsHtml } from "./code128.js";
|
|
12
13
|
// --- kleine hulpjes ---------------------------------------------------------
|
|
13
14
|
const arr = (v) => (Array.isArray(v) ? v : []);
|
|
14
15
|
const opt = (s, key, fallback) => s.opties?.[key] ?? fallback;
|
|
@@ -24,6 +25,24 @@ const cel = (p, veld) => {
|
|
|
24
25
|
return "";
|
|
25
26
|
return String(p[veld] ?? "");
|
|
26
27
|
};
|
|
28
|
+
/** Rendert uitsluitend een bestaande barcodewaarde uit de Engine-payload. */
|
|
29
|
+
function barcodeHtml(value, hoogteMm = 15) {
|
|
30
|
+
const tekst = String(value ?? "").trim();
|
|
31
|
+
if (!tekst)
|
|
32
|
+
return "";
|
|
33
|
+
const hoogte = Number.isFinite(hoogteMm) && hoogteMm > 0 ? hoogteMm : 15;
|
|
34
|
+
return `<div class="barcode"><div class="barcode-bars" data-barcode="${esc(tekst)}" style="height:${hoogte}mm">${code128BarsHtml(tekst)}</div><div>${esc(tekst)}</div></div>`;
|
|
35
|
+
}
|
|
36
|
+
/** Barcode van één pakbon; bij één geselecteerde kar mag diens Engine-barcode worden gebruikt. */
|
|
37
|
+
function pakbonBarcode(order) {
|
|
38
|
+
const direct = String(order.barcode ?? order.cart?.barcode ?? "").trim();
|
|
39
|
+
if (direct)
|
|
40
|
+
return direct;
|
|
41
|
+
const karren = Array.isArray(order.karren) ? order.karren : [];
|
|
42
|
+
if (karren.length !== 1)
|
|
43
|
+
return undefined;
|
|
44
|
+
return String(karren[0]?.barcode ?? "").trim() || undefined;
|
|
45
|
+
}
|
|
27
46
|
/**
|
|
28
47
|
* Vult ontbrekende onderdelen aan met veilige lege waarden, zodat een module
|
|
29
48
|
* die maar een deel van de gegevens heeft nooit een lege of stukke pakbon
|
|
@@ -38,11 +57,19 @@ export function maakPakbonOrder(input) {
|
|
|
38
57
|
out[v] = b[v] === undefined || b[v] === null ? "" : String(b[v]);
|
|
39
58
|
return out;
|
|
40
59
|
};
|
|
60
|
+
const cart = (bron["cart"] && typeof bron["cart"] === "object" ? bron["cart"] : {});
|
|
61
|
+
const topBarcode = String(bron["barcode"] ?? cart["barcode"] ?? "").trim();
|
|
41
62
|
const karren = arr(bron["karren"]).map((k, i) => ({
|
|
42
63
|
karnummer: Number(k["karnummer"] ?? i + 1),
|
|
64
|
+
barcode: String(k["barcode"] ?? "").trim() || undefined,
|
|
43
65
|
route: String(k["route"] ?? ""),
|
|
44
66
|
chauffeur: String(k["chauffeur"] ?? ""),
|
|
45
67
|
gewicht: String(k["gewicht"] ?? ""),
|
|
68
|
+
cart_label: String(k["cart_label"] ?? cart["cart_label"] ?? "").trim() || undefined,
|
|
69
|
+
cart_index: Number(k["cart_index"] ?? cart["cart_index"] ?? 0) || undefined,
|
|
70
|
+
cart_total: Number(k["cart_total"] ?? cart["cart_total"] ?? 0) || undefined,
|
|
71
|
+
cart_id: String(k["cart_id"] ?? cart["cart_id"] ?? "").trim() || undefined,
|
|
72
|
+
cart_items: Array.isArray(k["cart_items"] ?? cart["cart_items"]) ? (k["cart_items"] ?? cart["cart_items"]) : undefined,
|
|
46
73
|
gedeeld: k["gedeeld"] === true,
|
|
47
74
|
lagen: arr(k["lagen"]).map((l, j) => ({
|
|
48
75
|
laagnummer: Number(l["laagnummer"] ?? j + 1),
|
|
@@ -89,6 +116,15 @@ export function maakPakbonOrder(input) {
|
|
|
89
116
|
afleveradres: groep("afleveradres", ["naam", "adres", "plaats", "land"]),
|
|
90
117
|
factuuradres: groep("factuuradres", ["naam", "adres", "plaats", "land"]),
|
|
91
118
|
transport: groep("transport", ["transporteur", "route", "chauffeur", "track"]),
|
|
119
|
+
barcode: topBarcode || undefined,
|
|
120
|
+
cart: Object.keys(cart).length ? {
|
|
121
|
+
cart_label: String(cart["cart_label"] ?? "").trim() || undefined,
|
|
122
|
+
cart_index: Number(cart["cart_index"] ?? 0) || undefined,
|
|
123
|
+
cart_total: Number(cart["cart_total"] ?? 0) || undefined,
|
|
124
|
+
cart_id: String(cart["cart_id"] ?? "").trim() || undefined,
|
|
125
|
+
cart_items: Array.isArray(cart["cart_items"]) ? cart["cart_items"] : undefined,
|
|
126
|
+
barcode: String(cart["barcode"] ?? "").trim() || undefined,
|
|
127
|
+
} : undefined,
|
|
92
128
|
karren,
|
|
93
129
|
};
|
|
94
130
|
}
|
|
@@ -323,6 +359,14 @@ function karKop(s, kar, velden, bundelTekst = "") {
|
|
|
323
359
|
info.push(kar.route);
|
|
324
360
|
if (kar.gedeeld)
|
|
325
361
|
info.push("gedeelde kar");
|
|
362
|
+
const cartLabel = kar.cart_label ||
|
|
363
|
+
(kar.cart_index && kar.cart_total ? `Kar ${kar.cart_index} van ${kar.cart_total}` : "");
|
|
364
|
+
if (cartLabel)
|
|
365
|
+
info.push(cartLabel);
|
|
366
|
+
if (kar.cart_id)
|
|
367
|
+
info.push(`ID ${kar.cart_id}`);
|
|
368
|
+
if (kar.cart_items?.length)
|
|
369
|
+
info.push(`${kar.cart_items.length} karregels`);
|
|
326
370
|
const toonNummer = velden.includes("karnummer");
|
|
327
371
|
if (!toonNummer && !info.length && !bundelTekst)
|
|
328
372
|
return "";
|
|
@@ -344,6 +388,28 @@ function karKop(s, kar, velden, bundelTekst = "") {
|
|
|
344
388
|
.map((t) => `<span>${esc(t)}</span>`)
|
|
345
389
|
.join("")}</span></div>`;
|
|
346
390
|
}
|
|
391
|
+
function cartRegelsHtml(kar) {
|
|
392
|
+
const regels = arr(kar.cart_items);
|
|
393
|
+
if (!regels.length)
|
|
394
|
+
return "";
|
|
395
|
+
const velden = [
|
|
396
|
+
["artikelnummer", "Artikel"],
|
|
397
|
+
["artikelnaam", "Omschrijving"],
|
|
398
|
+
["omschrijving", "Omschrijving"],
|
|
399
|
+
["aantal", "Aantal"],
|
|
400
|
+
["eenheid", "Eenheid"],
|
|
401
|
+
];
|
|
402
|
+
const rijen = regels.map((regel) => {
|
|
403
|
+
const waarden = velden
|
|
404
|
+
.map(([sleutel]) => String(regel[sleutel] ?? "").trim())
|
|
405
|
+
.filter(Boolean);
|
|
406
|
+
const tekst = waarden.length
|
|
407
|
+
? waarden.map((waarde) => `<span>${esc(waarde)}</span>`).join(" ")
|
|
408
|
+
: Object.values(regel).map((waarde) => `<span>${esc(String(waarde ?? ""))}</span>`).join(" ");
|
|
409
|
+
return `<div class="cartregel">${tekst}</div>`;
|
|
410
|
+
});
|
|
411
|
+
return `<div class="cartregels"><div class="vet">Karregels</div>${rijen.join("")}</div>`;
|
|
412
|
+
}
|
|
347
413
|
function laagKop(s, laag, velden, compact, bundelTekst = "") {
|
|
348
414
|
const tekst = optS(s, "koptekst", "Laag {laag}").replace(/\{laag\}/g, String(laag.laagnummer));
|
|
349
415
|
const info = [];
|
|
@@ -367,14 +433,16 @@ function bundelMelding(tekst) {
|
|
|
367
433
|
return `<div class="bundel">${esc(tekst)}</div>`;
|
|
368
434
|
}
|
|
369
435
|
function karBlok(s, kar, lagen, zoek, omkaderd, aantal = 1) {
|
|
370
|
-
//
|
|
371
|
-
|
|
436
|
+
// Alleen bij écht samengevoegde karren vervalt het unieke karnummer; een
|
|
437
|
+
// losse kar (bijv. met eigen Engine-barcode) houdt zijn eigen gegevens.
|
|
438
|
+
const bundelt = opt(s, "bundel_identiek", true) !== false && aantal > 1;
|
|
372
439
|
const velden = lijst(s, "velden").filter((v) => !(bundelt && (v === "karnummer" || v === "barcode" || v === "qrcode")));
|
|
373
440
|
const lagenSectie = zoek("lagen");
|
|
374
441
|
const productSectie = zoek("producten");
|
|
375
442
|
const fustSectie = zoek("fusten");
|
|
376
443
|
const lagenModus = lagenSectie ? optS(lagenSectie, "modus", "apart") : "geen";
|
|
377
444
|
let inhoud = karKop(s, kar, velden, karBundelTekst(aantal));
|
|
445
|
+
inhoud += cartRegelsHtml(kar);
|
|
378
446
|
if (lagen.length && lagenSectie && lagenModus !== "geen") {
|
|
379
447
|
const lVelden = lijst(lagenSectie, "velden");
|
|
380
448
|
const toonProducten = lagenModus !== "nummer" && lagenModus !== "regel";
|
|
@@ -392,6 +460,10 @@ function karBlok(s, kar, lagen, zoek, omkaderd, aantal = 1) {
|
|
|
392
460
|
}
|
|
393
461
|
if (fustSectie && optS(fustSectie, "modus", "totaal") === "kar")
|
|
394
462
|
inhoud += fustTabel(lijst(fustSectie, "velden"), lagen.flatMap((l) => l.fusten), `Fusten kar ${kar.karnummer}`);
|
|
463
|
+
// Een volle-karpakbon kan meerdere karren/pagina's bevatten. De barcode
|
|
464
|
+
// hoort daarom in het eigen karblok en niet één keer onderaan de serie.
|
|
465
|
+
if (kar.barcode)
|
|
466
|
+
inhoud += barcodeHtml(kar.barcode);
|
|
395
467
|
return `<div class="karblok${omkaderd ? " omkaderd" : ""}">${inhoud}</div>`;
|
|
396
468
|
}
|
|
397
469
|
const FUST_KOLOM_LABELS = {
|
|
@@ -558,7 +630,7 @@ function sectieHtml(s, order, zoek, opties) {
|
|
|
558
630
|
return titelHtml(s) + body;
|
|
559
631
|
}
|
|
560
632
|
case "pickmethode":
|
|
561
|
-
if (optS(s, "methode", "kar")
|
|
633
|
+
if (optS(s, "methode", "kar") === "kar")
|
|
562
634
|
return "";
|
|
563
635
|
return fustVerzamellijstHtml(order, s);
|
|
564
636
|
case "karren":
|
|
@@ -591,8 +663,11 @@ function sectieHtml(s, order, zoek, opties) {
|
|
|
591
663
|
return `<div class="${opt(s, "kader", false) ? "kader" : ""}" style="font-size:${Number(opt(s, "fontgrootte", 9))}pt">${esc(vulVeldcodes(optS(s, "tekst", ""), order))}</div>`;
|
|
592
664
|
case "qrcode":
|
|
593
665
|
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>`;
|
|
594
|
-
case "barcode":
|
|
595
|
-
|
|
666
|
+
case "barcode": {
|
|
667
|
+
// Alleen de Engine-waarde mag op de pakbon komen; nooit een lokaal
|
|
668
|
+
// gegenereerde orderwaarde of een layout-default als barcode gebruiken.
|
|
669
|
+
return barcodeHtml(pakbonBarcode(order), Number(opt(s, "hoogte", 15)));
|
|
670
|
+
}
|
|
596
671
|
case "afbeelding": {
|
|
597
672
|
const url = optS(s, "url", "");
|
|
598
673
|
return url
|
|
@@ -610,20 +685,36 @@ export function renderPakbonBody(config, order, opties = {}) {
|
|
|
610
685
|
const ctx = conditieContext(order);
|
|
611
686
|
const alleSecties = arr(cfg.secties);
|
|
612
687
|
const methode = pickMethode(alleSecties);
|
|
613
|
-
const
|
|
688
|
+
const variant = pakbonLayoutVariant(opties.layoutVariant);
|
|
689
|
+
const secties = alleSecties.filter((s) => sectieZichtbaar(s, ctx) && !sectieOnderdruktVoorVariant(s.type, methode, variant));
|
|
690
|
+
const heeftKarBarcodes = secties.some((s) => s.type === "karren") &&
|
|
691
|
+
(Array.isArray(order.karren) ? order.karren : []).some((kar) => Boolean(String(kar.barcode ?? "").trim()));
|
|
614
692
|
const zoek = (t) => secties.find((s) => s.type === t);
|
|
615
693
|
const opts = { ...opties, logoId: opties.logoId ?? cfg.logo?.logoId ?? null };
|
|
616
|
-
const
|
|
694
|
+
const variantKop = variant
|
|
695
|
+
? `<div class="pakbonvariant">${variant === "full_cart" ? "HELE KAR" : "DEELKAR"}</div>`
|
|
696
|
+
: "";
|
|
697
|
+
const kop = variantKop + documentLogoHtml(cfg.logo, opts);
|
|
617
698
|
const totalenSectie = zoek("totalen");
|
|
618
699
|
const totalenElkePagina = !!totalenSectie && opt(totalenSectie, "elke_pagina", false) === true;
|
|
619
700
|
const inhoud = secties
|
|
620
701
|
.filter((s) => !(s.type === "totalen" && totalenElkePagina))
|
|
702
|
+
.filter((s) => !(s.type === "barcode" && heeftKarBarcodes))
|
|
621
703
|
.map((s) => s.type === "karren" ? karrenHtml(s, order, zoek) : blokFrame(s, sectieHtml(s, order, zoek, opts)))
|
|
622
704
|
.join("");
|
|
705
|
+
const pickSectie = alleSecties.find((s) => s.type === "pickmethode");
|
|
706
|
+
const pickInstellingen = pickSectie ?? alleSecties[0];
|
|
707
|
+
// Elke pakbon krijgt de Engine-barcode wanneer die is aangeleverd. Een
|
|
708
|
+
// expliciet barcodeblok rendert hem zelf; anders voegen we hem één keer
|
|
709
|
+
// automatisch toe. Zonder Engine-barcode wordt nooit een waarde verzonnen.
|
|
710
|
+
const automatischeBarcode = !heeftKarBarcodes && !secties.some((s) => s.type === "barcode");
|
|
711
|
+
const barcode = automatischeBarcode
|
|
712
|
+
? barcodeHtml(pakbonBarcode(order), Number(pickInstellingen ? opt(pickInstellingen, "barcodeHoogte", 15) : 15))
|
|
713
|
+
: "";
|
|
623
714
|
const voet = totalenElkePagina && totalenSectie
|
|
624
715
|
? `<div class="paginavoet">${sectieHtml(totalenSectie, order, zoek, opts)}</div>`
|
|
625
716
|
: "";
|
|
626
|
-
return kop + inhoud + voet;
|
|
717
|
+
return kop + inhoud + (barcode ? `<div class="blok">${barcode}</div>` : "") + voet;
|
|
627
718
|
}
|
|
628
719
|
/** Het documentlogo bovenaan de pakbon. */
|
|
629
720
|
function documentLogoHtml(logoCfg, opties) {
|
|
@@ -647,60 +738,61 @@ export function renderPakbonDocument(config, order, opties = {}) {
|
|
|
647
738
|
// leest dit uit de meta, zodat de wachtrij niets extra hoeft door te geven.
|
|
648
739
|
const totalen = arr(leesPakbonConfig(config).secties).find((s) => s.type === "totalen");
|
|
649
740
|
const paginanummers = !!totalen && opt(totalen, "toon_paginanummer", false) === true;
|
|
650
|
-
return `<!doctype html><html lang="nl"><head><meta charset="utf-8" />
|
|
651
|
-
<meta name="pdf-paginanummers" content="${paginanummers ? "ja" : "nee"}" />
|
|
652
|
-
<title>${esc(opties.titel ?? "Pakbon")}</title>
|
|
653
|
-
|
|
654
|
-
<style>
|
|
655
|
-
@page { size: ${b}mm ${h}mm; margin: ${marge}mm; }
|
|
656
|
-
* { box-sizing: border-box; }
|
|
657
|
-
html, body { margin: 0; padding: 0; }
|
|
658
|
-
body { font-family: ${font}; font-size: ${grootte}pt; color: #111827; line-height: 1.35; }
|
|
659
|
-
.
|
|
660
|
-
.
|
|
661
|
-
.
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
.
|
|
665
|
-
.kv
|
|
666
|
-
.kv .
|
|
667
|
-
.
|
|
668
|
-
.
|
|
669
|
-
|
|
670
|
-
table.regels
|
|
671
|
-
table.regels.vast
|
|
672
|
-
table.regels
|
|
673
|
-
table.regels th
|
|
674
|
-
.
|
|
675
|
-
.
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
.
|
|
679
|
-
.
|
|
680
|
-
.
|
|
681
|
-
.
|
|
682
|
-
.
|
|
683
|
-
.karblok
|
|
684
|
-
.
|
|
685
|
-
.
|
|
686
|
-
.
|
|
687
|
-
.
|
|
688
|
-
.laagkop
|
|
689
|
-
.
|
|
690
|
-
.
|
|
691
|
-
.
|
|
692
|
-
.
|
|
693
|
-
.
|
|
694
|
-
.lijnveld
|
|
695
|
-
.
|
|
696
|
-
.
|
|
697
|
-
.
|
|
698
|
-
.
|
|
699
|
-
.
|
|
700
|
-
.
|
|
701
|
-
.
|
|
702
|
-
|
|
703
|
-
|
|
741
|
+
return `<!doctype html><html lang="nl"><head><meta charset="utf-8" />
|
|
742
|
+
<meta name="pdf-paginanummers" content="${paginanummers ? "ja" : "nee"}" />
|
|
743
|
+
<title>${esc(opties.titel ?? "Pakbon")}</title>
|
|
744
|
+
|
|
745
|
+
<style>
|
|
746
|
+
@page { size: ${b}mm ${h}mm; margin: ${marge}mm; }
|
|
747
|
+
* { box-sizing: border-box; }
|
|
748
|
+
html, body { margin: 0; padding: 0; }
|
|
749
|
+
body { font-family: ${font}; font-size: ${grootte}pt; color: #111827; line-height: 1.35; }
|
|
750
|
+
.pakbonvariant { border-top: 0.6mm solid #111827; border-bottom: 0.6mm solid #111827; padding: 2mm 1mm; margin-bottom: 2mm; text-align: center; font-size: 15pt; font-weight: 800; line-height: 1.1; }
|
|
751
|
+
.blok { margin-bottom: 2mm; }
|
|
752
|
+
.sectitel { font-size: 90%; font-weight: 600; text-transform: uppercase; color: #6b7280; margin-bottom: 0.6mm; }
|
|
753
|
+
.fustgroepkop { display: flex; justify-content: space-between; font-weight: 600; color: #111827; border-bottom: 0.45mm solid #111827; padding-bottom: 0.5mm; margin-bottom: 0.6mm; }
|
|
754
|
+
|
|
755
|
+
.vet { font-weight: 600; }
|
|
756
|
+
.kv { display: flex; justify-content: space-between; gap: 3mm; }
|
|
757
|
+
.kv .k { color: #6b7280; }
|
|
758
|
+
.kv .v { font-weight: 500; }
|
|
759
|
+
.grid2 { display: grid; grid-template-columns: 1fr 1fr; gap: 0 6mm; }
|
|
760
|
+
.kader { border: 0.25mm solid #d1d5db; border-radius: 1mm; padding: 1.5mm; }
|
|
761
|
+
table.regels { width: 100%; border-collapse: collapse; }
|
|
762
|
+
table.regels.vast { table-layout: fixed; }
|
|
763
|
+
table.regels.vast td, table.regels.vast th { overflow-wrap: anywhere; }
|
|
764
|
+
table.regels th, table.regels td { padding: 0.7mm 1mm; vertical-align: top; }
|
|
765
|
+
table.regels th { font-size: 90%; text-transform: uppercase; color: #6b7280; font-weight: 600; text-align: left; }
|
|
766
|
+
.rand { border: 0.2mm solid #d1d5db; }
|
|
767
|
+
.onder { border-bottom: 0.2mm solid #d1d5db; }
|
|
768
|
+
table.regels.compact th, table.regels.compact td { padding: 0.3mm 0.8mm; }
|
|
769
|
+
tr.zebra td { background: #f3f4f6; }
|
|
770
|
+
.plijst { margin: 0; padding-left: 4mm; }
|
|
771
|
+
.groep { margin-bottom: 1.5mm; }
|
|
772
|
+
.regelrij { display: flex; gap: 2mm; }
|
|
773
|
+
.tweekolom { display: grid; grid-template-columns: 1fr 1fr; gap: 3mm; }
|
|
774
|
+
.karblok { margin-bottom: 2mm; }
|
|
775
|
+
.karblok.omkaderd { border: 0.25mm solid #d1d5db; border-radius: 1mm; padding: 1.5mm; }
|
|
776
|
+
.karkop { display: flex; justify-content: space-between; align-items: center; padding: 0.8mm 1.5mm; border-radius: 1mm; margin-bottom: 1mm; }
|
|
777
|
+
.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%; }
|
|
778
|
+
.laag { margin-bottom: 1.5mm; }
|
|
779
|
+
.laagkop { display: flex; justify-content: space-between; font-weight: 500; border-bottom: 0.2mm solid #d1d5db; padding-bottom: 0.4mm; margin-bottom: 0.6mm; }
|
|
780
|
+
.laagkop.compact { border-bottom: none; }
|
|
781
|
+
.meta { display: flex; gap: 3mm; font-size: 88%; font-weight: 400; color: #4b5563; }
|
|
782
|
+
.totalen { display: flex; flex-wrap: wrap; gap: 5mm; border-top: 0.25mm solid #d1d5db; padding-top: 1.2mm; font-weight: 500; }
|
|
783
|
+
.paginavoet { position: fixed; bottom: 0; left: 0; right: 0; background: #fff; border-top: 0.35mm solid #9ca3af; padding-top: 1mm; }
|
|
784
|
+
.handtekening { margin-top: 3mm; }
|
|
785
|
+
.lijnveld { margin-top: 4mm; border-bottom: 0.25mm solid #6b7280; padding-bottom: 3mm; }
|
|
786
|
+
.lijnveld.hoog { padding-bottom: 10mm; }
|
|
787
|
+
.lijn { border-top: 0.25mm solid #d1d5db; margin: 1.5mm 0; }
|
|
788
|
+
.balk { height: 1mm; background: #374151; margin: 1.5mm 0; }
|
|
789
|
+
.qr { border: 0.25mm dashed #9ca3af; }
|
|
790
|
+
.barcode { display:flex; flex-direction:column; align-items:center; gap:0.8mm; } .barcode-bars { width:45mm; display:flex; background:#fff; }
|
|
791
|
+
.klein { font-size: 85%; color: #6b7280; }
|
|
792
|
+
.breekvoor { break-before: page; page-break-before: always; }
|
|
793
|
+
.heelhouden { break-inside: avoid; page-break-inside: avoid; }
|
|
794
|
+
img { max-width: 100%; }
|
|
795
|
+
</style></head>
|
|
704
796
|
<body>${body}</body></html>`;
|
|
705
797
|
}
|
|
706
798
|
/** Handig voor testen: het document met de voorbeeldgegevens. */
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { type PakbonLayoutVariant } from "./pakbonBuilder.js";
|
|
2
|
+
export interface PakbonScanRegel {
|
|
3
|
+
artikelnummer: string;
|
|
4
|
+
artikelnaam: string;
|
|
5
|
+
potmaat: string;
|
|
6
|
+
kleur: string;
|
|
7
|
+
fustsoort: string;
|
|
8
|
+
planten_per_fust: number;
|
|
9
|
+
aantal: number;
|
|
10
|
+
fust_aantal: number;
|
|
11
|
+
}
|
|
12
|
+
export interface PakbonScanInhoud {
|
|
13
|
+
barcode: string;
|
|
14
|
+
order_ref: string | null;
|
|
15
|
+
order_nummer: string | null;
|
|
16
|
+
klant_naam: string | null;
|
|
17
|
+
klant_nummer: string | null;
|
|
18
|
+
cart_id: string | null;
|
|
19
|
+
cart_label: string | null;
|
|
20
|
+
cart_index: number | null;
|
|
21
|
+
cart_total: number | null;
|
|
22
|
+
layout_variant: PakbonLayoutVariant | null;
|
|
23
|
+
layout_id: string | null;
|
|
24
|
+
print_job_id: string | null;
|
|
25
|
+
is_herprint: boolean;
|
|
26
|
+
regels: PakbonScanRegel[];
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Bouwt de scanregel uit de order zoals die op de pakbon is gerenderd.
|
|
30
|
+
* Bij een deelkar (`partial_order`) staan uitsluitend de fusten van die
|
|
31
|
+
* deelkar op de pakbon, dus ook uitsluitend die regels in de registratie.
|
|
32
|
+
*/
|
|
33
|
+
export declare function bouwPakbonScanInhoud(order: unknown, opties: {
|
|
34
|
+
barcode: unknown;
|
|
35
|
+
layoutVariant?: PakbonLayoutVariant | null;
|
|
36
|
+
layoutId?: string | null;
|
|
37
|
+
printJobId?: string | null;
|
|
38
|
+
isHerprint?: boolean;
|
|
39
|
+
}): PakbonScanInhoud | null;
|
|
40
|
+
/** Slaat de scaninhoud op; een herprint werkt dezelfde barcode bij. */
|
|
41
|
+
export declare function bewaarPakbonScanInhoud(db: {
|
|
42
|
+
from: (tabel: string) => any;
|
|
43
|
+
}, inhoud: PakbonScanInhoud | null): Promise<void>;
|
|
44
|
+
//# sourceMappingURL=pakbonScan.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pakbonScan.d.ts","sourceRoot":"","sources":["../../src/lib/pakbonScan.ts"],"names":[],"mappings":"AASA,OAAO,EAAqB,KAAK,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAE9E,MAAM,WAAW,eAAe;IAC9B,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,gBAAgB,EAAE,MAAM,CAAC;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,cAAc,EAAE,mBAAmB,GAAG,IAAI,CAAC;IAC3C,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,WAAW,EAAE,OAAO,CAAC;IACrB,MAAM,EAAE,eAAe,EAAE,CAAC;CAC3B;AAWD;;;;GAIG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,OAAO,EACd,MAAM,EAAE;IACN,OAAO,EAAE,OAAO,CAAC;IACjB,aAAa,CAAC,EAAE,mBAAmB,GAAG,IAAI,CAAC;IAC3C,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB,GACA,gBAAgB,GAAG,IAAI,CA8EzB;AAED,uEAAuE;AACvE,wBAAsB,sBAAsB,CAC1C,EAAE,EAAE;IAAE,IAAI,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,GAAG,CAAA;CAAE,EACpC,MAAM,EAAE,gBAAgB,GAAG,IAAI,GAC9B,OAAO,CAAC,IAAI,CAAC,CAMf"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// src/lib/pakbonScan.ts — registreert wat er achter een pakbonbarcode zit
|
|
3
|
+
// ----------------------------------------------------------------------------
|
|
4
|
+
// De barcodewaarde blijft eigendom van de Logistics Engine: deze module
|
|
5
|
+
// genereert of wijzigt nooit een barcode. Wel legt zij vast WAT er op de
|
|
6
|
+
// gedrukte pakbon staat, zodat een scanner daaruit een afleverbrief kan
|
|
7
|
+
// opbouwen: order, klant, kar en de gedrukte regels.
|
|
8
|
+
// ============================================================================
|
|
9
|
+
import { fustVerzamellijst } from "./pakbonBuilder.js";
|
|
10
|
+
function tekst(waarde) {
|
|
11
|
+
return waarde === undefined || waarde === null ? "" : String(waarde).trim();
|
|
12
|
+
}
|
|
13
|
+
function getal(waarde) {
|
|
14
|
+
const n = Number(waarde);
|
|
15
|
+
return Number.isFinite(n) && n !== 0 ? n : null;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Bouwt de scanregel uit de order zoals die op de pakbon is gerenderd.
|
|
19
|
+
* Bij een deelkar (`partial_order`) staan uitsluitend de fusten van die
|
|
20
|
+
* deelkar op de pakbon, dus ook uitsluitend die regels in de registratie.
|
|
21
|
+
*/
|
|
22
|
+
export function bouwPakbonScanInhoud(order, opties) {
|
|
23
|
+
const barcode = tekst(opties.barcode);
|
|
24
|
+
if (!barcode)
|
|
25
|
+
return null;
|
|
26
|
+
const o = (order && typeof order === "object" ? order : {});
|
|
27
|
+
const alleKarren = Array.isArray(o["karren"]) ? o["karren"] : [];
|
|
28
|
+
const cart = (o["cart"] && typeof o["cart"] === "object" ? o["cart"] : {});
|
|
29
|
+
const cartId = tekst(cart["cart_id"]);
|
|
30
|
+
// Een order kan meerdere karren bevatten. Voor een deelkar mag de scanner
|
|
31
|
+
// nooit de inhoud van de andere karren terugkrijgen: selecteer dus eerst op
|
|
32
|
+
// het door de Engine meegestuurde cart_id.
|
|
33
|
+
const passendeKarren = cartId
|
|
34
|
+
? alleKarren.filter((kar) => tekst(kar["cart_id"]) === cartId)
|
|
35
|
+
: alleKarren;
|
|
36
|
+
// Bij partial_order is cart_items de autoritatieve inhoud van deze deelkar.
|
|
37
|
+
// Alleen als die lijst ontbreekt, gebruiken we de passende karregels.
|
|
38
|
+
const cartItems = Array.isArray(cart["cart_items"]) ? cart["cart_items"] : [];
|
|
39
|
+
const karren = passendeKarren;
|
|
40
|
+
const eersteKar = (karren[0] ?? {});
|
|
41
|
+
const regels = [];
|
|
42
|
+
if (opties.layoutVariant !== "partial_order" || !cartItems.length) {
|
|
43
|
+
for (const groep of fustVerzamellijst(karren)) {
|
|
44
|
+
for (const pr of groep.producten) {
|
|
45
|
+
const bestaand = regels.find((r) => r.artikelnummer === pr.artikelnummer && r.fustsoort === (pr.fustsoort || groep.fusttype));
|
|
46
|
+
if (bestaand) {
|
|
47
|
+
bestaand.aantal += Number(pr.aantal) || 0;
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
regels.push({
|
|
51
|
+
artikelnummer: pr.artikelnummer,
|
|
52
|
+
artikelnaam: pr.artikelnaam,
|
|
53
|
+
potmaat: pr.potmaat,
|
|
54
|
+
kleur: pr.kleur,
|
|
55
|
+
fustsoort: pr.fustsoort || groep.fusttype,
|
|
56
|
+
planten_per_fust: Number(pr.planten_per_fust) || 0,
|
|
57
|
+
aantal: Number(pr.aantal) || 0,
|
|
58
|
+
fust_aantal: Number(groep.aantal) || 0,
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
// De Engine kan de deelkarinhoud direct als cart_items meesturen. Die lijst
|
|
64
|
+
// wint bij partial_order en bevat dan uitsluitend deze barcode-kar.
|
|
65
|
+
if (opties.layoutVariant === "partial_order" && cartItems.length) {
|
|
66
|
+
for (const item of cartItems) {
|
|
67
|
+
regels.push({
|
|
68
|
+
artikelnummer: tekst(item["artikelnummer"] ?? item["artikelcode"]),
|
|
69
|
+
artikelnaam: tekst(item["artikelnaam"] ?? item["product_name"] ?? item["omschrijving"]),
|
|
70
|
+
potmaat: tekst(item["potmaat"]),
|
|
71
|
+
kleur: tekst(item["kleur"]),
|
|
72
|
+
fustsoort: tekst(item["fustsoort"] ?? item["fusttype"]),
|
|
73
|
+
planten_per_fust: Number(item["planten_per_fust"] ?? 0) || 0,
|
|
74
|
+
aantal: Number(item["aantal"] ?? item["quantity"] ?? 0) || 0,
|
|
75
|
+
fust_aantal: Number(item["fust_aantal"] ?? item["aantal_fusten"] ?? 0) || 0,
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return {
|
|
80
|
+
barcode,
|
|
81
|
+
order_ref: tekst(o["order"]?.referentie) || null,
|
|
82
|
+
order_nummer: tekst(o["order"]?.nummer) || null,
|
|
83
|
+
klant_naam: tekst(o["klant"]?.naam) || null,
|
|
84
|
+
klant_nummer: tekst(o["klant"]?.nummer) || null,
|
|
85
|
+
cart_id: tekst(cart["cart_id"] ?? eersteKar["cart_id"]) || null,
|
|
86
|
+
cart_label: tekst(cart["cart_label"] ?? eersteKar["cart_label"]) || null,
|
|
87
|
+
cart_index: getal(cart["cart_index"] ?? eersteKar["cart_index"]),
|
|
88
|
+
cart_total: getal(cart["cart_total"] ?? eersteKar["cart_total"]),
|
|
89
|
+
layout_variant: opties.layoutVariant ?? null,
|
|
90
|
+
layout_id: opties.layoutId ?? null,
|
|
91
|
+
print_job_id: opties.printJobId ?? null,
|
|
92
|
+
is_herprint: opties.isHerprint === true,
|
|
93
|
+
regels,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
/** Slaat de scaninhoud op; een herprint werkt dezelfde barcode bij. */
|
|
97
|
+
export async function bewaarPakbonScanInhoud(db, inhoud) {
|
|
98
|
+
if (!inhoud)
|
|
99
|
+
return;
|
|
100
|
+
const { error } = await db
|
|
101
|
+
.from("pakbon_barcode_inhoud")
|
|
102
|
+
.upsert({ ...inhoud, updated_at: new Date().toISOString() }, { onConflict: "barcode" });
|
|
103
|
+
if (error)
|
|
104
|
+
throw new Error(error.message ?? "Opslaan van pakbon-scaninhoud mislukt");
|
|
105
|
+
}
|
|
@@ -12,6 +12,10 @@ export declare function controleerClient(db: PrintDb): Promise<string | null>;
|
|
|
12
12
|
export declare class GeenDocumentFout extends Error {
|
|
13
13
|
name: string;
|
|
14
14
|
}
|
|
15
|
+
/** De Engine heeft de taak gepland, maar de layout is nog niet aangeleverd. */
|
|
16
|
+
export declare class WachtOpLayoutFout extends Error {
|
|
17
|
+
name: string;
|
|
18
|
+
}
|
|
15
19
|
/**
|
|
16
20
|
* `ontwerpWint` = deze layout is een echt ontwerp van deze module (actief, geen
|
|
17
21
|
* Floriday-systeemlayout) en gaat daarom vóór meegestuurde HTML. Er is bewust
|
|
@@ -30,6 +34,8 @@ export type BronUitkomst = {
|
|
|
30
34
|
};
|
|
31
35
|
/** Zet een binnenkomend documenttype om naar de sleutel van deze module. */
|
|
32
36
|
export declare function normaliseerDocumenttype(type: unknown): string;
|
|
37
|
+
/** Het layout-id zoals de Engine het aanlevert (kolom of payload). */
|
|
38
|
+
export declare function leesEngineLayoutId(job: Job): string;
|
|
33
39
|
/**
|
|
34
40
|
* Haalt een kant-en-klare PDF van de opdracht op (klantstickers, standaard
|
|
35
41
|
* Floriday-documenten). Die gaat ongewijzigd naar de printer.
|
|
@@ -50,6 +56,7 @@ export interface WachtrijResultaat {
|
|
|
50
56
|
opgepakt: number;
|
|
51
57
|
verstuurd: number;
|
|
52
58
|
mislukt: number;
|
|
59
|
+
wachtend: number;
|
|
53
60
|
bijgewerkt: number;
|
|
54
61
|
}
|
|
55
62
|
/** Eén ronde: vastgelopen opdrachten opruimen, afdrukken en statussen bijwerken. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"printQueue.server.d.ts","sourceRoot":"","sources":["../../src/lib/printQueue.server.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"printQueue.server.d.ts","sourceRoot":"","sources":["../../src/lib/printQueue.server.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAwB5D,KAAK,GAAG,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AAE/B,mFAAmF;AACnF,MAAM,MAAM,OAAO,GAAG,cAAc,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;AAEpD;;;;GAIG;AACH,wBAAsB,gBAAgB,CAAC,EAAE,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAW1E;AAUD,2EAA2E;AAC3E,qBAAa,gBAAiB,SAAQ,KAAK;IAChC,IAAI,SAAsB;CACpC;AAED,+EAA+E;AAC/E,qBAAa,iBAAkB,SAAQ,KAAK;IACjC,IAAI,SAAuB;CACrC;AAED;;;;;GAKG;AACH,MAAM,MAAM,WAAW,GAAG;IAAE,MAAM,EAAE,GAAG,GAAG,IAAI,CAAC;IAAC,WAAW,EAAE,OAAO,CAAA;CAAE,CAAC;AAEvE,yEAAyE;AACzE,MAAM,MAAM,aAAa,GAAG,YAAY,GAAG,cAAc,GAAG,aAAa,GAAG,aAAa,CAAC;AAE1F,6EAA6E;AAC7E,MAAM,MAAM,YAAY,GAAG;IAAE,IAAI,EAAE,aAAa,CAAA;CAAE,CAAC;AAcnD,4EAA4E;AAC5E,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,CAI7D;AASD,sEAAsE;AACtE,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,GAAG,GAAG,MAAM,CAQnD;AA4LD;;;GAGG;AACH,wBAAsB,cAAc,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CA+BnF;AA8RD;;;;GAIG;AACH,wBAAsB,QAAQ,CAC5B,EAAE,EAAE,OAAO,EACX,GAAG,EAAE,GAAG,EACR,KAAK,EAAE,WAAW,EAClB,QAAQ,CAAC,EAAE,YAAY,GACtB,OAAO,CAAC,MAAM,CAAC,CA2OjB;AAGD;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAY1D;AAuWD,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,oFAAoF;AACpF,wBAAsB,eAAe,CAAC,EAAE,EAAE,OAAO,EAAE,GAAG,SAAI,GAAG,OAAO,CAAC,iBAAiB,CAAC,CA2CtF;AAED;;;;GAIG;AACH,wBAAsB,mBAAmB,CACvC,EAAE,EAAE,OAAO,EACX,GAAG,EAAE,GAAG,GACP,OAAO,CAAC;IAAE,KAAK,EAAE,WAAW,CAAC;IAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC,CA4B1D"}
|