dolibarr  20.0.0-beta
pdf_azur.modules.php
Go to the documentation of this file.
1 <?php
2 /* Copyright (C) 2004-2014 Laurent Destailleur <eldy@users.sourceforge.net>
3  * Copyright (C) 2005-2012 Regis Houssin <regis.houssin@inodbox.com>
4  * Copyright (C) 2008 Raphael Bertrand <raphael.bertrand@resultic.fr>
5  * Copyright (C) 2010-2015 Juanjo Menent <jmenent@2byte.es>
6  * Copyright (C) 2012 Christophe Battarel <christophe.battarel@altairis.fr>
7  * Copyright (C) 2012 Cedric Salvador <csalvador@gpcsolutions.fr>
8  * Copyright (C) 2015 Marcos García <marcosgdf@gmail.com>
9  * Copyright (C) 2017-2018 Ferran Marcet <fmarcet@2byte.es>
10  * Copyright (C) 2018-2024 Frédéric France <frederic.france@free.fr>
11  * Copyright (C) 2019 Pierre Ardoin <mapiolca@me.com>
12  * Copyright (C) 2024 MDW <mdeweerd@users.noreply.github.com>
13  *
14  * This program is free software; you can redistribute it and/or modify
15  * it under the terms of the GNU General Public License as published by
16  * the Free Software Foundation; either version 3 of the License, or
17  * (at your option) any later version.
18  *
19  * This program is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22  * GNU General Public License for more details.
23  *
24  * You should have received a copy of the GNU General Public License
25  * along with this program. If not, see <https://www.gnu.org/licenses/>.
26  * or see https://www.gnu.org/
27  */
28 
35 require_once DOL_DOCUMENT_ROOT.'/core/modules/propale/modules_propale.php';
36 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
37 require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
38 require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
39 require_once DOL_DOCUMENT_ROOT.'/core/lib/pdf.lib.php';
40 
41 
46 {
50  public $db;
51 
55  public $entity;
56 
60  public $name;
61 
65  public $description;
66 
70  public $update_main_doc_field;
71 
75  public $type;
76 
81  public $version = 'dolibarr';
82 
83 
89  public function __construct($db)
90  {
91  global $langs, $mysoc;
92 
93  // Translations
94  $langs->loadLangs(array("main", "bills"));
95 
96  $this->db = $db;
97  $this->name = "azur";
98  $this->description = $langs->trans('DocModelAzurDescription');
99  $this->update_main_doc_field = 1; // Save the name of generated file as the main doc when generating a doc with this template
100 
101  // Dimension page
102  $this->type = 'pdf';
103  $formatarray = pdf_getFormat();
104  $this->page_largeur = $formatarray['width'];
105  $this->page_hauteur = $formatarray['height'];
106  $this->format = array($this->page_largeur, $this->page_hauteur);
107  $this->marge_gauche = getDolGlobalInt('MAIN_PDF_MARGIN_LEFT', 10);
108  $this->marge_droite = getDolGlobalInt('MAIN_PDF_MARGIN_RIGHT', 10);
109  $this->marge_haute = getDolGlobalInt('MAIN_PDF_MARGIN_TOP', 10);
110  $this->marge_basse = getDolGlobalInt('MAIN_PDF_MARGIN_BOTTOM', 10);
111 
112  $this->option_logo = 1; // Display logo
113  $this->option_tva = 1; // Manage the vat option FACTURE_TVAOPTION
114  $this->option_modereg = 1; // Display payment mode
115  $this->option_condreg = 1; // Display payment terms
116  $this->option_multilang = 1; // Available in several languages
117  $this->option_escompte = 0; // Displays if there has been a discount
118  $this->option_credit_note = 0; // Support credit notes
119  $this->option_freetext = 1; // Support add of a personalised text
120  $this->option_draft_watermark = 1; // Support add of a watermark on drafts
121  $this->watermark = '';
122 
123  // Get source company
124  $this->emetteur = $mysoc;
125  if (empty($this->emetteur->country_code)) {
126  $this->emetteur->country_code = substr($langs->defaultlang, -2); // By default, if was not defined
127  }
128 
129  // Define position of columns
130  $this->posxdesc = $this->marge_gauche + 1;
131  if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
132  $this->posxtva = 101;
133  $this->posxup = 118;
134  $this->posxqty = 135;
135  $this->posxunit = 151;
136  } else {
137  $this->posxtva = 106;
138  $this->posxup = 122;
139  $this->posxqty = 145;
140  $this->posxunit = 162;
141  }
142  $this->posxdiscount = 162;
143  $this->postotalht = 174;
144  if (getDolGlobalString('MAIN_GENERATE_DOCUMENTS_WITHOUT_VAT') || getDolGlobalString('MAIN_GENERATE_DOCUMENTS_WITHOUT_VAT_COLUMN')) {
145  $this->posxtva = $this->posxup;
146  }
147  $this->posxpicture = $this->posxtva - getDolGlobalInt('MAIN_DOCUMENTS_WITH_PICTURE_WIDTH', 20); // width of images
148  if ($this->page_largeur < 210) { // To work with US executive format
149  $this->posxpicture -= 20;
150  $this->posxtva -= 20;
151  $this->posxup -= 20;
152  $this->posxqty -= 20;
153  $this->posxunit -= 20;
154  $this->posxdiscount -= 20;
155  $this->postotalht -= 20;
156  }
157 
158  $this->tva = array();
159  $this->tva_array = array();
160  $this->localtax1 = array();
161  $this->localtax2 = array();
162  $this->atleastoneratenotnull = 0;
163  $this->atleastonediscount = 0;
164  }
165 
166  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
178  public function write_file($object, $outputlangs, $srctemplatepath = '', $hidedetails = 0, $hidedesc = 0, $hideref = 0)
179  {
180  // phpcs:enable
181  global $user, $langs, $conf, $mysoc, $db, $hookmanager, $nblines;
182 
183  dol_syslog("write_file outputlangs->defaultlang=".(is_object($outputlangs) ? $outputlangs->defaultlang : 'null'));
184 
185  if (!is_object($outputlangs)) {
186  $outputlangs = $langs;
187  }
188  // For backward compatibility with FPDF, force output charset to ISO, because FPDF expect text to be encoded in ISO
189  if (getDolGlobalString('MAIN_USE_FPDF')) {
190  $outputlangs->charset_output = 'ISO-8859-1';
191  }
192 
193  // Load translation files required by page
194  $outputlangs->loadLangs(array("main", "dict", "companies", "bills", "propal", "products"));
195 
196  global $outputlangsbis;
197  $outputlangsbis = null;
198  if (getDolGlobalString('PDF_USE_ALSO_LANGUAGE_CODE') && $outputlangs->defaultlang != getDolGlobalString('PDF_USE_ALSO_LANGUAGE_CODE')) {
199  $outputlangsbis = new Translate('', $conf);
200  $outputlangsbis->setDefaultLang(getDolGlobalString('PDF_USE_ALSO_LANGUAGE_CODE'));
201  $outputlangsbis->loadLangs(array("main", "dict", "companies", "bills", "products", "propal"));
202  }
203 
204  // Show Draft Watermark
205  if ($object->statut == $object::STATUS_DRAFT && getDolGlobalString('PROPALE_DRAFT_WATERMARK')) {
206  $this->watermark = getDolGlobalString('PROPALE_DRAFT_WATERMARK');
207  }
208 
209  $nblines = count($object->lines);
210 
211  // Loop on each lines to detect if there is at least one image to show
212  $realpatharray = array();
213  $this->atleastonephoto = false;
214  if (getDolGlobalString('MAIN_GENERATE_PROPOSALS_WITH_PICTURE')) {
215  $objphoto = new Product($this->db);
216 
217  for ($i = 0; $i < $nblines; $i++) {
218  if (empty($object->lines[$i]->fk_product)) {
219  continue;
220  }
221 
222  $objphoto->fetch($object->lines[$i]->fk_product);
223  //var_dump($objphoto->ref);exit;
224  $pdir = array();
225  if (getDolGlobalInt('PRODUCT_USE_OLD_PATH_FOR_PHOTO')) {
226  $pdir[0] = get_exdir($objphoto->id, 2, 0, 0, $objphoto, 'product').$objphoto->id."/photos/";
227  $pdir[1] = get_exdir(0, 0, 0, 0, $objphoto, 'product').dol_sanitizeFileName($objphoto->ref).'/';
228  } else {
229  $pdir[0] = get_exdir(0, 0, 0, 0, $objphoto, 'product'); // default
230  $pdir[1] = get_exdir($objphoto->id, 2, 0, 0, $objphoto, 'product').$objphoto->id."/photos/"; // alternative
231  }
232 
233  $arephoto = false;
234  $realpath = '';
235  foreach ($pdir as $midir) {
236  if (!$arephoto) {
237  if ($conf->entity != $objphoto->entity) {
238  $dir = $conf->product->multidir_output[$objphoto->entity].'/'.$midir; //Check repertories of current entities
239  } else {
240  $dir = $conf->product->dir_output.'/'.$midir; //Check repertory of the current product
241  }
242  foreach ($objphoto->liste_photos($dir, 1) as $key => $obj) {
243  if (!getDolGlobalInt('CAT_HIGH_QUALITY_IMAGES')) { // If CAT_HIGH_QUALITY_IMAGES not defined, we use thumb if defined and then original photo
244  if ($obj['photo_vignette']) {
245  $filename = $obj['photo_vignette'];
246  } else {
247  $filename = $obj['photo'];
248  }
249  } else {
250  $filename = $obj['photo'];
251  }
252 
253  $realpath = $dir.$filename;
254  $arephoto = true;
255  $this->atleastonephoto = true;
256  }
257  }
258  }
259 
260  if ($realpath && $arephoto) {
261  $realpatharray[$i] = $realpath;
262  }
263  }
264  }
265 
266  if (count($realpatharray) == 0) {
267  $this->posxpicture = $this->posxtva;
268  }
269 
270  if ($conf->propal->multidir_output[$conf->entity]) {
271  $object->fetch_thirdparty();
272 
273  $deja_regle = 0;
274 
275  // Definition of $dir and $file
276  if ($object->specimen) {
277  $dir = $conf->propal->multidir_output[$conf->entity];
278  $file = $dir."/SPECIMEN.pdf";
279  } else {
280  $objectref = dol_sanitizeFileName($object->ref);
281  $dir = $conf->propal->multidir_output[$object->entity]."/".$objectref;
282  $file = $dir."/".$objectref.".pdf";
283  }
284 
285  if (!file_exists($dir)) {
286  if (dol_mkdir($dir) < 0) {
287  $this->error = $langs->transnoentities("ErrorCanNotCreateDir", $dir);
288  return 0;
289  }
290  }
291 
292  if (file_exists($dir)) {
293  // Add pdfgeneration hook
294  if (!is_object($hookmanager)) {
295  include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php';
296  $hookmanager = new HookManager($this->db);
297  }
298  $hookmanager->initHooks(array('pdfgeneration'));
299  $parameters = array('file' => $file, 'object' => $object, 'outputlangs' => $outputlangs);
300  global $action;
301  $reshook = $hookmanager->executeHooks('beforePDFCreation', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
302 
303  // Set nblines with the new content of lines after hook
304  $nblines = count($object->lines);
305  //$nbpayments = count($object->getListOfPayments());
306 
307  // Create pdf instance
308  $pdf = pdf_getInstance($this->format);
309  $default_font_size = pdf_getPDFFontSize($outputlangs); // Must be after pdf_getInstance
310  $pdf->SetAutoPageBreak(1, 0);
311 
312  if (class_exists('TCPDF')) {
313  $pdf->setPrintHeader(false);
314  $pdf->setPrintFooter(false);
315  }
316  $pdf->SetFont(pdf_getPDFFont($outputlangs));
317  // Set path to the background PDF File
318  if (getDolGlobalString('MAIN_ADD_PDF_BACKGROUND')) {
319  $logodir = $conf->mycompany->dir_output;
320  if (!empty($conf->mycompany->multidir_output[$object->entity])) {
321  $logodir = $conf->mycompany->multidir_output[$object->entity];
322  }
323  $pagecount = $pdf->setSourceFile($logodir.'/' . getDolGlobalString('MAIN_ADD_PDF_BACKGROUND'));
324  $tplidx = $pdf->importPage(1);
325  }
326 
327  $pdf->Open();
328  $pagenb = 0;
329  $pdf->SetDrawColor(128, 128, 128);
330 
331  $pdf->SetTitle($outputlangs->convToOutputCharset($object->ref));
332  $pdf->SetSubject($outputlangs->transnoentities("PdfCommercialProposalTitle"));
333  $pdf->SetCreator("Dolibarr ".DOL_VERSION);
334  $pdf->SetAuthor($outputlangs->convToOutputCharset($user->getFullName($outputlangs)));
335  $pdf->SetKeyWords($outputlangs->convToOutputCharset($object->ref)." ".$outputlangs->transnoentities("PdfCommercialProposalTitle")." ".$outputlangs->convToOutputCharset($object->thirdparty->name));
336  if (getDolGlobalString('MAIN_DISABLE_PDF_COMPRESSION')) {
337  $pdf->SetCompression(false);
338  }
339 
340  // @phan-suppress-next-line PhanPluginSuspiciousParamOrder
341  $pdf->SetMargins($this->marge_gauche, $this->marge_haute, $this->marge_droite); // Left, Top, Right
342 
343  // Set $this->atleastonediscount if you have at least one discount
344  for ($i = 0; $i < $nblines; $i++) {
345  if ($object->lines[$i]->remise_percent) {
346  $this->atleastonediscount++;
347  }
348  }
349  if (empty($this->atleastonediscount)) {
350  $delta = ($this->postotalht - $this->posxdiscount);
351  $this->posxpicture += $delta;
352  $this->posxtva += $delta;
353  $this->posxup += $delta;
354  $this->posxqty += $delta;
355  $this->posxunit += $delta;
356  $this->posxdiscount += $delta;
357  // post of fields after are not modified, stay at same position
358  }
359 
360  // New page
361  $pdf->AddPage();
362  if (!empty($tplidx)) {
363  $pdf->useTemplate($tplidx);
364  }
365  $pagenb++;
366 
367  $heightforinfotot = 40; // Height reserved to output the info and total part
368  $heightforsignature = !getDolGlobalString('PROPAL_DISABLE_SIGNATURE') ? (pdfGetHeightForHtmlContent($pdf, $outputlangs->transnoentities("ProposalCustomerSignature")) + 10) : 0;
369  $heightforfreetext = (isset($conf->global->MAIN_PDF_FREETEXT_HEIGHT) ? $conf->global->MAIN_PDF_FREETEXT_HEIGHT : 5); // Height reserved to output the free text on last page
370  $heightforfooter = $this->marge_basse + 8; // Height reserved to output the footer (value include bottom margin)
371  if (getDolGlobalString('MAIN_GENERATE_DOCUMENTS_SHOW_FOOT_DETAILS')) {
372  $heightforfooter += 6;
373  }
374  //print $heightforinfotot + $heightforsignature + $heightforfreetext + $heightforfooter;exit;
375 
376  $top_shift = $this->_pagehead($pdf, $object, 1, $outputlangs, $outputlangsbis);
377  $pdf->SetFont('', '', $default_font_size - 1);
378  $pdf->MultiCell(0, 3, ''); // Set interline to 3
379  $pdf->SetTextColor(0, 0, 0);
380 
381 
382  $tab_top = 90 + $top_shift;
383  $tab_top_newpage = (!getDolGlobalInt('MAIN_PDF_DONOTREPEAT_HEAD') ? 42 + $top_shift : 10);
384 
385  // Incoterm
386  $height_incoterms = 0;
387  if (isModEnabled('incoterm')) {
388  $desc_incoterms = $object->getIncotermsForPDF();
389  if ($desc_incoterms) {
390  $tab_top -= 2;
391 
392  $pdf->SetFont('', '', $default_font_size - 1);
393  $pdf->writeHTMLCell(190, 3, $this->posxdesc - 1, $tab_top - 1, dol_htmlentitiesbr($desc_incoterms), 0, 1);
394  $nexY = $pdf->GetY();
395  $height_incoterms = $nexY - $tab_top;
396 
397  // Rect takes a length in 3rd parameter
398  $pdf->SetDrawColor(192, 192, 192);
399  $pdf->Rect($this->marge_gauche, $tab_top - 1, $this->page_largeur - $this->marge_gauche - $this->marge_droite, $height_incoterms + 1);
400 
401  $tab_top = $nexY + 6;
402  $height_incoterms += 4;
403  }
404  }
405 
406  // Displays notes
407  $notetoshow = empty($object->note_public) ? '' : $object->note_public;
408  if (getDolGlobalString('MAIN_ADD_SALE_REP_SIGNATURE_IN_NOTE')) {
409  // Get first sale rep
410  if (is_object($object->thirdparty)) {
411  $salereparray = $object->thirdparty->getSalesRepresentatives($user);
412  $salerepobj = new User($this->db);
413  $salerepobj->fetch($salereparray[0]['id']);
414  if (!empty($salerepobj->signature)) {
415  $notetoshow = dol_concatdesc($notetoshow, $salerepobj->signature);
416  }
417  }
418  }
419  // Extrafields in note
420  $extranote = $this->getExtrafieldsInHtml($object, $outputlangs);
421  if (!empty($extranote)) {
422  $notetoshow = dol_concatdesc($notetoshow, $extranote);
423  }
424  if (getDolGlobalString('MAIN_ADD_CREATOR_IN_NOTE') && $object->user_author_id > 0) {
425  $tmpuser = new User($this->db);
426  $tmpuser->fetch($object->user_author_id);
427 
428 
429  $creator_info = $langs->trans("CaseFollowedBy").' '.$tmpuser->getFullName($langs);
430  if ($tmpuser->email) {
431  $creator_info .= ', '.$langs->trans("EMail").': '.$tmpuser->email;
432  }
433  if ($tmpuser->office_phone) {
434  $creator_info .= ', '.$langs->trans("Phone").': '.$tmpuser->office_phone;
435  }
436 
437  $notetoshow = dol_concatdesc($notetoshow, $creator_info);
438  }
439 
440  if ($notetoshow) {
441  $tab_top -= 2;
442 
443  $substitutionarray = pdf_getSubstitutionArray($outputlangs, null, $object);
444  complete_substitutions_array($substitutionarray, $outputlangs, $object);
445  $notetoshow = make_substitutions($notetoshow, $substitutionarray, $outputlangs);
446  $notetoshow = convertBackOfficeMediasLinksToPublicLinks($notetoshow);
447 
448  $pdf->SetFont('', '', $default_font_size - 1);
449  $pdf->writeHTMLCell(190, 3, $this->posxdesc - 1, $tab_top - 1, dol_htmlentitiesbr($notetoshow), 0, 1);
450  $nexY = $pdf->GetY();
451  $height_note = $nexY - $tab_top;
452 
453  // Rect takes a length in 3rd parameter
454  $pdf->SetDrawColor(192, 192, 192);
455  $pdf->Rect($this->marge_gauche, $tab_top - 1, $this->page_largeur - $this->marge_gauche - $this->marge_droite, $height_note + 1);
456 
457  $tab_top = $nexY + 6;
458  }
459 
460  $iniY = $tab_top + 7;
461  $curY = $tab_top + 7;
462  $nexY = $tab_top + 7;
463 
464  // Loop on each lines
465  for ($i = 0; $i < $nblines; $i++) {
466  $curY = $nexY;
467  $pdf->SetFont('', '', $default_font_size - 1); // Into loop to work with multipage
468  $pdf->SetTextColor(0, 0, 0);
469 
470  // Define size of image if we need it
471  $imglinesize = array();
472  if (!empty($realpatharray[$i])) {
473  $imglinesize = pdf_getSizeForImage($realpatharray[$i]);
474  }
475 
476  $pdf->setTopMargin($tab_top_newpage);
477  $pdf->setPageOrientation('', 1, $heightforfooter + $heightforfreetext + $heightforsignature + $heightforinfotot); // The only function to edit the bottom margin of current page to set it.
478  $pageposbefore = $pdf->getPage();
479 
480  $showpricebeforepagebreak = 1;
481  $posYAfterImage = 0;
482  $posYAfterDescription = 0;
483 
484  // We start with Photo of product line
485  if (isset($imglinesize['width']) && isset($imglinesize['height']) && ($curY + $imglinesize['height']) > ($this->page_hauteur - ($heightforfooter + $heightforfreetext + $heightforsignature + $heightforinfotot))) { // If photo too high, we moved completely on new page
486  $pdf->AddPage('', '', true);
487  if (!empty($tplidx)) {
488  $pdf->useTemplate($tplidx);
489  }
490  if (!getDolGlobalInt('MAIN_PDF_DONOTREPEAT_HEAD')) {
491  $this->_pagehead($pdf, $object, 0, $outputlangs);
492  }
493  $pdf->setPage($pageposbefore + 1);
494 
495  $curY = $tab_top_newpage;
496 
497  // Allows data in the first page if description is long enough to break in multiples pages
498  if (getDolGlobalString('MAIN_PDF_DATA_ON_FIRST_PAGE')) {
499  $showpricebeforepagebreak = 1;
500  } else {
501  $showpricebeforepagebreak = 0;
502  }
503  }
504 
505  if (isset($imglinesize['width']) && isset($imglinesize['height'])) {
506  $curX = $this->posxpicture - 1;
507  $pdf->Image($realpatharray[$i], $curX + (($this->posxtva - $this->posxpicture - $imglinesize['width']) / 2), $curY, $imglinesize['width'], $imglinesize['height'], '', '', '', 2, 300); // Use 300 dpi
508  // $pdf->Image does not increase value return by getY, so we save it manually
509  $posYAfterImage = $curY + $imglinesize['height'];
510  }
511 
512  // Description of product line
513  $curX = $this->posxdesc - 1;
514 
515  $pdf->startTransaction();
516  pdf_writelinedesc($pdf, $object, $i, $outputlangs, $this->posxpicture - $curX, 3, $curX, $curY, $hideref, $hidedesc);
517  $pageposafter = $pdf->getPage();
518  if ($pageposafter > $pageposbefore) { // There is a pagebreak
519  $pdf->rollbackTransaction(true);
520  $pageposafter = $pageposbefore;
521  //print $pageposafter.'-'.$pageposbefore;exit;
522  $pdf->setPageOrientation('', 1, $heightforfooter); // The only function to edit the bottom margin of current page to set it.
523  pdf_writelinedesc($pdf, $object, $i, $outputlangs, $this->posxpicture - $curX, 3, $curX, $curY, $hideref, $hidedesc);
524 
525  $pageposafter = $pdf->getPage();
526  $posyafter = $pdf->GetY();
527  //var_dump($posyafter); var_dump(($this->page_hauteur - ($heightforfooter+$heightforfreetext+$heightforinfotot))); exit;
528  if ($posyafter > ($this->page_hauteur - ($heightforfooter + $heightforfreetext + $heightforsignature + $heightforinfotot))) { // There is no space left for total+free text
529  if ($i == ($nblines - 1)) { // No more lines, and no space left to show total, so we create a new page
530  $pdf->AddPage('', '', true);
531  if (!empty($tplidx)) {
532  $pdf->useTemplate($tplidx);
533  }
534  if (!getDolGlobalInt('MAIN_PDF_DONOTREPEAT_HEAD')) {
535  $this->_pagehead($pdf, $object, 0, $outputlangs);
536  }
537  $pdf->setPage($pageposafter + 1);
538  }
539  } else {
540  // We found a page break
541 
542  // Allows data in the first page if description is long enough to break in multiples pages
543  if (getDolGlobalString('MAIN_PDF_DATA_ON_FIRST_PAGE')) {
544  $showpricebeforepagebreak = 1;
545  } else {
546  $showpricebeforepagebreak = 0;
547  }
548  }
549  } else { // No pagebreak
550  $pdf->commitTransaction();
551  }
552  $posYAfterDescription = $pdf->GetY();
553 
554  $nexY = $pdf->GetY();
555  $pageposafter = $pdf->getPage();
556 
557  $pdf->setPage($pageposbefore);
558  $pdf->setTopMargin($this->marge_haute);
559  $pdf->setPageOrientation('', 1, 0); // The only function to edit the bottom margin of current page to set it.
560 
561  // We suppose that a too long description or photo were moved completely on next page
562  if ($pageposafter > $pageposbefore && empty($showpricebeforepagebreak)) {
563  $pdf->setPage($pageposafter);
564  $curY = $tab_top_newpage;
565  }
566 
567  $pdf->SetFont('', '', $default_font_size - 1); // On repositionne la police par default
568 
569  // VAT Rate
570  if (!getDolGlobalString('MAIN_GENERATE_DOCUMENTS_WITHOUT_VAT') && !getDolGlobalString('MAIN_GENERATE_DOCUMENTS_WITHOUT_VAT_COLUMN')) {
571  $vat_rate = pdf_getlinevatrate($object, $i, $outputlangs, $hidedetails);
572  $pdf->SetXY($this->posxtva - 5, $curY);
573  $pdf->MultiCell($this->posxup - $this->posxtva + 4, 3, $vat_rate, 0, 'R');
574  }
575 
576  // Unit price before discount
577  $up_excl_tax = pdf_getlineupexcltax($object, $i, $outputlangs, $hidedetails);
578  $pdf->SetXY($this->posxup, $curY);
579  $pdf->MultiCell($this->posxqty - $this->posxup - 0.8, 3, $up_excl_tax, 0, 'R', 0);
580 
581  // Quantity
582  $qty = pdf_getlineqty($object, $i, $outputlangs, $hidedetails);
583  $pdf->SetXY($this->posxqty, $curY);
584  $pdf->MultiCell($this->posxunit - $this->posxqty - 0.8, 4, $qty, 0, 'R'); // Enough for 6 chars
585 
586  // Unit
587  if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
588  $unit = pdf_getlineunit($object, $i, $outputlangs, $hidedetails);
589  $pdf->SetXY($this->posxunit, $curY);
590  $pdf->MultiCell($this->posxdiscount - $this->posxunit - 0.8, 4, $unit, 0, 'L');
591  }
592 
593  // Discount on line
594  $pdf->SetXY($this->posxdiscount, $curY);
595  if ($object->lines[$i]->remise_percent) {
596  $pdf->SetXY($this->posxdiscount - 2, $curY);
597  $remise_percent = pdf_getlineremisepercent($object, $i, $outputlangs, $hidedetails);
598  $pdf->MultiCell($this->postotalht - $this->posxdiscount + 2, 3, $remise_percent, 0, 'R');
599  }
600 
601  // Total HT line
602  $total_excl_tax = pdf_getlinetotalexcltax($object, $i, $outputlangs, $hidedetails);
603  $pdf->SetXY($this->postotalht, $curY);
604  $pdf->MultiCell($this->page_largeur - $this->marge_droite - $this->postotalht, 3, $total_excl_tax, 0, 'R', 0);
605 
606  // Collecte des totaux par valeur de tva dans $this->tva["taux"]=total_tva
607  if (isModEnabled("multicurrency") && $object->multicurrency_tx != 1) {
608  $tvaligne = $object->lines[$i]->multicurrency_total_tva;
609  } else {
610  $tvaligne = $object->lines[$i]->total_tva;
611  }
612 
613  $localtax1ligne = $object->lines[$i]->total_localtax1;
614  $localtax2ligne = $object->lines[$i]->total_localtax2;
615  $localtax1_rate = $object->lines[$i]->localtax1_tx;
616  $localtax2_rate = $object->lines[$i]->localtax2_tx;
617  $localtax1_type = $object->lines[$i]->localtax1_type;
618  $localtax2_type = $object->lines[$i]->localtax2_type;
619 
620  // TODO remise_percent is an obsolete field for object parent
621  /*if ($object->remise_percent) {
622  $tvaligne -= ($tvaligne * $object->remise_percent) / 100;
623  }
624  if ($object->remise_percent) {
625  $localtax1ligne -= ($localtax1ligne * $object->remise_percent) / 100;
626  }
627  if ($object->remise_percent) {
628  $localtax2ligne -= ($localtax2ligne * $object->remise_percent) / 100;
629  }*/
630 
631  $vatrate = (string) $object->lines[$i]->tva_tx;
632 
633  // Retrieve type from database for backward compatibility with old records
634  if ((!isset($localtax1_type) || $localtax1_type == '' || !isset($localtax2_type) || $localtax2_type == '') // if tax type not defined
635  && (!empty($localtax1_rate) || !empty($localtax2_rate))) { // and there is local tax
636  $localtaxtmp_array = getLocalTaxesFromRate($vatrate, 0, $object->thirdparty, $mysoc);
637  $localtax1_type = isset($localtaxtmp_array[0]) ? $localtaxtmp_array[0] : '';
638  $localtax2_type = isset($localtaxtmp_array[2]) ? $localtaxtmp_array[2] : '';
639  }
640 
641  // retrieve global local tax
642  if ($localtax1_type && $localtax1ligne != 0) {
643  if (empty($this->localtax1[$localtax1_type][$localtax1_rate])) {
644  $this->localtax1[$localtax1_type][$localtax1_rate] = $localtax1ligne;
645  } else {
646  $this->localtax1[$localtax1_type][$localtax1_rate] += $localtax1ligne;
647  }
648  }
649  if ($localtax2_type && $localtax2ligne != 0) {
650  if (empty($this->localtax2[$localtax2_type][$localtax2_rate])) {
651  $this->localtax2[$localtax2_type][$localtax2_rate] = $localtax2ligne;
652  } else {
653  $this->localtax2[$localtax2_type][$localtax2_rate] += $localtax2ligne;
654  }
655  }
656 
657  if (($object->lines[$i]->info_bits & 0x01) == 0x01) {
658  $vatrate .= '*';
659  }
660 
661  // Fill $this->tva and $this->tva_array
662  if (!isset($this->tva[$vatrate])) {
663  $this->tva[$vatrate] = 0;
664  }
665  $this->tva[$vatrate] += $tvaligne;
666  $vatcode = $object->lines[$i]->vat_src_code;
667  if (empty($this->tva_array[$vatrate.($vatcode ? ' ('.$vatcode.')' : '')]['amount'])) {
668  $this->tva_array[$vatrate.($vatcode ? ' ('.$vatcode.')' : '')]['amount'] = 0;
669  }
670  $this->tva_array[$vatrate.($vatcode ? ' ('.$vatcode.')' : '')] = array('vatrate' => $vatrate, 'vatcode' => $vatcode, 'amount' => $this->tva_array[$vatrate.($vatcode ? ' ('.$vatcode.')' : '')]['amount'] + $tvaligne);
671 
672  if ($posYAfterImage > $posYAfterDescription) {
673  $nexY = $posYAfterImage;
674  }
675 
676  // Add line
677  if (getDolGlobalString('MAIN_PDF_DASH_BETWEEN_LINES') && $i < ($nblines - 1)) {
678  $pdf->setPage($pageposafter);
679  $pdf->SetLineStyle(array('dash' => '1,1', 'color' => array(80, 80, 80)));
680  //$pdf->SetDrawColor(190,190,200);
681  $pdf->line($this->marge_gauche, $nexY + 1, $this->page_largeur - $this->marge_droite, $nexY + 1);
682  $pdf->SetLineStyle(array('dash' => 0));
683  }
684 
685  $nexY += 2; // Add space between lines
686 
687  // Detect if some page were added automatically and output _tableau for past pages
688  while ($pagenb < $pageposafter) {
689  $pdf->setPage($pagenb);
690  if ($pagenb == 1) {
691  $this->_tableau($pdf, $tab_top, $this->page_hauteur - $tab_top - $heightforfooter, 0, $outputlangs, 0, 1, $object->multicurrency_code);
692  } else {
693  $this->_tableau($pdf, $tab_top_newpage, $this->page_hauteur - $tab_top_newpage - $heightforfooter, 0, $outputlangs, 1, 1, $object->multicurrency_code);
694  }
695  $this->_pagefoot($pdf, $object, $outputlangs, 1);
696  $pagenb++;
697  $pdf->setPage($pagenb);
698  $pdf->setPageOrientation('', 1, 0); // The only function to edit the bottom margin of current page to set it.
699  if (!getDolGlobalInt('MAIN_PDF_DONOTREPEAT_HEAD')) {
700  $this->_pagehead($pdf, $object, 0, $outputlangs);
701  }
702  if (!empty($tplidx)) {
703  $pdf->useTemplate($tplidx);
704  }
705  }
706  if (isset($object->lines[$i + 1]->pagebreak) && $object->lines[$i + 1]->pagebreak) {
707  if ($pagenb == 1) {
708  $this->_tableau($pdf, $tab_top, $this->page_hauteur - $tab_top - $heightforfooter, 0, $outputlangs, 0, 1, $object->multicurrency_code);
709  } else {
710  $this->_tableau($pdf, $tab_top_newpage, $this->page_hauteur - $tab_top_newpage - $heightforfooter, 0, $outputlangs, 1, 1, $object->multicurrency_code);
711  }
712  $this->_pagefoot($pdf, $object, $outputlangs, 1);
713  // New page
714  $pdf->AddPage();
715  if (!empty($tplidx)) {
716  $pdf->useTemplate($tplidx);
717  }
718  $pagenb++;
719  if (!getDolGlobalInt('MAIN_PDF_DONOTREPEAT_HEAD')) {
720  $this->_pagehead($pdf, $object, 0, $outputlangs);
721  }
722  }
723  }
724 
725  // Show square
726  if ($pagenb == 1) {
727  $this->_tableau($pdf, $tab_top, $this->page_hauteur - $tab_top - $heightforinfotot - $heightforfreetext - $heightforsignature - $heightforfooter, 0, $outputlangs, 0, 0, $object->multicurrency_code);
728  $bottomlasttab = $this->page_hauteur - $heightforinfotot - $heightforfreetext - $heightforsignature - $heightforfooter + 1;
729  } else {
730  $this->_tableau($pdf, $tab_top_newpage, $this->page_hauteur - $tab_top_newpage - $heightforinfotot - $heightforfreetext - $heightforsignature - $heightforfooter, 0, $outputlangs, 1, 0, $object->multicurrency_code);
731  $bottomlasttab = $this->page_hauteur - $heightforinfotot - $heightforfreetext - $heightforsignature - $heightforfooter + 1;
732  }
733 
734  // Affiche zone infos
735  $posy = $this->_tableau_info($pdf, $object, $bottomlasttab, $outputlangs);
736 
737  // Affiche zone totaux
738  $posy = $this->_tableau_tot($pdf, $object, 0, $bottomlasttab, $outputlangs);
739 
740  // Affiche zone versements
741  /*
742  if ($deja_regle || $amount_credit_notes_included || $amount_deposits_included)
743  {
744  $posy=$this->_tableau_versements($pdf, $object, $posy, $outputlangs);
745  }
746  */
747 
748  // Customer signature area
749  if (!getDolGlobalString('PROPAL_DISABLE_SIGNATURE')) {
750  $posy = $this->_signature_area($pdf, $object, $posy, $outputlangs);
751  }
752 
753  // Pied de page
754  $this->_pagefoot($pdf, $object, $outputlangs);
755  if (method_exists($pdf, 'AliasNbPages')) {
756  $pdf->AliasNbPages();
757  }
758 
759  //If propal merge product PDF is active
760  if (getDolGlobalString('PRODUIT_PDF_MERGE_PROPAL')) {
761  require_once DOL_DOCUMENT_ROOT.'/product/class/propalmergepdfproduct.class.php';
762 
763  $already_merged = array();
764  foreach ($object->lines as $line) {
765  if (!empty($line->fk_product) && !(in_array($line->fk_product, $already_merged))) {
766  // Find the desired PDF
767  $filetomerge = new Propalmergepdfproduct($this->db);
768 
769  if (getDolGlobalInt('MAIN_MULTILANGS')) {
770  $filetomerge->fetch_by_product($line->fk_product, $outputlangs->defaultlang);
771  } else {
772  $filetomerge->fetch_by_product($line->fk_product);
773  }
774 
775  $already_merged[] = $line->fk_product;
776 
777  $product = new Product($this->db);
778  $product->fetch($line->fk_product);
779 
780  if ($product->entity != $conf->entity) {
781  $entity_product_file = $product->entity;
782  } else {
783  $entity_product_file = $conf->entity;
784  }
785 
786  // If PDF is selected and file is not empty
787  if (count($filetomerge->lines) > 0) {
788  foreach ($filetomerge->lines as $linefile) {
789  if (!empty($linefile->id) && !empty($linefile->file_name)) {
790  if (getDolGlobalInt('PRODUCT_USE_OLD_PATH_FOR_PHOTO')) {
791  if (isModEnabled("product")) {
792  $filetomerge_dir = $conf->product->multidir_output[$entity_product_file].'/'.get_exdir($product->id, 2, 0, 0, $product, 'product').$product->id."/photos";
793  } elseif (isModEnabled("service")) {
794  $filetomerge_dir = $conf->service->multidir_output[$entity_product_file].'/'.get_exdir($product->id, 2, 0, 0, $product, 'product').$product->id."/photos";
795  }
796  } else {
797  if (isModEnabled("product")) {
798  $filetomerge_dir = $conf->product->multidir_output[$entity_product_file].'/'.get_exdir(0, 0, 0, 0, $product, 'product');
799  } elseif (isModEnabled("service")) {
800  $filetomerge_dir = $conf->service->multidir_output[$entity_product_file].'/'.get_exdir(0, 0, 0, 0, $product, 'product');
801  }
802  }
803 
804  dol_syslog(get_class($this).':: upload_dir='.$filetomerge_dir, LOG_DEBUG);
805 
806  $infile = $filetomerge_dir.'/'.$linefile->file_name;
807  if (file_exists($infile) && is_readable($infile)) {
808  $pagecount = $pdf->setSourceFile($infile);
809  for ($i = 1; $i <= $pagecount; $i++) {
810  $tplIdx = $pdf->importPage($i);
811  if ($tplIdx !== false) {
812  $s = $pdf->getTemplatesize($tplIdx);
813  $pdf->AddPage($s['h'] > $s['w'] ? 'P' : 'L');
814  $pdf->useTemplate($tplIdx);
815  } else {
816  setEventMessages(null, array($infile.' cannot be added, probably protected PDF'), 'warnings');
817  }
818  }
819  }
820  }
821  }
822  }
823  }
824  }
825  }
826 
827  $pdf->Close();
828 
829  $pdf->Output($file, 'F');
830 
831  //Add pdfgeneration hook
832  $hookmanager->initHooks(array('pdfgeneration'));
833  $parameters = array('file' => $file, 'object' => $object, 'outputlangs' => $outputlangs);
834  global $action;
835  $reshook = $hookmanager->executeHooks('afterPDFCreation', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
836  if ($reshook < 0) {
837  $this->error = $hookmanager->error;
838  $this->errors = $hookmanager->errors;
839  }
840 
841  dolChmod($file);
842 
843  $this->result = array('fullpath' => $file);
844 
845  return 1; // No error
846  } else {
847  $this->error = $langs->trans("ErrorCanNotCreateDir", $dir);
848  return 0;
849  }
850  } else {
851  $this->error = $langs->trans("ErrorConstantNotDefined", "PROP_OUTPUTDIR");
852  return 0;
853  }
854  }
855 
856  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
857  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
867  protected function _tableau_versements(&$pdf, $object, $posy, $outputlangs)
868  {
869  // phpcs:enable
870  return 1;
871  }
872 
873  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
874  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
884  protected function _tableau_info(&$pdf, $object, $posy, $outputlangs)
885  {
886  // phpcs:enable
887  global $conf, $mysoc;
888  $default_font_size = pdf_getPDFFontSize($outputlangs);
889 
890  $pdf->SetFont('', '', $default_font_size - 1);
891 
892  $diffsizetitle = (!getDolGlobalString('PDF_DIFFSIZE_TITLE') ? 3 : $conf->global->PDF_DIFFSIZE_TITLE);
893 
894  // If France, show VAT mention if not applicable
895  if ($this->emetteur->country_code == 'FR' && empty($mysoc->tva_assuj)) {
896  $pdf->SetFont('', 'B', $default_font_size - $diffsizetitle);
897  $pdf->SetXY($this->marge_gauche, $posy);
898  $pdf->MultiCell(100, 3, $outputlangs->transnoentities("VATIsNotUsedForInvoice"), 0, 'L', 0);
899 
900  $posy = $pdf->GetY() + 4;
901  }
902 
903  $posxval = 52;
904  if (getDolGlobalString('MAIN_PDF_DELIVERY_DATE_TEXT')) {
905  $displaydate = "daytext";
906  } else {
907  $displaydate = "day";
908  }
909 
910  // Show shipping date
911  if (!empty($object->delivery_date)) {
912  $outputlangs->load("sendings");
913  $pdf->SetFont('', 'B', $default_font_size - $diffsizetitle);
914  $pdf->SetXY($this->marge_gauche, $posy);
915  $titre = $outputlangs->transnoentities("DateDeliveryPlanned").':';
916  $pdf->MultiCell(80, 4, $titre, 0, 'L');
917  $pdf->SetFont('', '', $default_font_size - $diffsizetitle);
918  $pdf->SetXY($posxval, $posy);
919  $dlp = dol_print_date($object->delivery_date, $displaydate, false, $outputlangs, true);
920  $pdf->MultiCell(80, 4, $dlp, 0, 'L');
921 
922  $posy = $pdf->GetY() + 1;
923  } elseif ($object->availability_code || $object->availability) { // Show availability conditions
924  $pdf->SetFont('', 'B', $default_font_size - $diffsizetitle);
925  $pdf->SetXY($this->marge_gauche, $posy);
926  $titre = $outputlangs->transnoentities("AvailabilityPeriod").':';
927  $pdf->MultiCell(80, 4, $titre, 0, 'L');
928  $pdf->SetTextColor(0, 0, 0);
929  $pdf->SetFont('', '', $default_font_size - $diffsizetitle);
930  $pdf->SetXY($posxval, $posy);
931  $lib_availability = ($outputlangs->transnoentities("AvailabilityType".$object->availability_code) != 'AvailabilityType'.$object->availability_code) ? $outputlangs->transnoentities("AvailabilityType".$object->availability_code) : $outputlangs->convToOutputCharset($object->availability);
932  $lib_availability = str_replace('\n', "\n", $lib_availability);
933  $pdf->MultiCell(80, 4, $lib_availability, 0, 'L');
934 
935  $posy = $pdf->GetY() + 1;
936  }
937 
938  // Show delivery mode
939  if (!getDolGlobalString('PROPOSAL_PDF_HIDE_DELIVERYMODE') && $object->shipping_method_id > 0) {
940  $outputlangs->load("sendings");
941 
942  $shipping_method_id = $object->shipping_method_id;
943  if (getDolGlobalString('SOCIETE_ASK_FOR_SHIPPING_METHOD') && !empty($this->emetteur->shipping_method_id)) {
944  $shipping_method_id = $this->emetteur->shipping_method_id;
945  }
946  $shipping_method_code = dol_getIdFromCode($this->db, $shipping_method_id, 'c_shipment_mode', 'rowid', 'code');
947  $shipping_method_label = dol_getIdFromCode($this->db, $shipping_method_id, 'c_shipment_mode', 'rowid', 'libelle');
948 
949  $pdf->SetFont('', 'B', $default_font_size - $diffsizetitle);
950  $pdf->SetXY($this->marge_gauche, $posy);
951  $titre = $outputlangs->transnoentities("SendingMethod").':';
952  $pdf->MultiCell(43, 4, $titre, 0, 'L');
953 
954  $pdf->SetFont('', '', $default_font_size - $diffsizetitle);
955  $pdf->SetXY($posxval, $posy);
956  $lib_condition_paiement = ($outputlangs->transnoentities("SendingMethod".strtoupper($shipping_method_code)) != "SendingMethod".strtoupper($shipping_method_code)) ? $outputlangs->trans("SendingMethod".strtoupper($shipping_method_code)) : $shipping_method_label;
957  $lib_condition_paiement = str_replace('\n', "\n", $lib_condition_paiement);
958  $pdf->MultiCell(67, 4, $lib_condition_paiement, 0, 'L');
959 
960  $posy = $pdf->GetY() + 1;
961  }
962 
963  // Show payments conditions
964  if (!getDolGlobalString('PROPOSAL_PDF_HIDE_PAYMENTTERM') && $object->cond_reglement_code) {
965  $pdf->SetFont('', 'B', $default_font_size - $diffsizetitle);
966  $pdf->SetXY($this->marge_gauche, $posy);
967  $titre = $outputlangs->transnoentities("PaymentConditions").':';
968  $pdf->MultiCell(43, 4, $titre, 0, 'L');
969 
970  $pdf->SetFont('', '', $default_font_size - $diffsizetitle);
971  $pdf->SetXY($posxval, $posy);
972  $lib_condition_paiement = $outputlangs->transnoentities("PaymentCondition".$object->cond_reglement_code) != 'PaymentCondition'.$object->cond_reglement_code ? $outputlangs->transnoentities("PaymentCondition".$object->cond_reglement_code) : $outputlangs->convToOutputCharset($object->cond_reglement_doc ? $object->cond_reglement_doc : $object->cond_reglement_label);
973  $lib_condition_paiement = str_replace('\n', "\n", $lib_condition_paiement);
974  if ($object->deposit_percent > 0) {
975  $lib_condition_paiement = str_replace('__DEPOSIT_PERCENT__', $object->deposit_percent, $lib_condition_paiement);
976  }
977  $pdf->MultiCell(67, 4, $lib_condition_paiement, 0, 'L');
978 
979  $posy = $pdf->GetY() + 3;
980  }
981 
982  if (!getDolGlobalString('PROPOSAL_PDF_HIDE_PAYMENTMODE')) {
983  // Show payment mode
984  if ($object->mode_reglement_code
985  && $object->mode_reglement_code != 'CHQ'
986  && $object->mode_reglement_code != 'VIR') {
987  $pdf->SetFont('', 'B', $default_font_size - $diffsizetitle);
988  $pdf->SetXY($this->marge_gauche, $posy);
989  $titre = $outputlangs->transnoentities("PaymentMode").':';
990  $pdf->MultiCell(80, 5, $titre, 0, 'L');
991  $pdf->SetFont('', '', $default_font_size - $diffsizetitle);
992  $pdf->SetXY($posxval, $posy);
993  $lib_mode_reg = $outputlangs->transnoentities("PaymentType".$object->mode_reglement_code) != 'PaymentType'.$object->mode_reglement_code ? $outputlangs->transnoentities("PaymentType".$object->mode_reglement_code) : $outputlangs->convToOutputCharset($object->mode_reglement);
994  $pdf->MultiCell(80, 5, $lib_mode_reg, 0, 'L');
995 
996  $posy = $pdf->GetY() + 2;
997  }
998 
999  // Show payment mode CHQ
1000  if (empty($object->mode_reglement_code) || $object->mode_reglement_code == 'CHQ') {
1001  // Si mode reglement non force ou si force a CHQ
1002  if (getDolGlobalInt('FACTURE_CHQ_NUMBER')) {
1003  if (getDolGlobalInt('FACTURE_CHQ_NUMBER') > 0) {
1004  $account = new Account($this->db);
1005  $account->fetch(getDolGlobalInt('FACTURE_CHQ_NUMBER'));
1006 
1007  $pdf->SetXY($this->marge_gauche, $posy);
1008  $pdf->SetFont('', 'B', $default_font_size - $diffsizetitle);
1009  $pdf->MultiCell(100, 3, $outputlangs->transnoentities('PaymentByChequeOrderedTo', $account->owner_name), 0, 'L', 0);
1010  $posy = $pdf->GetY() + 1;
1011 
1012  if (!getDolGlobalString('MAIN_PDF_HIDE_CHQ_ADDRESS')) {
1013  $pdf->SetXY($this->marge_gauche, $posy);
1014  $pdf->SetFont('', '', $default_font_size - $diffsizetitle);
1015  $pdf->MultiCell(100, 3, $outputlangs->convToOutputCharset($account->owner_address), 0, 'L', 0);
1016  $posy = $pdf->GetY() + 2;
1017  }
1018  }
1019  if ($conf->global->FACTURE_CHQ_NUMBER == -1) {
1020  $pdf->SetXY($this->marge_gauche, $posy);
1021  $pdf->SetFont('', 'B', $default_font_size - $diffsizetitle);
1022  $pdf->MultiCell(100, 3, $outputlangs->transnoentities('PaymentByChequeOrderedTo', $this->emetteur->name), 0, 'L', 0);
1023  $posy = $pdf->GetY() + 1;
1024 
1025  if (!getDolGlobalString('MAIN_PDF_HIDE_CHQ_ADDRESS')) {
1026  $pdf->SetXY($this->marge_gauche, $posy);
1027  $pdf->SetFont('', '', $default_font_size - $diffsizetitle);
1028  $pdf->MultiCell(100, 3, $outputlangs->convToOutputCharset($this->emetteur->getFullAddress()), 0, 'L', 0);
1029  $posy = $pdf->GetY() + 2;
1030  }
1031  }
1032  }
1033  }
1034 
1035  // If payment mode not forced or forced to VIR, show payment with BAN
1036  if (empty($object->mode_reglement_code) || $object->mode_reglement_code == 'VIR') {
1037  if (!empty($object->fk_account) || !empty($object->fk_bank) || getDolGlobalInt('FACTURE_RIB_NUMBER')) {
1038  $bankid = (empty($object->fk_account) ? $conf->global->FACTURE_RIB_NUMBER : $object->fk_account);
1039  if (!empty($object->fk_bank)) {
1040  $bankid = $object->fk_bank; // For backward compatibility when object->fk_account is forced with object->fk_bank
1041  }
1042  $account = new Account($this->db);
1043  $account->fetch($bankid);
1044 
1045  $curx = $this->marge_gauche;
1046  $cury = $posy;
1047 
1048  $posy = pdf_bank($pdf, $outputlangs, $curx, $cury, $account, 0, $default_font_size);
1049 
1050  $posy += 2;
1051  }
1052  }
1053  }
1054 
1055  return $posy;
1056  }
1057 
1058  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
1059  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1071  protected function _tableau_tot(&$pdf, $object, $deja_regle, $posy, $outputlangs, $outputlangsbis = null)
1072  {
1073  // phpcs:enable
1074  global $conf, $mysoc;
1075  $default_font_size = pdf_getPDFFontSize($outputlangs);
1076 
1077  $tab2_top = $posy;
1078  $tab2_hl = 4;
1079  $pdf->SetFont('', '', $default_font_size - 1);
1080 
1081  // Total table
1082  $col1x = 120;
1083  $col2x = 170;
1084  if ($this->page_largeur < 210) { // To work with US executive format
1085  $col2x -= 20;
1086  }
1087  $largcol2 = ($this->page_largeur - $this->marge_droite - $col2x);
1088 
1089  $useborder = 0;
1090  $index = 0;
1091 
1092  // Total HT
1093  $pdf->SetFillColor(255, 255, 255);
1094  $pdf->SetXY($col1x, $tab2_top);
1095  $pdf->MultiCell($col2x - $col1x, $tab2_hl, $outputlangs->transnoentities("TotalHT"), 0, 'L', 1);
1096 
1097  $total_ht = ((isModEnabled("multicurrency") && isset($object->multicurrency_tx) && $object->multicurrency_tx != 1) ? $object->multicurrency_total_ht : $object->total_ht);
1098  $pdf->SetXY($col2x, $tab2_top);
1099  $pdf->MultiCell($largcol2, $tab2_hl, price($total_ht + (!empty($object->remise) ? $object->remise : 0), 0, $outputlangs), 0, 'R', 1);
1100 
1101  // Show VAT by rates and total
1102  $pdf->SetFillColor(248, 248, 248);
1103 
1104  $total_ttc = (isModEnabled("multicurrency") && $object->multicurrency_tx != 1) ? $object->multicurrency_total_ttc : $object->total_ttc;
1105 
1106  $this->atleastoneratenotnull = 0;
1107  if (!getDolGlobalString('MAIN_GENERATE_DOCUMENTS_WITHOUT_VAT')) {
1108  $tvaisnull = ((!empty($this->tva) && count($this->tva) == 1 && isset($this->tva['0.000']) && is_float($this->tva['0.000'])) ? true : false);
1109  if (getDolGlobalString('MAIN_GENERATE_DOCUMENTS_WITHOUT_VAT_IFNULL') && $tvaisnull) {
1110  // Nothing to do
1111  } else {
1112  //Local tax 1 before VAT
1113  //if (!empty($conf->global->FACTURE_LOCAL_TAX1_OPTION) && $conf->global->FACTURE_LOCAL_TAX1_OPTION=='localtax1on')
1114  //{
1115  foreach ($this->localtax1 as $localtax_type => $localtax_rate) {
1116  if (in_array((string) $localtax_type, array('1', '3', '5'))) {
1117  continue;
1118  }
1119 
1120  foreach ($localtax_rate as $tvakey => $tvaval) {
1121  if ($tvakey != 0) { // On affiche pas taux 0
1122  //$this->atleastoneratenotnull++;
1123 
1124  $index++;
1125  $pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index);
1126 
1127  $tvacompl = '';
1128  if (preg_match('/\*/', $tvakey)) {
1129  $tvakey = str_replace('*', '', $tvakey);
1130  $tvacompl = " (".$outputlangs->transnoentities("NonPercuRecuperable").")";
1131  }
1132  $totalvat = $outputlangs->transcountrynoentities("TotalLT1", $mysoc->country_code).' ';
1133  $totalvat .= vatrate(abs($tvakey), 1).$tvacompl;
1134  $pdf->MultiCell($col2x - $col1x, $tab2_hl, $totalvat, 0, 'L', 1);
1135 
1136  $total_localtax = ((isModEnabled("multicurrency") && isset($object->multicurrency_tx) && $object->multicurrency_tx != 1) ? price2num($tvaval * $object->multicurrency_tx, 'MT') : $tvaval);
1137 
1138  $pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index);
1139  $pdf->MultiCell($largcol2, $tab2_hl, price($total_localtax, 0, $outputlangs), 0, 'R', 1);
1140  }
1141  }
1142  }
1143  //}
1144  //Local tax 2 before VAT
1145  //if (!empty($conf->global->FACTURE_LOCAL_TAX2_OPTION) && $conf->global->FACTURE_LOCAL_TAX2_OPTION=='localtax2on')
1146  //{
1147  foreach ($this->localtax2 as $localtax_type => $localtax_rate) {
1148  if (in_array((string) $localtax_type, array('1', '3', '5'))) {
1149  continue;
1150  }
1151 
1152  foreach ($localtax_rate as $tvakey => $tvaval) {
1153  if ($tvakey != 0) { // On affiche pas taux 0
1154  //$this->atleastoneratenotnull++;
1155 
1156  $index++;
1157  $pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index);
1158 
1159  $tvacompl = '';
1160  if (preg_match('/\*/', $tvakey)) {
1161  $tvakey = str_replace('*', '', $tvakey);
1162  $tvacompl = " (".$outputlangs->transnoentities("NonPercuRecuperable").")";
1163  }
1164  $totalvat = $outputlangs->transcountrynoentities("TotalLT2", $mysoc->country_code).' ';
1165  $totalvat .= vatrate(abs($tvakey), 1).$tvacompl;
1166  $pdf->MultiCell($col2x - $col1x, $tab2_hl, $totalvat, 0, 'L', 1);
1167 
1168  $total_localtax = ((isModEnabled("multicurrency") && isset($object->multicurrency_tx) && $object->multicurrency_tx != 1) ? price2num($tvaval * $object->multicurrency_tx, 'MT') : $tvaval);
1169 
1170  $pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index);
1171  $pdf->MultiCell($largcol2, $tab2_hl, price($total_localtax, 0, $outputlangs), 0, 'R', 1);
1172  }
1173  }
1174  }
1175  //}
1176 
1177  // VAT
1178  foreach ($this->tva_array as $tvakey => $tvaval) {
1179  if ($tvakey != 0) { // On affiche pas taux 0
1180  $this->atleastoneratenotnull++;
1181 
1182  $index++;
1183  $pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index);
1184 
1185  $tvacompl = '';
1186  if (preg_match('/\*/', $tvakey)) {
1187  $tvakey = str_replace('*', '', $tvakey);
1188  $tvacompl = " (".$outputlangs->transnoentities("NonPercuRecuperable").")";
1189  }
1190  $totalvat = $outputlangs->transcountrynoentities("TotalVAT", $mysoc->country_code).(is_object($outputlangsbis) ? ' / '.$outputlangsbis->transcountrynoentities("TotalVAT", $mysoc->country_code) : '');
1191  $totalvat .= ' ';
1192  if (getDolGlobalString('PDF_VAT_LABEL_IS_CODE_OR_RATE') == 'rateonly') {
1193  $totalvat .= vatrate($tvaval['vatrate'], 1).$tvacompl;
1194  } elseif (getDolGlobalString('PDF_VAT_LABEL_IS_CODE_OR_RATE') == 'codeonly') {
1195  $totalvat .= ($tvaval['vatcode'] ? $tvaval['vatcode'] : vatrate($tvaval['vatrate'], 1)).$tvacompl;
1196  } else {
1197  $totalvat .= vatrate($tvaval['vatrate'], 1).($tvaval['vatcode'] ? ' ('.$tvaval['vatcode'].')' : '').$tvacompl;
1198  }
1199  $pdf->MultiCell($col2x - $col1x, $tab2_hl, $totalvat, 0, 'L', 1);
1200 
1201  $pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index);
1202  $pdf->MultiCell($largcol2, $tab2_hl, price(price2num($tvaval['amount'], 'MT'), 0, $outputlangs), 0, 'R', 1);
1203  }
1204  }
1205 
1206  //Local tax 1 after VAT
1207  //if (!empty($conf->global->FACTURE_LOCAL_TAX1_OPTION) && $conf->global->FACTURE_LOCAL_TAX1_OPTION=='localtax1on')
1208  //{
1209  foreach ($this->localtax1 as $localtax_type => $localtax_rate) {
1210  if (in_array((string) $localtax_type, array('2', '4', '6'))) {
1211  continue;
1212  }
1213 
1214  foreach ($localtax_rate as $tvakey => $tvaval) {
1215  if ($tvakey != 0) { // On affiche pas taux 0
1216  //$this->atleastoneratenotnull++;
1217 
1218  $index++;
1219  $pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index);
1220 
1221  $tvacompl = '';
1222  if (preg_match('/\*/', $tvakey)) {
1223  $tvakey = str_replace('*', '', $tvakey);
1224  $tvacompl = " (".$outputlangs->transnoentities("NonPercuRecuperable").")";
1225  }
1226  $totalvat = $outputlangs->transcountrynoentities("TotalLT1", $mysoc->country_code).' ';
1227 
1228  $totalvat .= vatrate(abs($tvakey), 1).$tvacompl;
1229  $pdf->MultiCell($col2x - $col1x, $tab2_hl, $totalvat, 0, 'L', 1);
1230 
1231  $total_localtax = ((isModEnabled("multicurrency") && isset($object->multicurrency_tx) && $object->multicurrency_tx != 1) ? price2num($tvaval * $object->multicurrency_tx, 'MT') : $tvaval);
1232 
1233  $pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index);
1234  $pdf->MultiCell($largcol2, $tab2_hl, price($total_localtax, 0, $outputlangs), 0, 'R', 1);
1235  }
1236  }
1237  }
1238  //}
1239  //Local tax 2 after VAT
1240  //if (!empty($conf->global->FACTURE_LOCAL_TAX2_OPTION) && $conf->global->FACTURE_LOCAL_TAX2_OPTION=='localtax2on')
1241  //{
1242  foreach ($this->localtax2 as $localtax_type => $localtax_rate) {
1243  if (in_array((string) $localtax_type, array('2', '4', '6'))) {
1244  continue;
1245  }
1246 
1247  foreach ($localtax_rate as $tvakey => $tvaval) {
1248  // retrieve global local tax
1249  if ($tvakey != 0) { // On affiche pas taux 0
1250  //$this->atleastoneratenotnull++;
1251 
1252  $index++;
1253  $pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index);
1254 
1255  $tvacompl = '';
1256  if (preg_match('/\*/', $tvakey)) {
1257  $tvakey = str_replace('*', '', $tvakey);
1258  $tvacompl = " (".$outputlangs->transnoentities("NonPercuRecuperable").")";
1259  }
1260  $totalvat = $outputlangs->transcountrynoentities("TotalLT2", $mysoc->country_code).' ';
1261 
1262  $totalvat .= vatrate(abs($tvakey), 1).$tvacompl;
1263  $pdf->MultiCell($col2x - $col1x, $tab2_hl, $totalvat, 0, 'L', 1);
1264 
1265  $total_localtax = ((isModEnabled("multicurrency") && isset($object->multicurrency_tx) && $object->multicurrency_tx != 1) ? price2num($tvaval * $object->multicurrency_tx, 'MT') : $tvaval);
1266 
1267  $pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index);
1268  $pdf->MultiCell($largcol2, $tab2_hl, price($total_localtax, 0, $outputlangs), 0, 'R', 1);
1269  }
1270  }
1271  }
1272  //}
1273 
1274  // Total TTC
1275  $index++;
1276  $pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index);
1277  $pdf->SetTextColor(0, 0, 60);
1278  $pdf->SetFillColor(224, 224, 224);
1279  $pdf->MultiCell($col2x - $col1x, $tab2_hl, $outputlangs->transnoentities("TotalTTC"), $useborder, 'L', 1);
1280 
1281  $pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index);
1282  $pdf->MultiCell($largcol2, $tab2_hl, price($total_ttc, 0, $outputlangs), $useborder, 'R', 1);
1283  }
1284  }
1285 
1286  $pdf->SetTextColor(0, 0, 0);
1287 
1288  $resteapayer = 0;
1289  /*
1290  $resteapayer = $object->total_ttc - $deja_regle;
1291  if (!empty($object->paye)) $resteapayer=0;
1292  */
1293 
1294  if ($deja_regle > 0) {
1295  $index++;
1296 
1297  $pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index);
1298  $pdf->MultiCell($col2x - $col1x, $tab2_hl, $outputlangs->transnoentities("AlreadyPaid"), 0, 'L', 0);
1299 
1300  $pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index);
1301  $pdf->MultiCell($largcol2, $tab2_hl, price($deja_regle, 0, $outputlangs), 0, 'R', 0);
1302 
1303  /*
1304  if ($object->close_code == 'discount_vat') {
1305  $index++;
1306  $pdf->SetFillColor(255,255,255);
1307 
1308  $pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index);
1309  $pdf->MultiCell($col2x-$col1x, $tab2_hl, $outputlangs->transnoentities("EscompteOfferedShort"), $useborder, 'L', 1);
1310 
1311  $pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index);
1312  $pdf->MultiCell($largcol2, $tab2_hl, price($object->total_ttc - $deja_regle, 0, $outputlangs), $useborder, 'R', 1);
1313 
1314  $resteapayer=0;
1315  }
1316  */
1317 
1318  $index++;
1319  $pdf->SetTextColor(0, 0, 60);
1320  $pdf->SetFillColor(224, 224, 224);
1321  $pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index);
1322  $pdf->MultiCell($col2x - $col1x, $tab2_hl, $outputlangs->transnoentities("RemainderToPay"), $useborder, 'L', 1);
1323 
1324  $pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index);
1325  $pdf->MultiCell($largcol2, $tab2_hl, price($resteapayer, 0, $outputlangs), $useborder, 'R', 1);
1326 
1327  $pdf->SetFont('', '', $default_font_size - 1);
1328  $pdf->SetTextColor(0, 0, 0);
1329  }
1330 
1331  $index++;
1332  return ($tab2_top + ($tab2_hl * $index));
1333  }
1334 
1335  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
1349  protected function _tableau(&$pdf, $tab_top, $tab_height, $nexY, $outputlangs, $hidetop = 0, $hidebottom = 0, $currency = '')
1350  {
1351  global $conf;
1352 
1353  // Force to disable hidetop and hidebottom
1354  $hidebottom = 0;
1355  if ($hidetop) {
1356  $hidetop = -1;
1357  }
1358 
1359  $currency = !empty($currency) ? $currency : $conf->currency;
1360  $default_font_size = pdf_getPDFFontSize($outputlangs);
1361 
1362  // Amount in (at tab_top - 1)
1363  $pdf->SetTextColor(0, 0, 0);
1364  $pdf->SetFont('', '', $default_font_size - 2);
1365 
1366  if (empty($hidetop)) {
1367  $titre = $outputlangs->transnoentities("AmountInCurrency", $outputlangs->transnoentitiesnoconv("Currency".$currency));
1368  $pdf->SetXY($this->page_largeur - $this->marge_droite - ($pdf->GetStringWidth($titre) + 3), $tab_top - 4);
1369  $pdf->MultiCell(($pdf->GetStringWidth($titre) + 3), 2, $titre);
1370 
1371  //$conf->global->MAIN_PDF_TITLE_BACKGROUND_COLOR='230,230,230';
1372  if (getDolGlobalString('MAIN_PDF_TITLE_BACKGROUND_COLOR')) {
1373  $pdf->Rect($this->marge_gauche, $tab_top, $this->page_largeur - $this->marge_droite - $this->marge_gauche, 5, 'F', null, explode(',', getDolGlobalString('MAIN_PDF_TITLE_BACKGROUND_COLOR')));
1374  }
1375  }
1376 
1377  $pdf->SetDrawColor(128, 128, 128);
1378  $pdf->SetFont('', '', $default_font_size - 1);
1379 
1380  // Output Rect
1381  $this->printRect($pdf, $this->marge_gauche, $tab_top, $this->page_largeur - $this->marge_gauche - $this->marge_droite, $tab_height, $hidetop, $hidebottom); // Rect takes a length in 3rd parameter and 4th parameter
1382 
1383  if (empty($hidetop)) {
1384  $pdf->line($this->marge_gauche, $tab_top + 5, $this->page_largeur - $this->marge_droite, $tab_top + 5); // line takes a position y in 2nd parameter and 4th parameter
1385 
1386  $pdf->SetXY($this->posxdesc - 1, $tab_top + 1);
1387  $pdf->MultiCell(108, 2, $outputlangs->transnoentities("Designation"), '', 'L');
1388  }
1389 
1390  if (getDolGlobalString('MAIN_GENERATE_PROPOSALS_WITH_PICTURE')) {
1391  $pdf->line($this->posxpicture - 1, $tab_top, $this->posxpicture - 1, $tab_top + $tab_height);
1392  if (empty($hidetop)) {
1393  //$pdf->SetXY($this->posxpicture-1, $tab_top+1);
1394  //$pdf->MultiCell($this->posxtva-$this->posxpicture-1,2, $outputlangs->transnoentities("Photo"),'','C');
1395  }
1396  }
1397 
1398  if (!getDolGlobalString('MAIN_GENERATE_DOCUMENTS_WITHOUT_VAT') && !getDolGlobalString('MAIN_GENERATE_DOCUMENTS_WITHOUT_VAT_COLUMN')) {
1399  $pdf->line($this->posxtva - 1, $tab_top, $this->posxtva - 1, $tab_top + $tab_height);
1400  if (empty($hidetop)) {
1401  // Not do -3 and +3 instead of -1 -1 to have more space for text 'Sales tax'
1402  $pdf->SetXY($this->posxtva - 3, $tab_top + 1);
1403  $pdf->MultiCell($this->posxup - $this->posxtva + 3, 2, $outputlangs->transnoentities("VAT"), '', 'C');
1404  }
1405  }
1406 
1407  $pdf->line($this->posxup - 1, $tab_top, $this->posxup - 1, $tab_top + $tab_height);
1408  if (empty($hidetop)) {
1409  $pdf->SetXY($this->posxup - 1, $tab_top + 1);
1410  $pdf->MultiCell($this->posxqty - $this->posxup - 1, 2, $outputlangs->transnoentities("PriceUHT"), '', 'C');
1411  }
1412 
1413  $pdf->line($this->posxqty - 1, $tab_top, $this->posxqty - 1, $tab_top + $tab_height);
1414  if (empty($hidetop)) {
1415  $pdf->SetXY($this->posxqty - 1, $tab_top + 1);
1416  $pdf->MultiCell($this->posxunit - $this->posxqty - 1, 2, $outputlangs->transnoentities("Qty"), '', 'C');
1417  }
1418 
1419  if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
1420  $pdf->line($this->posxunit - 1, $tab_top, $this->posxunit - 1, $tab_top + $tab_height);
1421  if (empty($hidetop)) {
1422  $pdf->SetXY($this->posxunit - 1, $tab_top + 1);
1423  $pdf->MultiCell(
1424  $this->posxdiscount - $this->posxunit - 1,
1425  2,
1426  $outputlangs->transnoentities("Unit"),
1427  '',
1428  'C'
1429  );
1430  }
1431  }
1432 
1433  $pdf->line($this->posxdiscount - 1, $tab_top, $this->posxdiscount - 1, $tab_top + $tab_height);
1434  if (empty($hidetop)) {
1435  if ($this->atleastonediscount) {
1436  $pdf->SetXY($this->posxdiscount - 1, $tab_top + 1);
1437  $pdf->MultiCell($this->postotalht - $this->posxdiscount + 1, 2, $outputlangs->transnoentities("ReductionShort"), '', 'C');
1438  }
1439  }
1440  if ($this->atleastonediscount) {
1441  $pdf->line($this->postotalht, $tab_top, $this->postotalht, $tab_top + $tab_height);
1442  }
1443  if (empty($hidetop)) {
1444  $pdf->SetXY($this->postotalht - 1, $tab_top + 1);
1445  $pdf->MultiCell(30, 2, $outputlangs->transnoentities("TotalHTShort"), '', 'C');
1446  }
1447  }
1448 
1449  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
1460  protected function _pagehead(&$pdf, $object, $showaddress, $outputlangs, $outputlangsbis = null)
1461  {
1462  global $conf, $langs;
1463 
1464  $ltrdirection = 'L';
1465  if ($outputlangs->trans("DIRECTION") == 'rtl') {
1466  $ltrdirection = 'R';
1467  }
1468 
1469  // Load traductions files required by page
1470  $outputlangs->loadLangs(array("main", "propal", "companies", "bills"));
1471 
1472  $default_font_size = pdf_getPDFFontSize($outputlangs);
1473 
1474  pdf_pagehead($pdf, $outputlangs, $this->page_hauteur);
1475 
1476  $pdf->SetTextColor(0, 0, 60);
1477  $pdf->SetFont('', 'B', $default_font_size + 3);
1478 
1479  $w = 100;
1480 
1481  $posy = $this->marge_haute;
1482  $posx = $this->page_largeur - $this->marge_droite - $w;
1483 
1484  $pdf->SetXY($this->marge_gauche, $posy);
1485 
1486  // Logo
1487  if (!getDolGlobalInt('PDF_DISABLE_MYCOMPANY_LOGO')) {
1488  if ($this->emetteur->logo) {
1489  $logodir = $conf->mycompany->dir_output;
1490  if (!empty($conf->mycompany->multidir_output[$object->entity])) {
1491  $logodir = $conf->mycompany->multidir_output[$object->entity];
1492  }
1493  if (!getDolGlobalInt('MAIN_PDF_USE_LARGE_LOGO')) {
1494  $logo = $logodir.'/logos/thumbs/'.$this->emetteur->logo_small;
1495  } else {
1496  $logo = $logodir.'/logos/'.$this->emetteur->logo;
1497  }
1498  if (is_readable($logo)) {
1499  $height = pdf_getHeightForLogo($logo);
1500  $pdf->Image($logo, $this->marge_gauche, $posy, 0, $height); // width=0 (auto)
1501  } else {
1502  $pdf->SetTextColor(200, 0, 0);
1503  $pdf->SetFont('', 'B', $default_font_size - 2);
1504  $pdf->MultiCell(100, 3, $outputlangs->transnoentities("ErrorLogoFileNotFound", $logo), 0, 'L');
1505  $pdf->MultiCell(100, 3, $outputlangs->transnoentities("ErrorGoToGlobalSetup"), 0, 'L');
1506  }
1507  } else {
1508  $text = $this->emetteur->name;
1509  $pdf->MultiCell(100, 4, $outputlangs->convToOutputCharset($text), 0, $ltrdirection);
1510  }
1511  }
1512 
1513  $pdf->SetFont('', 'B', $default_font_size + 3);
1514  $pdf->SetXY($posx, $posy);
1515  $pdf->SetTextColor(0, 0, 60);
1516  $title = $outputlangs->transnoentities("PdfCommercialProposalTitle");
1517  $title .= ' '.$outputlangs->convToOutputCharset($object->ref);
1518  if ($object->statut == $object::STATUS_DRAFT) {
1519  $pdf->SetTextColor(128, 0, 0);
1520  $title .= ' - '.$outputlangs->transnoentities("NotValidated");
1521  }
1522  $pdf->MultiCell(100, 4, $title, '', 'R');
1523 
1524  $pdf->SetFont('', 'B', $default_font_size);
1525 
1526  /*
1527  $posy += 5;
1528  $pdf->SetXY($posx, $posy);
1529  $pdf->SetTextColor(0, 0, 60);
1530  $pdf->MultiCell(100, 4, $outputlangs->transnoentities("Ref")." : ".$outputlangs->convToOutputCharset($object->ref), '', 'R');
1531  */
1532 
1533  $posy += 3;
1534  $pdf->SetFont('', '', $default_font_size - 2);
1535 
1536  if ($object->ref_client) {
1537  $posy += 4;
1538  $pdf->SetXY($posx, $posy);
1539  $pdf->SetTextColor(0, 0, 60);
1540  $pdf->MultiCell(100, 3, $outputlangs->transnoentities("RefCustomer")." : ".$outputlangs->convToOutputCharset($object->ref_client), '', 'R');
1541  }
1542 
1543  if (getDolGlobalString('PDF_SHOW_PROJECT_TITLE')) {
1544  $object->fetch_projet();
1545  if (!empty($object->project->ref)) {
1546  $posy += 3;
1547  $pdf->SetXY($posx, $posy);
1548  $pdf->SetTextColor(0, 0, 60);
1549  $pdf->MultiCell($w, 3, $outputlangs->transnoentities("Project")." : ".(empty($object->project->title) ? '' : $object->project->title), '', 'R');
1550  }
1551  }
1552 
1553  if (getDolGlobalString('PDF_SHOW_PROJECT')) {
1554  $object->fetch_projet();
1555  if (!empty($object->project->ref)) {
1556  $outputlangs->load("projects");
1557  $posy += 3;
1558  $pdf->SetXY($posx, $posy);
1559  $pdf->SetTextColor(0, 0, 60);
1560  $pdf->MultiCell($w, 3, $outputlangs->transnoentities("RefProject")." : ".(empty($object->project->ref) ? '' : $object->project->ref), '', 'R');
1561  }
1562  }
1563 
1564  if (getDolGlobalString('MAIN_PDF_DATE_TEXT')) {
1565  $displaydate = "daytext";
1566  } else {
1567  $displaydate = "day";
1568  }
1569 
1570  //$posy += 4;
1571  $posy = $pdf->getY();
1572  $pdf->SetXY($posx, $posy);
1573  $pdf->SetTextColor(0, 0, 60);
1574  $pdf->MultiCell(100, 3, $outputlangs->transnoentities("DatePropal")." : ".dol_print_date($object->date, $displaydate, false, $outputlangs, true), '', 'R');
1575 
1576  $posy += 4;
1577  $pdf->SetXY($posx, $posy);
1578  $pdf->SetTextColor(0, 0, 60);
1579  $pdf->MultiCell(100, 3, $outputlangs->transnoentities("DateEndPropal")." : ".dol_print_date($object->fin_validite, $displaydate, false, $outputlangs, true), '', 'R');
1580 
1581  if (!getDolGlobalString('MAIN_PDF_HIDE_CUSTOMER_CODE') && $object->thirdparty->code_client) {
1582  $posy += 4;
1583  $pdf->SetXY($posx, $posy);
1584  $pdf->SetTextColor(0, 0, 60);
1585  $pdf->MultiCell(100, 3, $outputlangs->transnoentities("CustomerCode")." : ".$outputlangs->transnoentities($object->thirdparty->code_client), '', 'R');
1586  }
1587 
1588  // Get contact
1589  if (getDolGlobalString('DOC_SHOW_FIRST_SALES_REP')) {
1590  $arrayidcontact = $object->getIdContact('internal', 'SALESREPFOLL');
1591  if (count($arrayidcontact) > 0) {
1592  $usertmp = new User($this->db);
1593  $usertmp->fetch($arrayidcontact[0]);
1594  $posy += 4;
1595  $pdf->SetXY($posx, $posy);
1596  $pdf->SetTextColor(0, 0, 60);
1597  $pdf->MultiCell(100, 3, $langs->trans("SalesRepresentative")." : ".$usertmp->getFullName($langs), '', 'R');
1598  }
1599  }
1600 
1601  $posy += 2;
1602 
1603  $top_shift = 0;
1604  // Show list of linked objects
1605  $current_y = $pdf->getY();
1606  $posy = pdf_writeLinkedObjects($pdf, $object, $outputlangs, $posx, $posy, 100, 3, 'R', $default_font_size);
1607  if ($current_y < $pdf->getY()) {
1608  $top_shift = $pdf->getY() - $current_y;
1609  }
1610 
1611  if ($showaddress) {
1612  // Sender properties
1613  $carac_emetteur = '';
1614  // Add internal contact of object if defined
1615  $arrayidcontact = $object->getIdContact('internal', 'SALESREPFOLL');
1616  if (count($arrayidcontact) > 0) {
1617  $object->fetch_user($arrayidcontact[0]);
1618  $labelbeforecontactname = ($outputlangs->transnoentities("FromContactName") != 'FromContactName' ? $outputlangs->transnoentities("FromContactName") : $outputlangs->transnoentities("Name"));
1619  $carac_emetteur .= ($carac_emetteur ? "\n" : '').$labelbeforecontactname." ".$outputlangs->convToOutputCharset($object->user->getFullName($outputlangs));
1620  $carac_emetteur .= (getDolGlobalInt('PDF_SHOW_PHONE_AFTER_USER_CONTACT') || getDolGlobalInt('PDF_SHOW_EMAIL_AFTER_USER_CONTACT')) ? ' (' : '';
1621  $carac_emetteur .= (getDolGlobalInt('PDF_SHOW_PHONE_AFTER_USER_CONTACT') && !empty($object->user->office_phone)) ? $object->user->office_phone : '';
1622  $carac_emetteur .= (getDolGlobalInt('PDF_SHOW_PHONE_AFTER_USER_CONTACT') && getDolGlobalInt('PDF_SHOW_EMAIL_AFTER_USER_CONTACT')) ? ', ' : '';
1623  $carac_emetteur .= (getDolGlobalInt('PDF_SHOW_EMAIL_AFTER_USER_CONTACT') && !empty($object->user->email)) ? $object->user->email : '';
1624  $carac_emetteur .= (getDolGlobalInt('PDF_SHOW_PHONE_AFTER_USER_CONTACT') || getDolGlobalInt('PDF_SHOW_EMAIL_AFTER_USER_CONTACT')) ? ')' : '';
1625  $carac_emetteur .= "\n";
1626  }
1627 
1628  $carac_emetteur .= pdf_build_address($outputlangs, $this->emetteur, $object->thirdparty, '', 0, 'source', $object);
1629 
1630  // Show sender
1631  $posy = 42 + $top_shift;
1632  $posx = $this->marge_gauche;
1633  if (getDolGlobalString('MAIN_INVERT_SENDER_RECIPIENT')) {
1634  $posx = $this->page_largeur - $this->marge_droite - 80;
1635  }
1636  $hautcadre = 40;
1637 
1638  // Show sender frame
1639  if (!getDolGlobalString('MAIN_PDF_NO_SENDER_FRAME')) {
1640  $pdf->SetTextColor(0, 0, 0);
1641  $pdf->SetFont('', '', $default_font_size - 2);
1642  $pdf->SetXY($posx, $posy - 5);
1643  $pdf->MultiCell(80, 5, $outputlangs->transnoentities("BillFrom"), 0, $ltrdirection);
1644  $pdf->SetXY($posx, $posy);
1645  $pdf->SetFillColor(230, 230, 230);
1646  $pdf->MultiCell(82, $hautcadre, "", 0, 'R', 1);
1647  $pdf->SetTextColor(0, 0, 60);
1648  }
1649 
1650  // Show company name
1651  if (!getDolGlobalString('MAIN_PDF_HIDE_SENDER_NAME')) {
1652  $pdf->SetXY($posx + 2, $posy + 3);
1653  $pdf->SetFont('', 'B', $default_font_size);
1654  $pdf->MultiCell(80, 4, $outputlangs->convToOutputCharset($this->emetteur->name), 0, $ltrdirection);
1655  $posy = $pdf->getY();
1656  }
1657 
1658  // Show sender information
1659  $pdf->SetXY($posx + 2, $posy);
1660  $pdf->SetFont('', '', $default_font_size - 1);
1661  $pdf->MultiCell(80, 4, $carac_emetteur, 0, $ltrdirection);
1662 
1663 
1664  // If CUSTOMER contact defined, we use it
1665  $usecontact = false;
1666  $arrayidcontact = $object->getIdContact('external', 'CUSTOMER');
1667  if (count($arrayidcontact) > 0) {
1668  $usecontact = true;
1669  $result = $object->fetch_contact($arrayidcontact[0]);
1670  }
1671 
1672  // Recipient name
1673  if ($usecontact && ($object->contact->socid != $object->thirdparty->id && (!isset($conf->global->MAIN_USE_COMPANY_NAME_OF_CONTACT) || getDolGlobalString('MAIN_USE_COMPANY_NAME_OF_CONTACT')))) {
1674  $thirdparty = $object->contact;
1675  } else {
1676  $thirdparty = $object->thirdparty;
1677  }
1678 
1679  $carac_client_name = pdfBuildThirdpartyName($thirdparty, $outputlangs);
1680 
1681  $mode = 'target';
1682  $carac_client = pdf_build_address($outputlangs, $this->emetteur, $object->thirdparty, ($usecontact ? $object->contact : ''), $usecontact, $mode, $object);
1683 
1684  // Show recipient
1685  $widthrecbox = 100;
1686  if ($this->page_largeur < 210) {
1687  $widthrecbox = 84; // To work with US executive format
1688  }
1689  $posy = 42 + $top_shift;
1690  $posx = $this->page_largeur - $this->marge_droite - $widthrecbox;
1691  if (getDolGlobalString('MAIN_INVERT_SENDER_RECIPIENT')) {
1692  $posx = $this->marge_gauche;
1693  }
1694 
1695  // Show recipient frame
1696  if (!getDolGlobalString('MAIN_PDF_NO_RECIPENT_FRAME')) {
1697  $pdf->SetTextColor(0, 0, 0);
1698  $pdf->SetFont('', '', $default_font_size - 2);
1699  $pdf->SetXY($posx + 2, $posy - 5);
1700  $pdf->MultiCell($widthrecbox, 5, $outputlangs->transnoentities("BillTo"), 0, $ltrdirection);
1701  $pdf->Rect($posx, $posy, $widthrecbox, $hautcadre);
1702  }
1703 
1704  // Show recipient name
1705  $pdf->SetXY($posx + 2, $posy + 3);
1706  $pdf->SetFont('', 'B', $default_font_size);
1707  // @phan-suppress-next-line PhanPluginSuspiciousParamOrder
1708  $pdf->MultiCell($widthrecbox, 4, $carac_client_name, 0, $ltrdirection);
1709 
1710  $posy = $pdf->getY();
1711 
1712  // Show recipient information
1713  $pdf->SetFont('', '', $default_font_size - 1);
1714  $pdf->SetXY($posx + 2, $posy);
1715  // @phan-suppress-next-line PhanPluginSuspiciousParamOrder
1716  $pdf->MultiCell($widthrecbox, 4, $carac_client, 0, $ltrdirection);
1717  }
1718 
1719  $pdf->SetTextColor(0, 0, 0);
1720 
1721  return $top_shift;
1722  }
1723 
1724  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
1734  protected function _pagefoot(&$pdf, $object, $outputlangs, $hidefreetext = 0)
1735  {
1736  $showdetails = getDolGlobalInt('MAIN_GENERATE_DOCUMENTS_SHOW_FOOT_DETAILS', 0);
1737  return pdf_pagefoot($pdf, $outputlangs, 'PROPOSAL_FREE_TEXT', $this->emetteur, $this->marge_basse, $this->marge_gauche, $this->page_hauteur, $object, $showdetails, $hidefreetext, $this->page_largeur, $this->watermark);
1738  }
1739 
1740  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
1741  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1751  protected function _signature_area(&$pdf, $object, $posy, $outputlangs)
1752  {
1753  // phpcs:enable
1754  $default_font_size = pdf_getPDFFontSize($outputlangs);
1755  $tab_top = $posy + 4;
1756  $tab_hl = 4;
1757 
1758  $posx = 120;
1759  $largcol = ($this->page_largeur - $this->marge_droite - $posx);
1760 
1761  // Total HT
1762  $pdf->SetFillColor(255, 255, 255);
1763  $pdf->SetXY($posx, $tab_top);
1764  $pdf->SetFont('', '', $default_font_size - 2);
1765  $pdf->MultiCell($largcol, $tab_hl, $outputlangs->transnoentities("ProposalCustomerSignature"), 0, 'L', 1);
1766 
1767  $pdf->SetXY($posx, $tab_top + $tab_hl);
1768  $pdf->MultiCell($largcol, $tab_hl * 3, '', 1, 'R');
1769  if (getDolGlobalString('MAIN_PDF_PROPAL_USE_ELECTRONIC_SIGNING')) {
1770  // Can be retrieve with getSignatureAppearanceArray()
1771  // Can be also detected by putting the mouse over the area when using evince pdf reader
1772  $pdf->addEmptySignatureAppearance($posx, $tab_top + $tab_hl, $largcol, $tab_hl * 3);
1773  }
1774 
1775  return ($tab_hl * 7);
1776  }
1777 }
if($user->socid > 0) if(! $user->hasRight('accounting', 'chartofaccount')) $object
Definition: card.php:58
Class to manage bank accounts.
printRect($pdf, $x, $y, $l, $h, $hidetop=0, $hidebottom=0)
Rect pdf.
Class to manage hooks.
Class mere des modeles de propale.
Class to manage products or services.
Put here description of your class.
Class to manage translations.
Class to manage Dolibarr users.
Definition: user.class.php:50
Class to generate PDF proposal Azur.
_tableau_info(&$pdf, $object, $posy, $outputlangs)
Show miscellaneous information (payment mode, payment term, ...)
__construct($db)
Constructor.
_pagehead(&$pdf, $object, $showaddress, $outputlangs, $outputlangsbis=null)
Show top header of page.
_signature_area(&$pdf, $object, $posy, $outputlangs)
Show area for the customer to sign.
_tableau_versements(&$pdf, $object, $posy, $outputlangs)
Show payments table.
_tableau(&$pdf, $tab_top, $tab_height, $nexY, $outputlangs, $hidetop=0, $hidebottom=0, $currency='')
Show table for lines.
write_file($object, $outputlangs, $srctemplatepath='', $hidedetails=0, $hidedesc=0, $hideref=0)
Function to build pdf onto disk.
_pagefoot(&$pdf, $object, $outputlangs, $hidefreetext=0)
Show footer of page.
_tableau_tot(&$pdf, $object, $deja_regle, $posy, $outputlangs, $outputlangsbis=null)
Show total to pay.
print *****$script_file(".$version.") pid cd cd cd description as description
Only used if Module[ID]Desc translation string is not found.
if(!function_exists('dolEscapeXML')) convertBackOfficeMediasLinksToPublicLinks($notetoshow)
Convert links to local wrapper to medias files into a string into a public external URL readable on i...
vatrate($rate, $addpercent=false, $info_bits=0, $usestarfornpr=0, $html=0)
Return a string with VAT rate label formatted for view output Used into pdf and HTML pages.
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
price($amount, $form=0, $outlangs='', $trunc=1, $rounding=-1, $forcerounding=-1, $currency_code='')
Function to format a value into an amount for visual output Function used into PDF and HTML pages.
dolChmod($filepath, $newmask='')
Change mod of a file.
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
dol_getIdFromCode($db, $key, $tablename, $fieldkey='code', $fieldid='id', $entityfilter=0, $filters='')
Return an id or code from a code or id.
getLocalTaxesFromRate($vatrate, $local, $buyer, $seller, $firstparamisid=0)
Get type and rate of localtaxes for a particular vat rate/country of a thirdparty.
dol_print_date($time, $format='', $tzoutput='auto', $outputlangs=null, $encodetooutput=false)
Output date in a string format according to outputlangs (or langs if not defined).
dol_concatdesc($text1, $text2, $forxml=false, $invert=false)
Concat 2 descriptions with a new line between them (second operand after first one with appropriate n...
complete_substitutions_array(&$substitutionarray, $outputlangs, $object=null, $parameters=null, $callfunc="completesubstitutionarray")
Complete the $substitutionarray with more entries coming from external module that had set the "subst...
make_substitutions($text, $substitutionarray, $outputlangs=null, $converttextinhtmlifnecessary=0)
Make substitution into a text string, replacing keys with vals from $substitutionarray (oldval=>newva...
setEventMessages($mesg, $mesgs, $style='mesgs', $messagekey='', $noduplicate=0)
Set event messages in dol_events session object.
dol_sanitizeFileName($str, $newstr='_', $unaccent=1)
Clean a string to use it as a file name.
dol_htmlentitiesbr($stringtoencode, $nl2brmode=0, $pagecodefrom='UTF-8', $removelasteolbr=1)
This function is called to encode a string into a HTML string but differs from htmlentities because a...
getDolGlobalString($key, $default='')
Return dolibarr global constant string value.
isModEnabled($module)
Is Dolibarr module enabled.
get_exdir($num, $level, $alpha, $withoutslash, $object, $modulepart='')
Return a path to have a the directory according to object where files are stored.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
dol_mkdir($dir, $dataroot='', $newmask='')
Creation of a directory (this can create recursive subdir)
if(!defined( 'CSRFCHECK_WITH_TOKEN'))
pdf_getSizeForImage($realpath)
Return dimensions to use for images onto PDF checking that width and height are not higher than maxim...
Definition: pdf.lib.php:2620
pdf_writelinedesc(&$pdf, $object, $i, $outputlangs, $w, $h, $posx, $posy, $hideref=0, $hidedesc=0, $issupplierline=0, $align='J')
Output line description into PDF.
Definition: pdf.lib.php:1431
pdf_getlinetotalexcltax($object, $i, $outputlangs, $hidedetails=0)
Return line total excluding tax.
Definition: pdf.lib.php:2369
pdf_getPDFFontSize($outputlangs)
Return font size to use for PDF generation.
Definition: pdf.lib.php:290
pdf_getFormat(Translate $outputlangs=null, $mode='setup')
Return array with format properties of default PDF format.
Definition: pdf.lib.php:86
pdf_getHeightForLogo($logo, $url=false)
Return height to use for Logo onto PDF.
Definition: pdf.lib.php:315
pdf_pagefoot(&$pdf, $outputlangs, $paramfreetext, $fromcompany, $marge_basse, $marge_gauche, $page_hauteur, $object, $showdetails=0, $hidefreetext=0, $page_largeur=0, $watermark='')
Show footer of page for PDF generation.
Definition: pdf.lib.php:1020
pdf_getlineupexcltax($object, $i, $outputlangs, $hidedetails=0)
Return line unit price excluding tax.
Definition: pdf.lib.php:1987
pdf_getlinevatrate($object, $i, $outputlangs, $hidedetails=0)
Return line vat rate.
Definition: pdf.lib.php:1925
pdfGetHeightForHtmlContent(&$pdf, $htmlcontent)
Function to try to calculate height of a HTML Content.
Definition: pdf.lib.php:340
pdf_pagehead(&$pdf, $outputlangs, $page_height)
Show header of page for PDF generation.
Definition: pdf.lib.php:733
pdf_writeLinkedObjects(&$pdf, $object, $outputlangs, $posx, $posy, $w, $h, $align, $default_font_size)
Show linked objects for PDF generation.
Definition: pdf.lib.php:1394
pdf_bank(&$pdf, $outputlangs, $curx, $cury, $account, $onlynumber=0, $default_font_size=10)
Show bank information for PDF generation.
Definition: pdf.lib.php:843
pdf_getPDFFont($outputlangs)
Return font name to use for PDF generation.
Definition: pdf.lib.php:267
pdf_build_address($outputlangs, $sourcecompany, $targetcompany='', $targetcontact='', $usecontact=0, $mode='source', $object=null)
Return a string with full address formatted for output on PDF documents.
Definition: pdf.lib.php:436
pdf_getlineunit($object, $i, $outputlangs, $hidedetails=0)
Return line unit.
Definition: pdf.lib.php:2232
pdf_getlineremisepercent($object, $i, $outputlangs, $hidedetails=0)
Return line remise percent.
Definition: pdf.lib.php:2275
pdf_getlineqty($object, $i, $outputlangs, $hidedetails=0)
Return line quantity.
Definition: pdf.lib.php:2072
pdf_getSubstitutionArray($outputlangs, $exclude=null, $object=null, $onlykey=0, $include=null)
Return array of possible substitutions for PDF content (without external module substitutions).
Definition: pdf.lib.php:769
pdf_getInstance($format='', $metric='mm', $pagetype='P')
Return a PDF instance object.
Definition: pdf.lib.php:128
pdfBuildThirdpartyName($thirdparty, Translate $outputlangs, $includealias=0)
Returns the name of the thirdparty.
Definition: pdf.lib.php:388
if(preg_match('/crypted:/i', $dolibarr_main_db_pass)||!empty($dolibarr_main_db_encrypted_pass)) $conf db type
Definition: repair.php:123
$conf db name
Only used if Module[ID]Name translation string is not found.
Definition: repair.php:126