Template scripts allow further customization of PDF documents, email templates and other output formats from xentral. Even in the standard version, many configuration options are available. These include the adjustment of fonts and font sizes, positioning of elements and much more. However, if new data is to be added, calculations need to take place or special language settings need to be implemented, every detail can be customized using rendering scripts.
An example for the customization of templates in xentral is the overloading of the stationery. Here, additional information can be integrated that is not available in the standard stationery, such as the weight or height in addition to the item description for an item.
The file that is to transfer the letterhead must be called class.letterhead_custom.php. This file must be located in the www/lib/dokumente folder and is then valid for all document types (invoice, order delivery note, credit, etc.).
In the following example, the weight as well as the height, width, length and region of origin of the item should also be added to the item description of a line item. There is no custom letterhead yet, so for the time being the letterhead looks as defined in the default settings:

First, create the class. letterhead_custom.php file. The basic framework for the custom letterhead looks like this:
<?php
include_once "class.briefpapier.php";
class BriefpapierCustom extends Briefpapier {
function __construct(&$app)
{
$this->app = $app;
parent::__construct($app);
}
}
?>
Next, open the file class.letterhead.php, which is currently responsible for the layout of the letterhead and find out the function that should be transferred, copy it and paste it under the function __construct. In this case, the renderItems() function should be copied, since it is responsible for rendering the item positions.
Since the function renderItems() is quite extensive, here is only hinted how the code of class.letterhead_custom.php could look like afterwards.
<?php
include_once "class.briefpapier.php";
class BriefpapierCustom extends Briefpapier {
function __construct(&$app)
{
$this->app = $app;
parent::__construct($app);
}
public function renderItems()
{
$posWidth = $this->app->erp->Firmendaten("breite_position");
$amWidth = $this->app->erp->Firmendaten("breite_menge");
$itemNoWidth = $this->app->erp->Firmendaten("breite_nummer");
...
...
...
}
}
?>
Below:
if($item['ean']!="" && $item['ean']!="0"){
if($item['desc']!=""){
$item['desc']=$item['desc']."\r\n".$this->app->erp- >Beschriftung("dokument_ean").": ".$item['ean'];
}else{
$item['desc']=$this->app->erp->Beschriftung("dokument_ean").": ".$item['ean'];
}
}
the size, as well as the height, width, length and region of origin of the item is now added to the variable $item['desc'], since this contains the description text and to this the further values are to be added.
The finished section then looks like this:
if($item['ean']!="" && $item['ean']!="0"){
if($item['desc']!=""){
$item['desc']=$item['desc']."\r\n".$this->app->erp- >Beschriftung("dokument_ean").": ".$item['ean'];
}else{
$item['desc']=$this->app->erp->Beschriftung("dokument_ean").": ".$item['ean'];
}
}
$daten = $this->app->DB->SelectArr("SELECT gewicht, laenge, breite, hoehe, ursprungsregion FROM artikel WHERE id = '".$item['artikel']."'");
$daten = reset($daten);
if($item['desc'] != ""){
if($daten['gewicht'] != "" AND $daten['gewicht'] > 0){
$item['desc'] = $item['desc']."\r\n"."Gewicht: ".$daten['gewicht']." kg";
}
}else{
if($daten['gewicht'] != "" AND $daten['gewicht'] > 0){
$item['desc'] = $item['desc']."Gewicht: ".$daten['gewicht']." kg";
}
}
if($item['desc'] != ""){
if($daten['laenge'] != "" AND $daten['laenge'] > 0){
$item['desc'] = $item['desc']."\r\n"."Länge: ".$daten['laenge']." cm";
}
}else{
if($daten['laenge'] != "" AND $daten['laenge'] > 0){
$item['desc'] = $item['desc']."Länge: ".$daten['laenge']." cm";
}
}
if($item['desc'] != ""){
if($daten['breite'] != "" AND $daten['breite'] > 0){
$item['desc'] = $item['desc']."\r\n"."Breite: ".$daten['breite']." cm";
}
}else{
if($daten['breite'] != "" AND $daten['breite'] > 0){
$item['desc'] = $item['desc']."Breite: ".$daten['breite']." cm";
}
}
if($item['desc'] != ""){
if($daten['hoehe'] != "" AND $daten['hoehe'] > 0){
$item['desc'] = $item['desc']."\r\n"."Höhe: ".$daten['hoehe']." cm";
}
}else{
if($daten['hoehe'] != "" AND $daten['hoehe' > 0]){
$item['desc'] = $item['desc']."Höhe: ".$daten['hoehe']." cm";
}
}
$ursprungsregion = "";
switch($daten['ursprungsregion']){
case "01":
$ursprungsregion = "Schleswig-Holstein";
break;
case "02":
$ursprungsregion = "Hamburg";
break;
case "03":
$ursprungsregion = "Niedersachsen";
break;
case "04":
$ursprungsregion = "Bremen";
break;
case "05":
$ursprungsregion = "Nordrhein-Westfalen";
break;
case "06":
$ursprungsregion = "Hessen";
break;
case "07":
$ursprungsregion = "Rheinland-Pfalz";
break;
case "08":
$ursprungsregion = "Baden-Württemberg";
break;
case "09":
$ursprungsregion = "Bayern";
break;
case "10":
$ursprungsregion = "Saarland";
break;
case "11":
$ursprungsregion = "Berlin";
break;
case "12":
$ursprungsregion = "Brandenburg";
break;
case "13":
$ursprungsregion = "Mecklenburg-Vorpommern";
break;
case "14":
$ursprungsregion = "Sachsen";
break;
case "15":
$ursprungsregion = "Sachsen-Anhalt";
break;
case "16":
$ursprungsregion = "Thüringen";
break;
case "99":
$ursprungsregion = "ausländischer Ursprung";
break;
}
if($item['desc'] != ""){
if($ursprungsregion != ""){
$item['desc'] = $item['desc']."\r\n"."Ursprungsregion: ".$ursprungsregion;
}
}else{
if($ursprungsregion != ""){
$item['desc'] = $item['desc']."Ursprungsregion: ".$ursprungsregion;
}
}
The result looks like this:

Furthermore, it is possible to transfer only certain documents. In the following example, the next, i.e. smallest best-before date from the warehouse for this item is to be displayed on a picking slip for the item description. For this purpose, the delivery bill must be overloaded, since the delivery note is also created with the class.delivery.note.php. To overload the class.deliverynote.php file, a file named class.deliverynote_custom.php is created and placed in the xentralsordner to www/lib/dokumente. So far there is no class.deliverynote_custom.php file, so the delivery note looks like this:

The basic structure of the file looks like this:
<?php
if(!class_exists('BriefpapierCustom'))
{
class BriefpapierCustom extends Briefpapier
{
}
}
class LieferscheinPDFCustom extends BriefpapierCustom {
public $doctype;
function __construct($app,$projekt="")
{
$this->app=&$app;
//parent::Briefpapier();
$this->doctype="lieferschein";
$this->doctypeOrig="Lieferschein";
parent::__construct($this->app,$projekt);
}
}
?>
Next, the appropriate function to be transferred is searched for in class.deliverynote.php. In this case it is the function GetLieferschein. This function is copied and pasted under the constructor __construct. Since the function GetLieferschein is quite long, it is only indicated here how the file currently looks:
<?php
if(!class_exists('BriefpapierCustom'))
{
class BriefpapierCustom extends Briefpapier
{
}
}
class LieferscheinPDFCustom extends BriefpapierCustom {
public $doctype;
function __construct($app,$projekt="")
{
$this->app=&$app;
//parent::Briefpapier();
$this->doctype="lieferschein";
$this->doctypeOrig="Lieferschein";
parent::__construct($this->app,$projekt);
}
function GetLieferschein($id,$info="",$extrafreitext="")
{
$this->doctypeid = $id;
if(method_exists($this->app->erp,'LieferscheinSeriennummernberechnen'))$this- >app->erp->LieferscheinSeriennummernberechnen($id);
$briefpapier_bearbeiter_ausblenden = $this->app->erp- >Firmendaten('briefpapier_bearbeiter_ausblenden');
$briefpapier_vertrieb_ausblenden = $this->app->erp->Firmendaten('briefpapier_vertrieb_ausblenden');
$adresse = $this->app->DB->Select("SELECT adresse FROM lieferschein WHERE id='$id' LIMIT 1");
...
...
...
}
}
?>
Now the adjustments are made in the code. If these were simply added, they would be valid for delivery notes and picking notes. However, only the picking notes are to be adjusted here, which is why an if statement is used to check beforehand whether it is a picking note. The changes are made under:
/*
if(!empty($chargen)){
foreach($chargen as $chargen=>$charge){
$chargenliste = $chargenliste.$charge['charge'].";";
}
$chargenliste=substr($chargenliste, 0, -1);
$value['chargen'] = $this->app->DB->Select("SELECT chargenverwaltung FROM artikel WHERE id='".$value[artikel]."' LIMIT 1");
if($value['chargen']=="0"){
$value['beschreibung'] = $value['beschreibung'];
}else{
$value['beschreibung'] = $value['beschreibung']."\r\n".$this->app->erp- >Beschriftung("dokument_charge").": ". $chargenliste;
}
}
*/
It looks like this:
if($info == "kommissionierschein"){
if($value['freifeld1'] != ""){
//$value['beschreibung'] .= "\r\n"."Bitte folgendes MHD wählen: ".$value['freifeld1'];
}else{
$mhd = $this->app->DB->Select("SELECT MIN(mhddatum) FROM lager_mindesthaltbarkeitsdatum WHERE artikel= '".$value['artikel']."' AND mhddatum > CURDATE()");
$value['beschreibung'] .= "\r\n"."Kleinstes MHD im Lager: ".$mhd;
}
}
Final result:

Transferring the pick list is also possible in xentral.
The file that is to transfer the letterhead is created in /www/pages as kommissionierlauf_custom.php. In the file is the function Pick Run PDF this can then be customized.
In the following the standard function is listed, which can be adapted then after the conceptions.
public function KommissionierlaufPDF($id = null)
{
if($id)
{
$kommissionierlaufid = $id;
$intern = true;
}else{
$intern = false;
$kommissionierlaufid = (int)$this->app->Secure->GetGET('id');
}
$commission = $this->app->DB->SelectRow(
sprintf(
'SELECT `abgeschlossen`, `bezeichnung` FROM `kommissionierung` WHERE `id` = %d`',
$kommissionierlaufid
)
);
$bezeichnung = empty($commission)?'':$this->app->erp->ReadyForPDF(
(String)$commission['bezeichnung']
);
//$lieferscheinids = $this->app->DB->SelectArr("SELECT id FROM lieferschein WHERE kommissionierung = \"$kommissionierlaufid\"");
$abgeschlossen = !empty($commission['abgeschlossen']);
$artikelnummern = $this->getCommssionArticles($kommissionierlaufid, $abgeschlossen);
$articleArr = $this->getStorageLocationsByArticleArr($artikelnummern);
$storage = $this->getStorageByArticleArr($articleArr);
$lieferscheinnr = $this->app->DB->SelectArr(
sprintf(
'SELECT DISTINCT l.belegnr, l.name
FROM `lieferschein` AS `l`
WHERE l.kommissionierung = %d
ORDER BY l.id',
$kommissionierlaufid
)
);
$kistenarray = array();
$counter = 1;
$mengenarray = array();
$deliveryNoteNameByNumber = [];
foreach($lieferscheinnr as $key => $value){
$kistenarray[$value['belegnr']] = $counter;
$deliveryNoteNameByNumber[$value['belegnr']] = $value['name'];
$counter += 1;
}
//Neue PDF Datei erstellen
$pdf=new SuperFPDF('P','mm','A4',$this->app);
$pdf->AddPage();
$pdf->SetFont('Arial', 'B', 15);
$pdf->Cell(
100, 8,
'Kommissionierlauf '.$kommissionierlaufid.($bezeichnung !== ''?' '.$bezeichnung:''),
0, 0, 'L'
);
$pdf->SetFont('Arial','',10);
$pdf->Cell(0, 8, date('d.m.Y'), 0, 1, 'R');
$pdf->Ln();
$breitenummer = $this->app->erp->Firmendaten('breite_nummer');
$pdf->SetFont('Arial','B',10);
$pdf->Cell(15, 5, 'Pos', 0, 0, 'C');
$pdf->Cell(25, 5, 'Lagerort', 0, 0, 'C');
$pdf->Cell(50, 5, 'Artikelnr', 0, 0, 'L');
$pdf->Cell(55, 5, 'Artikel', 0, 0, 'L');
$pdf->Cell(20, 5, 'Menge', 0, 0,'C');
$pdf->Cell(25, 5, 'Lagerbest.', 0, 1, 'C');
$pdf->SetFont('Arial','',10);
$pdf->Line($pdf->GetX(), $pdf->GetY(), $pdf->GetX()+190, $pdf->GetY());
$position = 1;
foreach($artikelnummern as $key=>$value){
$artikelid = $value['id'];
$fontsize = 10;
if(isset($value['fontsize']) && (float)$value['fontsize'] > 1)
{
$fontsize = (float)$value['fontsize'];
}
$pdf->Cell(15, 5, $position, 0, 0, 'C');
$pdf->SetFont('Arial','B',10);
$artikellagerort = $value['kurzbezeichnung'];
if($artikellagerort == ''){
$artikellagerort = '-';
}
$pdf->Cell(25, 5, $artikellagerort, 0, 0, 'C');
$pdf->SetFont('Arial','',10);
$pdf->Cell(50, 5, $value['nummer'], 0, 0, 'L');
$artikelname = $value['name_de'];
$pdf->SetFont('Arial','B',$fontsize);
$pdf->MultiCell(0, 5, $artikelname, 0, 'L');
$pdf->SetFont('Arial','',$fontsize);
$lieferscheindaten = $this->getDeliveryNoteDataByArticle(
$kommissionierlaufid, $artikelid, $value['lager_platz'], $abgeschlossen
);
$ean = $value['ean'];
if($ean != ''){
$pdf->Cell(90, 5, '', 0, 0, 'L');
$pdf->Cell(0, 5, '- EAN: '.$ean, 0, 1, 'L');
$pdf->Cell(90, 5, "", 0, 0, 'L');
$pdf->Cell(25, 5, $pdf->Code39($pdf->GetX(), $pdf->GetY(), $ean, 1, 5), 0, 1, 'L');
$pdf->Cell(90, 5, "", 0, 1, 'L');
}
foreach($lieferscheindaten as $artikel=>$daten){
$pdf->Cell(90, 5, '', 0, 0, 'L');
$pdf->Cell(
0, 5,
'- LS '.$daten['belegnr'].' / Kiste '.$kistenarray[$daten['belegnr']].' / '
.$this->app->erp->ReadyForPDF($daten['name']).' ('.(float)$daten['menge'].')',
0, 1, 'L'
);
$mengenarray[$daten['belegnr']] += $daten['menge'];
}
$pdf->SetX($pdf->GetX()+145);
$artikelmenge = $value['menge'];
$pdf->SetFont('Arial','B',$fontsize);
$pdf->Cell(20, 5, (float)$artikelmenge, 0, 0, "C");
$pdf->SetFont('Arial','',$fontsize);
if(empty($storage[(int)$artikelid])
|| empty($storage[(int)$artikelid][(int)$value['lager_platz']])) {
$artikellagerbestand = $this->app->DB->SelectRow(
sprintf(
'SELECT IFNULL(SUM(`menge`),0) AS menge
FROM `lager_platz_inhalt`
WHERE `artikel` = %d AND `lager_platz` = %d
LIMIT 1',
(int)$artikelid, (int)$value['lager_platz']
)
);
}
else {
$artikellagerbestand = ['menge'=>$storage[(int)$artikelid][(int)$value['lager_platz']]];
}
if($artikellagerbestand['menge'] != ''){
$artikellagerbestand['menge'] = (float)$artikellagerbestand['menge'];
}else{
$artikellagerbestand['menge'] = 0;
}
$pdf->Cell(25, 5, $artikellagerbestand['menge'], 0, 1, 'C');
$pdf->Line($pdf->GetX(), $pdf->GetY(), $pdf->GetX()+190, $pdf->GetY());
$position++;
}
//$pdf->AddPage();
$pdf->Ln(20);
$pdf->SetFont('Arial','B',10);
$pdf->Cell(23, 5, 'Kisten-Nr', 0, 0, 'L');
$pdf->Cell(27, 5, 'LS-Nr', 0, 0, 'L');
$pdf->Cell(35, 5, 'Kundenname', 0, 0, 'L');
$pdf->Cell(15, 5, 'Teile', 0, 0, 'L');
$pdf->Cell(20, 5, 'Barcode LS', 0, 1, 'L');
$pdf->SetFont('Arial','',10);
$pdf->Line($pdf->GetX(), $pdf->GetY(), $pdf->GetX()+190, $pdf->GetY());
$pdf->Ln(1);
foreach($lieferscheinnr as $key => $value){
$pdf->Cell(23, 5, 'Kiste '.$kistenarray[$value['belegnr']], 0, 0, 'L');
$pdf->Cell(27, 5, $value['belegnr'], 0, 0, 'L');
$kundenname = !empty($deliveryNoteNameByNumber[$value['belegnr']])
?$deliveryNoteNameByNumber[$value['belegnr']]
:$this->app->DB->Select(
sprintf(
"SELECT `name` FROM `lieferschein` WHERE `belegnr` = '%s' LIMIT 1",
$this->app->DB->real_escape_string($value['belegnr'])
)
);
if(strlen($kundenname) > 17){
$kundenname = trim(substr($kundenname, 0, 17)).'...';
}
$pdf->Cell(35, 5, $this->app->erp->ReadyForPDF($kundenname), 0, 0, 'L');
$pdf->Cell(15, 5, $mengenarray[$value['belegnr']], 0, 0, 'L');
$pdf->Cell(20, 5, $pdf->Code39($pdf->GetX(), $pdf->GetY(), $value['belegnr'], 1, 5), 0, 1, 'L');
$pdf->Ln(18);
}
if($intern)
{
$pdf->filename = $this->app->erp->Dateinamen(date('Ymd').'_'.$kommissionierlaufid.'_Pickliste.pdf');
$pdf->Output($this->app->erp->GetTMP().$pdf->filename,'F');
return $this->app->erp->GetTMP().$pdf->filename;
}
$pdf->Output(date('Ymd').'_'.$kommissionierlaufid.'_Pickliste.pdf','D');
$this->app->ExitXentral();
}
Just like the letterhead, other documents can also be transferred, such as the address master sheet. The definitions for this can be found in the class AdressstammblattPDF in the file class.adressstammblatt.php in the folder www/lib/dokumente.
First the class.adressstammblatt_custom.php is created. The basic structure for this looks like this:
<?php
include_once "class.adressstammblatt.php";
class AdressstammblattPDFCustom extends AdressstammblattPDF {
function __construct($app,$projekt="")
{
$this->app=$app;
$this->doctype="adresse";
$this->doctypeOrig="Adressstammblatt";
parent::__construct($this->app,$projekt);
}
}
?>
Then additional data can be output in the master data sheet. For this purpose, the class AddressMasterSheetPDF and the two functions renderDocument() and GetAddressMasterSheet() contained therein are to be oriented.