@flowselections/layout-maker 1.32.0 → 1.38.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (115) hide show
  1. package/README.md +139 -139
  2. package/dist-lib/components/AfdrukKnop.d.ts +19 -19
  3. package/dist-lib/components/ComputersLijst.d.ts.map +1 -1
  4. package/dist-lib/components/ComputersLijst.js +5 -5
  5. package/dist-lib/components/DocumentenPage.d.ts.map +1 -1
  6. package/dist-lib/components/DocumentenPage.js +3 -2
  7. package/dist-lib/components/LayoutPrinterSelect.d.ts +8 -0
  8. package/dist-lib/components/LayoutPrinterSelect.d.ts.map +1 -0
  9. package/dist-lib/components/LayoutPrinterSelect.js +23 -0
  10. package/dist-lib/components/LayoutsPage.d.ts.map +1 -1
  11. package/dist-lib/components/LayoutsPage.js +16 -3
  12. package/dist-lib/components/NetwerkPrintersDialog.d.ts +7 -7
  13. package/dist-lib/components/PaginaDocument.d.ts +4 -1
  14. package/dist-lib/components/PaginaDocument.d.ts.map +1 -1
  15. package/dist-lib/components/PaginaDocument.js +10 -5
  16. package/dist-lib/components/PakbonBuilderPage.d.ts.map +1 -1
  17. package/dist-lib/components/PakbonBuilderPage.js +6 -2
  18. package/dist-lib/components/PakbonPreview.d.ts +4 -2
  19. package/dist-lib/components/PakbonPreview.d.ts.map +1 -1
  20. package/dist-lib/components/PakbonPreview.js +55 -12
  21. package/dist-lib/components/PakbonSectiesPanel.d.ts +2 -2
  22. package/dist-lib/components/PakbonSectiesPanel.d.ts.map +1 -1
  23. package/dist-lib/components/PakbonSectiesPanel.js +7 -2
  24. package/dist-lib/components/PdfMapAutoSaver.d.ts +6 -0
  25. package/dist-lib/components/PdfMapAutoSaver.d.ts.map +1 -0
  26. package/dist-lib/components/PdfMapAutoSaver.js +12 -0
  27. package/dist-lib/components/PdfMapGlobaleBanner.d.ts +12 -0
  28. package/dist-lib/components/PdfMapGlobaleBanner.d.ts.map +1 -0
  29. package/dist-lib/components/PdfMapGlobaleBanner.js +30 -0
  30. package/dist-lib/components/PdfMapToestemmingBanner.d.ts +7 -0
  31. package/dist-lib/components/PdfMapToestemmingBanner.d.ts.map +1 -0
  32. package/dist-lib/components/PdfMapToestemmingBanner.js +139 -0
  33. package/dist-lib/components/PrintQueuePage.d.ts.map +1 -1
  34. package/dist-lib/components/PrintQueuePage.js +42 -3
  35. package/dist-lib/components/PrintenPage.d.ts.map +1 -1
  36. package/dist-lib/components/PrintenPage.js +3 -2
  37. package/dist-lib/components/PrinterSetupWizard.d.ts.map +1 -1
  38. package/dist-lib/components/PrinterSetupWizard.js +28 -5
  39. package/dist-lib/components/PrintersRedirectPage.d.ts +1 -1
  40. package/dist-lib/components/PrintersRedirectPage.js +14 -14
  41. package/dist-lib/components/PrintplekDetailPage.d.ts.map +1 -1
  42. package/dist-lib/components/PrintplekDetailPage.js +30 -4
  43. package/dist-lib/components/PrintplekWizard.d.ts.map +1 -1
  44. package/dist-lib/components/PrintplekWizard.js +81 -3
  45. package/dist-lib/components/ProductiebonBuilderPage.d.ts.map +1 -1
  46. package/dist-lib/components/ProductiebonBuilderPage.js +3 -1
  47. package/dist-lib/index.d.ts +10 -2
  48. package/dist-lib/index.d.ts.map +1 -1
  49. package/dist-lib/index.js +13 -2
  50. package/dist-lib/integrations/supabase/auth-middleware.d.ts +1396 -727
  51. package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
  52. package/dist-lib/integrations/supabase/client.server.d.ts +1396 -727
  53. package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
  54. package/dist-lib/integrations/supabase/client.server.js +1 -1
  55. package/dist-lib/integrations/supabase/moduleClient.d.ts +24008 -0
  56. package/dist-lib/integrations/supabase/moduleClient.d.ts.map +1 -0
  57. package/dist-lib/integrations/supabase/moduleClient.js +29 -0
  58. package/dist-lib/integrations/supabase/types.d.ts +1403 -750
  59. package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
  60. package/dist-lib/lib/aiLayout.functions.d.ts +1396 -727
  61. package/dist-lib/lib/aiLayout.functions.d.ts.map +1 -1
  62. package/dist-lib/lib/aiLayout.server.js +9 -9
  63. package/dist-lib/lib/browserPrint.js +1 -1
  64. package/dist-lib/lib/code128.d.ts +5 -0
  65. package/dist-lib/lib/code128.d.ts.map +1 -0
  66. package/dist-lib/lib/code128.js +51 -0
  67. package/dist-lib/lib/htmlPdf.server.d.ts.map +1 -1
  68. package/dist-lib/lib/htmlPdf.server.js +101 -6
  69. package/dist-lib/lib/layoutPreview.d.ts.map +1 -1
  70. package/dist-lib/lib/layoutPreview.js +11 -8
  71. package/dist-lib/lib/orderData.server.d.ts.map +1 -1
  72. package/dist-lib/lib/orderData.server.js +123 -10
  73. package/dist-lib/lib/pakbonBuilder.d.ts +28 -2
  74. package/dist-lib/lib/pakbonBuilder.d.ts.map +1 -1
  75. package/dist-lib/lib/pakbonBuilder.js +69 -8
  76. package/dist-lib/lib/pakbonHtml.d.ts +14 -2
  77. package/dist-lib/lib/pakbonHtml.d.ts.map +1 -1
  78. package/dist-lib/lib/pakbonHtml.js +155 -63
  79. package/dist-lib/lib/pakbonScan.d.ts +44 -0
  80. package/dist-lib/lib/pakbonScan.d.ts.map +1 -0
  81. package/dist-lib/lib/pakbonScan.js +105 -0
  82. package/dist-lib/lib/pdfJobSaver.d.ts +20 -0
  83. package/dist-lib/lib/pdfJobSaver.d.ts.map +1 -0
  84. package/dist-lib/lib/pdfJobSaver.js +173 -0
  85. package/dist-lib/lib/pdfMap.d.ts +48 -0
  86. package/dist-lib/lib/pdfMap.d.ts.map +1 -0
  87. package/dist-lib/lib/pdfMap.js +219 -0
  88. package/dist-lib/lib/pdfPrinter.d.ts +9 -0
  89. package/dist-lib/lib/pdfPrinter.d.ts.map +1 -0
  90. package/dist-lib/lib/pdfPrinter.js +34 -0
  91. package/dist-lib/lib/previewPdf.d.ts.map +1 -1
  92. package/dist-lib/lib/previewPdf.js +41 -9
  93. package/dist-lib/lib/printApi.d.ts.map +1 -1
  94. package/dist-lib/lib/printApi.js +9 -1
  95. package/dist-lib/lib/printQueue.server.d.ts +7 -0
  96. package/dist-lib/lib/printQueue.server.d.ts.map +1 -1
  97. package/dist-lib/lib/printQueue.server.js +346 -52
  98. package/dist-lib/lib/printService.functions.d.ts +5365 -2689
  99. package/dist-lib/lib/printService.functions.d.ts.map +1 -1
  100. package/dist-lib/lib/productiebonHtml.js +15 -15
  101. package/dist-lib/lib/profielHtml.js +18 -18
  102. package/dist-lib/lib/supabaseAdmin.server.d.ts +5 -5
  103. package/dist-lib/lib/supabaseAdmin.server.js +43 -43
  104. package/dist-lib/lib/supabaseClient.d.ts +1 -3
  105. package/dist-lib/lib/supabaseClient.d.ts.map +1 -1
  106. package/dist-lib/lib/supabaseClient.js +4 -8
  107. package/dist-lib/lib/testpagina.js +3 -3
  108. package/dist-lib/lib/testprint.js +4 -4
  109. package/dist-lib/lib/useDocumentData.d.ts +10 -0
  110. package/dist-lib/lib/useDocumentData.d.ts.map +1 -1
  111. package/dist-lib/lib/useDocumentData.js +19 -0
  112. package/dist-lib/lib/voorbeeldPrint.d.ts +4 -0
  113. package/dist-lib/lib/voorbeeldPrint.d.ts.map +1 -1
  114. package/dist-lib/lib/voorbeeldPrint.js +13 -2
  115. package/package.json +151 -151
