Initialer Commit

This commit is contained in:
2026-06-09 22:13:00 +02:00
commit 1d8ca0b908
68 changed files with 4374 additions and 0 deletions

View File

@@ -0,0 +1,58 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Lieferschein HTML Tabelle</title>
<style>
@page { size: A4; margin: 14mm; }
body { font-family: Arial, sans-serif; font-size: 10pt; color: #111; }
h1 { font-size: 18pt; margin: 0 0 8mm 0; }
.header { display: flex; justify-content: space-between; align-items: flex-start; }
.box { border: 1px solid #999; padding: 3mm; margin-bottom: 5mm; }
table { width: 100%; border-collapse: collapse; table-layout: fixed; }
th, td { border: 1px solid #777; padding: 2mm; vertical-align: top; }
th { background: #eeeeee; font-weight: bold; }
.right { text-align: right; }
.center { text-align: center; }
.barcode { width: 42mm; height: 12mm; object-fit: contain; }
.qr { width: 28mm; height: 28mm; }
</style>
</head>
<body>
<div class="header">
<div>
<h1>Lieferschein</h1>
<div><b>Nr.:</b> {{text:deliveryNumber}}</div>
<div><b>Datum:</b> {{text:deliveryDate}}</div>
</div>
<img class="qr" alt="QR" src="{{qr:deliveryNumber}}">
</div>
<div class="box"><b>Kunde:</b> {{text:customer.name}}</div>
<table>
<thead>
<tr>
<th style="width:10mm">Pos.</th>
<th style="width:32mm">Artikel</th>
<th>Bezeichnung</th>
<th style="width:18mm">Menge</th>
<th style="width:38mm">Seriennummer</th>
<th style="width:45mm">Barcode</th>
</tr>
</thead>
<tbody>
{{#each:items}}
<tr>
<td class="center">{{text:pos}}</td>
<td>{{text:partNumber}}</td>
<td>{{text:description}}</td>
<td class="right">{{text:quantity}}</td>
<td>{{text:serialNumber}}</td>
<td class="center"><img class="barcode" alt="Barcode" src="{{barcode:CODE_128:serialNumber:360x90}}"></td>
</tr>
{{/each}}
</tbody>
</table>
</body>
</html>

View File

@@ -0,0 +1,74 @@
%PDF-1.4
%<25><><EFBFBD><EFBFBD> ReportLab Generated PDF document (opensource)
1 0 obj
<<
/F1 2 0 R /F2 3 0 R
>>
endobj
2 0 obj
<<
/BaseFont /Helvetica /Encoding /WinAnsiEncoding /Name /F1 /Subtype /Type1 /Type /Font
>>
endobj
3 0 obj
<<
/BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding /Name /F2 /Subtype /Type1 /Type /Font
>>
endobj
4 0 obj
<<
/Contents 8 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 7 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
5 0 obj
<<
/PageMode /UseNone /Pages 7 0 R /Type /Catalog
>>
endobj
6 0 obj
<<
/Author (\(anonymous\)) /CreationDate (D:20260608180457+00'00') /Creator (\(unspecified\)) /Keywords () /ModDate (D:20260608180457+00'00') /Producer (ReportLab PDF Library - \(opensource\))
/Subject (\(unspecified\)) /Title (\(anonymous\)) /Trapped /False
>>
endobj
7 0 obj
<<
/Count 1 /Kids [ 4 0 R ] /Type /Pages
>>
endobj
8 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1266
>>
stream
Gb"/'9lo&I&A@C2i2C8-gKe4WeQ^h)m^,a;`&dUTfW_L1Uo1RVSa!?HK%(TJ#u[04g)5_lBa?i6?!nKG#a.NbW;XE4PWGG8mscNM>"rlL0sI*OWje2>+!Xr3fn'SI=aA>dOhH^;DJZqS]9+eoMH#=36DtWQ9W3*2UDP*l';U+uUW/YLFIS`^lh`N@^Jg?"C@XufoeGIA8%Ee'@J#9f4kn\%j4'N6?,5gDdSD`LhnTc)]lq80k?)"rSS?UA$k]K+bhfEkO5G>Vc)[2K0E=/T,BjUnF^Et$Q\4/>T4b-#(Y+kEQh)h'<3KAM'4;(o&1MB>ph3C2&:*[(UUL1K5'7q/,dJ7i=]rI0L=M\%@+___9iAI(HJr-+gb!+k*LpYs"`9b^jagb#6(8(Hlr&gmH[:GF'1Y<nh@?H5h9;`,l#Em1U'N*u0\jNjqC>:Dn*>7I\(?L\&g3%XE%CBTI@'Zu)q$uFIQ7@-C8Ru5k>de9rS\k#A(c7I@XW-sT5O?u6kW(7;&bV7eC:7j,I1u2pM833XZI_bL0D)XpV.[0q/]FU^8,A,VYbFXSP=ag$BI`SU#];D59[1%kO*j[.;1==)o:bUZ].#hKCja)Sl%"T_7$]:C/M)iMEd=cUrF:5Zid&=440B-`.9l2WGF',!p9cU@V^'%.#X*ZU?I]D^lGqgOn!rAJh@sOK!)$42D\1ZABLPFjNs<X9.Vj8!DUrf/#o:c-k][K#1Qm)ndB_1AUlhZ00k]lc:[UUc:nu^7DN^Wj@)E\QU*e_)s[U!S8R=c).XmGOt20*_Jq,Z6;G&S%tGLea5G,`%mfKK082"k@/I$&MFqE8+iI2&EaZR:jis>HJRsk\6m4DF.2L_dZuBE&pBASTaVhDa\BO3TH8Cf^49inX4tJW[%D7*_&$tDtSj$+L2tJ8YAuHl"=D*@d04h!pLP<rSH!9b/Cj_D6PK]P-k22;hS8iA+QC3>XjHRiATHk[@j/i5Eb7$u9Gu)Bq,s1F_3T=:"Pl!sd`T-n-lK`N9:^_4&BKSBEBKXdt53=OTP^KGcV'mlo-;'gQm7njH&t+s)F<\&8/H^PHY`(:Jg-,>8-^bNsNU2k'H>%9]_Z?b5DbA5,o)1TNM\K_-DuY<uO=p3=s-7N1Id+IZHW4&u#WI+8f8SJJ%:"aQN.\c;2_g5f,pX\UYuSs1#%lDo"L2h$5PBn6D&p^e656arX.(]%LeO^^)77!.!bjYQ/.-$t^=`Q*WW%gRQYLrT"_-hGCdG+k*p@*\WR9TM4A+l~>endstream
endobj
xref
0 9
0000000000 65535 f
0000000061 00000 n
0000000102 00000 n
0000000209 00000 n
0000000321 00000 n
0000000524 00000 n
0000000592 00000 n
0000000872 00000 n
0000000931 00000 n
trailer
<<
/ID
[<03ba57a25334291279fbbf61957007c7><03ba57a25334291279fbbf61957007c7>]
% ReportLab generated PDF document -- digest (opensource)
/Info 6 0 R
/Root 5 0 R
/Size 9
>>
startxref
2288
%%EOF

View File

@@ -0,0 +1,128 @@
{
"coordinateUnit": "mm",
"coordinateOrigin": "TOP_LEFT",
"pdfFields": [
{
"name": "serialNumber",
"type": "TEXT",
"value": "{{serialNumber}}",
"page": 1,
"x": 35,
"y": 52,
"width": 70,
"height": 6,
"fontSize": 10
},
{
"name": "partNumber",
"type": "TEXT",
"value": "{{partNumber}}",
"page": 1,
"x": 35,
"y": 60,
"width": 70,
"height": 6,
"fontSize": 10
},
{
"name": "workorderNumber",
"type": "TEXT",
"value": "{{workorderNumber}}",
"page": 1,
"x": 35,
"y": 68,
"width": 70,
"height": 6,
"fontSize": 10
},
{
"name": "quantity",
"type": "TEXT",
"value": "{{quantity}}",
"page": 1,
"x": 35,
"y": 76,
"width": 70,
"height": 6,
"fontSize": 10,
"align": "RIGHT"
},
{
"name": "customer",
"type": "TEXT",
"value": "{{customer.name}}",
"page": 1,
"x": 35,
"y": 84,
"width": 70,
"height": 6,
"fontSize": 10
},
{
"name": "serialBarcode",
"type": "BARCODE",
"barcodeType": "CODE_128",
"value": "{{serialNumber}}",
"page": 1,
"x": 25,
"y": 94,
"width": 90,
"height": 18
},
{
"name": "serialQr",
"type": "QR_CODE",
"value": "{{serialNumber}}",
"page": 1,
"x": 155,
"y": 38,
"width": 32,
"height": 32
},
{
"name": "item1Pos",
"type": "TEXT",
"value": "{{items.0.pos}}",
"page": 1,
"x": 22,
"y": 142,
"width": 15,
"height": 5,
"fontSize": 9
},
{
"name": "item1Material",
"type": "TEXT",
"value": "{{items.0.material}}",
"page": 1,
"x": 40,
"y": 142,
"width": 55,
"height": 5,
"fontSize": 9
},
{
"name": "item1Description",
"type": "TEXT",
"value": "{{items.0.description}}",
"page": 1,
"x": 100,
"y": 142,
"width": 60,
"height": 5,
"fontSize": 9
},
{
"name": "item1Quantity",
"type": "TEXT",
"value": "{{items.0.quantity}}",
"page": 1,
"x": 165,
"y": 142,
"width": 20,
"height": 5,
"fontSize": 9,
"align": "RIGHT"
}
]
}

View File

@@ -0,0 +1,128 @@
{
"coordinateUnit": "mm",
"coordinateOrigin": "TOP_LEFT",
"pdfFields": [
{
"name": "deliveryNumber",
"type": "TEXT",
"value": "{{deliveryNumber}}",
"page": 1,
"x": 55,
"y": 30,
"width": 55,
"height": 8,
"fontSize": 10
},
{
"name": "deliveryDate",
"type": "TEXT",
"value": "{{deliveryDate}}",
"page": 1,
"x": 145,
"y": 30,
"width": 47,
"height": 8,
"fontSize": 10
},
{
"name": "customerName",
"type": "TEXT",
"value": "{{customer.name}}",
"page": 1,
"x": 45,
"y": 50,
"width": 147,
"height": 8,
"fontSize": 10
},
{
"name": "customerAddress",
"type": "TEXT",
"value": "{{customer.address}}",
"page": 1,
"x": 45,
"y": 63,
"width": 147,
"height": 8,
"fontSize": 9
},
{
"name": "workorderNumber",
"type": "TEXT",
"value": "{{workorderNumber}}",
"page": 1,
"x": 55,
"y": 87,
"width": 55,
"height": 8,
"fontSize": 10
},
{
"name": "partNumber",
"type": "TEXT",
"value": "{{partNumber}}",
"page": 1,
"x": 55,
"y": 101,
"width": 55,
"height": 8,
"fontSize": 10
},
{
"name": "quantity",
"type": "TEXT",
"value": "{{quantity}}",
"page": 1,
"x": 145,
"y": 101,
"width": 47,
"height": 8,
"fontSize": 10,
"align": "RIGHT"
},
{
"name": "serialNumber",
"type": "TEXT",
"value": "{{serialNumber}}",
"page": 1,
"x": 55,
"y": 115,
"width": 70,
"height": 8,
"fontSize": 10
},
{
"name": "serialBarcode",
"type": "BARCODE",
"barcodeType": "CODE_128",
"value": "{{serialNumber}}",
"page": 1,
"x": 18,
"y": 135,
"width": 100,
"height": 25
},
{
"name": "serialQr",
"type": "QR_CODE",
"value": "{{serialNumber}}",
"page": 1,
"x": 145,
"y": 125,
"width": 35,
"height": 35
},
{
"name": "note",
"type": "TEXT",
"value": "{{note}}",
"page": 1,
"x": 18,
"y": 205,
"width": 174,
"height": 18,
"fontSize": 9,
"multiline": true
}
]
}

View File

@@ -0,0 +1,80 @@
%PDF-1.4
%<25><><EFBFBD><EFBFBD> ReportLab Generated PDF document (opensource)
1 0 obj
<<
/F1 2 0 R /F2 3 0 R /F3 4 0 R
>>
endobj
2 0 obj
<<
/BaseFont /Helvetica /Encoding /WinAnsiEncoding /Name /F1 /Subtype /Type1 /Type /Font
>>
endobj
3 0 obj
<<
/BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding /Name /F2 /Subtype /Type1 /Type /Font
>>
endobj
4 0 obj
<<
/BaseFont /Times-Roman /Encoding /WinAnsiEncoding /Name /F3 /Subtype /Type1 /Type /Font
>>
endobj
5 0 obj
<<
/Contents 9 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 8 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
6 0 obj
<<
/PageMode /UseNone /Pages 8 0 R /Type /Catalog
>>
endobj
7 0 obj
<<
/Author (anonymous) /CreationDate (D:20260609194637+00'00') /Creator (anonymous) /Keywords () /ModDate (D:20260609194637+00'00') /Producer (ReportLab PDF Library - \(opensource\))
/Subject (unspecified) /Title (Lieferschein PDF_TEMPLATE Demo - gerendert) /Trapped /False
>>
endobj
8 0 obj
<<
/Count 1 /Kids [ 5 0 R ] /Type /Pages
>>
endobj
9 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1941
>>
stream
Gatn)gMRrh&;KZH'YJ77R-".j]=9j8Xck)lJ6Xp"Tu==5`7h(=#mL1tSR%(>bAsOR*$8`Eq9<m@kE%3tA-+sVq"*NtZ1BFAiONJsZ0=-0L)W%a@*b\`0(n_Mmf2]n-K>ZVPSf.R49/RHQ>Ioa8it#)GMGV%flY'E]9oM^e1X%89(VIt2Z:jsO1+'Q#NBE(n?@u&_/goQ(+bg"D.kS#T=V)QYL8T)VtooL4Pd<p:S7YIIJ37)`KTPm4!q[!Qcg0Yao7]MG9"&R]3s<D=_"tIV<+#VSVY[e&qMb3%$t`A0T%s\eQrC(?M/\)$92=,\CSOcNdWrmXVVKY5\&_"O2:%"^oqMZc_1@d%^FYSoZ:+*QHMW_ClhnZ+m[%-r^3-XQF$8WWh8)e)JLHUdd-;Aebi(OmFI1al4:gABAgt_h&Q&a8FEn>%!\9<H0T.!E1@8e3u'^53);6dbIY==f;]gU5:i4\C25^M/`]?&HVY)hcTp%K9F)gAO,ZghEoZU@9ggVhYY<h,/?(b@2Tf=i]Nd*kWFmk"++?CgTbc?f?#=7J:rn!cI:Db%FO\=9Hsq3+3m-V*boJ7Iqs@&aTnGB7[A2!o]qaSC3S=F)FeUfVqL*K8`''+X4k\;e%Y8EU//34TBJRA'`J"m9!f`cI`3(2uAH`N<)DrYqOt\#QrD&)?V1PjoMHgU%Z=Zf]Y(C_IbBgNGr<&T1f0c+2))Hj`.mR.c:5YMh[Ad=cJT%^@r.YF=6-o4!NdL5)h/OBYO<IY1EXIs_I!p%\n2SRk8rD@-_$h-TiOqF(8?RqpR<[%hn>=nEet<qjphD2"[?Utb#m*"_\S@`r,hJEp[HP-n:=lU6Z/EoflE59XgrifdM%lNd+^HsF&dh#4g=C9gV1T\&_ZA,G?$B[sj4]Lc'dm#>9$4_&/U2\h^U[:%YR95=7[lcop8B:i0C\pco(sNUG'37=V'"JppKGBS(K#OkZ$PRj==_[)K@mZfNE;n5?9hpK047E.3$WP3_80#h:pW#u3/$KbfR]]gL1k.OE5O">TT5<$e6,g;?lc0N=bo&/"Z99#W%%WoF-K&6dC-kE%5hT75eo.Z,3,m4&?ndn$!8&AFr$s9J;&8j_]GV5Dd`h;S_(0O,1f+BgS+uU^JGRHIUO9(r.6NV>43oq6:k"L&LCk@>?fa!-mXpX'U!/(9>d;+lp-G[<TM]U_NI:&6E.E":o`ns/5ddU(J8i9+,*V%-jlY+HW>0V'*.3t*TZb5C3HT_Wu6W?C(>QP<P&sNWbg`c`?6VT2?YS@^NE<!T\rj;#3@:9-jKehAs%UDd[2%;rO@dO&6T/5"T]!42$FBZfH.4/HRq7V(,!*+Pu^:S:o>+d,5]T!03n]:b[F!8#\HWCatlFb_n+-;.Kh<P(+uE3=XE_-/1gH.XB]ZL=GS)-U"Cer\]g9?oVj>HO-8lY49t$)[NUK^)Z_Jk<$p\`k[d,[[]&mjPs];-NCQr8"jFg.."YA1Z;-I-H*=)*>%M;K@-r.ALhbfYgV#)ACA/'Z$S9U,Vnd>.GNom#ZneYTOMV9AKHXd_\\"D4db#Nlb[ROcmJA^D.k?4%<K>o&XLo;t;#),#!`o-;6T_>>_@HTG&gVBo,)bC8*s04J?:Nig('t&OYXf>0MS(^&TLeELp]a/t(53sdrB`paT^ikOXKkp!Sje7XB.rrh>GnA2?P'#q^b<>r.1VphHX)R-PAQ+=ga;5u>#*Bi?3DfAJ7mHtV`<0UJR>p\&[4mo^Knm?O4Tu):;9;l@%gWU=XB,a=e!aTO!p<,4eXosO2."cF4]4W]C+.<?-X3m'5-M"eLIhJ1U-BM>+]omm*d/`YPih8c#r=Hrkj/5YT1eQe:qhS2NPB73FRZ!eXgIROhD\P*jbH@s7*:n`7E`ZHZOK+e*qZDLHfD:-!Pu9\*<6o[[Qa4bJ-*(qL?ujAp)]k2YdkU&"G3~>endstream
endobj
xref
0 10
0000000000 65535 f
0000000061 00000 n
0000000112 00000 n
0000000219 00000 n
0000000331 00000 n
0000000440 00000 n
0000000643 00000 n
0000000711 00000 n
0000001006 00000 n
0000001065 00000 n
trailer
<<
/ID
[<756205e546b68225b6eb780b6d82060b><756205e546b68225b6eb780b6d82060b>]
% ReportLab generated PDF document -- digest (opensource)
/Info 7 0 R
/Root 6 0 R
/Size 10
>>
startxref
3097
%%EOF

View File

@@ -0,0 +1,74 @@
%PDF-1.4
%<25><><EFBFBD><EFBFBD> ReportLab Generated PDF document (opensource)
1 0 obj
<<
/F1 2 0 R /F2 3 0 R
>>
endobj
2 0 obj
<<
/BaseFont /Helvetica /Encoding /WinAnsiEncoding /Name /F1 /Subtype /Type1 /Type /Font
>>
endobj
3 0 obj
<<
/BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding /Name /F2 /Subtype /Type1 /Type /Font
>>
endobj
4 0 obj
<<
/Contents 8 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 7 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
5 0 obj
<<
/PageMode /UseNone /Pages 7 0 R /Type /Catalog
>>
endobj
6 0 obj
<<
/Author (anonymous) /CreationDate (D:20260609194637+00'00') /Creator (anonymous) /Keywords () /ModDate (D:20260609194637+00'00') /Producer (ReportLab PDF Library - \(opensource\))
/Subject (unspecified) /Title (Lieferschein PDF_TEMPLATE Demo) /Trapped /False
>>
endobj
7 0 obj
<<
/Count 1 /Kids [ 4 0 R ] /Type /Pages
>>
endobj
8 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 997
>>
stream
Gau1-gN(as&;KZF'RL,p?$&88;Gpo"/Vi='/fBjhJn]$;fkIA5l+80%[ZcH3daBD/.$<\"I9QSQM$&ZShL87@JGfUW^#d$5,W"?c'dQ:gOGo:EjosLVR#bF-!5TS1kQS5XGHj"q-DV5]X[2!$LEqXf7iBQ"<0JDg(!e'!$-A*#f)YShI"q(8E7-`,?@d5[[Up9Ae+i=>jP]g^dHfin`fugo#DZk.VHK&4<6T#1V6D\u?1U(%2r\d2.Q3s-8)lGeTTqj;f0'N:dSJ^W\?YdHP?.gF[Gm7hmsPId1BGBe[6c=1]r<$MoeO9f0FS`ecoWOX_;V\edX;2YRhVreq0EsXWpZ'aa-!]?_&Gu@:0Cl!b?5k6q0blS;/n*JAY#C?U0-RRN>9#V%l]0^<6XfBA/q]c/&<#/)&7W?B[@(+cj7BYa$)r.l*As3<]&t('+KT*FUZkVk=fZq;IP4mlQ4G++1Df[pC%;UGfHKAGF7fg\CIa*=AR#G6`X=!-]chLf+MCq9kf`\;n2b0beI)d^>;+dXYbdCKj!rrN&R-#6m8(J6>"1_3ZUTE_&NnYs%LXj7BLu+YniU715>7taqJAi16c*K0O>s0b]h!hAQ>)E$s9>$@us^RWgt>L',QbpD<,fmmco;"m`hf75TPd):@5,g/F'Cao0jNlA*p/_5Wc0;V*NAgNgZ/"c"%?5QA57@gMb?K_sOqPqeMT8F]Mj54M/a[OPDosc&oMolnX?6D2B_Xmr\*8r!rC)7XI^<U(*V\AQ14*nSVbD=8u'tg[kg\8dj^s[(pTDNXrjDiS]jZK/3^PmnkphqtB9%&Z[<0gE#@I:EGG,5!2tLjp.LeO.IuW,6"m,ok-inhK=QpUY069\%^0<=FIWppRq&nV2*7U32;?R?8;2CUFLntWaV>.bc%NJhIf8O@34D[lb!XaFA<*K.Fdu&p<t]EVc5s0#+n]bhR%bb.$h:Cc7$IZM\jhLIo^+]o-RXi7Z2Ta3XDM<b?qF&~>endstream
endobj
xref
0 9
0000000000 65535 f
0000000061 00000 n
0000000102 00000 n
0000000209 00000 n
0000000321 00000 n
0000000524 00000 n
0000000592 00000 n
0000000875 00000 n
0000000934 00000 n
trailer
<<
/ID
[<ac0839f3e0e3c4c3aca8df4ec3a49cd2><ac0839f3e0e3c4c3aca8df4ec3a49cd2>]
% ReportLab generated PDF document -- digest (opensource)
/Info 6 0 R
/Root 5 0 R
/Size 9
>>
startxref
2021
%%EOF

View File

@@ -0,0 +1,111 @@
{
"coordinateUnit": "mm",
"coordinateOrigin": "TOP_LEFT",
"pdfFields": [
{
"name": "deliveryNumber",
"type": "TEXT",
"value": "{{deliveryNumber}}",
"page": 1,
"x": 38,
"y": 35,
"width": 70,
"height": 6,
"fontSize": 10
},
{
"name": "customerName",
"type": "TEXT",
"value": "{{customer.name}}",
"page": 1,
"x": 38,
"y": 43,
"width": 90,
"height": 6,
"fontSize": 10
},
{
"name": "deliveryDate",
"type": "TEXT",
"value": "{{deliveryDate}}",
"page": 1,
"x": 38,
"y": 51,
"width": 70,
"height": 6,
"fontSize": 10
},
{
"name": "deliveryQr",
"type": "QR_CODE",
"value": "{{deliveryNumber}}",
"page": 1,
"x": 162,
"y": 25,
"width": 28,
"height": 28
},
{
"name": "positionsTable",
"type": "TABLE",
"source": "items",
"page": 1,
"x": 18,
"y": 78,
"width": 174,
"height": 125,
"rowHeight": 13,
"headerHeight": 8,
"maxRows": 9,
"overflow": "CUT",
"fontSize": 7,
"border": true,
"drawHeader": true,
"repeatHeader": true,
"padding": 1.5,
"headerBackground": "#EEEEEE",
"headerColor": "#000000",
"gridColor": "#000000",
"columns": [
{
"title": "Pos.",
"field": "pos",
"width": 12,
"align": "CENTER"
},
{
"title": "Artikel",
"field": "partNumber",
"width": 29,
"align": "LEFT"
},
{
"title": "Bezeichnung",
"field": "description",
"width": 47,
"align": "LEFT",
"multiline": true
},
{
"title": "Menge",
"field": "quantity",
"width": 16,
"align": "RIGHT"
},
{
"title": "SNR",
"field": "serialNumber",
"width": 31,
"align": "LEFT"
},
{
"title": "Barcode",
"type": "BARCODE",
"barcodeType": "CODE_128",
"field": "serialNumber",
"width": 39
}
]
}
]
}

View File

@@ -0,0 +1,111 @@
{
"coordinateUnit": "mm",
"coordinateOrigin": "TOP_LEFT",
"pdfFields": [
{
"name": "deliveryNumber",
"type": "TEXT",
"value": "{{deliveryNumber}}",
"page": 1,
"x": 38,
"y": 35,
"width": 70,
"height": 6,
"fontSize": 10
},
{
"name": "customerName",
"type": "TEXT",
"value": "{{customer.name}}",
"page": 1,
"x": 38,
"y": 43,
"width": 90,
"height": 6,
"fontSize": 10
},
{
"name": "deliveryDate",
"type": "TEXT",
"value": "{{deliveryDate}}",
"page": 1,
"x": 38,
"y": 51,
"width": 70,
"height": 6,
"fontSize": 10
},
{
"name": "deliveryQr",
"type": "QR_CODE",
"value": "{{deliveryNumber}}",
"page": 1,
"x": 162,
"y": 25,
"width": 28,
"height": 28
},
{
"name": "positionsTable",
"type": "TABLE",
"source": "items",
"page": 1,
"x": 18,
"y": 78,
"width": 174,
"height": 125,
"rowHeight": 13,
"headerHeight": 8,
"maxRows": 9,
"overflow": "NEW_PAGE",
"fontSize": 7,
"border": true,
"drawHeader": true,
"repeatHeader": true,
"padding": 1.5,
"headerBackground": "#EEEEEE",
"headerColor": "#000000",
"gridColor": "#000000",
"columns": [
{
"title": "Pos.",
"field": "pos",
"width": 12,
"align": "CENTER"
},
{
"title": "Artikel",
"field": "partNumber",
"width": 29,
"align": "LEFT"
},
{
"title": "Bezeichnung",
"field": "description",
"width": 47,
"align": "LEFT",
"multiline": true
},
{
"title": "Menge",
"field": "quantity",
"width": 16,
"align": "RIGHT"
},
{
"title": "SNR",
"field": "serialNumber",
"width": 31,
"align": "LEFT"
},
{
"title": "Barcode",
"type": "BARCODE",
"barcodeType": "CODE_128",
"field": "serialNumber",
"width": 39
}
]
}
]
}

View File

@@ -0,0 +1,85 @@
%PDF-1.4
%<25><><EFBFBD><EFBFBD> ReportLab Generated PDF document (opensource)
1 0 obj
<<
/F1 2 0 R /F2 3 0 R
>>
endobj
2 0 obj
<<
/BaseFont /Helvetica /Encoding /WinAnsiEncoding /Name /F1 /Subtype /Type1 /Type /Font
>>
endobj
3 0 obj
<<
/BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding /Name /F2 /Subtype /Type1 /Type /Font
>>
endobj
4 0 obj
<<
/BitsPerComponent 8 /ColorSpace /DeviceRGB /Filter [ /ASCII85Decode /FlateDecode ] /Height 290 /Length 3456 /Subtype /Image
/Type /XObject /Width 290
>>
stream
Gb"0M5n_rq&4=k#s3f$?S%+1cZ<HbT^)J6^^OH7azzzzzzzzzzzzJBOSF./3Bll.auaBqnP:n,8LJqO7%nDPXYe.,FWHb'e\4qDL91g1Z`=HU$W+^*\8t)_#V\9)$#kEaN1;^<MY%2B&FhhG:\cY=[X67S4VZdXCS^gt1/RDf[/"NCN0HpJ^MGCXT]&OjZ^oFPlsEp[pE.gY>>6UJ\VkrKR3URe1H7UpSOJYmi"#C0#?/)kEn3%4-(1<cQk5SYr_%-$*g%ESDG'Vm-[WF)"`qgDc]]Ymi"#C0#?/)kEn3%4-(1<cQk5SYr_%-$*g%ESDG'Vm-[WF)"`qgDc]]Ymi"#C0#?/)kEn3%4-(1<cQk5S]CKT.8lSc\m$o\^'\60oD"bRUb],r[pnKJ'u%oTnMR::AR=\?`Q\3/I$r=@CJp;;][knrRJ0_V17uV=U`u(LXh,<(n#Pqh5*)m=74Rn4G:R:^=5h>M*ab+g<9CUV<m84[_O('\^*XSob\Q%:R!E%m0CsI:9e'&/f=G0@o=bQ=k4AA:<2;:QFgkiBI20K?kOCF/n7>WWT"ESN$=DrepmP1W1+O9h?0com0CsI:9e(OY'ohCZotJ;0q)lA<'u%nL`cKaX?fB1U*ab*<?Y2ae\SJm^n7=eEY=XF$Z_I!F[GG![17uV=Ur&I$d<P(_mjidRcf8e=iQJ<94Emq%'u%oTnMR<8AQCXZ]@Qid?fptTRT0&;.iQ`>l[=I?o6G^X.V<:a\_3<!Q2*PV-5A<mceRk[Q-+Iq?^$A8R:gF2V+P;_O0;-obfhk%qbj7GT?#QZ@V4bjRsh86REso/;DC!!bg!k$WQaPq25ut2H0/^GW`8-PpmP1+bWKsQ03'LnH$i=9AnIXFfp=1Rq)lA<'f.Ylh!@\!b']f#2LLp*[P9Sg]>i&YNG\GGcYR*'WcsGjI2^l0AY.SpbZ+$L1]'#;iQF?iNL?d&2.lPrCr;TbAfEO)]kbjYe(BC.o?9+)VL$j]fr)(a?fmbnbY7]40saYc)nsNPgR+Do?"7K=`\r44B=9NNe[riF5)jFS1='cHAu^'7R=3rHBVaFmEOO9lIF",FHU$W+NO+W$msCXJhtnksD4GJ8Ic'amqqb,ZDrYZ/p\UO0h=lB@I2cf<^#/QHl+Zmjmb9_Ukl&6Y8b]ojEo4mi7dp(7:V-#nPl!Aa2L<.,H.%-0Vqeb$[aFu#c]ddD5NTV=Z]3i1MmMs8!,h^GJ/`tUi5T+.\FGmdDZ"'r)h/a``a8q[nWs8or+0j:I6#Fd4-Qj>AN#fGU"lT/Y79A.V>-KYAlGZCb)I>8g@2dQMQ]T_2O-n.go-@=%Gr,$pmP/o;lLJRN>hWHn@_\VY=W:`PtIb*W0+7mh]&EeRT*ltW>*L7)Yr+V]hsZj8S'fF8tk%=Ofo5O)l<=u<+](.'ZuCRq:b>O;2VPKAN#fGU"lT/Y79A.V>-KYAlGZCb)I>8RT0'"Pa]9;Q*U3J8ln3j)l7.b9;QGgO4';Smma;0gJTJic_GUDj1A0'p9mrCk4:!,q=nilR_:;<ZM?UT=$F>;V.OX=V//>VdZom^7]G(q;CS*]UNGVX]2<>:[T(E3o-do?phl=b]e,MTq)h8N5/%u4AoC>=Cd(XpBA^86=^N+(2u@3*j6Kj1b.Q<]^,BS@p<DI6f<ZZdH*+],D9Uf%S%XfbB14<A>'KktR(:m/HQ-><MH&`Gme4%CF0ffY2SPatWQ]/6m;$4oB&DrlHp&uXRFf%)f>c%Z^=h99H<-gsYB@=XNL?8aZa]JtX@Qo/lY^&K1H52CDr^.dOik>fh7bdZDYD?7G;FU>V/#oT)`-Mgjt$4)21DfiRIN-*'muu!^Y$QMGaeF8^/U)!Ch%'>c$T\%Wl?^#0f,D5^4J"<itpI;),E%q_3_1lMf$[sVjXaH9@`gV2J0tXbZF;1CA_N[1ZN33/,3""I^+m#ch$"h:F`]V3S!37F7D?L)u3geGHPjBQ.^@6Icl&&h[B;+p!'?hqp*1LHuJ(m6/T5efi2S@\bM`aNI3DC44cE\b3V2+^[o.$mfe.&qIWZoIHTU6^,PVrT^Q.CCaud[>l;nA7_T]2Sa2b>AbM[Qhtc-MpO^,#IC#os^2rg+h])Atd!)X22O2s>0(;*)D_cI<P"ODdS#*jme@A1$AM\4iqV"m2.]JZW\68*4:S"P^;E!QIWNSI-Y*)E1N16ZXp=-WF.V1DRQMT<6k:3Hqc+$pACJUM%Uf',2AuVssFfpm"fo2Ds^@p'e8h+C.Sp[`6ik)D"3AmjRp1:stnjYG2m]1/!bpo32R;\k5d7h:O.[sACb%>#CgF-tsPOH6.k1$K<fpDLmarAUd]<AM#261QeA\O/p7Pjl.X)(PU.[tjSDkjtm9YUBRRo?Z9Ama]T-uUr+n%Qs,e?#t/Qh`P^)pRHUYHg/GX).K@^5_\Q1\pI>Cei5)Rsg]Ce(BqKRJWT3eP(rshY-',mb?CE9T;d'p>r+<gXt%MND8:'jeV>!f[WZT:#>U*V!d!ieP(rshY-',mb?CE9T;d'p>r+<gXt%MND8:'jeV>!f[WZT:#>U*V!d!ieP(rshY-',mb?CE9YOS)Ub],rpQ^0?:6BGdCJ#PF=%LT5m-AeOFJE2Ag.8nDC,TX1Y>_59rL2/(l4m4'.JnF^,2MJ7PVuFCOIAnsfBX.87_bh-Oa;`2Cs'IuVarcC25Q;^/#6cUp@UI8]$[]1D5:J]24LnTf.#.-ISFN$oa7\OP^U5?O`'f,8fOa28Bn$ulZJ),UL-sR8A2lcZTc*&n!ij3H6_=XNO(g_<:?=HP^4S3\W*pZ^/XQjnW\>Ehmn`Q5/("bS+2_h,N$%kZTc*&n!ij3H6_=XNO(g_<:?=HP^4S3\W*pZ^/XQjnW\>Ehmn`Q5/("bS+2_h,N$%kZTc*&n!ij3H6_=XNO(g_<:?=HP^4S3\[4(7Z]aT98fOa2:hFG%S'4L)o-eIO?9t!UNGgR&WQq9GOa71BQ8ZJ!XJ@n<dRNL:PZc`8-F8m#4A$Ql=&<LCj."7bWq.WUUg??TV2]CK/#ggh[#oY<AX56gafM:!O4$M_'pMkTpa3>1l9%ZXWE2o.C#b],8uhJ>[c,EBmlO]/Z]1C92JVS.ou6dOCh)V<N>fE$IBbslgR*8(UW0H7?`_St2J/Q8W3NDQ([El`NLArfC@!GC`VRepA*1=])S8UiYs'tuR!ESE`i`3HXO#*KAtIbUZ)c@jlSGl,RHHJClmh\IqTNbck.-KFI48*jmcur9ft@#9I$OLc8s;'-mcur9ft@#9I$OLc8s;'-mcur9ft@#9I$OLc8s;'-mcur9ft@#9I$OLc8s;'-mcur9ft@#9I$OLc8s;'-mcur9ft@#9I$OLc8s;'-mcur9ft@#9I$OLc8s;'-mcur9ft@#9I$OLc8s;'-mcur9ft@#9I$OLc8s;'-mcur9ft@#9I$GQLzzzzzzzzzzzz!;QHo'r)09~>endstream
endobj
5 0 obj
<<
/Contents 9 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 8 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] /XObject <<
/FormXob.8de85583b9e7bfa26ef5e1750cf0a554 4 0 R
>>
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
6 0 obj
<<
/PageMode /UseNone /Pages 8 0 R /Type /Catalog
>>
endobj
7 0 obj
<<
/Author (anonymous) /CreationDate (D:20260608163430+00'00') /Creator (anonymous) /Keywords () /ModDate (D:20260608163430+00'00') /Producer (ReportLab PDF Library - \(opensource\))
/Subject (unspecified) /Title (untitled) /Trapped /False
>>
endobj
8 0 obj
<<
/Count 1 /Kids [ 5 0 R ] /Type /Pages
>>
endobj
9 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 2534
>>
stream
Gat%gbB"U]&r5rP.ra:RehGqkhlKB=<lN?2&J'607?=.dPt65s88[Il:X9R]c:ArF2g1HlVS6bp4Up=Z&$:P[hn&^VIOW4s9RQV,$Z3gu0!pg;beAo!fgYYH&N[>')_JttF^7=Y/X<TWLmXH$+_WT7MLU"V2"P=c^&QY7<^2DnJ&Ub[NMf2]/t\$`6C-I5O]7Po\h;RsB%*h&Ra&"gMT]V:rOus:HSJU8.7Cc$`#tA_4"$$V^%<jnq!F"NIuNHI$o+4-Rp#-Fb@^?cphjDFBN03#c[2,O3B6(JXYHfeip(]r?[dBn/?QLQ@U@A];j*jbrl&\lhA^%CH_lrj4oXu_1>/l.mB5,ucc`qMS[i2`A!NhsZ5p#jec.6Mj#/BP\ngUrSBoVs'.="6Oggnhn)D5\lcnublT:LURcJn&GI`QSo32ls\SlT^l@,mR]m&INIIZ'GbdRHr?N.W`lrSba8SYAdE7mjPWthL)O,d\uM#"Xje]j%*.eOA-"K,GqVa=N6Q1iT96JW&fWg>&YT1)R_l5Xi2U0lMV3N1KhV6\doA*@GOKoU'-B]qjPbM'O9kYI](ip"EScll$AS3BEop-]Apd<3seAQlQqka0P$c/S`WC;5'7N*qp61g/GRZBs-(qo/nB3D#RMR/bcMp"0[_;lF4nlK+nt"k4L%Z!L!:;]Nfkq;q?:A=B+VWplAlX_=KWDa\b,b3Z+WLO$Gdgohfgf?<fG+psdIU7@64A:mRIb$_l<LlsUZ_e,o=.Z!-9$6"n..qfLgP;\Zr0N(1V]iNKnoNFP5kb">_Iotn-gID,<Y2-c-cQe^n,LoC)*Qbba0t6msO<6>X7Q!J:7cT'1N!=j4N0JXnO7nL2VML.E(r,JpFH+b4NZq9N,\\:dUf8AV[3sI^c)G9V<"ogPWLhk-K%PHqCC!S`S!1/qCCb-0fLS47!FlEB`^"?:nke6MCSX]C6^.-c?u335:b61?c:t(<gQncpgXQb<[aQQ/V-T']/]J"Jr<-SnbV<LH+D!sYOu7>&#I-Q!PHh_h6Bc,k@;MgXdEhscFQUuJ$,uRJ;2H-GV6,It)bR;P9g0jO^Ku7)K7t)Gf<IJj^c;t#Yr$,lorPASR.=Yf@]a`564;6]-UTdgktjD2;$jJ6Om!c>2U`i4Z2%RE5Tl@N-u;!<Uadg'X3HhUd4kc':@C;-aq5rT.B93nEb$Veai*V+2C!7\EMplT5ON)X0mGW$7iZr7*;-(65;uBMc$HdB>-8t@@q*BO+CZ$r80k6g;>c4de_EjfU4-A@+uA4I2*:!7LaCg1P,>_dKqbt@+I7E`@G/+$f#s_<`E7H<8V?VGNrrLq-W,$\"CRdrce8k=g!'M_*=>*=CjuSmdE,(R$F/->&@QfqK!H19Cb-;3Yj=CVC1L3X/'*+j)_dTVjZH0D9e$mO4)sm/J?Ag&YkHF!Jlm1:KIorNT^3p(9<W"A%?+jG6!/:CK_7*UKY=k[0*oc#!jI?VW3H(uImM`H0*o]qK!=LdSN=rcns=M-+=GR#r'f#lA<$!%;L@,)C>:9;dZ=:#V^"q%e,E7?:[sd#oW,t0\(uN+\D?$0rY!ht?@=u=0Qj$;]q,-fAu)nWbg-EIkm(gUZ)8W)Qm(6n>c*3IV^UOY&6ldM@\'l,7>rf.K<X,9K%SKVK%T'$&6nur#g7W=Lip#(*/H2++G?R+UZn;ffHNEh>FJ"ebAm`jL<d;^jfF7PKU/JI`"nJef[=.o=>h4NXi:IJV7Qi7`Ju'!9:J&.#MS!FG>]Ps&LiMfboMU'L_PLXO[<09,D[pl"L1CU+I8'\KIq3IZ),!`YUi9U><4YR)^M6r>+*p:cE\hbLd.n7YbQAGmQNP<VmIDJ,E=k%NWsL,keK$pN7\_4T3t0AkVpb$]A>Y/McPtYTBjbNH8TW4G)Q$.1#bbejFgR[@)t=%8MsAT-4Sj)d3LZF8MsLu-KRTMgU"Y!!f/0HCh`Q-Cj?/<YfBrLYcL"obNa_a=U#hM\:JhJi39^kI1S;[.gTWXY6SL:-6Xr-KSP6lYcLCrS+P\*/.g!nKWsf,#g5C6Ybd0ZTqsIHZ/pNS`0LX?!:/D,bm3I@*VBeKlZZ.MZeTMO=S="nAPYYFi^jEuScl:q&.E#@EscC0^CZ_0$Xq`'!4I1Y_Nk\W=n\YdcqPnL[Z_\bN)-j*o^u.KUSDe`.?;i;HQ?Ti.?-:o8"\chrTKTm]\2]gO)5Qnrd"$^`(P?B$2p@00QsOI;mS8G9_s<C9`0^6eGK=(c!f:up!eC,7q;^1\UT79gqQ/-nluK1bB"tcAN9hE.Qdu@Sj>Xn$P1&(?4XmoSl\3+HPF>;>6nW;miZHB/eQ#k?Jakul7`KJ*eZg1pMUV/I0X/\C`k;'HHrie0gSnW'7d3jJZtu#K:qQL#[:O95cYP@/>(`(bP`!&jbH89H[#MnTr\dff\r15?[\"0eCXEYK.LS#NW8I#O(-]<IlD;;l1KO`hgU(Tj8,G]qpg=dICf>s/XD<c-sn6KJ+NX4H2$=q`-"J:X.tqC)"=D*ll+9$YTke"#-i5f_#~>endstream
endobj
xref
0 10
0000000000 65535 f
0000000061 00000 n
0000000102 00000 n
0000000209 00000 n
0000000321 00000 n
0000003968 00000 n
0000004234 00000 n
0000004302 00000 n
0000004563 00000 n
0000004622 00000 n
trailer
<<
/ID
[<ed214652d9a671a065426ad4a7d752cd><ed214652d9a671a065426ad4a7d752cd>]
% ReportLab generated PDF document -- digest (opensource)
/Info 7 0 R
/Root 6 0 R
/Size 10
>>
startxref
7247
%%EOF

View File

@@ -0,0 +1,74 @@
%PDF-1.4
%<25><><EFBFBD><EFBFBD> ReportLab Generated PDF document (opensource)
1 0 obj
<<
/F1 2 0 R /F2 3 0 R
>>
endobj
2 0 obj
<<
/BaseFont /Helvetica /Encoding /WinAnsiEncoding /Name /F1 /Subtype /Type1 /Type /Font
>>
endobj
3 0 obj
<<
/BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding /Name /F2 /Subtype /Type1 /Type /Font
>>
endobj
4 0 obj
<<
/Contents 8 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 7 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
5 0 obj
<<
/PageMode /UseNone /Pages 7 0 R /Type /Catalog
>>
endobj
6 0 obj
<<
/Author (anonymous) /CreationDate (D:20260608163430+00'00') /Creator (anonymous) /Keywords () /ModDate (D:20260608163430+00'00') /Producer (ReportLab PDF Library - \(opensource\))
/Subject (unspecified) /Title (PDF Tabellenvorlage Lieferschein) /Trapped /False
>>
endobj
7 0 obj
<<
/Count 1 /Kids [ 4 0 R ] /Type /Pages
>>
endobj
8 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 672
>>
stream
Gat%`9lJ`N&;KZL'm"2lAF#fb1_PR3JK@.Y1pM?m&HY(F/$D7Y1sNf(fFT_c.'5%G@C5L8ccj[:6H"q$hU;IDPJH@q"Gr5a5\cu9)*fjcB]9<Gf5[%l*o_3Q@AJ]U=TfO2_HSN6$1MAbd>Z6@F?s("&X&cJI+nHhB^F_WDAlCI(r8^;Gc)LaqqM)ul$^5e>UMZ?=Tuq?c(K:2V`\g!(G-79$\Q_k!2HqedgG4+pmT`YMM%*^@@b;::;nO_.Qb[Br@PsddG/SrPht/2@j_tbD\T\@C(iZ.;_uP'EmgCdg0mU!p5@([DjrGSo'&!K6+[4?:,hKRaVftF*9]Q9&c)Do:>n_m4?@=)]NlVnn?$C<$^>>XMu1[G7Yuh>;</6P8p\0.$+@!h'=b)q0Th=)l/&T)r2?`,/QBsGn!"MYH.r=KIqV,;buU.$F(EuJ!^?.W[,#S4XjG'K5"#^[K`Yi\1_mPQpMd1ciO^#cmg&4:n.2(A>c_&-#!Wu$.*nmNgSJ_tkeQYioH@JV9@l&eF-]jbY&q8S_im&e<*b>-KMuTWjuWa?\-p!,[_qoN=m"a)FWbTsr+MA@7b)aHe#9"Fb./SsLYrMO[&s[ZU9AaW9;oJ!J7g?1?<1%Bh/ZYq=j_ocp&;1i]K.i9r4UH^Y9To7b4lP$;S^6n&iD;q~>endstream
endobj
xref
0 9
0000000000 65535 f
0000000061 00000 n
0000000102 00000 n
0000000209 00000 n
0000000321 00000 n
0000000524 00000 n
0000000592 00000 n
0000000877 00000 n
0000000936 00000 n
trailer
<<
/ID
[<721e7688792119f7e9da1c836b6aa329><721e7688792119f7e9da1c836b6aa329>]
% ReportLab generated PDF document -- digest (opensource)
/Info 6 0 R
/Root 5 0 R
/Size 9
>>
startxref
1698
%%EOF

View File

@@ -0,0 +1,74 @@
%PDF-1.4
%<25><><EFBFBD><EFBFBD> ReportLab Generated PDF document (opensource)
1 0 obj
<<
/F1 2 0 R /F2 3 0 R
>>
endobj
2 0 obj
<<
/BaseFont /Helvetica /Encoding /WinAnsiEncoding /Name /F1 /Subtype /Type1 /Type /Font
>>
endobj
3 0 obj
<<
/BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding /Name /F2 /Subtype /Type1 /Type /Font
>>
endobj
4 0 obj
<<
/Contents 8 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 7 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
5 0 obj
<<
/PageMode /UseNone /Pages 7 0 R /Type /Catalog
>>
endobj
6 0 obj
<<
/Author (anonymous) /CreationDate (D:20260608150433+00'00') /Creator (anonymous) /Keywords () /ModDate (D:20260608150433+00'00') /Producer (ReportLab PDF Library - \(opensource\))
/Subject (unspecified) /Title (PDF-Koordinatenvorlage Lieferschein) /Trapped /False
>>
endobj
7 0 obj
<<
/Count 1 /Kids [ 4 0 R ] /Type /Pages
>>
endobj
8 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 914
>>
stream
Gat=i92?K]&:j6F'm"1'(m!S:80L-\[QM"[L]VS5dX=O?NN3!T:7P&2"Ge1XPdO!@R;X.<6/_CQo`%_hS7Z_&6pLe+"AgfVf`dmY".h?P^CChS%Ot;,C%TD0+#J!bU_."B@8:kI0u2$Vfb8mR@8A"%SNW-GkF2%&(EUCrA=RI$<l1bm4A8(X*Zd[T_(j`*@]8_4Zj:rh'Rkf'$*o3/fE0bSB$T0a+'LCZg*Ro.B>^M%FY$#2!;0h:?s(Hi?B!kh']Tb`#gPnVW8MTMA,=D?oW>:V33O+W:2(W82O(OE[+dt_2C9]k`%E=O'2A&j1+MA,`\%Y=;k]]paimJ8q5AfcoC>)`PFar"KN/q!qb'A[^j@D)(bj5iLX$!Ja-=pbK4UQWB:&9rpVEmM<'[pKH'-iYD"iZ<L7prG+,'1nV=]W([Z5j9@/DW+acoY756bP`dUq0D=7];>k3]pE,7)T9T@sg,94k<X$/s(?C$Tf#/84U5aMiK;;T$9&X@ddc/Z3ciO^Y=s2XAodH\p>Jf7iaZ*UZVin3NjmY-^Pc6)oT[3f4I@Qn5(XoNjXQA&fEcTEW"tmM-#0.<UZP0;k8k)c'VYUsj5WY3I?Po=EWm'lTeb.A8oZ;4;JrBStelc8uT:'UXb<3U#,Z;TH4UTGkWp,QULF[V1m8D0(*,PS(M-qV'h@ghgW4G=B$Eb5EigJOY+tq>Isp^4&cob:FRFkp]T\d*J$?['D)Q>MiE$kA5u-`"KG!9N+QUAHc)>V^q#F(oR82/((f&:%-,@W30[c]!SN6jE<n7-/7"9Zs0rn6s.R>Fh)#=)RZ9NOqfWr'Ho&H^N4V&%uZ,8QK/@:jWXPZbq)c?+T$G0GB#gj%cK8*bq'CEd;ZWcRlie;.!f+tDDn*A9h8jNYc"AdpH&f_"%YoX>l~>endstream
endobj
xref
0 9
0000000000 65535 f
0000000061 00000 n
0000000102 00000 n
0000000209 00000 n
0000000321 00000 n
0000000524 00000 n
0000000592 00000 n
0000000880 00000 n
0000000939 00000 n
trailer
<<
/ID
[<3c3495ea3f083feb5cec8e62223c0531><3c3495ea3f083feb5cec8e62223c0531>]
% ReportLab generated PDF document -- digest (opensource)
/Info 6 0 R
/Root 5 0 R
/Size 9
>>
startxref
1943
%%EOF

View File

@@ -0,0 +1,7 @@
{
"userName": "admin",
"displayName": "Administrator",
"tokenName": "admin-token",
"enabled": true,
"scopes": ["ALL"]
}

View File

@@ -0,0 +1,7 @@
{
"userName": "line01",
"displayName": "Linie 01",
"tokenName": "line01-print-token",
"enabled": true,
"scopes": ["PRINT", "PREVIEW"]
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,10 @@
{
"name": "lieferschein_html_table",
"documentName": "lieferschein_html_table",
"sourceFileName": "lieferschein_html_table.html.tpl",
"description": "HTML-Lieferschein mit dynamischer Tabelle, Barcode je Zeile und QR-Code im Kopf",
"contentType": "text/html",
"templateType": "HTML",
"encoding": "UTF-8",
"content": "<!doctype html>\n<html>\n<head>\n <meta charset=\"utf-8\">\n <title>Lieferschein HTML Tabelle</title>\n <style>\n @page { size: A4; margin: 14mm; }\n body { font-family: Arial, sans-serif; font-size: 10pt; color: #111; }\n h1 { font-size: 18pt; margin: 0 0 8mm 0; }\n .header { display: flex; justify-content: space-between; align-items: flex-start; }\n .box { border: 1px solid #999; padding: 3mm; margin-bottom: 5mm; }\n table { width: 100%; border-collapse: collapse; table-layout: fixed; }\n th, td { border: 1px solid #777; padding: 2mm; vertical-align: top; }\n th { background: #eeeeee; font-weight: bold; }\n .right { text-align: right; }\n .center { text-align: center; }\n .barcode { width: 42mm; height: 12mm; object-fit: contain; }\n .qr { width: 28mm; height: 28mm; }\n </style>\n</head>\n<body>\n <div class=\"header\">\n <div>\n <h1>Lieferschein</h1>\n <div><b>Nr.:</b> {{text:deliveryNumber}}</div>\n <div><b>Datum:</b> {{text:deliveryDate}}</div>\n </div>\n <img class=\"qr\" alt=\"QR\" src=\"{{qr:deliveryNumber}}\">\n </div>\n\n <div class=\"box\"><b>Kunde:</b> {{text:customer.name}}</div>\n\n <table>\n <thead>\n <tr>\n <th style=\"width:10mm\">Pos.</th>\n <th style=\"width:32mm\">Artikel</th>\n <th>Bezeichnung</th>\n <th style=\"width:18mm\">Menge</th>\n <th style=\"width:38mm\">Seriennummer</th>\n <th style=\"width:45mm\">Barcode</th>\n </tr>\n </thead>\n <tbody>\n {{#each:items}}\n <tr>\n <td class=\"center\">{{text:pos}}</td>\n <td>{{text:partNumber}}</td>\n <td>{{text:description}}</td>\n <td class=\"right\">{{text:quantity}}</td>\n <td>{{text:serialNumber}}</td>\n <td class=\"center\"><img class=\"barcode\" alt=\"Barcode\" src=\"{{barcode:CODE_128:serialNumber:360x90}}\"></td>\n </tr>\n {{/each}}\n </tbody>\n </table>\n</body>\n</html>\n"
}

View File

@@ -0,0 +1,169 @@
{
"name": "lieferschein_pdf_simple",
"documentName": "Lieferschein PDF einfach",
"templateType": "PDF_TEMPLATE",
"contentType": "application/pdf",
"sourceFileName": "lieferschein_pdf_simple_template.pdf",
"documentFile": "document.pdf",
"coordinateUnit": "mm",
"coordinateOrigin": "TOP_LEFT",
"pdfFields": [
{
"name": "deliveryNumber",
"type": "TEXT",
"value": "{{deliveryNumber}}",
"page": 1,
"x": 55,
"y": 30,
"width": 55,
"height": 8,
"fontSize": 10
},
{
"name": "deliveryDate",
"type": "TEXT",
"value": "{{deliveryDate}}",
"page": 1,
"x": 145,
"y": 30,
"width": 47,
"height": 8,
"fontSize": 10
},
{
"name": "customerName",
"type": "TEXT",
"value": "{{customer.name}}",
"page": 1,
"x": 45,
"y": 50,
"width": 147,
"height": 8,
"fontSize": 10
},
{
"name": "customerAddress",
"type": "TEXT",
"value": "{{customer.address}}",
"page": 1,
"x": 45,
"y": 63,
"width": 147,
"height": 8,
"fontSize": 9
},
{
"name": "workorderNumber",
"type": "TEXT",
"value": "{{workorderNumber}}",
"page": 1,
"x": 55,
"y": 87,
"width": 55,
"height": 8,
"fontSize": 10
},
{
"name": "partNumber",
"type": "TEXT",
"value": "{{partNumber}}",
"page": 1,
"x": 55,
"y": 101,
"width": 55,
"height": 8,
"fontSize": 10
},
{
"name": "quantity",
"type": "TEXT",
"value": "{{quantity}}",
"page": 1,
"x": 145,
"y": 101,
"width": 47,
"height": 8,
"fontSize": 10,
"align": "RIGHT"
},
{
"name": "serialNumber",
"type": "TEXT",
"value": "{{serialNumber}}",
"page": 1,
"x": 55,
"y": 115,
"width": 70,
"height": 8,
"fontSize": 10
},
{
"name": "serialBarcode",
"type": "BARCODE",
"barcodeType": "CODE_128",
"value": "{{serialNumber}}",
"page": 1,
"x": 18,
"y": 135,
"width": 100,
"height": 25
},
{
"name": "serialQr",
"type": "QR_CODE",
"value": "{{serialNumber}}",
"page": 1,
"x": 145,
"y": 125,
"width": 35,
"height": 35
},
{
"name": "note",
"type": "TEXT",
"value": "{{note}}",
"page": 1,
"x": 18,
"y": 205,
"width": 174,
"height": 18,
"fontSize": 9,
"multiline": true
}
],
"sampleData": {
"deliveryNumber": "LS-2026-0001",
"deliveryDate": "09.06.2026",
"customer": {
"name": "Muster GmbH",
"address": "Musterstraße 1, 12345 Musterstadt"
},
"workorderNumber": "WO-100200",
"partNumber": "A123456",
"quantity": "120",
"serialNumber": "SNR000001",
"note": "Lieferung frei Rampe. Bitte Ware bei Annahme prüfen."
},
"examplePrintRequest": {
"printerAlias": "OFFICE_A4",
"templateName": "lieferschein_pdf_simple",
"renderMode": "PDF_TEMPLATE",
"copies": 1,
"pdfDpi": 300,
"includeRenderedBase64": false,
"data": {
"deliveryNumber": "LS-2026-0001",
"deliveryDate": "09.06.2026",
"customer": {
"name": "Muster GmbH",
"address": "Musterstraße 1, 12345 Musterstadt"
},
"workorderNumber": "WO-100200",
"partNumber": "A123456",
"quantity": "120",
"serialNumber": "SNR000001",
"note": "Lieferung frei Rampe. Bitte Ware bei Annahme prüfen."
}
},
"contentBase64": "JVBERi0xLjQKJZOMi54gUmVwb3J0TGFiIEdlbmVyYXRlZCBQREYgZG9jdW1lbnQgKG9wZW5zb3VyY2UpCjEgMCBvYmoKPDwKL0YxIDIgMCBSIC9GMiAzIDAgUgo+PgplbmRvYmoKMiAwIG9iago8PAovQmFzZUZvbnQgL0hlbHZldGljYSAvRW5jb2RpbmcgL1dpbkFuc2lFbmNvZGluZyAvTmFtZSAvRjEgL1N1YnR5cGUgL1R5cGUxIC9UeXBlIC9Gb250Cj4+CmVuZG9iagozIDAgb2JqCjw8Ci9CYXNlRm9udCAvSGVsdmV0aWNhLUJvbGQgL0VuY29kaW5nIC9XaW5BbnNpRW5jb2RpbmcgL05hbWUgL0YyIC9TdWJ0eXBlIC9UeXBlMSAvVHlwZSAvRm9udAo+PgplbmRvYmoKNCAwIG9iago8PAovQ29udGVudHMgOCAwIFIgL01lZGlhQm94IFsgMCAwIDU5NS4yNzU2IDg0MS44ODk4IF0gL1BhcmVudCA3IDAgUiAvUmVzb3VyY2VzIDw8Ci9Gb250IDEgMCBSIC9Qcm9jU2V0IFsgL1BERiAvVGV4dCAvSW1hZ2VCIC9JbWFnZUMgL0ltYWdlSSBdCj4+IC9Sb3RhdGUgMCAvVHJhbnMgPDwKCj4+IAogIC9UeXBlIC9QYWdlCj4+CmVuZG9iago1IDAgb2JqCjw8Ci9QYWdlTW9kZSAvVXNlTm9uZSAvUGFnZXMgNyAwIFIgL1R5cGUgL0NhdGFsb2cKPj4KZW5kb2JqCjYgMCBvYmoKPDwKL0F1dGhvciAoYW5vbnltb3VzKSAvQ3JlYXRpb25EYXRlIChEOjIwMjYwNjA5MTk0NjM3KzAwJzAwJykgL0NyZWF0b3IgKGFub255bW91cykgL0tleXdvcmRzICgpIC9Nb2REYXRlIChEOjIwMjYwNjA5MTk0NjM3KzAwJzAwJykgL1Byb2R1Y2VyIChSZXBvcnRMYWIgUERGIExpYnJhcnkgLSBcKG9wZW5zb3VyY2VcKSkgCiAgL1N1YmplY3QgKHVuc3BlY2lmaWVkKSAvVGl0bGUgKExpZWZlcnNjaGVpbiBQREZfVEVNUExBVEUgRGVtbykgL1RyYXBwZWQgL0ZhbHNlCj4+CmVuZG9iago3IDAgb2JqCjw8Ci9Db3VudCAxIC9LaWRzIFsgNCAwIFIgXSAvVHlwZSAvUGFnZXMKPj4KZW5kb2JqCjggMCBvYmoKPDwKL0ZpbHRlciBbIC9BU0NJSTg1RGVjb2RlIC9GbGF0ZURlY29kZSBdIC9MZW5ndGggOTk3Cj4+CnN0cmVhbQpHYXUxLWdOKGFzJjtLWkYnUkwscD8kJjg4O0dwbyIvVmk9Jy9mQmpoSm5dJDtma0lBNWwrODAlW1pjSDNkYUJELy4kPFwiSTlRU1FNJCZaU2hMODdASkdmVVdeI2QkNSxXIj9jJ2RROmdPR286RWpvc0xWUiNiRi0hNVRTMWtRUzVYR0hqInEtRFY1XVhbMiEkTEVxWGY3aUJRIjwwSkRnKCFlJyEkLUEqI2YpWVNoSSJxKDhFNy1gLD9AZDVbW1VwOUFlK2k9PmpQXWdeZEhmaW5gZnVnbyNEWmsuVkhLJjQ8NlQjMVY2RFx1PzFVKCUyclxkMi5RM3MtOClsR2VUVHFqO2YwJ046ZFNKXldcP1lkSFA/LmdGW0dtN2htc1BJZDFCR0JlWzZjPTFdcjwkTW9lTzlmMEZTYGVjb1dPWF87VlxlZFg7MllSaFZyZXEwRXNYV3BaJ2FhLSFdP18mR3VAOjBDbCFiPzVrNnEwYmxTOy9uKkpBWSNDP1UwLVJSTj45I1YlbF0wXjw2WGZCQS9xXWMvJjwjLykmN1c/QltAKCtjajdCWWEkKXIubCpBczM8XSZ0KCcrS1QqRlVaa1ZrPWZacTtJUDRtbFE0RysrMURmW3BDJTtVR2ZIS0FHRjdmZ1xDSWEqPUFSI0c2YFg9IS1dY2hMZitNQ3E5a2ZgXDtuMmIwYmVJKWRePjsrZFhZYmRDS2ohcnJOJlItIzZtOChKNj4iMV8zWlVURV8mTm5ZcyVMWGo3Qkx1K1luaVU3MTU+N3RhcUpBaTE2YypLME8+czBiXWghaEFRPilFJHM5PiRAdXNeUldndD5MJyxRYnBEPCxmbW1jbzsibWBoZjc1VFBkKTpANSxnL0YnQ2FvMGpObEEqcC9fNVdjMDtWKk5BZ05nWi8iYyIlPzVRQTU3QGdNYj9LX3NPcVBxZU1UOEZdTWo1NE0vYVtPUERvc2Mmb01vbG5YPzZEMkJfWG1yXCo4ciFyQyk3WElePFUoKlZcQVExNCpuU1ZiRD04dSd0Z1trZ1w4ZGpec1socFRETlhyakRpU11qWksvM15QbW5rcGhxdEI5JSZaWzwwZ0UjQEk6RUdHLDUhMnRManAuTGVPLkl1Vyw2Im0sb2staW5oSz1RcFVZMDY5XCVeMDw9RklXcHBScSZuVjIqN1UzMjs/Uj84OzJDVUZMbnRXYVY+LmJjJU5KaElmOE9AMzREW2xiIVhhRkE8KksuRmR1JnA8dF1FVmM1czAjK25dYmhSJWJiLiRoOkNjNyRJWk1camhMSW9eK11vLVJYaTdaMlRhM1hETTxiP3FGJn4+ZW5kc3RyZWFtCmVuZG9iagp4cmVmCjAgOQowMDAwMDAwMDAwIDY1NTM1IGYgCjAwMDAwMDAwNjEgMDAwMDAgbiAKMDAwMDAwMDEwMiAwMDAwMCBuIAowMDAwMDAwMjA5IDAwMDAwIG4gCjAwMDAwMDAzMjEgMDAwMDAgbiAKMDAwMDAwMDUyNCAwMDAwMCBuIAowMDAwMDAwNTkyIDAwMDAwIG4gCjAwMDAwMDA4NzUgMDAwMDAgbiAKMDAwMDAwMDkzNCAwMDAwMCBuIAp0cmFpbGVyCjw8Ci9JRCAKWzxhYzA4MzlmM2UwZTNjNGMzYWNhOGRmNGVjM2E0OWNkMj48YWMwODM5ZjNlMGUzYzRjM2FjYThkZjRlYzNhNDljZDI+XQolIFJlcG9ydExhYiBnZW5lcmF0ZWQgUERGIGRvY3VtZW50IC0tIGRpZ2VzdCAob3BlbnNvdXJjZSkKCi9JbmZvIDYgMCBSCi9Sb290IDUgMCBSCi9TaXplIDkKPj4Kc3RhcnR4cmVmCjIwMjEKJSVFT0YK"
}

View File

@@ -0,0 +1,136 @@
{
"name": "lieferschein_pdf",
"documentName": "lieferschein_pdf",
"sourceFileName": "lieferschein_pdf_template.pdf",
"description": "Beispiel: PDF als Vorlage plus Koordinatenfelder für Text, Barcode und QR-Code.",
"contentType": "application/pdf",
"templateType": "PDF",
"encoding": "UTF-8",
"contentBase64": "JVBERi0xLjQKJZOMi54gUmVwb3J0TGFiIEdlbmVyYXRlZCBQREYgZG9jdW1lbnQgKG9wZW5zb3VyY2UpCjEgMCBvYmoKPDwKL0YxIDIgMCBSIC9GMiAzIDAgUgo+PgplbmRvYmoKMiAwIG9iago8PAovQmFzZUZvbnQgL0hlbHZldGljYSAvRW5jb2RpbmcgL1dpbkFuc2lFbmNvZGluZyAvTmFtZSAvRjEgL1N1YnR5cGUgL1R5cGUxIC9UeXBlIC9Gb250Cj4+CmVuZG9iagozIDAgb2JqCjw8Ci9CYXNlRm9udCAvSGVsdmV0aWNhLUJvbGQgL0VuY29kaW5nIC9XaW5BbnNpRW5jb2RpbmcgL05hbWUgL0YyIC9TdWJ0eXBlIC9UeXBlMSAvVHlwZSAvRm9udAo+PgplbmRvYmoKNCAwIG9iago8PAovQ29udGVudHMgOCAwIFIgL01lZGlhQm94IFsgMCAwIDU5NS4yNzU2IDg0MS44ODk4IF0gL1BhcmVudCA3IDAgUiAvUmVzb3VyY2VzIDw8Ci9Gb250IDEgMCBSIC9Qcm9jU2V0IFsgL1BERiAvVGV4dCAvSW1hZ2VCIC9JbWFnZUMgL0ltYWdlSSBdCj4+IC9Sb3RhdGUgMCAvVHJhbnMgPDwKCj4+IAogIC9UeXBlIC9QYWdlCj4+CmVuZG9iago1IDAgb2JqCjw8Ci9QYWdlTW9kZSAvVXNlTm9uZSAvUGFnZXMgNyAwIFIgL1R5cGUgL0NhdGFsb2cKPj4KZW5kb2JqCjYgMCBvYmoKPDwKL0F1dGhvciAoYW5vbnltb3VzKSAvQ3JlYXRpb25EYXRlIChEOjIwMjYwNjA4MTUwNDMzKzAwJzAwJykgL0NyZWF0b3IgKGFub255bW91cykgL0tleXdvcmRzICgpIC9Nb2REYXRlIChEOjIwMjYwNjA4MTUwNDMzKzAwJzAwJykgL1Byb2R1Y2VyIChSZXBvcnRMYWIgUERGIExpYnJhcnkgLSBcKG9wZW5zb3VyY2VcKSkgCiAgL1N1YmplY3QgKHVuc3BlY2lmaWVkKSAvVGl0bGUgKFBERi1Lb29yZGluYXRlbnZvcmxhZ2UgTGllZmVyc2NoZWluKSAvVHJhcHBlZCAvRmFsc2UKPj4KZW5kb2JqCjcgMCBvYmoKPDwKL0NvdW50IDEgL0tpZHMgWyA0IDAgUiBdIC9UeXBlIC9QYWdlcwo+PgplbmRvYmoKOCAwIG9iago8PAovRmlsdGVyIFsgL0FTQ0lJODVEZWNvZGUgL0ZsYXRlRGVjb2RlIF0gL0xlbmd0aCA5MTQKPj4Kc3RyZWFtCkdhdD1pOTI/S10mOmo2RidtIjEnKG0hUzo4MEwtXFtRTSJbTF1WUzVkWD1PP05OMyFUOjdQJjIiR2UxWFBkTyFAUjtYLjw2L19DUW9gJV9oUzdaXyY2cExlKyJBZ2ZWZmBkbVkiLmg/UF5DQ2hTJU90OyxDJVREMCsjSiFiVV8uIkJAODprSTB1MiRWZmI4bVJAOEEiJVNOVy1Ha0YyJSYoRVVDckE9UkkkPGwxYm00QTgoWCpaZFtUXyhqYCpAXThfNFpqOnJoJ1JrZickKm8zL2ZFMGJTQiRUMGErJ0xDWmcqUm8uQj5eTSVGWSQjMiE7MGg6P3MoSGk/QiFraCddVGJgI2dQblZXOE1UTUEsPUQ/b1c+OlYzM08rVzoyKFc4Mk8oT0VbK2R0XzJDOV1rYCVFPU8nMkEmajErTUEsYFwlWT07a11dcGFpbUo4cTVBZmNvQz4pYFBGYXIiS04vcSFxYidBW15qQEQpKGJqNWlMWCQhSmEtPXBiSzRVUVdCOiY5cnBWRW1NPCdbcEtIJy1pWUQiaVo8TDdwckcrLCcxblY9XVcoW1o1ajlAL0RXK2Fjb1k3NTZiUGBkVXEwRD03XTs+azNdcEUsNylUOVRAc2csOTRrPFgkL3MoP0MkVGYjLzg0VTVhTWlLOztUJDkmWEBkZGMvWjNjaU9eWT1zMlhBb2RIXHA+SmY3aWFaKlVaVmluM05qbVktXlBjNilvVFszZjRJQFFuNShYb05qWFFBJmZFY1RFVyJ0bU0tIzAuPFVaUDA7azhrKWMnVllVc2o1V1kzST9Qbz1FV20nbFRlYi5BOG9aOzQ7SnJCU3RlbGM4dVQ6J1VYYjwzVSMsWjtUSDRVVEdrV3AsUVVMRltWMW04RDAoKixQUyhNLXFWJ2hAZ2hnVzRHPUIkRWI1RWlnSk9ZK3RxPklzcF40JmNvYjpGUkZrcF1UXGQqSiQ/WydEKVE+TWlFJGtBNXUtYCJLRyE5TitRVUFIYyk+Vl5xI0Yob1I4Mi8oKGYmOiUtLEBXMzBbY10hU042akU8bjctLzciOVpzMHJuNnMuUj5GaCkjPSlSWjlOT3FmV3InSG8mSF5ONFYmJXVaLDhRSy9AOmpXWFBaYnEpYz8rVCRHMEdCI2dqJWNLOCpicSdDRWQ7WldjUmxpZTsuIWYrdEREbipBOWg4ak5ZYyJBZHBIJmZfIiVZb1g+bH4+ZW5kc3RyZWFtCmVuZG9iagp4cmVmCjAgOQowMDAwMDAwMDAwIDY1NTM1IGYgCjAwMDAwMDAwNjEgMDAwMDAgbiAKMDAwMDAwMDEwMiAwMDAwMCBuIAowMDAwMDAwMjA5IDAwMDAwIG4gCjAwMDAwMDAzMjEgMDAwMDAgbiAKMDAwMDAwMDUyNCAwMDAwMCBuIAowMDAwMDAwNTkyIDAwMDAwIG4gCjAwMDAwMDA4ODAgMDAwMDAgbiAKMDAwMDAwMDkzOSAwMDAwMCBuIAp0cmFpbGVyCjw8Ci9JRCAKWzwzYzM0OTVlYTNmMDgzZmViNWNlYzhlNjIyMjNjMDUzMT48M2MzNDk1ZWEzZjA4M2ZlYjVjZWM4ZTYyMjIzYzA1MzE+XQolIFJlcG9ydExhYiBnZW5lcmF0ZWQgUERGIGRvY3VtZW50IC0tIGRpZ2VzdCAob3BlbnNvdXJjZSkKCi9JbmZvIDYgMCBSCi9Sb290IDUgMCBSCi9TaXplIDkKPj4Kc3RhcnR4cmVmCjE5NDMKJSVFT0YK",
"coordinateUnit": "mm",
"coordinateOrigin": "TOP_LEFT",
"pdfFields": [
{
"name": "serialNumber",
"type": "TEXT",
"value": "{{serialNumber}}",
"page": 1,
"x": 35,
"y": 52,
"width": 70,
"height": 6,
"fontSize": 10
},
{
"name": "partNumber",
"type": "TEXT",
"value": "{{partNumber}}",
"page": 1,
"x": 35,
"y": 60,
"width": 70,
"height": 6,
"fontSize": 10
},
{
"name": "workorderNumber",
"type": "TEXT",
"value": "{{workorderNumber}}",
"page": 1,
"x": 35,
"y": 68,
"width": 70,
"height": 6,
"fontSize": 10
},
{
"name": "quantity",
"type": "TEXT",
"value": "{{quantity}}",
"page": 1,
"x": 35,
"y": 76,
"width": 70,
"height": 6,
"fontSize": 10,
"align": "RIGHT"
},
{
"name": "customer",
"type": "TEXT",
"value": "{{customer.name}}",
"page": 1,
"x": 35,
"y": 84,
"width": 70,
"height": 6,
"fontSize": 10
},
{
"name": "serialBarcode",
"type": "BARCODE",
"barcodeType": "CODE_128",
"value": "{{serialNumber}}",
"page": 1,
"x": 25,
"y": 94,
"width": 90,
"height": 18
},
{
"name": "serialQr",
"type": "QR_CODE",
"value": "{{serialNumber}}",
"page": 1,
"x": 155,
"y": 38,
"width": 32,
"height": 32
},
{
"name": "item1Pos",
"type": "TEXT",
"value": "{{items.0.pos}}",
"page": 1,
"x": 22,
"y": 142,
"width": 15,
"height": 5,
"fontSize": 9
},
{
"name": "item1Material",
"type": "TEXT",
"value": "{{items.0.material}}",
"page": 1,
"x": 40,
"y": 142,
"width": 55,
"height": 5,
"fontSize": 9
},
{
"name": "item1Description",
"type": "TEXT",
"value": "{{items.0.description}}",
"page": 1,
"x": 100,
"y": 142,
"width": 60,
"height": 5,
"fontSize": 9
},
{
"name": "item1Quantity",
"type": "TEXT",
"value": "{{items.0.quantity}}",
"page": 1,
"x": 165,
"y": 142,
"width": 20,
"height": 5,
"fontSize": 9,
"align": "RIGHT"
}
]
}

View File

@@ -0,0 +1,118 @@
{
"name": "lieferschein_pdf_table",
"documentName": "lieferschein_pdf_table",
"sourceFileName": "lieferschein_pdf_table_template.pdf",
"description": "PDF_TEMPLATE Beispiel mit dynamischer Tabelle, Barcode-Spalte und QR-Code",
"contentType": "application/pdf",
"templateType": "PDF",
"coordinateUnit": "mm",
"coordinateOrigin": "TOP_LEFT",
"contentBase64": "JVBERi0xLjQKJZOMi54gUmVwb3J0TGFiIEdlbmVyYXRlZCBQREYgZG9jdW1lbnQgKG9wZW5zb3VyY2UpCjEgMCBvYmoKPDwKL0YxIDIgMCBSIC9GMiAzIDAgUgo+PgplbmRvYmoKMiAwIG9iago8PAovQmFzZUZvbnQgL0hlbHZldGljYSAvRW5jb2RpbmcgL1dpbkFuc2lFbmNvZGluZyAvTmFtZSAvRjEgL1N1YnR5cGUgL1R5cGUxIC9UeXBlIC9Gb250Cj4+CmVuZG9iagozIDAgb2JqCjw8Ci9CYXNlRm9udCAvSGVsdmV0aWNhLUJvbGQgL0VuY29kaW5nIC9XaW5BbnNpRW5jb2RpbmcgL05hbWUgL0YyIC9TdWJ0eXBlIC9UeXBlMSAvVHlwZSAvRm9udAo+PgplbmRvYmoKNCAwIG9iago8PAovQ29udGVudHMgOCAwIFIgL01lZGlhQm94IFsgMCAwIDU5NS4yNzU2IDg0MS44ODk4IF0gL1BhcmVudCA3IDAgUiAvUmVzb3VyY2VzIDw8Ci9Gb250IDEgMCBSIC9Qcm9jU2V0IFsgL1BERiAvVGV4dCAvSW1hZ2VCIC9JbWFnZUMgL0ltYWdlSSBdCj4+IC9Sb3RhdGUgMCAvVHJhbnMgPDwKCj4+IAogIC9UeXBlIC9QYWdlCj4+CmVuZG9iago1IDAgb2JqCjw8Ci9QYWdlTW9kZSAvVXNlTm9uZSAvUGFnZXMgNyAwIFIgL1R5cGUgL0NhdGFsb2cKPj4KZW5kb2JqCjYgMCBvYmoKPDwKL0F1dGhvciAoYW5vbnltb3VzKSAvQ3JlYXRpb25EYXRlIChEOjIwMjYwNjA4MTYzNDMwKzAwJzAwJykgL0NyZWF0b3IgKGFub255bW91cykgL0tleXdvcmRzICgpIC9Nb2REYXRlIChEOjIwMjYwNjA4MTYzNDMwKzAwJzAwJykgL1Byb2R1Y2VyIChSZXBvcnRMYWIgUERGIExpYnJhcnkgLSBcKG9wZW5zb3VyY2VcKSkgCiAgL1N1YmplY3QgKHVuc3BlY2lmaWVkKSAvVGl0bGUgKFBERiBUYWJlbGxlbnZvcmxhZ2UgTGllZmVyc2NoZWluKSAvVHJhcHBlZCAvRmFsc2UKPj4KZW5kb2JqCjcgMCBvYmoKPDwKL0NvdW50IDEgL0tpZHMgWyA0IDAgUiBdIC9UeXBlIC9QYWdlcwo+PgplbmRvYmoKOCAwIG9iago8PAovRmlsdGVyIFsgL0FTQ0lJODVEZWNvZGUgL0ZsYXRlRGVjb2RlIF0gL0xlbmd0aCA2NzIKPj4Kc3RyZWFtCkdhdCVgOWxKYE4mO0taTCdtIjJsQUYjZmIxX1BSM0pLQC5ZMXBNP20mSFkoRi8kRDdZMXNOZihmRlRfYy4nNSVHQEM1TDhjY2pbOjZIInEkaFU7SURQSkhAcSJHcjVhNVxjdTkpKmZqY0JdOTxHZjVbJWwqb18zUUBBSl1VPVRmTzJfSFNONiQxTUFiZD5aNkBGP3MoIiZYJmNKSStuSGhCXkZfV0RBbENJKHI4XjtHYylMYXFxTSl1bCReNWU+VU1aPz1UdXE/YyhLOjJWYFxnIShHLTc5JFxRX2shMkhxZWRnRzQrcG1UYFlNTSUqXkBAYjs6OjtuT18uUWJbQnJAUHNkZEcvU3JQaHQvMkBqX3RiRFxUXEBDKGlaLjtfdVAnRW1nQ2RnMG1VIXA1QChbRGpyR1NvJyYhSzYrWzQ/OixoS1JhVmZ0Rio5XVE5JmMpRG86Pm5fbTQ/QD0pXU5sVm5uPyRDPCRePj5YTXUxW0c3WXVoPjs8LzZQOHBcMC4kK0AhaCc9YilxMFRoPSlsLyZUKXIyP2AsL1FCc0duISJNWUgucj1LSXFWLDtidVUuJEYoRXVKIV4/LldbLCNTNFhqRydLNSIjXltLYFlpXDFfbVBRcE1kMWNpT14jY21nJjQ6bi4yKEE+Y18mLSMhV3UkLipubU5nU0pfdGtlUVlpb0hASlY5QGwmZUYtXWpiWSZxOFNfaW0mZTwqYj4tS011VFdqdVdhP1wtcCEsW19xb049bSJhKUZXYlRzcitNQUA3YilhSGUjOSJGYi4vU3NMWXJNT1smc1taVTlBYVc5O29KIUo3Zz8xPzwxJUJoL1pZcT1qX29jcCY7MWldSy5pOXI0VUheWTlUbzdiNGxQJDtTXjZuJmlEO3F+PmVuZHN0cmVhbQplbmRvYmoKeHJlZgowIDkKMDAwMDAwMDAwMCA2NTUzNSBmIAowMDAwMDAwMDYxIDAwMDAwIG4gCjAwMDAwMDAxMDIgMDAwMDAgbiAKMDAwMDAwMDIwOSAwMDAwMCBuIAowMDAwMDAwMzIxIDAwMDAwIG4gCjAwMDAwMDA1MjQgMDAwMDAgbiAKMDAwMDAwMDU5MiAwMDAwMCBuIAowMDAwMDAwODc3IDAwMDAwIG4gCjAwMDAwMDA5MzYgMDAwMDAgbiAKdHJhaWxlcgo8PAovSUQgCls8NzIxZTc2ODg3OTIxMTlmN2U5ZGExYzgzNmI2YWEzMjk+PDcyMWU3Njg4NzkyMTE5ZjdlOWRhMWM4MzZiNmFhMzI5Pl0KJSBSZXBvcnRMYWIgZ2VuZXJhdGVkIFBERiBkb2N1bWVudCAtLSBkaWdlc3QgKG9wZW5zb3VyY2UpCgovSW5mbyA2IDAgUgovUm9vdCA1IDAgUgovU2l6ZSA5Cj4+CnN0YXJ0eHJlZgoxNjk4CiUlRU9GCg==",
"pdfFields": [
{
"name": "deliveryNumber",
"type": "TEXT",
"value": "{{deliveryNumber}}",
"page": 1,
"x": 38,
"y": 35,
"width": 70,
"height": 6,
"fontSize": 10
},
{
"name": "customerName",
"type": "TEXT",
"value": "{{customer.name}}",
"page": 1,
"x": 38,
"y": 43,
"width": 90,
"height": 6,
"fontSize": 10
},
{
"name": "deliveryDate",
"type": "TEXT",
"value": "{{deliveryDate}}",
"page": 1,
"x": 38,
"y": 51,
"width": 70,
"height": 6,
"fontSize": 10
},
{
"name": "deliveryQr",
"type": "QR_CODE",
"value": "{{deliveryNumber}}",
"page": 1,
"x": 162,
"y": 25,
"width": 28,
"height": 28
},
{
"name": "positionsTable",
"type": "TABLE",
"source": "items",
"page": 1,
"x": 18,
"y": 78,
"width": 174,
"height": 125,
"rowHeight": 13,
"headerHeight": 8,
"maxRows": 9,
"overflow": "CUT",
"fontSize": 7,
"border": true,
"drawHeader": true,
"repeatHeader": true,
"padding": 1.5,
"headerBackground": "#EEEEEE",
"headerColor": "#000000",
"gridColor": "#000000",
"columns": [
{
"title": "Pos.",
"field": "pos",
"width": 12,
"align": "CENTER"
},
{
"title": "Artikel",
"field": "partNumber",
"width": 29,
"align": "LEFT"
},
{
"title": "Bezeichnung",
"field": "description",
"width": 47,
"align": "LEFT",
"multiline": true
},
{
"title": "Menge",
"field": "quantity",
"width": 16,
"align": "RIGHT"
},
{
"title": "SNR",
"field": "serialNumber",
"width": 31,
"align": "LEFT"
},
{
"title": "Barcode",
"type": "BARCODE",
"barcodeType": "CODE_128",
"field": "serialNumber",
"width": 39
}
]
}
]
}

View File

@@ -0,0 +1,8 @@
{
"alias": "LABEL_LINE_01",
"type": "RAW_TCP",
"host": "192.168.10.50",
"port": 9100,
"language": "ZPL",
"description": "Zebra Etikettendrucker direkt per TCP"
}

View File

@@ -0,0 +1,6 @@
{
"alias": "OFFICE_A4",
"type": "WINDOWS_SPOOLER",
"systemName": "HP_LaserJet_A4",
"description": "A4 Drucker ueber Windows Spooler"
}

View File

@@ -0,0 +1,12 @@
{
"osName": "Windows Server 2022",
"windows": true,
"javaUser": "SYSTEM",
"likelyLocalSystem": true,
"spoolerPrinterCount": 0,
"recommendations": [
"GlassFish läuft wahrscheinlich als LocalSystem. Benutzergebundene Drucker werden meistens nicht gefunden.",
"Für PDF/HTML/PDF_TEMPLATE: Drucker lokal/serverweit installieren oder maschinenweite Printserver-Verbindung verwenden.",
"Für ZPL/EPL: RAW_TCP-Alias verwenden und direkt an IP:9100 senden."
]
}

View File

@@ -0,0 +1 @@
curl http://localhost:8080/api/docx/converters

View File

@@ -0,0 +1,2 @@
curl "http://localhost:8080/api/queue?full=true&user=line01&status=SUBMITTED&templateName=serial_label_zpl" \
-H "X-Printservice-Token: YOUR_ADMIN_TOKEN"

View File

@@ -0,0 +1,2 @@
curl "http://localhost:8080/api/queue?full=true&limit=500&includeRequest=false" \
-H "X-Printservice-Token: YOUR_ADMIN_TOKEN"

View File

@@ -0,0 +1,2 @@
curl http://localhost:8080/api/queue?limit=100 \
-H "X-Printservice-Token: YOUR_PRINT_TOKEN"

View File

@@ -0,0 +1,23 @@
{
"printerAlias": "OFFICE_A4",
"templateName": "lieferschein_docx",
"renderMode": "DOCX",
"docxConverter": "DOCX4J_FO",
"copies": 1,
"pdfDpi": 300,
"includeRenderedBase64": false,
"data": {
"deliveryNumber": "LS-2026-0001",
"deliveryDate": "09.06.2026",
"customer": {
"name": "Muster GmbH",
"address": "Musterstraße 1, 12345 Musterstadt"
},
"workorderNumber": "WO-100200",
"partNumber": "A123456",
"quantity": "120",
"serialNumber": "SNR000001",
"note": "Lieferung frei Rampe. Bitte Ware bei Annahme prüfen.",
"description": "Lieferschein Demo-Artikel"
}
}

View File

@@ -0,0 +1,16 @@
{
"printerAlias": "OFFICE_A4",
"templateName": "serienbegleitschein_docx",
"renderMode": "DOCX",
"docxConverter": "DOCX4J_FO",
"copies": 1,
"pdfDpi": 300,
"data": {
"serialNumber": "SNR000001",
"partNumber": "A123456",
"workorderNumber": "WO-100200",
"customer": {
"name": "Muster GmbH"
}
}
}

View File

@@ -0,0 +1,37 @@
{
"printerAlias": "OFFICE_A4",
"templateName": "lieferschein_html_table",
"renderMode": "PDF",
"copies": 1,
"pdfDpi": 300,
"data": {
"deliveryNumber": "LS-2026-0001",
"deliveryDate": "08.06.2026",
"customer": {
"name": "Muster GmbH"
},
"items": [
{
"pos": "1",
"partNumber": "A123456",
"description": "Gehäuse Oberteil",
"quantity": "10",
"serialNumber": "SNR000001"
},
{
"pos": "2",
"partNumber": "B987654",
"description": "Elektronikmodul",
"quantity": "5",
"serialNumber": "SNR000002"
},
{
"pos": "3",
"partNumber": "C555111",
"description": "Dichtung",
"quantity": "20",
"serialNumber": "SNR000003"
}
]
}
}

View File

@@ -0,0 +1,21 @@
{
"printerAlias": "OFFICE_A4",
"templateName": "lieferschein_pdf_simple",
"renderMode": "PDF_TEMPLATE",
"copies": 1,
"pdfDpi": 300,
"includeRenderedBase64": false,
"data": {
"deliveryNumber": "LS-2026-0001",
"deliveryDate": "09.06.2026",
"customer": {
"name": "Muster GmbH",
"address": "Musterstraße 1, 12345 Musterstadt"
},
"workorderNumber": "WO-100200",
"partNumber": "A123456",
"quantity": "120",
"serialNumber": "SNR000001",
"note": "Lieferung frei Rampe. Bitte Ware bei Annahme prüfen."
}
}

View File

@@ -0,0 +1,24 @@
{
"printerAlias": "OFFICE_A4",
"templateName": "lieferschein_pdf",
"renderMode": "PDF_TEMPLATE",
"copies": 1,
"pdfDpi": 300,
"data": {
"serialNumber": "SNR000001",
"partNumber": "A123456",
"workorderNumber": "WO-100200",
"quantity": "120",
"customer": {
"name": "Muster GmbH"
},
"items": [
{
"pos": "10",
"material": "A123456",
"description": "Baugruppe rechts",
"quantity": "120"
}
]
}
}

View File

@@ -0,0 +1,52 @@
{
"printerAlias": "OFFICE_A4",
"templateName": "lieferschein_pdf_table",
"renderMode": "PDF_TEMPLATE",
"copies": 1,
"pdfDpi": 300,
"includeRenderedBase64": false,
"data": {
"deliveryNumber": "LS-2026-0001",
"deliveryDate": "08.06.2026",
"customer": {
"name": "Muster GmbH"
},
"items": [
{
"pos": "1",
"partNumber": "A123456",
"description": "Gehäuse Oberteil",
"quantity": "10",
"serialNumber": "SNR000001"
},
{
"pos": "2",
"partNumber": "B987654",
"description": "Elektronikmodul",
"quantity": "5",
"serialNumber": "SNR000002"
},
{
"pos": "3",
"partNumber": "C555111",
"description": "Dichtungssatz EPDM",
"quantity": "20",
"serialNumber": "SNR000003"
},
{
"pos": "4",
"partNumber": "D444222",
"description": "Sensorleitung vorkonfektioniert",
"quantity": "7",
"serialNumber": "SNR000004"
},
{
"pos": "5",
"partNumber": "E777888",
"description": "Befestigungsclip",
"quantity": "30",
"serialNumber": "SNR000005"
}
]
}
}

View File

@@ -0,0 +1,12 @@
{
"printerAlias": "LABEL_LINE_01",
"templateName": "serial_label_zpl",
"renderMode": "RAW",
"copies": 1,
"encoding": "UTF-8",
"data": {
"serialNumber": "SNR000001",
"partNumber": "A123456",
"workorderNumber": "WO-100200"
}
}

View File

@@ -0,0 +1,25 @@
{
"total": 1,
"returned": 1,
"filteredToCurrentUser": true,
"currentUser": "line01",
"jobs": [
{
"jobId": "31b5a0e2-6c99-4098-9c6c-8aa80d7e2231",
"status": "SUBMITTED",
"createdAt": "2026-06-09T07:12:15Z",
"updatedAt": "2026-06-09T07:12:16Z",
"userName": "line01",
"displayName": "Linie 01",
"printerAlias": "LABEL_LINE_01",
"systemName": "192.168.10.50:9100",
"templateName": "serial_label_zpl",
"documentName": "Serienetikett ZPL",
"renderMode": "RAW",
"contentType": "text/plain",
"copies": 1,
"renderedBytes": 164,
"message": "Print job submitted"
}
]
}

View File

@@ -0,0 +1,7 @@
{
"copies": 1,
"printerAlias": "LABEL_LINE_01",
"dataOverride": {
"reprintReason": "Etikett beschädigt"
}
}

View File

@@ -0,0 +1,4 @@
curl -X POST http://localhost:8080/api/queue/JOB_ID/reprint \
-H "Content-Type: application/json" \
-H "X-Printservice-Token: YOUR_PRINT_TOKEN" \
-d @rest-reprint-request.json

View File

@@ -0,0 +1,8 @@
{
"enabled": true,
"protectPrint": true,
"protectRenderedDownload": true,
"protectPreview": false,
"protectAdmin": true,
"headerName": "X-Printservice-Token"
}

View File

@@ -0,0 +1,20 @@
{
"templateName": "lieferschein_docx",
"renderMode": "DOCX",
"docxConverter": "DOCX4J_FO",
"includeRenderedBase64": false,
"data": {
"deliveryNumber": "LS-2026-0001",
"deliveryDate": "09.06.2026",
"customer": {
"name": "Muster GmbH",
"address": "Musterstraße 1, 12345 Musterstadt"
},
"workorderNumber": "WO-100200",
"partNumber": "A123456",
"quantity": "120",
"serialNumber": "SNR000001",
"note": "Lieferung frei Rampe. Bitte Ware bei Annahme prüfen.",
"description": "Lieferschein Demo-Artikel"
}
}

View File

@@ -0,0 +1,15 @@
{
"printerAlias": "OFFICE_A4",
"templateName": "serienbegleitschein_docx",
"renderMode": "DOCX",
"docxConverter": "DOCX4J_FO",
"includeRenderedBase64": true,
"data": {
"serialNumber": "SNR000001",
"partNumber": "A123456",
"workorderNumber": "WO-100200",
"customer": {
"name": "Muster GmbH"
}
}
}

View File

@@ -0,0 +1,4 @@
curl -X POST "http://localhost:8080/api/docx/validate?converter=DOCX4J_FO" \
-H "Content-Type: application/json" \
-H "X-Printservice-Token: YOUR_TOKEN" \
-d @src/main/resources/examples/rest-validate-docx4j-fo-request.json

View File

@@ -0,0 +1,20 @@
{
"templateName": "lieferschein_html_table",
"renderMode": "PDF",
"data": {
"deliveryNumber": "LS-2026-0001",
"deliveryDate": "08.06.2026",
"customer": {
"name": "Muster GmbH"
},
"items": [
{
"pos": "1",
"partNumber": "A123456",
"description": "Gehäuse Oberteil",
"quantity": "10",
"serialNumber": "SNR000001"
}
]
}
}

View File

@@ -0,0 +1,7 @@
^XA
^CF0,35
^FO40,40^FDSerial: {{serialNumber}}^FS
^FO40,90^FDPart: {{partNumber}}^FS
^FO40,140^FDOrder: {{workorderNumber}}^FS
^FO40,200^BCN,100,Y,N,N^FD{{serialNumber}}^FS
^XZ

View File

@@ -0,0 +1,7 @@
^XA
^CF0,35
^FO40,40^FDSerial: {{serialNumber}}^FS
^FO40,90^FDPart: {{partNumber}}^FS
^FO40,145^BCN,80,Y,N,N^FD{{serialNumber}}^FS
^FO40,260^BQN,2,6^FDLA,{{serialNumber}}^FS
^XZ

Binary file not shown.

View File

@@ -0,0 +1,12 @@
<!doctype html>
<html>
<head><meta charset="utf-8"><title>Warenanhänger</title></head>
<body style="font-family: Arial; width: 90mm; padding: 10mm;">
<h2>Warenanhänger</h2>
<p><b>Serial:</b> {{text:serialNumber}}</p>
<p><b>Part:</b> {{text:partNumber}}</p>
<p><b>Order:</b> {{text:workorderNumber}}</p>
<div><img alt="Barcode" src="{{barcode:CODE_128:serialNumber:360x90}}"></div>
<div><img alt="QR" src="{{qr:serialNumber}}" style="width:120px;height:120px;"></div>
</body>
</html>

View File

@@ -0,0 +1,23 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<style>
@page { size: 90mm 130mm; margin: 6mm; }
body { font-family: Arial, sans-serif; font-size: 12pt; }
.label { width: 78mm; }
h1 { font-size: 18pt; margin: 0 0 8px; }
</style>
</head>
<body>
<div class="label">
<h1>Warenanhänger</h1>
<p><b>Serial:</b> {{text:serialNumber}}</p>
<p><b>Part:</b> {{text:partNumber}}</p>
<p><b>Order:</b> {{text:workorderNumber}}</p>
<img alt="Barcode" src="{{barcode:CODE_128:serialNumber:360x90}}">
<br>
<img alt="QR" src="{{qr:serialNumber}}" style="width:120px;height:120px;">
</div>
</body>
</html>

View File

@@ -0,0 +1,399 @@
<!doctype html>
<html lang="de">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Printservice Standalone</title>
<style>
:root { font-family: Arial, Helvetica, sans-serif; color: #17202a; background: #f3f5f7; }
body { margin: 0; }
header { background: #1f2937; color: white; padding: 18px 24px; }
main { padding: 24px; display: grid; gap: 18px; max-width: 1480px; margin: auto; }
section { background: white; border-radius: 12px; box-shadow: 0 2px 10px rgba(0,0,0,.08); padding: 18px; }
h1, h2, h3 { margin: 0 0 12px; }
h1 { font-size: 24px; } h2 { font-size: 18px; } h3 { font-size: 15px; margin-top: 16px; }
button { border: 0; border-radius: 8px; padding: 9px 13px; background: #2563eb; color: white; cursor: pointer; margin: 4px 4px 4px 0; }
button.secondary { background: #4b5563; } button.danger { background: #dc2626; } button.success { background: #059669; }
input, textarea, select { width: 100%; box-sizing: border-box; padding: 9px; border: 1px solid #cbd5e1; border-radius: 8px; margin: 4px 0 10px; font-family: inherit; }
textarea { min-height: 170px; font-family: Consolas, monospace; }
label { font-weight: 600; font-size: 13px; }
table { width: 100%; border-collapse: collapse; margin-top: 10px; }
th, td { border-bottom: 1px solid #e5e7eb; padding: 9px; text-align: left; vertical-align: top; }
th { background: #f9fafb; }
pre { white-space: pre-wrap; background: #0f172a; color: #e5e7eb; padding: 12px; border-radius: 8px; overflow: auto; min-height: 80px; }
iframe { width: 100%; height: 360px; border: 1px solid #cbd5e1; border-radius: 8px; background: white; }
.grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(360px, 1fr)); gap: 18px; }
.small { color: #d1d5db; font-size: 13px; }
.muted { color: #6b7280; font-size: 13px; }
.status { border-left: 5px solid #2563eb; padding: 10px 12px; background: #eff6ff; border-radius: 8px; }
.hint { background: #f8fafc; border: 1px solid #e2e8f0; border-radius: 8px; padding: 10px; font-size: 13px; color: #334155; }
.two { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.three { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 12px; }
.designer { min-height: 260px; border: 1px solid #cbd5e1; border-radius: 8px; padding: 12px; background: white; overflow: auto; }
.badge { display: inline-block; background: #e5e7eb; border-radius: 999px; padding: 3px 8px; font-size: 12px; margin-right: 4px; }
.ok { border-left-color: #059669; background: #ecfdf5; }
.warn { border-left-color: #d97706; background: #fffbeb; }
.cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 12px; margin-top: 12px; }
.card { border: 1px solid #e2e8f0; border-radius: 10px; padding: 12px; background: #ffffff; }
.codebox { white-space: pre-wrap; background: #111827; color: #f9fafb; border-radius: 8px; padding: 10px; font-family: Consolas, monospace; font-size: 12px; min-height: 60px; }
@media (max-width: 900px) { .two, .three { grid-template-columns: 1fr; } }
</style>
</head>
<body>
<header>
<h1>Printservice Standalone</h1>
<div class="small">Eigenständige Java-25-Applikation · REST Druckservice · Drucker-Aliase · HTML→PDF mit Tabellen · PDF-Koordinatenvorlagen · DOCX-Vorlagen · ZPL/EPL-Rohdruck · Barcode · QR-Code · Ordnerbasierte Vorlagenspeicherung</div>
</header>
<main>
<section><h2>Status</h2><div id="status" class="status">Bereit.</div></section>
<section>
<h2>Authentifizierung, Benutzer und Token</h2>
<div class="hint">
Authentifizierung ist optional. Wenn sie deaktiviert ist, kann jeder, der den REST-Endpunkt erreicht, drucken. Wenn sie aktiviert wird, kann der Benutzer im Webfrontend angelegt werden und erhält ein Token. Das Token wird als Header <code>X-Printservice-Token</code> oder <code>Authorization: Bearer ...</code> gesendet.
</div>
<div class="grid">
<div>
<h3>Token im Browser nutzen</h3>
<label>Druck-Token</label><input id="apiToken" type="password" placeholder="Token für /api/print">
<button onclick="saveToken()">Token im Browser merken</button><button class="secondary" onclick="clearToken()">Token entfernen</button>
<button class="secondary" onclick="loadAuthStatus()">Status laden</button>
<label>Server-Auth-Status</label><pre id="authStatus">Noch nicht geladen.</pre>
</div>
<div>
<h3>Auth einschalten / ausschalten</h3>
<label><input id="authEnabled" type="checkbox" style="width:auto"> Authentifizierung aktivieren</label><br>
<label><input id="protectPrint" type="checkbox" style="width:auto" checked> Drucken schuetzen</label><br>
<label><input id="protectPreview" type="checkbox" style="width:auto"> Vorschau schuetzen</label><br>
<label><input id="protectAdmin" type="checkbox" style="width:auto"> Admin-Funktionen schuetzen</label><br>
<label>Header-Name</label><input id="authHeaderName" value="X-Printservice-Token">
<button onclick="saveAuthConfig()">Auth-Konfiguration speichern</button>
<div class="muted">Empfehlung: zuerst Admin-Benutzer anlegen, Token speichern, dann Auth aktivieren.</div>
</div>
<div>
<h3>Benutzer / Token anlegen</h3>
<label>User</label><input id="newUserName" placeholder="z.B. line01">
<label>Anzeigename</label><input id="newDisplayName" placeholder="Linie 01">
<label>Token-Name</label><input id="newTokenName" placeholder="line01-print-token">
<label>Scopes</label><select id="newScopes" multiple size="4"><option value="PRINT" selected>PRINT</option><option value="PREVIEW">PREVIEW</option><option value="ADMIN">ADMIN</option><option value="ALL">ALL</option></select>
<button onclick="createAuthUser()">User + Token erzeugen</button>
<button class="secondary" onclick="loadAuthUsers()">Benutzer laden</button>
</div>
</div>
<h3>Benutzerliste</h3>
<table><thead><tr><th>User</th><th>Name</th><th>Token</th><th>Scopes</th><th>Aktiv</th><th></th></tr></thead><tbody id="authUsersBody"></tbody></table>
<h3>Neues Token - nur einmal sichtbar</h3>
<pre id="newTokenResult">Noch kein Token erzeugt.</pre>
</section>
<section>
<h2>Speicherstruktur</h2>
<div class="hint">
Vorlagen werden produktionsnah als Ordner gespeichert: <code>templates/&lt;templateName&gt;/template.json</code>, <code>document.*</code>, <code>fields.json</code>, <code>sample-data.json</code> und <code>print-request.json</code>. Dadurch bleiben PDF/DOCX/HTML-Dateien auffindbar, sicherbar und separat versionierbar.
</div>
<button onclick="loadTemplateStorage()">Speicherpfade anzeigen</button>
<pre id="storageInfo">Noch nicht geladen.</pre>
</section>
<section>
<h2>1. Windows-/Server-Drucker-Assistent</h2>
<div class="hint">
Der Assistent erkennt, ob der Service unter Windows und wahrscheinlich unter <b>LocalSystem</b> laeuft. Danach zeigt er die passenden Wege: gefundene Spooler-Drucker nutzen, lokale TCP/IP-Drucker serverweit installieren, Printserver-Drucker maschinenweit verbinden oder ZPL/EPL direkt per RAW_TCP drucken.
</div>
<button onclick="loadPrinterDiagnostics()">Assistent starten / Diagnose laden</button>
<button class="secondary" onclick="scanPrinters()">Spooler/CUPS-Drucker neu suchen</button>
<div id="diagnosticsSummary" class="status" style="margin-top:10px">Noch keine Diagnose geladen.</div>
<div class="cards">
<div class="card">
<h3>A. Gefundene Windows-Spooler-/CUPS-Drucker</h3>
<div class="muted">Geeignet fuer PDF, HTML-&gt;PDF, PDF_TEMPLATE und auch RAW-Druck ueber installierte Treiber.</div>
<table><thead><tr><th>Systemname</th><th>Standard</th><th>Aktion</th></tr></thead><tbody id="printerScanBody"></tbody></table>
</div>
<div class="card">
<h3>B. RAW_TCP fuer ZPL/EPL</h3>
<div class="muted">Schnellster Weg fuer Etikettendrucker. Umgeht den Windows-Spooler und funktioniert auch bei LocalSystem.</div>
<label>Drucker-IP / Host</label><input id="rawHost" placeholder="192.168.10.50">
<label>Port</label><input id="rawPort" type="number" min="1" max="65535" value="9100">
<label>Druckersprache</label><select id="rawLanguage"><option value="ZPL">ZPL</option><option value="EPL">EPL</option><option value="ESC_POS">ESC/POS</option><option value="PCL">PCL</option><option value="TEXT">TEXT</option></select>
<button onclick="prepareRawTcpAlias()">RAW_TCP Alias vorbereiten</button>
</div>
<div class="card">
<h3>C. Windows-Kommandos</h3>
<div class="muted">Diese Hinweise werden vom Server geliefert und passen zum Dienstkontext.</div>
<label>Lokaler TCP/IP-Drucker</label><div id="cmdLocalTcp" class="codebox">Noch nicht geladen.</div>
<label>Printserver maschinenweit</label><div id="cmdMachinePrinter" class="codebox">Noch nicht geladen.</div>
</div>
</div>
</section>
<section>
<h2>2. Eindeutigen Druckernamen zuweisen</h2>
<div class="three">
<div><label>Alias / eindeutiger Name</label><input id="printerAlias" placeholder="z.B. LABEL_LINE_01"></div>
<div><label>Druckertyp</label><select id="printerType" onchange="togglePrinterTypeFields()"><option value="WINDOWS_SPOOLER">WINDOWS_SPOOLER</option><option value="RAW_TCP">RAW_TCP</option></select></div>
<div><label>Beschreibung</label><input id="printerDescription" placeholder="z.B. Zebra an Linie 1"></div>
</div>
<div id="spoolerFields"><label>Systemdruckername</label><input id="systemName" placeholder="Name aus Windows Spooler oder Linux CUPS"></div>
<div id="rawTcpFields" class="three" style="display:none">
<div><label>Host/IP</label><input id="printerHost" placeholder="192.168.10.50"></div>
<div><label>Port</label><input id="printerPort" type="number" min="1" max="65535" value="9100"></div>
<div><label>Sprache</label><input id="printerLanguage" placeholder="ZPL, EPL, ESC_POS, PCL"></div>
</div>
<button onclick="savePrinterAlias()">Alias speichern</button><button class="secondary" onclick="loadPrinterAliases()">Aliase laden</button>
<table><thead><tr><th>Alias</th><th>Typ</th><th>Ziel</th><th>Aktiv</th><th></th></tr></thead><tbody id="printerAliasBody"></tbody></table>
</section>
<section>
<h2>3. Dokumentenvorlage einchecken oder online erstellen</h2>
<div class="hint">
Schnellster Druckpfad: <b>ZPL/EPL</b> direkt als Rohdaten oder <b>PDF_TEMPLATE</b> für fertige PDF-Vorlagen mit Koordinatenfeldern. HTML→PDF bleibt für Webvorlagen nutzbar und unterstützt Tabellen mit JSON-Wiederholungsblöcken wie <code>{{#each:items}}</code>.
PDF-Koordinatenfelder nutzen <code>x/y/width/height</code> in <code>pt</code> oder <code>mm</code>; Text, Barcode und QR werden direkt ins PDF gestempelt.
</div>
<div class="grid">
<div>
<label>Datei einchecken: .pdf, .html, .zpl, .epl, .txt, .xml, .docx</label>
<input id="templateFile" type="file" accept=".pdf,.html,.htm,.zpl,.epl,.txt,.tpl,.xml,.json,.docx">
<button class="success" onclick="checkInDocument()">Dokument einchecken</button>
<button class="secondary" onclick="insertHtmlSample()">HTML-Beispiel</button>
<button class="secondary" onclick="insertHtmlTableSample()">HTML-Tabellenbeispiel</button>
<button class="secondary" onclick="insertZplSample()">ZPL-Beispiel</button>
<button class="secondary" onclick="insertDocxInfo()">DOCX-Hinweis</button>
<button class="secondary" onclick="insertPdfFieldSample()">PDF-Feldbeispiel</button>
<button class="secondary" onclick="insertPdfTableSample()">PDF-Tabellenbeispiel</button>
</div>
<div>
<label>Vorlagenname / Dokumentname</label><input id="templateName" placeholder="z.B. warenanhaenger_html">
<label>Beschreibung</label><input id="templateDescription" placeholder="z.B. Warenanhänger mit Seriennummer und QR-Code">
<div id="binaryNotice" class="hint" style="display:none">Binärvorlage ist eingecheckt. DOCX/PDF wird als Datei im Vorlagenordner gespeichert. Für PDF werden die Koordinatenfelder separat in fields.json gepflegt.</div>
</div>
</div>
<div class="three">
<div><label>Content-Type</label><select id="templateContentType" onchange="syncTemplateTypeFromContentType()">
<option value="text/html">text/html - HTML-Dokument</option>
<option value="text/plain">text/plain - Text, ZPL, EPL, PCL</option>
<option value="application/xml">application/xml</option>
<option value="application/json">application/json</option>
<option value="application/vnd.openxmlformats-officedocument.wordprocessingml.document">application/vnd.openxmlformats-officedocument.wordprocessingml.document</option>
<option value="application/pdf">application/pdf - PDF-Koordinatenvorlage</option>
</select></div>
<div><label>Vorlagentyp</label><select id="templateType" onchange="toggleBinaryNotice()">
<option value="HTML">HTML</option><option value="PDF">PDF</option><option value="ZPL">ZPL</option><option value="EPL">EPL</option><option value="TEXT">TEXT</option><option value="XML">XML</option><option value="DOCX">DOCX</option>
</select></div>
<div><label>Encoding</label><input id="templateEncoding" value="UTF-8"></div>
</div>
<div class="two">
<div>
<label>Vorlageninhalt / Quelltext</label>
<textarea id="templateContent"></textarea>
<input id="templateContentBase64" type="hidden">
<button onclick="saveTemplate()">Vorlage speichern</button>
<button class="secondary" onclick="loadTemplates()">Vorlagen laden</button>
<button class="secondary" onclick="loadBarcodeFormats()">Barcode-Arten anzeigen</button>
<label>PDF-Koordinatenfelder als JSON</label>
<textarea id="pdfFieldsJson" placeholder='{"coordinateUnit":"mm","coordinateOrigin":"TOP_LEFT","pdfFields":[...]}'></textarea>
</div>
<div>
<label>Einfacher HTML-Designer</label>
<div id="htmlDesigner" class="designer" contenteditable="true"></div>
<button class="secondary" onclick="sourceToDesigner()">HTML aus Quelltext übernehmen</button>
<button class="secondary" onclick="designerToSource()">Designer nach Quelltext übernehmen</button>
<div class="muted">Der Designer ist für HTML-Vorlagen gedacht. DOCX bitte in Word oder LibreOffice bearbeiten und erneut einchecken.</div>
</div>
</div>
<h3>Eingecheckte Vorlagen</h3>
<table><thead><tr><th>Name</th><th>Dokument</th><th>Typ</th><th>Speicher</th><th>Größe</th><th>Geändert</th><th></th></tr></thead><tbody id="templateBody"></tbody></table>
<h3>Barcode-Arten</h3><div id="barcodeFormats" class="hint">Noch nicht geladen.</div>
</section>
<section>
<h2>4. JSON übergeben, Vorschau erzeugen und drucken</h2>
<div class="grid">
<div>
<label>Printer Alias</label><input id="printAlias" placeholder="LABEL_LINE_01">
<label>Template / Dokumentname</label><input id="printTemplate" placeholder="warenanhaenger_html">
<div class="three">
<div><label>Kopien</label><input id="copies" type="number" min="1" max="99" value="1"></div>
<div><label>Render-Modus</label><select id="renderMode"><option value="AUTO">AUTO</option><option value="PDF_TEMPLATE">PDF_TEMPLATE</option><option value="PDF">PDF</option><option value="RAW">RAW</option><option value="DOCX">DOCX</option></select></div>
<div><label>PDF-DPI</label><input id="pdfDpi" type="number" min="0" max="1200" value="203"></div>
</div>
<label>DOCX-Konverter</label><select id="docxConverter"><option value="NONE">NONE - nur DOCX herunterladen</option><option value="DOCX4J_FO">DOCX4J_FO - Java-only DOCX zu PDF</option><option value="LIBREOFFICE">LIBREOFFICE - DOCX zu PDF und drucken</option></select>
<label>JSON-Daten</label>
<textarea id="printData">{
"serialNumber": "SNR000001",
"partNumber": "A123456",
"workorderNumber": "WO-100200",
"deliveryNumber": "LS-2026-0001",
"deliveryDate": "08.06.2026",
"customer": { "name": "Muster GmbH" },
"items": [
{ "pos": "1", "partNumber": "A123456", "description": "Gehäuse Oberteil", "quantity": "10", "serialNumber": "SNR000001" },
{ "pos": "2", "partNumber": "B987654", "description": "Elektronikmodul", "quantity": "5", "serialNumber": "SNR000002" },
{ "pos": "3", "partNumber": "C555111", "description": "Dichtung", "quantity": "20", "serialNumber": "SNR000003" }
]
}</textarea>
<button onclick="previewPrint()">JSON-Vorschau</button>
<button onclick="previewHtmlOnly()">HTML-Vorschau</button>
<button onclick="downloadRendered()">Gerendertes Dokument herunterladen</button>
<button class="secondary" onclick="loadDocxConverters()">DOCX-Konverterstatus</button>
<button class="secondary" onclick="validateDocxConverter()">DOCX-Konverter validieren</button>
<button onclick="sendPrint()">Drucken</button>
</div>
<div>
<h3>Antwort / gerenderter Inhalt</h3><pre id="printResult">Noch kein Druckjob.</pre>
<h3>HTML-Vorschau</h3><iframe id="htmlPreview" title="HTML Vorschau"></iframe>
</div>
</div>
</section>
<section>
<h2>5. Druckwarteschlange / Druckhistorie und Redruck</h2>
<div class="hint">
Der Printservice speichert jeden gesendeten Druckjob unter <code>queue/print-jobs.json</code>. Damit kann ein Benutzer seine eigenen Jobs erneut drucken. Ein Admin kann mit <code>full=true</code> die volle Liste abrufen und filtern.
</div>
<div class="three">
<div><label>User-Filter</label><input id="queueUser" placeholder="leer = eigener User; Admin mit full=true sieht alle"></div>
<div><label>Status</label><select id="queueStatus"><option value="">Alle</option><option value="SUBMITTED">SUBMITTED</option><option value="FAILED">FAILED</option><option value="ACCEPTED">ACCEPTED</option></select></div>
<div><label>Limit</label><input id="queueLimit" type="number" min="1" max="1000" value="100"></div>
</div>
<div class="three">
<div><label>Printer Alias</label><input id="queuePrinterAlias" placeholder="optional"></div>
<div><label>Template</label><input id="queueTemplateName" placeholder="optional"></div>
<div><label><input id="queueFull" type="checkbox" style="width:auto"> Volle Liste abrufen (ADMIN)</label><br><label><input id="queueIncludeRequest" type="checkbox" style="width:auto"> Originalrequest anzeigen</label></div>
</div>
<button onclick="loadQueue()">Druckwarteschlange laden</button>
<table><thead><tr><th>Zeit</th><th>Status</th><th>User</th><th>Drucker</th><th>Vorlage</th><th>Kopien</th><th>Job-ID</th><th></th></tr></thead><tbody id="queueBody"></tbody></table>
<h3>Job-Details</h3><pre id="queueDetails">Noch kein Job ausgewählt.</pre>
</section>
</main>
<script>
const appBase = window.location.pathname.endsWith('/') ? window.location.pathname : window.location.pathname.replace(/\/[^\/]*$/, '/');
const api = appBase.replace(/\/$/, '') + '/api';
function setStatus(text, isError = false) { const box = document.getElementById('status'); box.textContent = text; box.style.borderLeftColor = isError ? '#dc2626' : '#2563eb'; box.style.background = isError ? '#fef2f2' : '#eff6ff'; }
function authHeaders(extra = {}) { const token = localStorage.getItem('printservice.token') || valOrEmpty('apiToken'); const headers = { ...(extra || {}) }; if (token) headers['X-Printservice-Token'] = token; return headers; }
async function request(path, options = {}) { const baseHeaders = { 'Content-Type': 'application/json', ...(options.headers || {}) }; const response = await fetch(api + path, { headers: authHeaders(baseHeaders), ...options }); if (!response.ok) { let message = response.status + ' ' + response.statusText; try { const err = await response.json(); message = err.message || message; } catch (ignore) {} throw new Error(message); } if (response.status === 204) return null; const contentType = response.headers.get('content-type') || ''; if (contentType.includes('application/json')) return response.json(); return response.text(); }
function saveToken() { localStorage.setItem('printservice.token', val('apiToken')); setStatus('Token lokal im Browser gespeichert.'); }
function clearToken() { localStorage.removeItem('printservice.token'); document.getElementById('apiToken').value = ''; setStatus('Token entfernt.'); }
async function loadAuthStatus() { try { const info = await request('/auth/status'); document.getElementById('authStatus').textContent = JSON.stringify(info, null, 2); document.getElementById('authEnabled').checked = !!info.enabled; document.getElementById('protectPrint').checked = !!info.protectPrint; document.getElementById('protectPreview').checked = !!info.protectPreview; document.getElementById('protectAdmin').checked = !!info.protectAdmin; document.getElementById('authHeaderName').value = info.headerName || 'X-Printservice-Token'; await loadAuthUsers(); } catch (e) { document.getElementById('authStatus').textContent = e.message; } }
async function saveAuthConfig() { try { const payload = { enabled: document.getElementById('authEnabled').checked, protectPrint: document.getElementById('protectPrint').checked, protectPreview: document.getElementById('protectPreview').checked, protectAdmin: document.getElementById('protectAdmin').checked, headerName: val('authHeaderName') || 'X-Printservice-Token' }; const info = await request('/auth/config', { method:'PUT', body: JSON.stringify(payload) }); document.getElementById('authStatus').textContent = JSON.stringify(info, null, 2); setStatus(info.openPrintWithoutToken ? 'Auth gespeichert: Drucken ist weiterhin offen.' : 'Auth gespeichert: Drucken ist jetzt token-geschützt.'); } catch(e) { setStatus(e.message, true); } }
function selectedScopes() { return Array.from(document.getElementById('newScopes').selectedOptions).map(o => o.value); }
async function createAuthUser() { try { const payload = { userName: val('newUserName'), displayName: val('newDisplayName'), tokenName: val('newTokenName'), scopes: selectedScopes(), enabled: true }; const created = await request('/auth/users', { method:'POST', body: JSON.stringify(payload) }); document.getElementById('newTokenResult').textContent = JSON.stringify(created, null, 2); if (created.token) { document.getElementById('apiToken').value = created.token; localStorage.setItem('printservice.token', created.token); } await loadAuthUsers(); await loadAuthStatus(); setStatus('Benutzer/Token erzeugt. Token wurde lokal im Browser gesetzt.'); } catch(e) { setStatus(e.message, true); } }
async function loadAuthUsers() { try { const users = await request('/auth/users'); const body = document.getElementById('authUsersBody'); if (!body) return; body.innerHTML = ''; users.forEach(u => { const tr = document.createElement('tr'); tr.innerHTML = `<td>${escapeHtml(u.userName)}</td><td>${escapeHtml(u.displayName || '')}</td><td>${escapeHtml(u.tokenName || '')}</td><td>${escapeHtml((u.scopes || []).join(', '))}</td><td>${u.enabled ? 'Ja' : 'Nein'}</td>`; const td = document.createElement('td'); const del = document.createElement('button'); del.textContent = 'Löschen'; del.className = 'danger'; del.onclick = async () => { await request('/auth/users/' + encodeURIComponent(u.id || u.userName), { method:'DELETE' }); await loadAuthUsers(); await loadAuthStatus(); }; td.appendChild(del); tr.appendChild(td); body.appendChild(tr); }); } catch(e) { const body = document.getElementById('authUsersBody'); if (body) body.innerHTML = `<tr><td colspan="6">${escapeHtml(e.message)}</td></tr>`; } }
async function loadTemplateStorage() { try { const info = await request('/templates/storage'); document.getElementById('storageInfo').textContent = JSON.stringify(info, null, 2); setStatus('Speicherstruktur geladen (Ordnerstruktur ohne Legacy): ' + info.templatesDir); } catch (e) { setStatus(e.message, true); } }
async function loadPrinterDiagnostics() { try { setStatus('Lade Windows-/Druckerdiagnose ...'); const d = await request('/printers/diagnostics'); const summary = document.getElementById('diagnosticsSummary'); const warnings = (d.warnings || []).map(escapeHtml).join('<br>'); const recs = (d.recommendations || []).map(r => '• ' + escapeHtml(r)).join('<br>'); summary.className = 'status ' + ((d.warnings || []).length ? 'warn' : 'ok'); summary.innerHTML = `<b>OS:</b> ${escapeHtml(d.osName)} · <b>Java-User:</b> ${escapeHtml(d.javaUser)} · <b>USERNAME:</b> ${escapeHtml(d.userNameEnv)} · <b>Drucker:</b> ${d.discoveredPrinterCount}<br>${warnings ? '<b>Warnungen:</b><br>' + warnings + '<br>' : ''}<b>Empfehlung:</b><br>${recs || 'Keine besondere Massnahme erforderlich.'}`; document.getElementById('cmdLocalTcp').textContent = (d.commands || {}).localTcpPrinterPowerShell || ''; document.getElementById('cmdMachinePrinter').textContent = (d.commands || {}).perMachineSharedPrinterCmd || ''; await scanPrinters(false); setStatus('Diagnose geladen. Waehle nun WINDOWS_SPOOLER oder RAW_TCP.'); } catch (e) { setStatus(e.message, true); } }
async function scanPrinters(showStatus = true) { try { if (showStatus) setStatus('Suche Drucker ...'); const printers = await request('/printers/scan'); const body = document.getElementById('printerScanBody'); body.innerHTML = ''; printers.forEach(p => { const tr = document.createElement('tr'); tr.innerHTML = `<td>${escapeHtml(p.systemName)}</td><td>${p.defaultPrinter ? 'Ja' : 'Nein'}</td>`; const td = document.createElement('td'); const btn = document.createElement('button'); btn.textContent = 'Als WINDOWS_SPOOLER nutzen'; btn.onclick = () => { document.getElementById('printerType').value = 'WINDOWS_SPOOLER'; togglePrinterTypeFields(); document.getElementById('systemName').value = p.systemName; document.getElementById('printerAlias').value = p.systemName.replace(/[^A-Za-z0-9._-]/g, '_').toUpperCase(); document.getElementById('printAlias').value = document.getElementById('printerAlias').value; }; td.appendChild(btn); tr.appendChild(td); body.appendChild(tr); }); if (showStatus) setStatus(printers.length + ' Drucker gefunden.'); } catch (e) { setStatus(e.message, true); } }
function togglePrinterTypeFields() { const type = val('printerType') || 'WINDOWS_SPOOLER'; document.getElementById('spoolerFields').style.display = type === 'WINDOWS_SPOOLER' ? '' : 'none'; document.getElementById('rawTcpFields').style.display = type === 'RAW_TCP' ? '' : 'none'; }
function prepareRawTcpAlias() { document.getElementById('printerType').value = 'RAW_TCP'; togglePrinterTypeFields(); document.getElementById('printerHost').value = val('rawHost'); document.getElementById('printerPort').value = val('rawPort') || '9100'; document.getElementById('printerLanguage').value = val('rawLanguage'); if (!val('printerAlias')) document.getElementById('printerAlias').value = 'LABEL_LINE_01'; if (!val('printerDescription')) document.getElementById('printerDescription').value = 'Direkter RAW_TCP Etikettendrucker'; document.getElementById('printAlias').value = val('printerAlias'); setStatus('RAW_TCP Alias vorbereitet. Pruefe Alias/Host/Port und speichere den Drucker.'); }
async function savePrinterAlias() { try { const type = val('printerType') || 'WINDOWS_SPOOLER'; const payload = { alias: val('printerAlias'), type, description: val('printerDescription'), enabled: true }; if (type === 'RAW_TCP') { payload.host = val('printerHost'); payload.port = Number(val('printerPort') || 9100); payload.language = val('printerLanguage') || val('rawLanguage'); } else { payload.systemName = val('systemName'); } const saved = await request('/printers', { method: 'POST', body: JSON.stringify(payload) }); setStatus('Alias gespeichert: ' + saved.alias + ' (' + (saved.type || 'WINDOWS_SPOOLER') + ')'); await loadPrinterAliases(); } catch (e) { setStatus(e.message, true); } }
async function loadPrinterAliases() { try { const mappings = await request('/printers'); const body = document.getElementById('printerAliasBody'); body.innerHTML = ''; mappings.forEach(m => { const type = m.type || 'WINDOWS_SPOOLER'; const target = type === 'RAW_TCP' ? `${escapeHtml(m.host || '')}:${escapeHtml(String(m.port || 9100))} ${escapeHtml(m.language || '')}` : escapeHtml(m.systemName || ''); const tr = document.createElement('tr'); tr.innerHTML = `<td>${escapeHtml(m.alias)}</td><td>${escapeHtml(type)}</td><td>${target}</td><td>${m.enabled ? 'Ja' : 'Nein'}</td>`; const td = document.createElement('td'); const use = document.createElement('button'); use.textContent = 'Nutzen'; use.onclick = () => document.getElementById('printAlias').value = m.alias; const edit = document.createElement('button'); edit.textContent = 'Bearbeiten'; edit.className = 'secondary'; edit.onclick = () => { document.getElementById('printerAlias').value = m.alias || ''; document.getElementById('printerType').value = type; togglePrinterTypeFields(); document.getElementById('systemName').value = m.systemName || ''; document.getElementById('printerHost').value = m.host || ''; document.getElementById('printerPort').value = m.port || 9100; document.getElementById('printerLanguage').value = m.language || ''; document.getElementById('printerDescription').value = m.description || ''; }; const del = document.createElement('button'); del.textContent = 'Löschen'; del.className = 'danger'; del.onclick = async () => { await request('/printers/' + encodeURIComponent(m.alias), { method: 'DELETE' }); await loadPrinterAliases(); }; td.appendChild(use); td.appendChild(edit); td.appendChild(del); tr.appendChild(td); body.appendChild(tr); }); } catch (e) { setStatus(e.message, true); } }
async function checkInDocument() { try { const file = document.getElementById('templateFile').files[0]; if (!file) throw new Error('Bitte zuerst eine Datei auswählen.'); const name = stripExtension(file.name).replace(/[^A-Za-z0-9._-]/g, '_'); document.getElementById('templateName').value = val('templateName') || name; document.getElementById('templateContentType').value = inferContentType(file.name); document.getElementById('templateType').value = inferTemplateType(file.name, val('templateContentType')); if (file.name.toLowerCase().endsWith('.docx') || file.name.toLowerCase().endsWith('.pdf')) { document.getElementById('templateContentBase64').value = await fileToBase64(file); document.getElementById('templateContent').value = (file.name.toLowerCase().endsWith('.pdf') ? 'PDF-Datei eingecheckt: ' : 'DOCX-Datei eingecheckt: ') + file.name + '\nBei PDF bitte die Koordinatenfelder im JSON-Bereich definieren.'; if (file.name.toLowerCase().endsWith('.pdf')) insertPdfFieldSample(false); } else { document.getElementById('templateContent').value = await file.text(); document.getElementById('templateContentBase64').value = ''; if (val('templateType') === 'HTML') sourceToDesigner(); } toggleBinaryNotice(); await saveTemplate(file.name); } catch (e) { setStatus(e.message, true); } }
async function saveTemplate(sourceFileName) { try { const isBinary = val('templateType') === 'DOCX' || val('templateType') === 'PDF' || val('templateType') === 'PDF_TEMPLATE'; const pdfCfg = parsePdfFieldsJson(); let sampleData = null; try { sampleData = JSON.parse(val('printData') || '{}'); } catch(ignore) {} const payload = { name: val('templateName'), documentName: val('templateName'), sourceFileName: sourceFileName || (document.getElementById('templateFile').files[0] || {}).name || '', description: val('templateDescription'), encoding: val('templateEncoding'), content: isBinary ? null : val('templateContent'), contentBase64: isBinary ? val('templateContentBase64') : null, contentType: val('templateContentType'), templateType: val('templateType'), coordinateUnit: pdfCfg.coordinateUnit || 'pt', coordinateOrigin: pdfCfg.coordinateOrigin || 'BOTTOM_LEFT', pdfFields: pdfCfg.pdfFields || [], sampleData: sampleData, examplePrintRequest: { printerAlias: val('printAlias') || 'OFFICE_A4', templateName: val('templateName'), renderMode: val('renderMode') || 'AUTO', copies: Number(val('copies') || 1), pdfDpi: Number(val('pdfDpi') || 203), data: sampleData || {} } }; const saved = await request('/templates', { method: 'POST', body: JSON.stringify(payload) }); document.getElementById('printTemplate').value = saved.name; setStatus('Vorlage im Ordner gespeichert: ' + (saved.templateDirectory || saved.name)); await loadTemplates(); await loadTemplateStorage(); } catch (e) { setStatus(e.message, true); } }
async function loadTemplates() { try { const templates = await request('/templates'); const body = document.getElementById('templateBody'); body.innerHTML = ''; templates.forEach(t => { const storage = (t.storageMode || '') + (t.templateDirectory ? '<br><span class="muted">' + escapeHtml(t.templateDirectory) + '/' + escapeHtml(t.documentFile || 'document.*') + '</span>' : ''); const tr = document.createElement('tr'); tr.innerHTML = `<td>${escapeHtml(t.name)}</td><td>${escapeHtml(t.sourceFileName || t.documentName || '')}</td><td>${escapeHtml(t.templateType || '')}</td><td>${storage}</td><td>${formatBytes(t.sizeBytes)}</td><td>${escapeHtml(t.updatedAt || '')}</td>`; const td = document.createElement('td'); const edit = document.createElement('button'); edit.textContent = 'Bearbeiten'; edit.onclick = async () => { const full = await request('/templates/' + encodeURIComponent(t.name)); document.getElementById('templateName').value = full.name; document.getElementById('templateDescription').value = full.description || ''; document.getElementById('templateEncoding').value = full.encoding || 'UTF-8'; document.getElementById('templateContent').value = full.content || (((full.templateType === 'DOCX') || (full.templateType === 'PDF') || (full.templateType === 'PDF_TEMPLATE')) ? full.templateType + '-Datei geladen: ' + (full.documentFile || full.sourceFileName || full.documentName || full.name) : ''); document.getElementById('templateContentBase64').value = full.contentBase64 || ''; document.getElementById('pdfFieldsJson').value = JSON.stringify({coordinateUnit: full.coordinateUnit || 'pt', coordinateOrigin: full.coordinateOrigin || 'BOTTOM_LEFT', pdfFields: full.pdfFields || []}, null, 2); document.getElementById('templateContentType').value = full.contentType || 'text/plain'; document.getElementById('templateType').value = full.templateType || 'TEXT'; document.getElementById('printTemplate').value = full.name; if (full.sampleData) document.getElementById('printData').value = JSON.stringify(full.sampleData, null, 2); toggleBinaryNotice(); if ((full.templateType || '') === 'HTML') sourceToDesigner(); setStatus('Vorlage geladen aus ' + (full.templateDirectory || full.storageMode || 'Speicher') + ': ' + full.name); }; const use = document.createElement('button'); use.textContent = 'Nutzen'; use.onclick = () => document.getElementById('printTemplate').value = t.name; const del = document.createElement('button'); del.textContent = 'Löschen'; del.className = 'danger'; del.onclick = async () => { await request('/templates/' + encodeURIComponent(t.name), { method: 'DELETE' }); await loadTemplates(); }; td.appendChild(edit); td.appendChild(use); td.appendChild(del); tr.appendChild(td); body.appendChild(tr); }); } catch (e) { setStatus(e.message, true); } }
async function previewPrint() { try { const payload = printPayload(true); payload.includeRenderedBase64 = false; const result = await request('/print/preview', { method: 'POST', body: JSON.stringify(payload) }); document.getElementById('printResult').textContent = JSON.stringify(result, null, 2); if (result.renderedContent && (result.renderedContent.trim().startsWith('<') || (result.contentType || '').includes('html'))) document.getElementById('htmlPreview').srcdoc = result.renderedContent; setStatus('Vorschau erstellt. Render-Modus: ' + result.renderMode); } catch (e) { setStatus(e.message, true); } }
async function previewHtmlOnly() { try { const payload = printPayload(true); payload.renderMode = 'RAW'; const result = await request('/print/preview/html', { method: 'POST', body: JSON.stringify(payload) }); document.getElementById('htmlPreview').srcdoc = result; document.getElementById('printResult').textContent = result; setStatus('HTML-Vorschau erstellt.'); } catch (e) { setStatus(e.message, true); } }
async function sendPrint() { try { const result = await request('/print', { method: 'POST', body: JSON.stringify(printPayload(false)) }); document.getElementById('printResult').textContent = JSON.stringify(result, null, 2); setStatus('Druckjob gesendet: ' + result.jobId); await loadQueue(); } catch (e) { setStatus(e.message, true); await loadQueue().catch(()=>{}); } }
async function loadDocxConverters() { try { const info = await request('/docx/converters'); document.getElementById('printResult').textContent = JSON.stringify(info, null, 2); setStatus('DOCX-Konverterstatus geladen.'); } catch(e) { setStatus(e.message, true); } }
async function validateDocxConverter() { try { const converter = val('docxConverter') || 'DOCX4J_FO'; const payload = printPayload(true); payload.renderMode = 'DOCX'; payload.docxConverter = converter; payload.includeRenderedBase64 = false; const result = await request('/docx/validate?converter=' + encodeURIComponent(converter), { method: 'POST', body: JSON.stringify(payload) }); document.getElementById('printResult').textContent = JSON.stringify(result, null, 2); setStatus('DOCX-Konverter validiert: ' + result.converter + ', PDF Bytes: ' + result.pdfBytes + ', Zeit: ' + result.elapsedMs + ' ms'); } catch(e) { setStatus(e.message, true); } }
async function downloadRendered() { try { const response = await fetch(api + '/print/rendered', { method: 'POST', headers: authHeaders({'Content-Type':'application/json'}), body: JSON.stringify(printPayload(true)) }); if (!response.ok) throw new Error(await response.text()); const blob = await response.blob(); const cd = response.headers.get('content-disposition') || ''; const m = cd.match(/filename="?([^";]+)"?/i); const name = m ? m[1] : 'printdocument'; const a = document.createElement('a'); a.href = URL.createObjectURL(blob); a.download = name; a.click(); URL.revokeObjectURL(a.href); setStatus('Dokument heruntergeladen: ' + name); } catch (e) { setStatus(e.message, true); } }
function printPayload(dryRun) { return { printerAlias: val('printAlias'), templateName: val('printTemplate'), copies: Number(val('copies') || 1), dryRun, renderMode: val('renderMode'), pdfDpi: Number(val('pdfDpi') || 203), docxConverter: val('docxConverter'), data: JSON.parse(val('printData')) }; }
function insertHtmlSample() { document.getElementById('templateContentType').value = 'text/html'; document.getElementById('templateType').value = 'HTML'; document.getElementById('templateName').value = val('templateName') || 'warenanhaenger_html'; document.getElementById('templateContentBase64').value = ''; document.getElementById('templateContent').value = `<!doctype html>
<html>
<head><meta charset="utf-8"><title>Warenanhänger</title>
<style>@page { size: 90mm 130mm; margin: 6mm; } body { font-family: Arial, sans-serif; font-size: 12pt; } .label { width: 78mm; }</style></head>
<body><div class="label">
<h2>Warenanhänger</h2>
<p><b>Serial:</b> {{text:serialNumber}}</p>
<p><b>Part:</b> {{text:partNumber}}</p>
<p><b>Order:</b> {{text:workorderNumber}}</p>
<div><img alt="Barcode" src="{{barcode:CODE_128:serialNumber:360x90}}"></div>
<div><img alt="QR" src="{{qr:serialNumber}}" style="width:120px;height:120px;"></div>
</div></body></html>`; toggleBinaryNotice(); sourceToDesigner(); }
function insertHtmlTableSample() { document.getElementById('templateContentType').value = 'text/html'; document.getElementById('templateType').value = 'HTML'; document.getElementById('templateName').value = val('templateName') || 'lieferschein_html_table'; document.getElementById('templateContentBase64').value = ''; document.getElementById('templateContent').value = `<!doctype html>
<html>
<head><meta charset="utf-8"><title>Lieferschein HTML Tabelle</title>
<style>
@page { size: A4; margin: 14mm; }
body { font-family: Arial, sans-serif; font-size: 10pt; color: #111; }
h1 { font-size: 18pt; margin: 0 0 8mm 0; }
.header { display: flex; justify-content: space-between; align-items: flex-start; }
.box { border: 1px solid #999; padding: 3mm; margin-bottom: 5mm; }
table { width: 100%; border-collapse: collapse; table-layout: fixed; }
th, td { border: 1px solid #777; padding: 2mm; vertical-align: top; }
th { background: #eeeeee; font-weight: bold; }
.right { text-align: right; } .center { text-align: center; }
.barcode { width: 42mm; height: 12mm; object-fit: contain; }
.qr { width: 28mm; height: 28mm; }
</style></head>
<body>
<div class="header">
<div><h1>Lieferschein</h1><div><b>Nr.:</b> {{text:deliveryNumber}}</div><div><b>Datum:</b> {{text:deliveryDate}}</div></div>
<img class="qr" alt="QR" src="{{qr:deliveryNumber}}">
</div>
<div class="box"><b>Kunde:</b> {{text:customer.name}}</div>
<table>
<thead><tr><th style="width:10mm">Pos.</th><th style="width:32mm">Artikel</th><th>Bezeichnung</th><th style="width:18mm">Menge</th><th style="width:38mm">Seriennummer</th><th style="width:45mm">Barcode</th></tr></thead>
<tbody>
{{#each:items}}
<tr>
<td class="center">{{text:pos}}</td>
<td>{{text:partNumber}}</td>
<td>{{text:description}}</td>
<td class="right">{{text:quantity}}</td>
<td>{{text:serialNumber}}</td>
<td class="center"><img class="barcode" alt="Barcode" src="{{barcode:CODE_128:serialNumber:360x90}}"></td>
</tr>
{{/each}}
</tbody>
</table>
</body>
</html>`; document.getElementById('renderMode').value = 'PDF'; toggleBinaryNotice(); sourceToDesigner(); }
function insertZplSample() { document.getElementById('templateContentType').value = 'text/plain'; document.getElementById('templateType').value = 'ZPL'; document.getElementById('templateName').value = val('templateName') || 'serial_label_zpl'; document.getElementById('templateContentBase64').value = ''; document.getElementById('templateContent').value = `^XA
^CF0,35
^FO40,40^FDSerial: {{serialNumber}}^FS
^FO40,90^FDPart: {{partNumber}}^FS
^FO40,145^BCN,80,Y,N,N^FD{{serialNumber}}^FS
^FO40,260^BQN,2,6^FDLA,{{serialNumber}}^FS
^XZ`; toggleBinaryNotice(); }
function insertDocxInfo() { document.getElementById('templateContentType').value = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'; document.getElementById('templateType').value = 'DOCX'; document.getElementById('templateContent').value = 'DOCX-Vorlage in Word/LibreOffice erstellen. Platzhalter im Dokument verwenden:\n{{serialNumber}}\n{{text:partNumber}}\n{{barcode:CODE_128:serialNumber:360x90}} als alleiniger Absatz\n{{qr:serialNumber}} als alleiniger Absatz'; toggleBinaryNotice(); }
function insertPdfFieldSample(setName = true) { if (setName) { document.getElementById('templateContentType').value = 'application/pdf'; document.getElementById('templateType').value = 'PDF'; document.getElementById('templateName').value = val('templateName') || 'lieferschein_pdf'; } document.getElementById('pdfFieldsJson').value = JSON.stringify({ coordinateUnit: 'mm', coordinateOrigin: 'TOP_LEFT', pdfFields: [ { name:'serialNumber', type:'TEXT', value:'{{serialNumber}}', page:1, x:35, y:45, width:60, height:6, fontSize:10 }, { name:'partNumber', type:'TEXT', value:'{{partNumber}}', page:1, x:35, y:53, width:60, height:6, fontSize:10 }, { name:'workorderNumber', type:'TEXT', value:'{{workorderNumber}}', page:1, x:35, y:61, width:60, height:6, fontSize:10 }, { name:'serialBarcode', type:'BARCODE', barcodeType:'CODE_128', value:'{{serialNumber}}', page:1, x:25, y:84, width:90, height:18 }, { name:'serialQr', type:'QR_CODE', value:'{{serialNumber}}', page:1, x:155, y:38, width:32, height:32 } ] }, null, 2); toggleBinaryNotice(); }
function insertPdfTableSample() { document.getElementById('templateContentType').value = 'application/pdf'; document.getElementById('templateType').value = 'PDF'; document.getElementById('templateName').value = val('templateName') || 'lieferschein_pdf_table'; document.getElementById('pdfFieldsJson').value = JSON.stringify({ coordinateUnit: 'mm', coordinateOrigin: 'TOP_LEFT', pdfFields: [ { name:'deliveryNumber', type:'TEXT', value:'{{deliveryNumber}}', page:1, x:38, y:35, width:70, height:6, fontSize:10 }, { name:'customerName', type:'TEXT', value:'{{customer.name}}', page:1, x:38, y:43, width:90, height:6, fontSize:10 }, { name:'serialQr', type:'QR_CODE', value:'{{deliveryNumber}}', page:1, x:162, y:25, width:28, height:28 }, { name:'positionsTable', type:'TABLE', source:'items', page:1, x:18, y:78, width:174, height:125, rowHeight:13, headerHeight:8, maxRows:9, overflow:'CUT', fontSize:7, border:true, headerBackground:'#EEEEEE', columns:[ { title:'Pos.', field:'pos', width:12, align:'CENTER' }, { title:'Artikel', field:'partNumber', width:29 }, { title:'Bezeichnung', field:'description', width:47, multiline:true }, { title:'Menge', field:'quantity', width:16, align:'RIGHT' }, { title:'SNR', field:'serialNumber', width:31 }, { title:'Barcode', type:'BARCODE', barcodeType:'CODE_128', field:'serialNumber', width:39 } ] } ] }, null, 2); toggleBinaryNotice(); }
function placeholderSvg(label) {
const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="260" height="70"><rect x="1" y="1" width="258" height="68" rx="6" fill="#f8fafc" stroke="#64748b" stroke-dasharray="6 4"/><text x="130" y="39" text-anchor="middle" font-family="Arial" font-size="14" fill="#334155">${label}</text></svg>`;
return 'data:image/svg+xml;charset=utf-8,' + encodeURIComponent(svg);
}
function htmlForDesigner(html) {
// Prevent the browser from requesting literal {{barcode:...}} or {{qr:...}} URLs while editing.
return String(html || '').replace(/src=(['"])(\{\{\s*(barcode|qr|data_matrix|datamatrix)[^'"]*\}\})\1/gi,
function(match, quote, token, kind) {
const label = kind.toLowerCase().startsWith('qr') ? 'QR-Platzhalter' : 'Barcode-Platzhalter';
return 'src="' + placeholderSvg(label) + '" data-template-src="' + escapeHtml(token) + '"';
});
}
function htmlFromDesigner(html) {
return String(html || '').replace(/src="data:image\/svg\+xml[^\"]*"\s+data-template-src="([^"]+)"/gi,
function(match, token) { return 'src="' + token.replace(/&quot;/g, '"').replace(/&amp;/g, '&') + '"'; });
}
function sourceToDesigner() { document.getElementById('htmlDesigner').innerHTML = htmlForDesigner(val('templateContent')); }
function designerToSource() { document.getElementById('templateContent').value = htmlFromDesigner(document.getElementById('htmlDesigner').innerHTML); }
async function loadQueue() { try { const params = new URLSearchParams(); if (val('queueUser')) params.set('user', val('queueUser')); if (val('queueStatus')) params.set('status', val('queueStatus')); if (val('queuePrinterAlias')) params.set('printerAlias', val('queuePrinterAlias')); if (val('queueTemplateName')) params.set('templateName', val('queueTemplateName')); if (val('queueLimit')) params.set('limit', val('queueLimit')); if (document.getElementById('queueFull').checked) params.set('full','true'); if (document.getElementById('queueIncludeRequest').checked) params.set('includeRequest','true'); const q = await request('/queue?' + params.toString(), { headers: {} }); const body = document.getElementById('queueBody'); body.innerHTML = ''; (q.jobs || []).forEach(job => { const tr = document.createElement('tr'); tr.innerHTML = `<td>${escapeHtml(job.createdAt || '')}</td><td>${escapeHtml(job.status || '')}</td><td>${escapeHtml(job.userName || '')}</td><td>${escapeHtml(job.printerAlias || '')}<br><span class="muted">${escapeHtml(job.systemName || '')}</span></td><td>${escapeHtml(job.templateName || '')}</td><td>${escapeHtml(String(job.copies || ''))}</td><td><span class="muted">${escapeHtml(job.jobId || '')}</span></td>`; const td = document.createElement('td'); const details = document.createElement('button'); details.textContent = 'Details'; details.onclick = () => showQueueJob(job.jobId); const reprint = document.createElement('button'); reprint.textContent = 'Redruck'; reprint.className = 'success'; reprint.onclick = () => reprintJob(job.jobId); td.appendChild(details); td.appendChild(reprint); tr.appendChild(td); body.appendChild(tr); }); document.getElementById('queueDetails').textContent = JSON.stringify(q, null, 2); setStatus('Druckwarteschlange geladen: ' + q.returned + ' von ' + q.total); } catch(e) { setStatus(e.message, true); } }
async function showQueueJob(jobId) { try { const job = await request('/queue/' + encodeURIComponent(jobId)); document.getElementById('queueDetails').textContent = JSON.stringify(job, null, 2); } catch(e) { setStatus(e.message, true); } }
async function reprintJob(jobId) { try { const payload = {}; const c = prompt('Kopien für Redruck leer lassen = Original verwenden:', ''); if (c && c.trim()) payload.copies = Number(c); const result = await request('/queue/' + encodeURIComponent(jobId) + '/reprint', { method:'POST', body: JSON.stringify(payload) }); document.getElementById('printResult').textContent = JSON.stringify(result, null, 2); setStatus('Redruck gesendet: ' + result.jobId + ' aus Original ' + jobId); await loadQueue(); } catch(e) { setStatus(e.message, true); } }
async function loadBarcodeFormats() { try { const list = await request('/barcodes/formats'); document.getElementById('barcodeFormats').innerHTML = list.map(f => `<span class="badge">${escapeHtml(f.name)} · ${escapeHtml(f.type)}</span> ${escapeHtml(f.description)}`).join('<br>'); } catch(e) { setStatus(e.message, true); } }
function syncTemplateTypeFromContentType() { const ct = val('templateContentType'); document.getElementById('templateType').value = ct.includes('pdf') ? 'PDF' : ct.includes('wordprocessingml') ? 'DOCX' : ct.includes('html') ? 'HTML' : ct.includes('xml') ? 'XML' : 'TEXT'; toggleBinaryNotice(); }
function toggleBinaryNotice() { const bin = val('templateType') === 'DOCX' || val('templateType') === 'PDF' || val('templateType') === 'PDF_TEMPLATE'; document.getElementById('binaryNotice').style.display = bin ? 'block' : 'none'; document.getElementById('templateContent').readOnly = bin; }
function inferContentType(fileName) { const f = String(fileName || '').toLowerCase(); if (f.endsWith('.html') || f.endsWith('.htm')) return 'text/html'; if (f.endsWith('.pdf')) return 'application/pdf'; if (f.endsWith('.docx')) return 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'; if (f.endsWith('.xml')) return 'application/xml'; if (f.endsWith('.json')) return 'application/json'; return 'text/plain'; }
function inferTemplateType(fileName, contentType) { const f = String(fileName || '').toLowerCase(); if (f.endsWith('.pdf') || String(contentType || '').includes('pdf')) return 'PDF'; if (f.endsWith('.docx') || String(contentType || '').includes('wordprocessingml')) return 'DOCX'; if (String(contentType || '').includes('html')) return 'HTML'; if (f.endsWith('.zpl')) return 'ZPL'; if (f.endsWith('.epl')) return 'EPL'; if (String(contentType || '').includes('xml')) return 'XML'; return 'TEXT'; }
function parsePdfFieldsJson() { const raw = val('pdfFieldsJson').trim(); if (!raw) return {}; try { return JSON.parse(raw); } catch(e) { throw new Error('PDF-Koordinatenfelder enthalten kein gültiges JSON: ' + e.message); } }
function fileToBase64(file) { return new Promise((resolve, reject) => { const reader = new FileReader(); reader.onload = () => resolve(String(reader.result).split(',')[1]); reader.onerror = reject; reader.readAsDataURL(file); }); }
function stripExtension(name) { return String(name || '').replace(/\.[A-Za-z0-9]{1,12}$/, ''); }
function formatBytes(value) { const n = Number(value || 0); if (n < 1024) return n + ' B'; if (n < 1024 * 1024) return (n / 1024).toFixed(1) + ' KB'; return (n / 1024 / 1024).toFixed(1) + ' MB'; }
function escapeHtml(value) { return String(value || '').replace(/[&<>'"]/g, c => ({'&':'&amp;','<':'&lt;','>':'&gt;',"'":'&#39;','"':'&quot;'}[c])); }
function val(id) { return document.getElementById(id).value; }
function valOrEmpty(id) { const el = document.getElementById(id); return el ? el.value : ''; }
document.getElementById('apiToken').value = localStorage.getItem('printservice.token') || '';
loadAuthStatus();
insertHtmlSample(); togglePrinterTypeFields(); loadPrinterDiagnostics(); loadPrinterAliases(); loadTemplates(); loadBarcodeFormats(); loadQueue();
</script>
</body>
</html>