Rechnungs Design nur bedingt änderbar?

Ich habe gestern versucht die Rechnung nur minimal zu verschönern indem ich einfach das crabe Layout kopiert habe.

Nach 1-2 Stunden Code-Analyse musste ich feststellen, dass es scheinbar bei vielen Dingen scheinbar nicht wirklich vorgesehen ist sie anzupassen.

Zum Beispiel die Bank-Daten auf der Rechnung sind unglaublich klein und dann ist auch noch der Kontoinhaber nochmal kleiner als der Rest. Das ist unpraktisch und sieht einfach nicht gut aus. Genau diesen Code findet man allerdings in core/lib/pdf.lib.php - also nicht im Rechnungs-Template selbst.

Wenn man genau nachsieht findet man „geheime Variablen“. Allerdings klingt es nicht all zu vernünftig die Variable PDF_DIFFSIZE_CONTENT zu ändern, nur weil eine Zeile bei den Bank-Daten nicht passt.

Wie geht ihr damit um? Verwendet ihr alle die Standard-Ansicht? Habt ihr Variablen gefunden mit denen sich das Layout Sinnvoll verbessern lässt?

LG Alex

Hallo Alex,

ich habe das selbe Problem quick n dirty so gelöst, dass ich unter den erweiterten Einstellungen die Variable „PDF_BANK_HIDE_NUMBER_SHOW_ONLY_BICIBAN“ auf „1“ gesetzt habe -> Hide bank number and keep only BIC/IBAN on PDF. Das sieht schon mal wesentlich professioneller aus :wink:

Aber dein grundsätzliches Problem ist ein anderes, was sich allerdings auch recht elegant in dolibarr lösen lässt. Hier ein Beispiel für die Rechnung, wie ich meinen Footer (function _pagefoot) angepasst habe. Das funktioniert mit allen anderen Funktionen genau gleich.
Ich eine original Vorlage, bei der Rechnung „dolibarr/htdocs/core/modules/facture/doc/pdf_crabe.modules.php“ kopiert und umbenannt in z.B. „dolibarr/htdocs/core/modules/facture/doc/pdf_Rechnung.modules.php“ (Dateiname beginnt mit dem Klassenname - pdf_crabe muss eingebunden werden) und dann meinen Bedürfnissen angepasst.

//snipp
require_once DOL_DOCUMENT_ROOT.'/core/modules/facture/doc/pdf_crabe.modules.php';
//snipp
class pdf_Rechnung extends pdf_crabe

Das grundsätzliche vorgehen findest du hier: https://wiki.dolibarr.org/index.php/Create_a_PDF_document_template beschrieben.

meine pdf_Rechnung.modules.php mit angepasstem Footer sieht so aus:

<?php
/* Copyright (C) 2004-2014	Laurent Destailleur	<[email protected]>
 * Copyright (C) 2005-2012	Regis Houssin		<[email protected]>
 * Copyright (C) 2008		Raphael Bertrand	<[email protected]>
 * Copyright (C) 2010-2013	Juanjo Menent		<[email protected]>
 * Copyright (C) 2012      	Christophe Battarel <[email protected]>
 * Copyright (C) 2012       Cedric Salvador     <[email protected]>
 * Copyright (C) 2015       Marcos García       <[email protected]>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
 * or see https://www.gnu.org/
 */

/**
 *	\file       htdocs/core/facture/propale/doc/pdf_Rechnung.modules.php
 *	\ingroup    facture
 *	\brief      File of Class to generate PDF
 */

require_once DOL_DOCUMENT_ROOT.'/core/modules/facture/doc/pdf_crabe.modules.php';
require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
require_once DOL_DOCUMENT_ROOT.'/core/lib/pdf.lib.php';

class pdf_Rechnung extends pdf_crabe
{

