dolibarr 25.0.0-alpha
pdf_standard_myobject.modules.php
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-2014 Juanjo Menent <jmenent@2byte.es>
6 * Copyright (C) 2012 Christophe Battarel <christophe.battarel@altairis.fr>
7 * Copyright (C) 2012 Cédric Salvador <csalvador@gpcsolutions.fr>
8 * Copyright (C) 2012-2014 Raphaël Doursenaud <rdoursenaud@gpcsolutions.fr>
9 * Copyright (C) 2015 Marcos García <marcosgdf@gmail.com>
10 * Copyright (C) 2017 Ferran Marcet <fmarcet@2byte.es>
11 * Copyright (C) 2018-2025 Frédéric France <frederic.france@free.fr>
12 * Copyright (C) 2024-2025 MDW <mdeweerd@users.noreply.github.com>
13 * Copyright (C) 2024 Alexandre Spangaro <alexandre@inovea-conseil.com>
14 * Copyright (C) ---Replace with your own copyright and developer email---
15 *
16 * This program is free software; you can redistribute it and/or modify
17 * it under the terms of the GNU General Public License as published by
18 * the Free Software Foundation; either version 3 of the License, or
19 * (at your option) any later version.
20 *
21 * This program is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 * GNU General Public License for more details.
25 *
26 * You should have received a copy of the GNU General Public License
27 * along with this program. If not, see <https://www.gnu.org/licenses/>.
28 * or see https://www.gnu.org/
29 */
30
37dol_include_once('/mymodule/core/modules/mymodule/modules_myobject.php');
38require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
39require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
40require_once DOL_DOCUMENT_ROOT.'/core/class/commoninvoice.class.php';
41require_once DOL_DOCUMENT_ROOT . '/core/lib/functions.lib.php';
42require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
43require_once DOL_DOCUMENT_ROOT.'/core/lib/pdf.lib.php';
44
45
50{
54 public $db;
55
59 public $entity;
60
64 public $name;
65
69 public $description;
70
74 public $update_main_doc_field;
75
79 public $type;
80
85 public $phpmin = array(7, 0);
86
91 public $version = 'dolibarr';
92
97 public $emetteur;
98
102 public $cols;
103
104
110 public function __construct($db)
111 {
112 global $langs, $mysoc;
113
114 // Translations
115 $langs->loadLangs(array("main", "bills"));
116
117 $this->db = $db;
118 $this->name = "standard";
119 $this->description = $langs->trans('DocumentModelStandardPDF');
120 $this->update_main_doc_field = 1; // Save the name of generated file as the main doc when generating a doc with this template
121
122 // Dimension page
123 $this->type = 'pdf';
124 $formatarray = pdf_getFormat();
125 $this->page_largeur = $formatarray['width'];
126 $this->page_hauteur = $formatarray['height'];
127 $this->format = array($this->page_largeur, $this->page_hauteur);
128 $this->marge_gauche = getDolGlobalInt('MAIN_PDF_MARGIN_LEFT', 10);
129 $this->marge_droite = getDolGlobalInt('MAIN_PDF_MARGIN_RIGHT', 10);
130 $this->marge_haute = getDolGlobalInt('MAIN_PDF_MARGIN_TOP', 10);
131 $this->marge_basse = getDolGlobalInt('MAIN_PDF_MARGIN_BOTTOM', 10);
132
133 // Define position of columns
134 $this->posxdesc = $this->marge_gauche + 1; // used for notes and other stuff
135
136 $this->tabTitleHeight = 5; // default height
137
138 // Use new system for position of columns, view $this->defineColumnField()
139
140 $this->tva = array();
141 $this->tva_array = array();
142 $this->localtax1 = array();
143 $this->localtax2 = array();
144 $this->atleastoneratenotnull = 0;
145 $this->atleastonediscount = 0;
146
147 if ($mysoc === null) {
148 dol_syslog(get_class($this).'::__construct() Global $mysoc should not be null.'. getCallerInfoString(), LOG_ERR);
149 return;
150 }
151
152 // Get source company
153 $this->emetteur = $mysoc;
154 if (empty($this->emetteur->country_code)) {
155 $this->emetteur->country_code = dol_substr($langs->defaultlang, -2); // By default, if was not defined
156 }
157 }
158
159
160 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
172 public function write_file($object, $outputlangs, $srctemplatepath = '', $hidedetails = 0, $hidedesc = 0, $hideref = 0)
173 {
174 // phpcs:enable
175 global $user, $langs, $conf, $mysoc, $hookmanager, $nblines;
176
177 dol_syslog("write_file outputlangs->defaultlang=".(is_object($outputlangs) ? $outputlangs->defaultlang : 'null'));
178
179 if (!is_object($outputlangs)) {
180 $outputlangs = $langs;
181 }
182 // For backward compatibility with FPDF, force output charset to ISO, because FPDF expect text to be encoded in ISO
183 if (getDolGlobalInt('MAIN_USE_FPDF')) {
184 $outputlangs->charset_output = 'ISO-8859-1';
185 }
186
187 // Load translation files required by the page
188 $langfiles = array("main", "bills", "products", "dict", "companies", "compta");
189 $outputlangs->loadLangs($langfiles);
190
191 // Show Draft Watermark
192 if (getDolGlobalString('MYOBJECT_DRAFT_WATERMARK') && $object->status == $object::STATUS_DRAFT) {
193 $this->watermark = getDolGlobalString('MYOBJECT_DRAFT_WATERMARK');
194 }
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($langfiles);
202 }
203
204 $nblines = (is_array($object->lines) ? count($object->lines) : 0);
205
206 $hidetop = 0;
207 if (getDolGlobalString('MAIN_PDF_DISABLE_COL_HEAD_TITLE')) {
208 $hidetop = getDolGlobalString('MAIN_PDF_DISABLE_COL_HEAD_TITLE');
209 }
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 /*
215 if (getDolGlobalInt('MAIN_GENERATE_MYOBJECT_WITH_PICTURE'))) {
216 $objphoto = new Product($this->db);
217
218 for ($i = 0; $i < $nblines; $i++) {
219 if (empty($object->lines[$i]->fk_product)) {
220 continue;
221 }
222
223 $objphoto->fetch($object->lines[$i]->fk_product);
224 //var_dump($objphoto->ref);exit;
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 foreach ($pdir as $midir) {
235 if (!$arephoto) {
236 if ($conf->entity != $objphoto->entity) {
237 $dir = $conf->product->multidir_output[$objphoto->entity].'/'.$midir; //Check repertories of current entities
238 } else {
239 $dir = $conf->product->dir_output.'/'.$midir; //Check repertory of the current product
240 }
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
267 //if (count($realpatharray) == 0) $this->posxpicture=$this->posxtva;
268 $dir_output = getMultidirOutput($object, $object->module);
269 if (!empty($dir_output)) {
270 $dir_output .= '/' . $object->element;
271
272 $object->fetch_thirdparty();
273
274 $dir = null;
275 // Definition of $dir and $file
276 if ($object->specimen) {
277 $dir = $dir_output;
278 $file = $dir."/SPECIMEN.pdf";
279 } else {
280 $objectref = dol_sanitizeFileName($object->ref);
281 $dir = $dir_output."/".$objectref;
282 $file = $dir."/".$objectref.".pdf";
283 }
284 // if ($dir === null) {
285 // return 0;
286 // }
287 if (!file_exists($dir)) {
288 if (dol_mkdir($dir) < 0) {
289 $this->error = $langs->transnoentities("ErrorCanNotCreateDir", $dir);
290 return 0;
291 }
292 }
293
294 if (file_exists($dir)) {
295 // Add pdfgeneration hook
296 if (!is_object($hookmanager)) {
297 include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php';
298 $hookmanager = new HookManager($this->db);
299 }
300 $hookmanager->initHooks(array('pdfgeneration'));
301 $parameters = array('file' => $file, 'object' => $object, 'outputlangs' => $outputlangs);
302 global $action;
303 $reshook = $hookmanager->executeHooks('beforePDFCreation', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
304
305 // Set nblines with the new lines content after hook
306 $nblines = (is_array($object->lines) ? count($object->lines) : 0);
307
308 // Create pdf instance
309 $pdf = pdf_getInstance($this->format);
310 '@phan-var-force TCPDI|TCPDF $pdf';
311 $default_font_size = pdf_getPDFFontSize($outputlangs); // Must be after pdf_getInstance
312 $pdf->setAutoPageBreak(true, 0);
313
314 $heightforinfotot = 50; // Height reserved to output the info and total part and payment part
315 $heightforfreetext = getDolGlobalInt('MAIN_PDF_FREETEXT_HEIGHT', 5); // Height reserved to output the free text on last page
316 $heightforfooter = $this->marge_basse + (getDolGlobalInt('MAIN_GENERATE_DOCUMENTS_SHOW_FOOT_DETAILS') ? 12 : 22); // Height reserved to output the footer (value include bottom margin)
317
318 if (class_exists('TCPDF')) {
319 $pdf->setPrintHeader(false);
320 $pdf->setPrintFooter(false);
321 }
322 $pdf->SetFont(pdf_getPDFFont($outputlangs));
323
324 // Set path to the background PDF File
325 if (getDolGlobalString('MAIN_ADD_PDF_BACKGROUND')) {
326 $logodir = $conf->mycompany->dir_output;
327 if (!empty($conf->mycompany->multidir_output[$object->entity ?? $conf->entity])) {
328 $logodir = $conf->mycompany->multidir_output[$object->entity ?? $conf->entity];
329 }
330 $pagecount = $pdf->setSourceFile($logodir.'/'.getDolGlobalString('MAIN_ADD_PDF_BACKGROUND'));
331 $tplidx = $pdf->importPage(1);
332 }
333
334 $pdf->Open();
335 $pagenb = 0;
336 $pdf->SetDrawColor(128, 128, 128);
337
338 $pdf->SetTitle($outputlangs->convToOutputCharset($object->ref));
339 $pdf->SetSubject($outputlangs->transnoentities("PdfTitle"));
340 $pdf->SetCreator("Dolibarr ".DOL_VERSION);
341 $pdf->SetAuthor($outputlangs->convToOutputCharset($user->getAnonymisableFullName($outputlangs)));
342 $pdf->SetKeyWords($outputlangs->convToOutputCharset($object->ref)." ".$outputlangs->transnoentities("PdfTitle")." ".$outputlangs->convToOutputCharset($object->thirdparty->name));
343 if (getDolGlobalString('MAIN_DISABLE_PDF_COMPRESSION')) {
344 $pdf->SetCompression(false);
345 }
346
347 // Set certificate
348 $cert = empty($user->conf->CERTIFICATE_CRT) ? '' : $user->conf->CERTIFICATE_CRT;
349 // If user has no certificate, we try to take the company one
350 if (!$cert) {
351 $cert = getDolGlobalString('CERTIFICATE_CRT');
352 }
353 // If a certificate is found
354 if ($cert) {
355 $info = array(
356 'Name' => $this->emetteur->name,
357 'Location' => getCountry($this->emetteur->country_code, ''),
358 'Reason' => 'MYOBJECT',
359 'ContactInfo' => $this->emetteur->email
360 );
361 $pdf->setSignature($cert, $cert, $this->emetteur->name, '', 2, $info);
362 }
363
364 // @phan-suppress-next-line PhanPluginSuspiciousParamOrder
365 $pdf->SetMargins($this->marge_gauche, $this->marge_haute, $this->marge_droite); // Left, Top, Right
366
367
368 // New page
369 $pdf->AddPage();
370 if (!empty($tplidx)) {
371 $pdf->useTemplate($tplidx);
372 }
373 $pagenb++;
374
375 $top_shift = $this->_pagehead($pdf, $object, 1, $outputlangs, $outputlangsbis);
376 $pdf->SetFont('', '', $default_font_size - 1);
377 $pdf->MultiCell(0, 3, ''); // Set interline to 3
378 $pdf->SetTextColor(0, 0, 0);
379
380 $tab_top = 90 + $top_shift;
381 $tab_top_newpage = (!getDolGlobalInt('MAIN_PDF_DONOTREPEAT_HEAD') ? 42 + $top_shift : 10);
382
383 $tab_height = $this->page_hauteur - $tab_top - $heightforfooter - $heightforfreetext;
384
385 $tab_height_newpage = 150;
386 if (!getDolGlobalInt('MAIN_PDF_DONOTREPEAT_HEAD')) {
387 $tab_height_newpage -= $top_shift;
388 }
389
390 $nexY = $tab_top - 1;
391
392 // Display notes
393 $notetoshow = empty($object->note_public) ? '' : $object->note_public;
394 // Extrafields in note
395 $extranote = $this->getExtrafieldsInHtml($object, $outputlangs);
396 if (!empty($extranote)) {
397 $notetoshow = dol_concatdesc($notetoshow, $extranote);
398 }
399
400 $pagenb = $pdf->getPage();
401 if ($notetoshow) {
402 $tab_top -= 2;
403
404 $tab_width = $this->page_largeur - $this->marge_gauche - $this->marge_droite;
405 $pageposbeforenote = $pagenb;
406
407 $substitutionarray = pdf_getSubstitutionArray($outputlangs, null, $object);
408 complete_substitutions_array($substitutionarray, $outputlangs, $object);
409 $notetoshow = make_substitutions($notetoshow, $substitutionarray, $outputlangs);
410 $notetoshow = convertBackOfficeMediasLinksToPublicLinks($notetoshow);
411
412 $pdf->startTransaction();
413
414 $pdf->SetFont('', '', $default_font_size - 1);
415 $pdf->writeHTMLCell(190, 3, $this->posxdesc - 1, $tab_top, dol_htmlentitiesbr($notetoshow), 0, 1);
416 // Description
417 $pageposafternote = $pdf->getPage();
418 $posyafter = $pdf->GetY();
419
420 if ($pageposafternote > $pageposbeforenote) {
421 $pdf->rollbackTransaction(true);
422
423 // prepare pages to receive notes
424 while ($pagenb < $pageposafternote) {
425 $pdf->AddPage();
426 $pagenb++;
427 if (!empty($tplidx)) {
428 $pdf->useTemplate($tplidx);
429 }
430 if (!getDolGlobalInt('MAIN_PDF_DONOTREPEAT_HEAD')) {
431 $this->_pagehead($pdf, $object, 0, $outputlangs);
432 }
433 // $this->_pagefoot($pdf,$object,$outputlangs,1);
434 $pdf->setTopMargin($tab_top_newpage);
435 // The only function to edit the bottom margin of current page to set it.
436 $pdf->setPageOrientation('', true, $heightforfooter + $heightforfreetext);
437 }
438
439 // back to start
440 $pdf->setPage($pageposbeforenote);
441 $pdf->setPageOrientation('', true, $heightforfooter + $heightforfreetext);
442 $pdf->SetFont('', '', $default_font_size - 1);
443 $pdf->writeHTMLCell(190, 3, $this->posxdesc - 1, $tab_top, dol_htmlentitiesbr($notetoshow), 0, 1);
444 $pageposafternote = $pdf->getPage();
445
446 $posyafter = $pdf->GetY();
447
448 if ($posyafter > ($this->page_hauteur - ($heightforfooter + $heightforfreetext + 20))) { // There is no space left for total+free text
449 $pdf->AddPage('', '', true);
450 $pagenb++;
451 $pageposafternote++;
452 $pdf->setPage($pageposafternote);
453 $pdf->setTopMargin($tab_top_newpage);
454 // The only function to edit the bottom margin of current page to set it.
455 $pdf->setPageOrientation('', true, $heightforfooter + $heightforfreetext);
456 //$posyafter = $tab_top_newpage;
457 }
458
459
460 // apply note frame to previous pages
461 $i = $pageposbeforenote;
462 while ($i < $pageposafternote) {
463 $pdf->setPage($i);
464
465
466 $pdf->SetDrawColor(128, 128, 128);
467 // Draw note frame
468 if ($i > $pageposbeforenote) {
469 $height_note = $this->page_hauteur - ($tab_top_newpage + $heightforfooter);
470 $pdf->Rect($this->marge_gauche, $tab_top_newpage - 1, $tab_width, $height_note + 1);
471 } else {
472 $height_note = $this->page_hauteur - ($tab_top + $heightforfooter);
473 $pdf->Rect($this->marge_gauche, $tab_top - 1, $tab_width, $height_note + 1);
474 }
475
476 // Add footer
477 $pdf->setPageOrientation('', true, 0); // The only function to edit the bottom margin of current page to set it.
478 $this->_pagefoot($pdf, $object, $outputlangs, 1);
479
480 $i++;
481 }
482
483 // apply note frame to last page
484 $pdf->setPage($pageposafternote);
485 if (!empty($tplidx)) {
486 $pdf->useTemplate($tplidx);
487 }
488 if (!getDolGlobalInt('MAIN_PDF_DONOTREPEAT_HEAD')) {
489 $this->_pagehead($pdf, $object, 0, $outputlangs);
490 }
491 $height_note = $posyafter - $tab_top_newpage;
492 $pdf->Rect($this->marge_gauche, $tab_top_newpage - 1, $tab_width, $height_note + 1);
493 } else {
494 // No pagebreak
495 $pdf->commitTransaction();
496 $posyafter = $pdf->GetY();
497 $height_note = $posyafter - $tab_top;
498 $pdf->Rect($this->marge_gauche, $tab_top - 1, $tab_width, $height_note + 1);
499
500
501 if ($posyafter > ($this->page_hauteur - ($heightforfooter + $heightforfreetext + 20))) {
502 // not enough space, need to add page
503 $pdf->AddPage('', '', true);
504 $pagenb++;
505 $pageposafternote++;
506 $pdf->setPage($pageposafternote);
507 if (!empty($tplidx)) {
508 $pdf->useTemplate($tplidx);
509 }
510 if (!getDolGlobalInt('MAIN_PDF_DONOTREPEAT_HEAD')) {
511 $this->_pagehead($pdf, $object, 0, $outputlangs);
512 }
513
514 $posyafter = $tab_top_newpage;
515 }
516 }
517
518 $tab_height -= $height_note;
519 $tab_top = $posyafter + 6;
520 } else {
521 $height_note = 0;
522 }
523
524 // Use new auto column system
525 $this->prepareArrayColumnField($object, $outputlangs, $hidedetails, $hidedesc, $hideref);
526
527 // Table simulation to know the height of the title line
528 $pdf->startTransaction();
529 $this->pdfTabTitles($pdf, $tab_top, $tab_height, $outputlangs, $hidetop);
530 $pdf->rollbackTransaction(true);
531
532 $nexY = $tab_top + $this->tabTitleHeight;
533
534 // Loop on each lines
535 $pageposbeforeprintlines = $pdf->getPage();
536 $pagenb = $pageposbeforeprintlines;
537 for ($i = 0; $i < $nblines; $i++) {
538 $curY = $nexY;
539 $pdf->SetFont('', '', $default_font_size - 1); // Into loop to work with multipage
540 $pdf->SetTextColor(0, 0, 0);
541
542 // Define size of image if we need it
543 // $imglinesize = array();
544 // if (!empty($realpatharray[$i])) {
545 // $imglinesize = pdf_getSizeForImage($realpatharray[$i]);
546 // }
547
548 $pdf->setTopMargin($tab_top_newpage);
549 $pdf->setPageOrientation('', true, $heightforfooter + $heightforfreetext + $heightforinfotot); // The only function to edit the bottom margin of current page to set it.
550 $pageposbefore = $pdf->getPage();
551
552 $showpricebeforepagebreak = 1;
553 $posYAfterImage = 0;
554 $posYAfterDescription = 0;
555
556 // if ($this->getColumnStatus('photo')) {
557 // // We start with Photo of product line
558 // if (isset($imglinesize['width']) && isset($imglinesize['height']) && ($curY + $imglinesize['height']) > ($this->page_hauteur - ($heightforfooter + $heightforfreetext + $heightforinfotot))) { // If photo too high, we moved completely on new page
559 // $pdf->AddPage('', '', true);
560 // if (!empty($tplidx)) {
561 // $pdf->useTemplate($tplidx);
562 // }
563 // $pdf->setPage($pageposbefore + 1);
564
565 // $curY = $tab_top_newpage;
566
567 // // Allows data in the first page if description is long enough to break in multiples pages
568 // if (getDolGlobalInt('MAIN_PDF_DATA_ON_FIRST_PAGE')) {
569 // $showpricebeforepagebreak = 1;
570 // } else {
571 // $showpricebeforepagebreak = 0;
572 // }
573 // }
574
575 // if (!empty($this->cols['photo']) && isset($imglinesize['width']) && isset($imglinesize['height'])) {
576 // $pdf->Image($realpatharray[$i], $this->getColumnContentXStart('photo'), $curY + 1, $imglinesize['width'], $imglinesize['height'], '', '', '', 2, 300); // Use 300 dpi
577 // // $pdf->Image does not increase value return by getY, so we save it manually
578 // $posYAfterImage = $curY + $imglinesize['height'];
579 // }
580 // }
581
582 // Description of product line
583 if ($this->getColumnStatus('desc')) {
584 $pdf->startTransaction();
585
586 $this->printColDescContent($pdf, $curY, 'desc', $object, $i, $outputlangs, $hideref, $hidedesc);
587 $pageposafter = $pdf->getPage();
588
589 if ($pageposafter > $pageposbefore) { // There is a pagebreak
590 $pdf->rollbackTransaction(true);
591 $pageposafter = $pageposbefore;
592 $pdf->setPageOrientation('', true, $heightforfooter); // The only function to edit the bottom margin of current page to set it.
593
594 $this->printColDescContent($pdf, $curY, 'desc', $object, $i, $outputlangs, $hideref, $hidedesc);
595
596 $pageposafter = $pdf->getPage();
597 $posyafter = $pdf->GetY();
598
599 if ($posyafter > ($this->page_hauteur - ($heightforfooter + $heightforfreetext + $heightforinfotot))) { // There is no space left for total+free text
600 if ($i == ($nblines - 1)) { // No more lines, and no space left to show total, so we create a new page
601 $pdf->AddPage('', '', true);
602 if (!empty($tplidx)) {
603 $pdf->useTemplate($tplidx);
604 }
605 $pdf->setPage($pageposafter + 1);
606 }
607 } else {
608 // We found a page break
609 // Allows data in the first page if description is long enough to break in multiples pages
610 if (getDolGlobalInt('MAIN_PDF_DATA_ON_FIRST_PAGE')) {
611 $showpricebeforepagebreak = 1;
612 } else {
613 $showpricebeforepagebreak = 0;
614 }
615 }
616 } else { // No pagebreak
617 $pdf->commitTransaction();
618 }
619 $posYAfterDescription = $pdf->GetY();
620 }
621
622 $nexY = max($pdf->GetY(), $posYAfterImage);
623
624
625 $pageposafter = $pdf->getPage();
626 $pdf->setPage($pageposbefore);
627 $pdf->setTopMargin($this->marge_haute);
628 $pdf->setPageOrientation('', true, 0); // The only function to edit the bottom margin of current page to set it.
629
630 // We suppose that a too long description or photo were moved completely on next page
631 if ($pageposafter > $pageposbefore && empty($showpricebeforepagebreak)) {
632 $pdf->setPage($pageposafter);
633 $curY = $tab_top_newpage;
634 }
635
636 $pdf->SetFont('', '', $default_font_size - 1); // We reposition the default font
637
638 // Quantity
639 // Enough for 6 chars
640 if ($this->getColumnStatus('qty')) {
641 $qty = pdf_getlineqty($object, $i, $outputlangs, $hidedetails);
642 $this->printStdColumnContent($pdf, $curY, 'qty', $qty);
643 $nexY = max($pdf->GetY(), $nexY);
644 }
645
646 // Extrafields
647 if (!empty($object->lines[$i]->array_options)) {
648 foreach ($object->lines[$i]->array_options as $extrafieldColKey => $extrafieldValue) {
649 if ($this->getColumnStatus($extrafieldColKey)) {
650 $extrafieldValue = $this->getExtrafieldContent($object->lines[$i], $extrafieldColKey, $outputlangs);
651 $this->printStdColumnContent($pdf, $curY, $extrafieldColKey, $extrafieldValue);
652 $nexY = max($pdf->GetY(), $nexY);
653 }
654 }
655 }
656
657
658 $parameters = array(
659 'object' => $object,
660 'i' => $i,
661 'pdf' => & $pdf,
662 'curY' => & $curY,
663 'nexY' => & $nexY,
664 'outputlangs' => $outputlangs,
665 'hidedetails' => $hidedetails
666 );
667 $reshook = $hookmanager->executeHooks('printPDFline', $parameters, $this); // Note that $object may have been modified by hook
668
669
670 $sign = 1;
671 // Collection of totals by value of VAT in $this->tva["taux"]=total_tva
672 $prev_progress = $object->lines[$i]->get_prev_progress($object->id);
673 if ($prev_progress > 0 && $object->lines[$i] instanceof CommonInvoiceLine && !empty($object->lines[$i]->situation_percent)) { // Compute progress from previous situation
674 if (isModEnabled("multicurrency") && $object->multicurrency_tx != 1) {
675 $tvaligne = $sign * $object->lines[$i]->multicurrency_total_tva * ($object->lines[$i]->situation_percent - $prev_progress) / $object->lines[$i]->situation_percent; // @phan-suppress-current-line PhanUndeclaredProperty
676 } else {
677 $tvaligne = $sign * $object->lines[$i]->total_tva * ($object->lines[$i]->situation_percent - $prev_progress) / $object->lines[$i]->situation_percent; // @phan-suppress-current-line PhanUndeclaredProperty
678 }
679 } else {
680 if (isModEnabled("multicurrency") && $object->multicurrency_tx != 1) {
681 $tvaligne = $sign * $object->lines[$i]->multicurrency_total_tva;
682 } else {
683 $tvaligne = $sign * $object->lines[$i]->total_tva;
684 }
685 }
686
687 $localtax1ligne = $object->lines[$i]->total_localtax1;
688 $localtax2ligne = $object->lines[$i]->total_localtax2;
689 // @phan-suppress-next-line PhanUndeclaredProperty
690 $localtax1_rate = $object->lines[$i]->localtax1_tx;
691 $localtax2_rate = $object->lines[$i]->localtax2_tx; // @phan-suppress-current-line PhanUndeclaredProperty
692 $localtax1_type = $object->lines[$i]->localtax1_type; // @phan-suppress-current-line PhanUndeclaredProperty
693 $localtax2_type = $object->lines[$i]->localtax2_type; // @phan-suppress-current-line PhanUndeclaredProperty
694
695 $vatrate = (string) $object->lines[$i]->tva_tx;
696
697 // Retrieve type from database for backward compatibility with old records
698 if ((!isset($localtax1_type) || $localtax1_type == '' || !isset($localtax2_type) || $localtax2_type == '') // if tax type not defined
699 && (!empty($localtax1_rate) || !empty($localtax2_rate))) { // and there is local tax
700 $localtaxtmp_array = getLocalTaxesFromRate($vatrate, 0, $object->thirdparty, $mysoc);
701 $localtax1_type = isset($localtaxtmp_array[0]) ? $localtaxtmp_array[0] : '';
702 $localtax2_type = isset($localtaxtmp_array[2]) ? $localtaxtmp_array[2] : '';
703 }
704
705 // retrieve global local tax
706 if ($localtax1_type && $localtax1ligne != 0) {
707 if (empty($this->localtax1[$localtax1_type][$localtax1_rate])) {
708 $this->localtax1[$localtax1_type][$localtax1_rate] = $localtax1ligne;
709 } else {
710 $this->localtax1[$localtax1_type][$localtax1_rate] += $localtax1ligne;
711 }
712 }
713 if ($localtax2_type && $localtax2ligne != 0) {
714 if (empty($this->localtax2[$localtax2_type][$localtax2_rate])) {
715 $this->localtax2[$localtax2_type][$localtax2_rate] = $localtax2ligne;
716 } else {
717 $this->localtax2[$localtax2_type][$localtax2_rate] += $localtax2ligne;
718 }
719 }
720
721 if (((int) $object->lines[$i]->info_bits & 0x01) == 0x01) {
722 $vatrate .= '*';
723 }
724
725 // Fill $this->tva and $this->tva_array
726 if (!isset($this->tva[$vatrate])) {
727 $this->tva[$vatrate] = 0;
728 }
729 $this->tva[$vatrate] += $tvaligne;
730 $vatcode = $object->lines[$i]->vat_src_code; // @phan-suppress-current-line PhanUndeclaredProperty
731 if (empty($this->tva_array[$vatrate.($vatcode ? ' ('.$vatcode.')' : '')]['amount'])) {
732 $this->tva_array[$vatrate.($vatcode ? ' ('.$vatcode.')' : '')]['amount'] = 0;
733 }
734 $this->tva_array[$vatrate.($vatcode ? ' ('.$vatcode.')' : '')] = array('vatrate' => $vatrate, 'vatcode' => $vatcode, 'amount' => $this->tva_array[$vatrate.($vatcode ? ' ('.$vatcode.')' : '')]['amount'] + $tvaligne);
735
736 $nexY = max($nexY, $posYAfterImage);
737
738 // Add line
739 if (getDolGlobalInt('MAIN_PDF_DASH_BETWEEN_LINES') && $i < ($nblines - 1)) {
740 $pdf->setPage($pageposafter);
741 $pdf->SetLineStyle(array('dash' => '1,1', 'color' => array(80, 80, 80)));
742 //$pdf->SetDrawColor(190,190,200);
743 $pdf->line($this->marge_gauche, $nexY, $this->page_largeur - $this->marge_droite, $nexY);
744 $pdf->SetLineStyle(array('dash' => 0));
745 }
746
747 // Detect if some page were added automatically and output _tableau for past pages
748 while ($pagenb < $pageposafter) {
749 $pdf->setPage($pagenb);
750 if ($pagenb == $pageposbeforeprintlines) {
751 $this->_tableau($pdf, $tab_top, $this->page_hauteur - $tab_top - $heightforfooter, 0, $outputlangs, $hidetop, 1, $object->multicurrency_code, $outputlangsbis);
752 } else {
753 $this->_tableau($pdf, $tab_top_newpage, $this->page_hauteur - $tab_top_newpage - $heightforfooter, 0, $outputlangs, 1, 1, $object->multicurrency_code, $outputlangsbis);
754 }
755 $this->_pagefoot($pdf, $object, $outputlangs, 1);
756 $pagenb++;
757 $pdf->setPage($pagenb);
758 $pdf->setPageOrientation('', true, 0); // The only function to edit the bottom margin of current page to set it.
759 if (!getDolGlobalInt('MAIN_PDF_DONOTREPEAT_HEAD')) {
760 $this->_pagehead($pdf, $object, 0, $outputlangs);
761 }
762 if (!empty($tplidx)) {
763 $pdf->useTemplate($tplidx);
764 }
765 }
766
767 if (isset($object->lines[$i + 1]->pagebreak) && $object->lines[$i + 1]->pagebreak) { // @phan-suppress-current-line PhanUndeclaredProperty
768 if ($pagenb == $pageposafter) {
769 $this->_tableau($pdf, $tab_top, $this->page_hauteur - $tab_top - $heightforfooter, 0, $outputlangs, $hidetop, 1, $object->multicurrency_code, $outputlangsbis);
770 } else {
771 $this->_tableau($pdf, $tab_top_newpage, $this->page_hauteur - $tab_top_newpage - $heightforfooter, 0, $outputlangs, 1, 1, $object->multicurrency_code, $outputlangsbis);
772 }
773 $this->_pagefoot($pdf, $object, $outputlangs, 1);
774 // New page
775 $pdf->AddPage();
776 if (!empty($tplidx)) {
777 $pdf->useTemplate($tplidx);
778 }
779 $pagenb++;
780 if (!getDolGlobalInt('MAIN_PDF_DONOTREPEAT_HEAD')) {
781 $this->_pagehead($pdf, $object, 0, $outputlangs);
782 }
783 }
784 }
785
786 // Show square
787 if ($pagenb == $pageposbeforeprintlines) {
788 $this->_tableau($pdf, $tab_top, $this->page_hauteur - $tab_top - $heightforinfotot - $heightforfreetext - $heightforfooter, 0, $outputlangs, $hidetop, 0, $object->multicurrency_code, $outputlangsbis);
789 } else {
790 $this->_tableau($pdf, $tab_top_newpage, $this->page_hauteur - $tab_top_newpage - $heightforinfotot - $heightforfreetext - $heightforfooter, 0, $outputlangs, 1, 0, $object->multicurrency_code, $outputlangsbis);
791 }
792 $bottomlasttab = $this->page_hauteur - $heightforinfotot - $heightforfreetext - $heightforfooter + 1;
793
794 // Display infos area
795 //$posy = $this->drawInfoTable($pdf, $object, $bottomlasttab, $outputlangs);
796
797 // Display total zone
798 //$posy = $this->drawTotalTable($pdf, $object, $deja_regle, $bottomlasttab, $outputlangs);
799
800 // Display payment area
801 /*
802 if ($deja_regle)
803 {
804 $posy = $this->drawPaymentsTable($pdf, $object, $posy, $outputlangs);
805 }
806 */
807
808 // Pagefoot
809 $this->_pagefoot($pdf, $object, $outputlangs);
810 if (method_exists($pdf, 'AliasNbPages')) {
811 $pdf->AliasNbPages(); // @phan-suppress-current-line PhanUndeclaredMethod
812 }
813
814 $pdf->Close();
815
816 $pdf->Output($file, 'F');
817
818 // Add pdfgeneration hook
819 $hookmanager->initHooks(array('pdfgeneration'));
820 $parameters = array('file' => $file, 'object' => $object, 'outputlangs' => $outputlangs);
821 global $action;
822 $reshook = $hookmanager->executeHooks('afterPDFCreation', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
823 $this->warnings = $hookmanager->warnings;
824 if ($reshook < 0) {
825 $this->error = $hookmanager->error;
826 $this->errors = $hookmanager->errors;
827 dolChmod($file);
828 return -1;
829 }
830
831 dolChmod($file);
832
833 $this->result = array('fullpath' => $file);
834
835 return 1; // No error
836 } else {
837 $this->error = $langs->transnoentities("ErrorCanNotCreateDir", $dir);
838 return 0;
839 }
840 } else {
841 $this->error = $langs->transnoentities("ErrorConstantNotDefined", "FAC_OUTPUTDIR");
842 return 0;
843 }
844 }
845
846 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
854 public static function liste_modeles($db, $maxfilenamelength = 0)
855 {
856 // phpcs:enable
857 return parent::liste_modeles($db, $maxfilenamelength); // TODO: Change the autogenerated stub
858 }
859
860 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
875 protected function _tableau(&$pdf, $tab_top, $tab_height, $nexY, $outputlangs, $hidetop = 0, $hidebottom = 0, $currency = '', $outputlangsbis = null)
876 {
877 global $conf;
878
879 // Force to disable hidetop and hidebottom
880 $hidebottom = 0;
881 if ($hidetop) {
882 $hidetop = -1;
883 }
884
885 $currency = !empty($currency) ? $currency : $conf->currency;
886 $default_font_size = pdf_getPDFFontSize($outputlangs);
887
888 // Amount in (at tab_top - 1)
889 $pdf->SetTextColor(0, 0, 0);
890 $pdf->SetFont('', '', $default_font_size - 2);
891
892 if (empty($hidetop)) {
893 $titre = $outputlangs->transnoentities("AmountInCurrency", $outputlangs->transnoentitiesnoconv("Currency".$currency));
894 if (getDolGlobalInt('PDF_USE_ALSO_LANGUAGE_CODE') && is_object($outputlangsbis)) {
895 $titre .= ' - '.$outputlangsbis->transnoentities("AmountInCurrency", $outputlangsbis->transnoentitiesnoconv("Currency".$currency));
896 }
897
898 $pdf->SetXY($this->page_largeur - $this->marge_droite - ($pdf->GetStringWidth($titre) + 3), $tab_top - 4);
899 $pdf->MultiCell(($pdf->GetStringWidth($titre) + 3), 2, $titre);
900
901 //$conf->global->MAIN_PDF_TITLE_BACKGROUND_COLOR='230,230,230';
902 if (getDolGlobalString('MAIN_PDF_TITLE_BACKGROUND_COLOR')) {
903 $pdf->Rect($this->marge_gauche, $tab_top, $this->page_largeur - $this->marge_droite - $this->marge_gauche, $this->tabTitleHeight, 'F', array(), explode(',', getDolGlobalString('MAIN_PDF_TITLE_BACKGROUND_COLOR')));
904 }
905 }
906
907 $pdf->SetDrawColor(128, 128, 128);
908 $pdf->SetFont('', '', $default_font_size - 1);
909
910 // Output Rect
911 $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
912
913
914 $this->pdfTabTitles($pdf, $tab_top, $tab_height, $outputlangs, $hidetop);
915
916 if (empty($hidetop)) {
917 $pdf->line($this->marge_gauche, $tab_top + $this->tabTitleHeight, $this->page_largeur - $this->marge_droite, $tab_top + $this->tabTitleHeight); // line takes a position y in 2nd parameter and 4th parameter
918 }
919 }
920
921 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
932 protected function _pagehead(&$pdf, $object, $showaddress, $outputlangs, $outputlangsbis = null)
933 {
934 // phpcs:enable
935 global $conf, $langs;
936
937 $ltrdirection = 'L';
938 if ($outputlangs->trans("DIRECTION") == 'rtl') {
939 $ltrdirection = 'R';
940 }
941
942 // Load traductions files required by page
943 $outputlangs->loadLangs(array("main", "bills", "propal", "companies"));
944
945 $default_font_size = pdf_getPDFFontSize($outputlangs);
946
947 pdf_pagehead($pdf, $outputlangs, $this->page_hauteur);
948
949 // Show Draft Watermark
950 if (getDolGlobalString('MYMODULE_DRAFT_WATERMARK') && $object->status == $object::STATUS_DRAFT) {
951 pdf_watermark($pdf, $outputlangs, $this->page_hauteur, $this->page_largeur, 'mm', dol_escape_htmltag(getDolGlobalString('MYMODULE_DRAFT_WATERMARK')));
952 }
953
954 $pdf->SetTextColor(0, 0, 60);
955 $pdf->SetFont('', 'B', $default_font_size + 3);
956
957 $w = 110;
958
959 $posy = $this->marge_haute;
960 $posx = $this->page_largeur - $this->marge_droite - $w;
961
962 $pdf->SetXY($this->marge_gauche, $posy);
963
964 // Logo
965 if (!getDolGlobalInt('PDF_DISABLE_MYCOMPANY_LOGO')) {
966 if ($this->emetteur->logo) {
967 $logodir = $conf->mycompany->dir_output;
968 if (!empty(getMultidirOutput($object, 'mycompany'))) {
969 $logodir = getMultidirOutput($object, 'mycompany');
970 }
971 if (!getDolGlobalInt('MAIN_PDF_USE_LARGE_LOGO')) {
972 $logo = $logodir.'/logos/thumbs/'.$this->emetteur->logo_small;
973 } else {
974 $logo = $logodir.'/logos/'.$this->emetteur->logo;
975 }
976 if (is_readable($logo)) {
977 $height = pdf_getHeightForLogo($logo);
978 $pdf->Image($logo, $this->marge_gauche, $posy, 0, $height); // width=0 (auto)
979 } else {
980 $pdf->SetTextColor(200, 0, 0);
981 $pdf->SetFont('', 'B', $default_font_size - 2);
982 $pdf->MultiCell($w, 3, $outputlangs->transnoentities("ErrorLogoFileNotFound", $logo), 0, 'L');
983 $pdf->MultiCell($w, 3, $outputlangs->transnoentities("ErrorGoToGlobalSetup"), 0, 'L');
984 }
985 } else {
986 $text = $this->emetteur->name;
987 $pdf->MultiCell($w, 4, $outputlangs->convToOutputCharset($text), 0, 'L');
988 }
989 }
990
991 $pdf->SetFont('', 'B', $default_font_size + 3);
992 $pdf->SetXY($posx, $posy);
993 $pdf->SetTextColor(0, 0, 60);
994 $title = $outputlangs->transnoentities("PdfTitle");
995 if (getDolGlobalInt('PDF_USE_ALSO_LANGUAGE_CODE') && is_object($outputlangsbis)) {
996 $title .= ' - ';
997 $title .= $outputlangsbis->transnoentities("PdfTitle");
998 }
999 $pdf->MultiCell($w, 3, $title, '', 'R');
1000
1001 $pdf->SetFont('', 'B', $default_font_size);
1002
1003 $posy += 5;
1004 $pdf->SetXY($posx, $posy);
1005 $pdf->SetTextColor(0, 0, 60);
1006 $textref = $outputlangs->transnoentities("Ref")." : ".$outputlangs->convToOutputCharset($object->ref);
1007 if ($object->status == $object::STATUS_DRAFT) {
1008 $pdf->SetTextColor(128, 0, 0);
1009 $textref .= ' - '.$outputlangs->transnoentities("NotValidated");
1010 }
1011 $pdf->MultiCell($w, 4, $textref, '', 'R');
1012
1013 $posy += 1;
1014 $pdf->SetFont('', '', $default_font_size - 2);
1015
1016 // @phan-suppress-next-line PhanUndeclaredProperty
1017 if (property_exists($object, 'ref_client') && $object->ref_client) {
1018 $posy += 4;
1019 $pdf->SetXY($posx, $posy);
1020 $pdf->SetTextColor(0, 0, 60);
1021 // @phan-suppress-next-line PhanUndeclaredProperty
1022 $pdf->MultiCell($w, 3, $outputlangs->transnoentities("RefCustomer")." : ".pdf_truncate_text($pdf, $outputlangs->convToOutputCharset($object->ref_client), 65), '', 'R');
1023 }
1024
1025 if (getDolGlobalInt('PDF_SHOW_PROJECT_TITLE')) {
1026 $object->fetchProject();
1027 if (!empty($object->project->ref)) {
1028 $posy += 3;
1029 $pdf->SetXY($posx, $posy);
1030 $pdf->SetTextColor(0, 0, 60);
1031 $pdf->MultiCell($w, 3, $outputlangs->transnoentities("Project")." : ".(empty($object->project->title) ? '' : $object->project->title), '', 'R');
1032 }
1033 }
1034
1035 if (getDolGlobalInt('PDF_SHOW_PROJECT')) {
1036 $object->fetchProject();
1037 if (!empty($object->project->ref)) {
1038 $outputlangs->load("projects");
1039 $posy += 3;
1040 $pdf->SetXY($posx, $posy);
1041 $pdf->SetTextColor(0, 0, 60);
1042 $pdf->MultiCell($w, 3, $outputlangs->transnoentities("RefProject")." : ".(empty($object->project->ref) ? '' : $object->project->ref), '', 'R');
1043 }
1044 }
1045
1046 $posy += 4;
1047 $pdf->SetXY($posx, $posy);
1048 $pdf->SetTextColor(0, 0, 60);
1049
1050 $title = $outputlangs->transnoentities("Date");
1051 if (getDolGlobalInt('PDF_USE_ALSO_LANGUAGE_CODE') && is_object($outputlangsbis)) {
1052 $title .= ' - '.$outputlangsbis->transnoentities("Date");
1053 }
1054 $pdf->MultiCell($w, 3, $title." : ".dol_print_date($object->date_creation, "day", false, $outputlangs, true), '', 'R');
1055
1056 if (!getDolGlobalString('MAIN_PDF_HIDE_CUSTOMER_CODE') && !empty($object->thirdparty->code_client)) {
1057 $posy += 3;
1058 $pdf->SetXY($posx, $posy);
1059 $pdf->SetTextColor(0, 0, 60);
1060 $pdf->MultiCell($w, 3, $outputlangs->transnoentities("CustomerCode")." : ".$outputlangs->transnoentities((string) $object->thirdparty->code_client), '', 'R');
1061 }
1062
1063 if (!getDolGlobalString('MAIN_PDF_HIDE_CUSTOMER_ACCOUNTING_CODE') && !empty($object->thirdparty->code_compta_client)) {
1064 $posy += 3;
1065 $pdf->SetXY($posx, $posy);
1066 $pdf->SetTextColor(0, 0, 60);
1067 $pdf->MultiCell($w, 3, $outputlangs->transnoentities("CustomerAccountancyCode")." : ".$outputlangs->transnoentities((string) $object->thirdparty->code_compta_client), '', 'R');
1068 }
1069
1070 // Get contact
1071 if (getDolGlobalInt('DOC_SHOW_FIRST_SALES_REP')) {
1072 $arrayidcontact = $object->getIdContact('internal', 'SALESREPFOLL');
1073 if (count($arrayidcontact) > 0) {
1074 $usertmp = new User($this->db);
1075 $usertmp->fetch($arrayidcontact[0]);
1076 $posy += 4;
1077 $pdf->SetXY($posx, $posy);
1078 $pdf->SetTextColor(0, 0, 60);
1079 $pdf->MultiCell($w, 3, $outputlangs->transnoentities("SalesRepresentative")." : ".$usertmp->getFullName($langs), '', 'R');
1080 }
1081 }
1082
1083 $posy += 1;
1084
1085 $top_shift = 0;
1086 // Show list of linked objects
1087 $current_y = $pdf->getY();
1088 $posy = pdf_writeLinkedObjects($pdf, $object, $outputlangs, $posx, $posy, $w, 3, 'R', $default_font_size);
1089 if ($current_y < $pdf->getY()) {
1090 $top_shift = $pdf->getY() - $current_y;
1091 }
1092
1093 if ($showaddress) {
1094 // Sender properties
1095 $carac_emetteur = pdf_build_address($outputlangs, $this->emetteur, $object->thirdparty, '', 0, 'source', $object);
1096
1097 // Show sender
1098 $posy = getDolGlobalInt('MAIN_PDF_USE_ISO_LOCATION') ? 40 : 42;
1099 $posy += $top_shift;
1100 $posx = $this->marge_gauche;
1101 if (getDolGlobalInt('MAIN_INVERT_SENDER_RECIPIENT')) {
1102 $posx = $this->page_largeur - $this->marge_droite - 80;
1103 }
1104
1105 $hautcadre = getDolGlobalInt('MAIN_PDF_USE_ISO_LOCATION') ? 38 : 40;
1106 $widthrecbox = getDolGlobalInt('MAIN_PDF_USE_ISO_LOCATION') ? 92 : 82;
1107
1108
1109 // Show sender frame
1110 if (!getDolGlobalString('MAIN_PDF_NO_SENDER_FRAME')) {
1111 $pdf->SetTextColor(0, 0, 0);
1112 $pdf->SetFont('', '', $default_font_size - 2);
1113 $pdf->SetXY($posx, $posy - 5);
1114 $pdf->MultiCell($widthrecbox, 5, $outputlangs->transnoentities("BillFrom").":", 0, $ltrdirection);
1115 $pdf->SetXY($posx, $posy);
1116 $pdf->SetFillColor(230, 230, 230);
1117 $pdf->MultiCell($widthrecbox, $hautcadre, "", 0, 'R', true);
1118 $pdf->SetTextColor(0, 0, 60);
1119 }
1120
1121 // Show sender name
1122 if (!getDolGlobalString('MAIN_PDF_HIDE_SENDER_NAME')) {
1123 $pdf->SetXY($posx + 2, $posy + 3);
1124 $pdf->SetFont('', 'B', $default_font_size);
1125 $pdf->MultiCell($widthrecbox - 2, 4, $outputlangs->convToOutputCharset($this->emetteur->name), 0, $ltrdirection);
1126 $posy = $pdf->getY();
1127 }
1128
1129 // Show sender information
1130 $pdf->SetXY($posx + 2, $posy);
1131 $pdf->SetFont('', '', $default_font_size - 1);
1132 $pdf->MultiCell($widthrecbox - 2, 4, $carac_emetteur, 0, $ltrdirection);
1133
1134 // If BILLING contact defined, we use it
1135 $usecontact = false;
1136 $arrayidcontact = $object->getIdContact('external', 'BILLING');
1137 if (count($arrayidcontact) > 0) {
1138 $usecontact = true;
1139 $result = $object->fetch_contact($arrayidcontact[0]);
1140 }
1141
1142 // Recipient name
1143 if ($usecontact && $object->contact->socid != $object->thirdparty->id && getDolGlobalInt('MAIN_USE_COMPANY_NAME_OF_CONTACT')) {
1144 $thirdparty = $object->contact;
1145 } else {
1146 $thirdparty = $object->thirdparty;
1147 }
1148
1149 if (is_object($thirdparty)) {
1150 $carac_client_name = pdfBuildThirdpartyName($thirdparty, $outputlangs);
1151 } else {
1152 $carac_client_name = null;
1153 }
1154
1155 $mode = 'target';
1156 $carac_client = pdf_build_address($outputlangs, $this->emetteur, $object->thirdparty, ($usecontact ? $object->contact : ''), ($usecontact ? 1 : 0), $mode, $object);
1157
1158 // Show recipient
1159 $widthrecbox = getDolGlobalInt('MAIN_PDF_USE_ISO_LOCATION') ? 92 : 100;
1160 if ($this->page_largeur < 210) {
1161 $widthrecbox = 84; // To work with US executive format
1162 }
1163 $posy = getDolGlobalInt('MAIN_PDF_USE_ISO_LOCATION') ? 40 : 42;
1164 $posy += $top_shift;
1165 $posx = $this->page_largeur - $this->marge_droite - $widthrecbox;
1166 if (getDolGlobalInt('MAIN_INVERT_SENDER_RECIPIENT')) {
1167 $posx = $this->marge_gauche;
1168 }
1169
1170 // Show recipient frame
1171 if (!getDolGlobalString('MAIN_PDF_NO_RECIPENT_FRAME')) {
1172 $pdf->SetTextColor(0, 0, 0);
1173 $pdf->SetFont('', '', $default_font_size - 2);
1174 $pdf->SetXY($posx + 2, $posy - 5);
1175 $pdf->MultiCell($widthrecbox, 5, $outputlangs->transnoentities("To").":", 0, $ltrdirection);
1176 $pdf->Rect($posx, $posy, $widthrecbox, $hautcadre);
1177 }
1178
1179 // Show recipient name
1180 $pdf->SetXY($posx + 2, $posy + 3);
1181 $pdf->SetFont('', 'B', $default_font_size);
1182 // @phan-suppress-next-line PhanPluginSuspiciousParamOrder
1183 $pdf->MultiCell($widthrecbox, 2, (string) $carac_client_name, 0, $ltrdirection);
1184
1185 $posy = $pdf->getY();
1186
1187 // Show recipient information
1188 $pdf->SetFont('', '', $default_font_size - 1);
1189 $pdf->SetXY($posx + 2, $posy);
1190 // @phan-suppress-next-line PhanPluginSuspiciousParamOrder
1191 $pdf->MultiCell($widthrecbox, 4, $carac_client, 0, $ltrdirection);
1192 }
1193
1194 $pdf->SetTextColor(0, 0, 0);
1195 return $top_shift;
1196 }
1197
1198 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
1208 protected function _pagefoot(&$pdf, $object, $outputlangs, $hidefreetext = 0)
1209 {
1210 global $conf;
1211 $showdetails = !getDolGlobalInt('MAIN_GENERATE_DOCUMENTS_SHOW_FOOT_DETAILS') ? 0 : getDolGlobalInt('MAIN_GENERATE_DOCUMENTS_SHOW_FOOT_DETAILS');
1212 return pdf_pagefoot($pdf, $outputlangs, 'INVOICE_FREE_TEXT', $this->emetteur, $this->marge_basse, $this->marge_gauche, $this->page_hauteur, $object, $showdetails, $hidefreetext);
1213 }
1214
1225 public function defineColumnField($object, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0)
1226 {
1227 global $hookmanager;
1228
1229 // Default field style for content
1230 $this->defaultContentsFieldsStyle = array(
1231 'align' => 'R', // R,C,L
1232 'padding' => array(1, 0.5, 1, 0.5), // Like css 0 => top , 1 => right, 2 => bottom, 3 => left
1233 );
1234
1235 // Default field style for content
1236 $this->defaultTitlesFieldsStyle = array(
1237 'align' => 'C', // R,C,L
1238 'padding' => array(0.5, 0, 0.5, 0), // Like css 0 => top , 1 => right, 2 => bottom, 3 => left
1239 );
1240
1241 /*
1242 * For example
1243 $this->cols['theColKey'] = array(
1244 'rank' => $rank, // int : use for ordering columns
1245 'width' => 20, // the column width in mm
1246 'title' => array(
1247 'textkey' => 'yourLangKey', // if there is no label, yourLangKey will be translated to replace label
1248 'label' => ' ', // the final label : used fore final generated text
1249 'align' => 'L', // text alignment : R,C,L
1250 'padding' => array(0.5,0.5,0.5,0.5), // Like css 0 => top , 1 => right, 2 => bottom, 3 => left
1251 ),
1252 'content' => array(
1253 'align' => 'L', // text alignment : R,C,L
1254 'padding' => array(0.5,0.5,0.5,0.5), // Like css 0 => top , 1 => right, 2 => bottom, 3 => left
1255 ),
1256 );
1257 */
1258
1259 $rank = 0; // do not use negative rank
1260 $this->cols['desc'] = array(
1261 'rank' => $rank,
1262 'width' => false, // only for desc
1263 'status' => true,
1264 'title' => array(
1265 'textkey' => 'Designation', // use lang key is useful in some case with module
1266 'align' => 'L',
1267 // 'textkey' => 'yourLangKey', // if there is no label, yourLangKey will be translated to replace label
1268 // 'label' => ' ', // the final label
1269 'padding' => array(0.5, 0.5, 0.5, 0.5), // Like css 0 => top , 1 => right, 2 => bottom, 3 => left
1270 ),
1271 'content' => array(
1272 'align' => 'L',
1273 'padding' => array(1.0, 0.5, 1.0, 1.5), // Like css 0 => top , 1 => right, 2 => bottom, 3 => left
1274 ),
1275 );
1276
1277 // PHOTO
1278 // $rank += 10;
1279 // $this->cols['photo'] = array(
1280 // 'rank' => $rank,
1281 // 'width' => (!getDolGlobalInt('MAIN_DOCUMENTS_WITH_PICTURE_WIDTH') ? 20 : getDolGlobalInt('MAIN_DOCUMENTS_WITH_PICTURE_WIDTH')), // in mm
1282 // 'status' => false,
1283 // 'title' => array(
1284 // 'textkey' => 'Photo',
1285 // 'label' => ' '
1286 // ),
1287 // 'content' => array(
1288 // 'padding' => array(0, 0, 0, 0), // Like css 0 => top , 1 => right, 2 => bottom, 3 => left
1289 // ),
1290 // 'border-left' => false, // remove left line separator
1291 // );
1292
1293 // if (getDolGlobalInt('MAIN_GENERATE_INVOICES_WITH_PICTURE') && !empty($this->atleastonephoto)) {
1294 // $this->cols['photo']['status'] = true;
1295 // }
1296
1297
1298 $rank += 10;
1299 $this->cols['vat'] = array(
1300 'rank' => $rank,
1301 'status' => false,
1302 'width' => 16.0, // in mm
1303 'title' => array(
1304 'textkey' => 'VAT'
1305 ),
1306 'border-left' => true, // add left line separator
1307 );
1308
1309 if (!getDolGlobalInt('MAIN_GENERATE_DOCUMENTS_WITHOUT_VAT') && !getDolGlobalInt('MAIN_GENERATE_DOCUMENTS_WITHOUT_VAT_COLUMN')) {
1310 $this->cols['vat']['status'] = true;
1311 }
1312
1313 $rank += 10;
1314 $this->cols['subprice'] = array(
1315 'rank' => $rank,
1316 'width' => 19.0, // in mm
1317 'status' => true,
1318 'title' => array(
1319 'textkey' => 'PriceUHT'
1320 ),
1321 'border-left' => true, // add left line separator
1322 );
1323
1324 $rank += 10;
1325 $this->cols['qty'] = array(
1326 'rank' => $rank,
1327 'width' => 16.0, // in mm
1328 'status' => true,
1329 'title' => array(
1330 'textkey' => 'Qty'
1331 ),
1332 'border-left' => true, // add left line separator
1333 );
1334
1335 $rank += 10;
1336 $this->cols['unit'] = array(
1337 'rank' => $rank,
1338 'width' => 11.0, // in mm
1339 'status' => false,
1340 'title' => array(
1341 'textkey' => 'Unit'
1342 ),
1343 'border-left' => true, // add left line separator
1344 );
1345 if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
1346 $this->cols['unit']['status'] = true;
1347 }
1348
1349 $rank += 10;
1350 $this->cols['discount'] = array(
1351 'rank' => $rank,
1352 'width' => 13.0, // in mm
1353 'status' => false,
1354 'title' => array(
1355 'textkey' => 'ReductionShort'
1356 ),
1357 'border-left' => true, // add left line separator
1358 );
1359 if ($this->atleastonediscount) {
1360 $this->cols['discount']['status'] = true;
1361 }
1362
1363 $rank += 1000; // add a big offset to be sure is the last col because default extrafield rank is 100
1364 $this->cols['totalexcltax'] = array(
1365 'rank' => $rank,
1366 'width' => 26.0, // in mm
1367 'status' => true,
1368 'title' => array(
1369 'textkey' => 'TotalHTShort'
1370 ),
1371 'border-left' => true, // add left line separator
1372 );
1373
1374 // Add extrafields cols
1375 if (!empty($object->lines)) {
1376 $line = reset($object->lines);
1377 $this->defineColumnExtrafield($line, $outputlangs, $hidedetails);
1378 }
1379
1380 $parameters = array(
1381 'object' => $object,
1382 'outputlangs' => $outputlangs,
1383 'hidedetails' => $hidedetails,
1384 'hidedesc' => $hidedesc,
1385 'hideref' => $hideref
1386 );
1387
1388 $reshook = $hookmanager->executeHooks('defineColumnField', $parameters, $this); // Note that $object may have been modified by hook
1389 if ($reshook < 0) {
1390 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
1391 } elseif (empty($reshook)) {
1392 // @phan-suppress-next-line PhanPluginSuspiciousParamOrderInternal
1393 $this->cols = array_replace($this->cols, $hookmanager->resArray); // array_replace is used to preserve keys
1394 } else {
1395 $this->cols = $hookmanager->resArray;
1396 }
1397 }
1398}
if(! $sortfield) if(! $sortorder) $object
Definition account.php:100
$propal type
'integer', 'integer:ObjectClass:PathToClass[:AddCreateButtonOrNot[:Filter[:Sortfield]]]',...
Definition propal.php:280
prepareArrayColumnField($object, $outputlangs, $hidedetails=0, $hidedesc=0, $hideref=0)
Prepare Array Column Field.
getColumnStatus($colKey)
get column status from column key
printStdColumnContent($pdf, &$curY, $colKey, $columnText='')
print standard column content
printColDescContent($pdf, &$curY, $colKey, $object, $i, $outputlangs, $hideref=0, $hidedesc=0, $issupplierline=0)
print description column content
printRect($pdf, $x, $y, $l, $h, $hidetop=0, $hidebottom=0)
Rect pdf.
getExtrafieldContent($object, $extrafieldKey, $outputlangs=null)
get extrafield content for pdf writeHtmlCell compatibility usage for PDF line columns and object note...
defineColumnExtrafield($object, $outputlangs, $hidedetails=0)
Define Array Column Field for extrafields.
Parent class of all other business classes for details of elements (invoices, contracts,...
Class to manage hooks.
Parent class for document models.
Class to manage translations.
Class to manage Dolibarr users.
Class to manage PDF template standard_myobject.
defineColumnField($object, $outputlangs, $hidedetails=0, $hidedesc=0, $hideref=0)
Define Array Column Field.
_pagehead(&$pdf, $object, $showaddress, $outputlangs, $outputlangsbis=null)
Show top header of page.
write_file($object, $outputlangs, $srctemplatepath='', $hidedetails=0, $hidedesc=0, $hideref=0)
Function to build and write pdf to disk.
static liste_modeles($db, $maxfilenamelength=0)
Return list of active generation models.
_tableau(&$pdf, $tab_top, $tab_height, $nexY, $outputlangs, $hidetop=0, $hidebottom=0, $currency='', $outputlangsbis=null)
Show table for lines.
_pagefoot(&$pdf, $object, $outputlangs, $hidefreetext=0)
Show footer of page.
getCountry($searchkey, $withcode='', $dbtouse=null, $outputlangs=null, $entconv=1, $searchlabel='')
Return country label, code or id from an id, code or label.
global $mysoc
print $script_file $mode $langs defaultlang(is_numeric($duration_value) ? " delay=". $duration_value :"").(is_numeric($duration_value2) ? " after cd cd cd description as description
Only used if Module[ID]Desc translation string is not found.
if(!isModEnabled('ai')||!getDolGlobalString('AI_ASSISTANT_ENABLED')) global $conf
The main.inc.php has been included so the following variable are now defined:
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...
getCallerInfoString()
Get caller info as a string that can be appended to a log message.
dol_sanitizeFileName($str, $newstr='_', $unaccent=1, $includequotes=0, $allowdash=0)
Clean a string to use it as a file name.
dolChmod($filepath, $newmask='')
Change mod of a file.
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
getLocalTaxesFromRate($vatrate, $local, $buyer, $seller, $firstparamisid=0)
Get type and rate of localtaxes for a particular vat rate/country of a thirdparty.
if(!function_exists( 'dol_getprefix')) dol_include_once($relpath, $classname='')
Make an include_once using default root and alternate root if it fails.
getMultidirOutput($object, $module='', $forobject=0, $mode='output')
Return the full path of the directory where a module (or an object of a module) stores its files.
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...
dol_substr($string, $start, $length=null, $stringencoding='', $trunconbytes=0)
Make a substring.
make_substitutions($text, $substitutionarray, $outputlangs=null, $converttextinhtmlifnecessary=0)
Make substitution into a text string, replacing keys with vals from $substitutionarray (oldval=>newva...
dol_print_date($time, $format='', $tzoutput='auto', $outputlangs=null, $encodetooutput=false, $decorate=0)
Output date in a string format according to outputlangs (or langs if not defined).
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 a Dolibarr global constant string value.
isModEnabled($module)
Is Dolibarr module enabled.
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)
dol_escape_htmltag($stringtoescape, $keepb=0, $keepn=0, $noescapetags='', $escapeonlyhtmltags=0, $cleanalsojavascript=0)
Definition html.lib.php:181
pdf_watermark($pdf, $outputlangs, $h, $w, $unit, $text)
Add a draft watermark on PDF files.
Definition pdf.lib.php:1200
pdf_getFormat($outputlangs=null, $mode='setup')
Return array with format properties of default PDF format.
Definition pdf.lib.php:87
pdf_writeLinkedObjects($pdf, $object, $outputlangs, $posx, $posy, $w, $h, $align, $default_font_size)
Show linked objects for PDF generation.
Definition pdf.lib.php:1853
pdf_getPDFFontSize($outputlangs)
Return font size to use for PDF generation.
Definition pdf.lib.php:296
pdf_getHeightForLogo($logo, $url=false)
Return height to use for Logo onto PDF.
Definition pdf.lib.php:319
pdf_pagehead($pdf, $outputlangs, $page_height)
Show header of page for PDF generation.
Definition pdf.lib.php:792
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:1466
pdf_getPDFFont($outputlangs)
Return font name to use for PDF generation.
Definition pdf.lib.php:275
pdf_truncate_text($pdf, $text, $max_width, $ellipsis_pos=1, $ellipsis="\u{2026}")
Truncates text to fit a specified width in TCPDF, with configurable ellipsis position.
Definition pdf.lib.php:3437
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:481
pdf_getlineqty($object, $i, $outputlangs, $hidedetails=0)
Return line quantity.
Definition pdf.lib.php:2610
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:1180
pdf_getInstance($format='', $metric='mm', $pagetype='P')
Return a PDF instance object.
Definition pdf.lib.php:129
pdfBuildThirdpartyName($thirdparty, Translate $outputlangs, $includealias=0)
Returns the name of the thirdparty.
Definition pdf.lib.php:436
$conf db name
Only used if Module[ID]Name translation string is not found.
Definition repair.php:140