@@ -212,7 +212,7 @@ export const SECTIE_REGISTER = [
212
212
  {
213
213
  type: "pickmethode",
214
214
  label: "Pickmethode",
215
- omschrijving: "Bepaalt de opbouw van de pakbon: per kar (met karren en lagen) of per fust (verzamellijst).",
215
+ omschrijving: "Bepaalt of orders per kar, per fust of op beide manieren worden gepickt.",
216
216
  opties: [
217
217
  {
218
218
  key: "methode",
@@ -222,8 +222,9 @@ export const SECTIE_REGISTER = [
222
222
  opties: [
223
223
  { value: "kar", label: "Order picken per kar" },
224
224
  { value: "fusten", label: "Order picken per fusten (zelf karindeling maken)" },
225
+ { value: "beide", label: "Order picken per kar én per fusten (karren + verzamellijst)" },
225
226
  ],
226
- hint: "Bij picken per fusten vervallen de blokken Karren, Lagen, Producten en Fusten en toont de pakbon een fustverzamellijst.",
227
+ hint: "Bij picken per fusten vervallen de blokken Karren, Lagen, Producten en Fusten. Bij de gecombineerde keuze bepaalt de Engine per opdracht: full_cart toont de karindeling, partial_order alleen de te verzamelen fusten.",
227
228
  },
228
229
  {
229
230
  key: "kop",
@@ -249,6 +250,14 @@ export const SECTIE_REGISTER = [
249
250
  },
250
251
  { key: "toonFusthoogte", label: "Fusthoogte en belading tonen", type: "schakelaar", standaard: true },
251
252
  { key: "toonTotaal", label: "Totaal aantal fusten tonen", type: "schakelaar", standaard: true },
253
+ {
254
+ key: "toonBarcode",
255
+ label: "Barcode voor afleverbrief tonen",
256
+ type: "schakelaar",
257
+ standaard: true,
258
+ hint: "De barcode wordt uitsluitend overgenomen uit de Engine-opdracht.",
259
+ },
260
+ { key: "barcodeHoogte", label: "Barcodehoogte (mm)", type: "getal", standaard: 15 },
252
261
  ],
253
262
  },
254
263
  {
@@ -498,9 +507,9 @@ export const SECTIE_REGISTER = [
498
507
  {
499
508
  type: "barcode",
500
509
  label: "Barcode",
501
- omschrijving: "Barcode met orderwaarde.",
510
+ omschrijving: "Scanbare barcode. Bij Engine-pakbonnen komt de waarde uit de opdracht.",
502
511
  opties: [
503
- { key: "waarde", label: "Inhoud", type: "tekst", standaard: "{{order.nummer}}" },
512
+ { key: "waarde", label: "Inhoud", type: "tekst", standaard: "{{barcode}}" },
504
513
  { key: "hoogte", label: "Hoogte (mm)", type: "getal", standaard: 15 },
505
514
  ],
506
515
  },
@@ -572,11 +581,20 @@ function migreerPickmethode(secties) {
572
581
  const karIndex = secties.findIndex((s) => s.type === "karren");
573
582
  const oud = karIndex >= 0 ? String(secties[karIndex].opties?.["pickmethode"] ?? "kar") : "kar";
574
583
  const blok = nieuweSectie("pickmethode");
575
- blok.opties["methode"] = oud === "fusten" ? "fusten" : "kar";
584
+ blok.opties["methode"] = oud === "fusten" || oud === "beide" ? oud : "kar";
576
585
  const kopie = [...secties];
577
586
  kopie.splice(karIndex >= 0 ? karIndex : kopie.length, 0, blok);
578
587
  return kopie;
579
588
  }
589
+ /** Normaliseert alleen de twee varianten uit het gedeelde Engine-contract. */
590
+ export function pakbonLayoutVariant(value) {
591
+ const variant = String(value ?? "").trim().toLowerCase();
592
+ if (variant === "full_cart")
593
+ return "full_cart";
594
+ if (variant === "partial_order")
595
+ return "partial_order";
596
+ return null;
597
+ }
580
598
  /** Actieve pickmethode van een pakbonconfiguratie. */
581
599
  export function pickMethode(secties) {
582
600
  const lijst = Array.isArray(secties) ? secties : [];
@@ -584,13 +602,31 @@ export function pickMethode(secties) {
584
602
  const waarde = blok
585
603
  ? String(blok.opties?.["methode"] ?? "kar")
586
604
  : String(lijst.find((s) => s.type === "karren")?.opties?.["pickmethode"] ?? "kar");
587
- return waarde === "fusten" ? "fusten" : "kar";
605
+ if (waarde === "fusten")
606
+ return "fusten";
607
+ if (waarde === "beide")
608
+ return "beide";
609
+ return "kar";
588
610
  }
589
611
  const KAR_SECTIES = ["karren", "lagen", "producten", "fusten"];
590
612
  /** Blokken die bij de gekozen pickmethode niet van toepassing zijn. */
591
613
  export function sectieOnderdrukt(type, methode) {
592
614
  return methode === "fusten" && KAR_SECTIES.includes(type);
593
615
  }
616
+ /**
617
+ * Blokken die de Engine-variant weglaat. Zonder variant blijft de editor/het
618
+ * losse voorbeeld compleet; een echte Engine-opdracht wordt in de wachtrij
619
+ * geblokkeerd totdat de variant aanwezig is.
620
+ */
621
+ export function sectieOnderdruktVoorVariant(type, methode, variant) {
622
+ if (sectieOnderdrukt(type, methode))
623
+ return true;
624
+ if (methode !== "beide" || !variant)
625
+ return false;
626
+ if (variant === "full_cart")
627
+ return type === "pickmethode";
628
+ return type === "pickmethode" ? false : [...KAR_SECTIES, "orderregels"].includes(type);
629
+ }
594
630
  const p = (nr, naam, potmaat, aantal, locatie, groep, kleur) => ({
595
631
  artikelnummer: nr,
596
632
  artikelnaam: naam,
@@ -651,6 +687,7 @@ export const DEMO_ORDER = {
651
687
  karren: [
652
688
  {
653
689
  karnummer: 5,
690
+ barcode: "VOORBEELD-KAR-05",
654
691
  route: "Route Zuid",
655
692
  chauffeur: "J. Bakker",
656
693
  gewicht: "142 kg",
@@ -687,6 +724,7 @@ export const DEMO_ORDER = {
687
724
  // ze samengevoegd tot één blok met "Deze kar 3× pakken".
688
725
  ...[6, 7, 8].map((nr) => ({
689
726
  karnummer: nr,
727
+ barcode: `VOORBEELD-KAR-${String(nr).padStart(2, "0")}`,
690
728
  route: "Route Zuid",
691
729
  chauffeur: "J. Bakker",
692
730
  gewicht: "118 kg",
@@ -813,8 +851,10 @@ function laagVingerafdruk(l) {
813
851
  return [l.belading, l.potmaat, l.gewicht, l.opmerking, producten, fusten].join("#");
814
852
  }
815
853
  function karVingerafdruk(k) {
816
- // karnummer bewust niet meegenomen: alleen de indeling telt
817
- return [k.route, k.chauffeur, k.gewicht, k.gedeeld ? "gedeeld" : "", (k.lagen ?? []).map(laagVingerafdruk).join("~")].join("#");
854
+ // karnummer bewust niet meegenomen: alleen de indeling telt. Een kar met een
855
+ // eigen barcode of cart_id van de Engine is wél uniek en wordt nooit gebundeld.
856
+ const uniek = String(k.barcode ?? "") || String(k.cart_id ?? "") || String(k.cart_label ?? "");
857
+ return [uniek, k.route, k.chauffeur, k.gewicht, k.gedeeld ? "gedeeld" : "", (k.lagen ?? []).map(laagVingerafdruk).join("~")].join("#");
818
858
  }
819
859
  /** Groepeert identieke karren. Bij `aan = false` blijft elke kar los. */
820
860
  export function groepeerIdentiekeKarren(karren, aan) {
@@ -928,3 +968,24 @@ export function fustVerzamellijst(karren) {
928
968
  }
929
969
  return [...map.values()].sort((a, b) => a.fusttype.localeCompare(b.fusttype));
930
970
  }
971
+ /**
972
+ * Voorbeeld van een deelkar (niet-volle kar): alleen een deel van de fusten,
973
+ * zonder karindeling. Uitsluitend bedoeld voor het voorbeeld in de builder —
974
+ * echte opdrachten krijgen hun gegevens van de Logistics Engine.
975
+ */
976
+ export function deelkarVoorbeeldOrder(order = DEMO_ORDER) {
977
+ const eerste = order.karren[0];
978
+ const laag = eerste?.lagen?.[0];
979
+ if (!eerste || !laag)
980
+ return order;
981
+ return {
982
+ ...order,
983
+ karren: [
984
+ {
985
+ ...eerste,
986
+ gedeeld: true,
987
+ lagen: [{ ...laag, producten: laag.producten.slice(0, 1), fusten: laag.fusten?.slice(0, 1) ?? [] }],
988
+ },
989
+ ],
990
+ };
991
+ }
@@ -1,6 +1,16 @@
1
1
  import { type DemoOrder, type PakbonConfig } from "./pakbonBuilder.js";
2
- /** Gegevensvorm van een pakbon. Gelijk aan de vorm van het live voorbeeld. */
3
- export type PakbonOrder = DemoOrder;
2
+ /** Gegevensvorm van een pakbon. Engine-metadata blijft naast ordergegevens beschikbaar. */
3
+ export type PakbonOrder = DemoOrder & {
4
+ barcode?: string;
5
+ cart?: {
6
+ cart_label?: string;
7
+ cart_index?: number;
8
+ cart_total?: number;
9
+ cart_id?: string;
10
+ cart_items?: unknown[];
11
+ barcode?: string;
12
+ };
13
+ };
4
14
  export interface PakbonLogo {
5
15
  id: string;
6
16
  naam?: string;
@@ -20,6 +30,8 @@ export interface PakbonRenderOpties {
20
30
  logos?: PakbonLogo[];
21
31
  /** Documentlogo: gebruikt wanneer een logoblok zelf geen logo heeft. */
22
32
  logoId?: string | null;
33
+ /** Variant die uitsluitend door de Logistics Engine wordt bepaald. */
34
+ layoutVariant?: string | null;
23
35
  }
24
36
  export declare function esc(v: unknown): string;
25
37
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"pakbonHtml.d.ts","sourceRoot":"","sources":["../../src/lib/pakbonHtml.ts"],"names":[],"mappings":"AAUA,OAAO,EAmBL,KAAK,SAAS,EAEd,KAAK,YAAY,EAGlB,MAAM,iBAAiB,CAAC;AAGzB,8EAA8E;AAC9E,MAAM,MAAM,WAAW,GAAG,SAAS,CAAC;AAEpC,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,kDAAkD;IAClD,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAED,MAAM,WAAW,kBAAkB;IACjC,iDAAiD;IACjD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,0BAA0B;IAC1B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,oEAAoE;IACpE,KAAK,CAAC,EAAE,UAAU,EAAE,CAAC;IACrB,wEAAwE;IACxE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB;AAWD,wBAAgB,GAAG,CAAC,CAAC,EAAE,OAAO,GAAG,MAAM,CAKtC;AAQD;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,WAAW,CAgE3D;AA8oBD,oEAAoE;AACpE,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,YAAY,GAAG,OAAO,EAC9B,KAAK,EAAE,WAAW,EAClB,MAAM,GAAE,kBAAuB,GAC9B,MAAM,CA8BR;AAgBD,gEAAgE;AAChE,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,YAAY,GAAG,OAAO,EAC9B,KAAK,EAAE,WAAW,EAClB,MAAM,GAAE,kBAAuB,GAC9B,MAAM,CAmER;AAED,iEAAiE;AACjE,wBAAgB,oBAAoB,IAAI,WAAW,CAElD;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CACzB,MAAM,EAAE,YAAY,GAAG,OAAO,EAC9B,KAAK,EAAE,WAAW,EAClB,MAAM,GAAE,kBAAuB,GAC9B,IAAI,CAsCN;AAED;;;;;GAKG;AACH,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,YAAY,GAAG,OAAO,EAC9B,KAAK,EAAE,WAAW,EAClB,MAAM,GAAE,kBAAuB,GAC9B,OAAO,CAAC,IAAI,CAAC,CAUf"}
1
+ {"version":3,"file":"pakbonHtml.d.ts","sourceRoot":"","sources":["../../src/lib/pakbonHtml.ts"],"names":[],"mappings":"AAUA,OAAO,EAoBL,KAAK,SAAS,EAEd,KAAK,YAAY,EAGlB,MAAM,iBAAiB,CAAC;AAKzB,2FAA2F;AAC3F,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG;IACpC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE;QACL,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,UAAU,CAAC,EAAE,OAAO,EAAE,CAAC;QACvB,OAAO,CAAC,EAAE,MAAM,CAAC;KAClB,CAAC;CACH,CAAC;AAEF,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,kDAAkD;IAClD,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAED,MAAM,WAAW,kBAAkB;IACjC,iDAAiD;IACjD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,0BAA0B;IAC1B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,oEAAoE;IACpE,KAAK,CAAC,EAAE,UAAU,EAAE,CAAC;IACrB,wEAAwE;IACxE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,sEAAsE;IACtE,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B;AAWD,wBAAgB,GAAG,CAAC,CAAC,EAAE,OAAO,GAAG,MAAM,CAKtC;AAyBD;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,WAAW,CAiF3D;AAgrBD,oEAAoE;AACpE,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,YAAY,GAAG,OAAO,EAC9B,KAAK,EAAE,WAAW,EAClB,MAAM,GAAE,kBAAuB,GAC9B,MAAM,CA+CR;AAgBD,gEAAgE;AAChE,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,YAAY,GAAG,OAAO,EAC9B,KAAK,EAAE,WAAW,EAClB,MAAM,GAAE,kBAAuB,GAC9B,MAAM,CAoER;AAED,iEAAiE;AACjE,wBAAgB,oBAAoB,IAAI,WAAW,CAElD;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CACzB,MAAM,EAAE,YAAY,GAAG,OAAO,EAC9B,KAAK,EAAE,WAAW,EAClB,MAAM,GAAE,kBAAuB,GAC9B,IAAI,CAsCN;AAED;;;;;GAKG;AACH,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,YAAY,GAAG,OAAO,EAC9B,KAAK,EAAE,WAAW,EAClB,MAAM,GAAE,kBAAuB,GAC9B,OAAO,CAAC,IAAI,CAAC,CAUf"}
@@ -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, sectieOnderdrukt, laagBundelTekst, leesPakbonConfig, sectieZichtbaar, sorteerProducten, totalen, vulVeldcodes, } from "./pakbonBuilder.js";
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
- // Bij samenvoegen van identieke karren vervalt het unieke karnummer.
371
- const bundelt = opt(s, "bundel_identiek", true) !== false;
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") !== "fusten")
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
- return `<div><div class="streepjes" style="height:${Number(opt(s, "hoogte", 15))}mm"></div><div>${esc(vulVeldcodes(optS(s, "waarde", ""), order))}</div></div>`;
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 secties = alleSecties.filter((s) => sectieZichtbaar(s, ctx) && !sectieOnderdrukt(s.type, methode));
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 kop = documentLogoHtml(cfg.logo, opts);
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
- .blok { margin-bottom: 2mm; }
660
- .sectitel { font-size: 90%; font-weight: 600; text-transform: uppercase; color: #6b7280; margin-bottom: 0.6mm; }
661
- .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; }
662
-
663
- .vet { font-weight: 600; }
664
- .kv { display: flex; justify-content: space-between; gap: 3mm; }
665
- .kv .k { color: #6b7280; }
666
- .kv .v { font-weight: 500; }
667
- .grid2 { display: grid; grid-template-columns: 1fr 1fr; gap: 0 6mm; }
668
- .kader { border: 0.25mm solid #d1d5db; border-radius: 1mm; padding: 1.5mm; }
669
- table.regels { width: 100%; border-collapse: collapse; }
670
- table.regels.vast { table-layout: fixed; }
671
- table.regels.vast td, table.regels.vast th { overflow-wrap: anywhere; }
672
- table.regels th, table.regels td { padding: 0.7mm 1mm; vertical-align: top; }
673
- table.regels th { font-size: 90%; text-transform: uppercase; color: #6b7280; font-weight: 600; text-align: left; }
674
- .rand { border: 0.2mm solid #d1d5db; }
675
- .onder { border-bottom: 0.2mm solid #d1d5db; }
676
- table.regels.compact th, table.regels.compact td { padding: 0.3mm 0.8mm; }
677
- tr.zebra td { background: #f3f4f6; }
678
- .plijst { margin: 0; padding-left: 4mm; }
679
- .groep { margin-bottom: 1.5mm; }
680
- .regelrij { display: flex; gap: 2mm; }
681
- .tweekolom { display: grid; grid-template-columns: 1fr 1fr; gap: 3mm; }
682
- .karblok { margin-bottom: 2mm; }
683
- .karblok.omkaderd { border: 0.25mm solid #d1d5db; border-radius: 1mm; padding: 1.5mm; }
684
- .karkop { display: flex; justify-content: space-between; align-items: center; padding: 0.8mm 1.5mm; border-radius: 1mm; margin-bottom: 1mm; }
685
- .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%; }
686
- .laag { margin-bottom: 1.5mm; }
687
- .laagkop { display: flex; justify-content: space-between; font-weight: 500; border-bottom: 0.2mm solid #d1d5db; padding-bottom: 0.4mm; margin-bottom: 0.6mm; }
688
- .laagkop.compact { border-bottom: none; }
689
- .meta { display: flex; gap: 3mm; font-size: 88%; font-weight: 400; color: #4b5563; }
690
- .totalen { display: flex; flex-wrap: wrap; gap: 5mm; border-top: 0.25mm solid #d1d5db; padding-top: 1.2mm; font-weight: 500; }
691
- .paginavoet { position: fixed; bottom: 0; left: 0; right: 0; background: #fff; border-top: 0.35mm solid #9ca3af; padding-top: 1mm; }
692
- .handtekening { margin-top: 3mm; }
693
- .lijnveld { margin-top: 4mm; border-bottom: 0.25mm solid #6b7280; padding-bottom: 3mm; }
694
- .lijnveld.hoog { padding-bottom: 10mm; }
695
- .lijn { border-top: 0.25mm solid #d1d5db; margin: 1.5mm 0; }
696
- .balk { height: 1mm; background: #374151; margin: 1.5mm 0; }
697
- .qr { border: 0.25mm dashed #9ca3af; }
698
- .streepjes { width: 45mm; background: repeating-linear-gradient(to right, #111827 0 2px, transparent 2px 5px); }
699
- .klein { font-size: 85%; color: #6b7280; }
700
- .breekvoor { break-before: page; page-break-before: always; }
701
- .heelhouden { break-inside: avoid; page-break-inside: avoid; }
702
- img { max-width: 100%; }
703
- </style></head>
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"}