	/**
	 *	Constructor
	 *
	 *  @param		DoliDB		$db      Database handler
	 */
	public function __construct($db)
	{
		global $conf,$langs,$mysoc;

		parent::__construct($db);

		$this->name = "Rechnung";
		$this->description = "Rechnungsvorlage - Update sicher - Erweiterung zum Modul crabe";
	}


	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
	/**
	 *  Show footer of page.
	 *
	 *  @param	TCPDF		$pdf     		Object PDF
	 *  @param  Object		$object     	Object to show
	 *  @param  Translate	$outputlangs	Object lang for output
	 *  @param	int			$hidefreetext	1=Hide free text
	 *  @return	int                         Return topshift value
	 */
	protected function _pagefoot(&$pdf, $object, $outputlangs, $hidefreetext = 0)
	{
		global $conf,$user,$mysoc;
		
		$showdetails=$conf->global->MAIN_GENERATE_DOCUMENTS_SHOW_FOOT_DETAILS;
		
		// set default values here, copied from the default _pagefoot() function
		$paramfreetext='INVOICE_FREE_TEXT';
		$fromcompany=$this->emetteur;
		$marge_basse=$this->marge_basse;
		$marge_gauche=$this->marge_gauche;
		$page_hauteur=$this->page_hauteur;
			
	    // begin here default pdf drawing
		$outputlangs->load("dict");
		$line='';
		$dims=$pdf->getPageDimensions();

		// Line of free text
		if (empty($hidefreetext) && ! empty($conf->global->$paramfreetext))
		{
			$substitutionarray=pdf_getSubstitutionArray($outputlangs, null, $object);
			
			// More substitution keys
			$substitutionarray['__FROM_NAME__']=$fromcompany->name;
			$substitutionarray['__FROM_EMAIL__']=$fromcompany->email;
			complete_substitutions_array($substitutionarray, $outputlangs, $object);
			$newfreetext=make_substitutions($conf->global->$paramfreetext, $substitutionarray, $outputlangs);
			$newfreetext=preg_replace('/(<img.*src=")[^\"]*viewimage\.php[^\"]*modulepart=medias[^\"]*file=([^\"]*)("[^\/]*\/>)/', '\1'.DOL_DATA_ROOT.'/medias/\2\3', $newfreetext);

			$line.=$outputlangs->convToOutputCharset($newfreetext);
		}

		// First line of company infos
		$line1=""; $line2=""; $line3=""; $line4="";

	        if ($showdetails == 1 || $showdetails == 3)
		{
			// Line 1			
			// Company name
			if ($fromcompany->name)
			{
				//$line1.=($line1?" - ":"").$outputlangs->transnoentities("RegisteredOffice").": ".$fromcompany->name;
				$line1.=($line1?" - ":"").$fromcompany->name;
			}
			
			// Address
			if ($fromcompany->address)
			{
				$line1.=($line1?" - ":"").str_replace("\n", ", ", $fromcompany->address);
			}
			
			// Zip code
			if ($fromcompany->zip)
			{
				$line1.=($line1?" - ":"").$fromcompany->zip;
			}
			
			// Town
			if ($fromcompany->town)
			{
				$line1.=($line1?" ":"").$fromcompany->town;
			}
			
			// Line 2			
			// Phone
			if ($fromcompany->phone)
			{
				//$line2.=($line2?" - ":"").$outputlangs->transnoentities("Phone").": ".$fromcompany->phone;
			}
			
			// Fax
			if ($fromcompany->fax)
			{
				//$line2.=($line2?" - ":"").$outputlangs->transnoentities("Fax").": ".$fromcompany->fax;
			}

			// URL
			if ($fromcompany->url)
			{
				//$line2.=($line2?" - ":"").$fromcompany->url;
			}
			
			// Email
			if ($fromcompany->email)
			{
				//$line2.=($line2?" - ":"").$fromcompany->email;
			}
		}
		
		if ($showdetails == 2 || $showdetails == 3 || ($fromcompany->country_code == 'DE'))
		{
			// Managers
			if ($fromcompany->managers)
			{
				$line2.=($line2?" - ":"").$outputlangs->transnoentities("ManagingDirectors").": ".$fromcompany->managers;
			}
		}

		// Line 3 of company infos	
		// Juridical status
		if ($fromcompany->forme_juridique_code)
		{
            // Brauchen wir nicht
			//$line3.=($line3?" - ":"").$outputlangs->convToOutputCharset(getFormeJuridiqueLabel($fromcompany->forme_juridique_code));
		}
		
		// Capital
		if ($fromcompany->capital)
		{
			$tmpamounttoshow = price2num($fromcompany->capital); // This field is a free string
			$tmpamounttoshow = 0;
			
			if (is_numeric($tmpamounttoshow) && $tmpamounttoshow > 0) 
			  $line3.=($line3?" - ":"").$outputlangs->transnoentities("CapitalOf", price($tmpamounttoshow, 0, $outputlangs, 0, 0, 0, $conf->currency));
			//else 
			//  $line3.=($line3?" - ":"").$outputlangs->transnoentities("CapitalOf", $tmpamounttoshow, $outputlangs);
		}
		
		// Prof Id 1 -> Steuernummer
		if ($fromcompany->idprof1 && ($fromcompany->country_code != 'FR' || ! $fromcompany->idprof2))
		{
			$field=$outputlangs->transcountrynoentities("ProfId1", $fromcompany->country_code);
			
			if (preg_match('/\((.*)\)/i', $field, $reg)) $field=$reg[1];
			  $line3.=($line3?" - ":"").$field.": ".$outputlangs->convToOutputCharset($fromcompany->idprof1);
		}
		
		// Prof Id 3 -> Handelsregister Nummer
		if ($fromcompany->idprof3)
		{
			$field=$outputlangs->transcountrynoentities("ProfId3", $fromcompany->country_code);
			if (preg_match('/\((.*)\)/i', $field, $reg)) $field=$reg[1];
			$line3.=($line3?" - ":"").$field.": ".$outputlangs->convToOutputCharset($fromcompany->idprof3);
		}
		
		// Prof Id 2 -> Amtsgericht
		if ($fromcompany->idprof2)
		{
			$field=$outputlangs->transcountrynoentities("ProfId2", $fromcompany->country_code);
			
			if (preg_match('/\((.*)\)/i', $field, $reg)) $field=$reg[1];
			  $line3.=($line3?" - ":"").$field.": ".$outputlangs->convToOutputCharset($fromcompany->idprof2);
		}

		// Line 4 of company infos
		// Prof Id 4 -> 6 Gibt es in Deutschland nicht
		
		// IntraCommunautary VAT
		if ($fromcompany->tva_intra != '')
		{
			$line4.=($line4?" - ":"").$outputlangs->transnoentities("VATIntraShort").": ".$outputlangs->convToOutputCharset($fromcompany->tva_intra);
		}

        // set default pdf settings here
		$pdf->SetFont('', '', 7);
		$pdf->SetDrawColor(224, 224, 224);

		// the start of the bottom of this page footer is positioned according to # of lines
		$freetextheight=0;
		
		// Free text
		if ($line)	
		{
			//$line="sample text<br>\nfd<strong>sf</strong>sdf<br>\nghfghg<br>";
		        if (empty($conf->global->PDF_ALLOW_HTML_FOR_FREE_TEXT))
			{
				// By default, ask a manual break: We use a large value 20000, to not have automatic wrap. This make user understand, he need to add CR on its text.
				$width=20000; 
				$align='L';	
	    		        
	    		        if (! empty($conf->global->MAIN_USE_AUTOWRAP_ON_FREETEXT)) {
	    			   $width=200; 
	    			   $align='C';
	    		        }
			        
			        $freetextheight=$pdf->getStringHeight($width, $line);
			}
			else
			{
	                    // New method (works for HTML content)
	                    $freetextheight=pdfGetHeightForHtmlContent($pdf, dol_htmlentitiesbr($line, 1, 'UTF-8', 0));  
	                        
	                    //print '<br>'.$freetextheight;exit;
			}
		}

        // set footer position
		$marginwithfooter=$marge_basse + $freetextheight + (! empty($line1)?3:0) + (! empty($line2)?3:0) + (! empty($line3)?3:0) + (! empty($line4)?3:0);
		$posy=$marginwithfooter+0;

        // Free text
		if ($line)	
		{
			$pdf->SetXY($dims['lm'], -$posy);
			
			if (empty($conf->global->PDF_ALLOW_HTML_FOR_FREE_TEXT))   // by default
			{	           
	                  $pdf->MultiCell(0, 3, $line, 0, $align, 0);
			}
			else
			{
	                  $pdf->writeHTMLCell($pdf->page_largeur - $pdf->margin_left - $pdf->margin_right, $freetextheight, $dims['lm'], $dims['hk']-$marginwithfooter, dol_htmlentitiesbr($line, 1, 'UTF-8', 0));
			}
			
			$posy-=$freetextheight;
		}

        // set footer content
		$pdf->SetY(-$posy);
		$pdf->line($dims['lm'], $dims['hk']-$posy, $dims['wk']-$dims['rm'], $dims['hk']-$posy);
		$posy--;

		if (! empty($line1))
		{
			$pdf->SetFont('', 'B', 7);
			$pdf->SetXY($dims['lm'], -$posy);
			$pdf->MultiCell($dims['wk']-$dims['rm']-$dims['lm'], 2, $line1, 0, 'L', 0);
			
			$posy-=3;
			$pdf->SetFont('', '', 7);
		}

		if (! empty($line2))
		{
			$pdf->SetFont('', '', 7);
			$pdf->SetXY($dims['lm'], -$posy);
			$pdf->MultiCell($dims['wk']-$dims['rm']-$dims['lm'], 2, $line2, 0, 'L', 0);
			
			$posy-=3;
			$pdf->SetFont('', '', 7);
		}

		if (! empty($line3))
		{
			$pdf->SetXY($dims['lm'], -$posy);
			$pdf->MultiCell($dims['wk']-$dims['rm']-$dims['lm'], 2, $line3, 0, 'L', 0);
		}

		if (! empty($line4))
		{
			$posy-=3;
			$pdf->SetXY($dims['lm'], -$posy);
			$pdf->MultiCell($dims['wk']-$dims['rm']-$dims['lm'], 2, $line4, 0, 'L', 0);
		}

		// show page nb only on iso languages (so default Helvetica font)
		if (strtolower(pdf_getPDFFont($outputlangs)) == 'helvetica')
		{
			$pdf->SetXY($dims['wk']-$dims['rm']-15, -$posy);
			
			//print 'xxx'.$pdf->PageNo().'-'.$pdf->getAliasNbPages().'-'.$pdf->getAliasNumPage();exit;
			$pdf->MultiCell(15, 2, $pdf->PageNo().'/'.$pdf->getAliasNbPages(), 0, 'R', 0);
		}

		return $marginwithfooter;
	}
}

