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