Ich hoffe, das hilft dir weiter
Gruß Andi

@arusa

Beim Aufruf der Funktion pdf_bank aus dem PDF template wird als letztes Argument die Schriftgröße übergeben. Man kann dort statt $default_font_size auch $default_font_size +1 oder +2 angeben. Das lindert das Problem ein wenig. Bei größeren Zahlen sieht es dann allerdings etwas schräg aus, weil die Zeilenabstände nicht alle entsprechend erhöht werden.

Hallo!

Sofern es um die Anpassung bestimmter Templates geht, ist Andi69’s Lösung sicher sehr elegant. Bei dem Punkt zum Footer gehe ich damit allerdings nicht konform. Die Funktion, die den Footer generiert ist zentral in der core/lib/pdf.lib.php hinterlegt. Möchte ich den Footer in allen Templates anpassen, müsste ich hier jedes Template kopieren und anpassen. Ändere ich den Footer zentral, laufe ich gefahr, dass meine Anpassungen beim nächsten Update überschrieben werden. Das Hook System bietet einen netten Ansatz, aber greift hier nicht, da es hier keinen Hook gibt. Mir ist es ehrlich gesagt ein Rätsel, wie es in Version 12 immer noch nicht möglich ist, den Footer zentral anzupassen. Zumal dieser zB. in Kombination mit einer Körperschaft nicht den Namen eben dieser anzeigt, sondern lediglich den Namen des Geschäftsführers. Abgesehen von der Auswahlmöglichkeit zwischen 3 verschiedenen Optionen im PDF-Setup unter „Weitere Details in der PDF-Fußzeile anzeigen (bspw. Firmenadresse / Name des CEOs)“, gibt es hier keine Möglichkeit für ein anständiges Customizing.

Formatierungsprobleme tauchen in den PDFs auch an den unmöglichsten Stellen auf und lassen sich meist nur mit Anpassungen am Code ändern. Ich denke da insbesondere an Zeilenumbrüche.

Umgehen tue ich damit gar nicht mehr … sofern möglich habe ich alles auf ODT umgestellt. Ist zwar etwas mehr Aufwand beim Erstellen der jeweiligen Belege, aber ich habe wesentlich weniger Formatierungsprobleme und sofern ein Formatierungsfehler auftritt, kann ich diesen noch korrigieren.

Ich bin auch relativ schockiert wie schlecht die Rechnungen aussehen und wie wenig Gestaltungsspielraum man hat.
Mit ODT hab ich es auch schon probiert, das hat aber leider überhaupt nicht funktioniert.

Ich vermute mal ich hab jetzt eine der 3 erwähnten verschiedenen Optionen für die Fußzeile konfiguriert, ich weiß es nicht mehr genau, aber bei mir steht in der Fußzeile einfach nur „GmbH - Gesellschaft mit beschränkter Haftung“ und weder der Firmenname noch der Geschäftsführer. Den Firmennamen habe ich somit nur oben im Address-Feld.