pdf_sponge::_tableau()   B
last analyzed

Complexity

Conditions 10

Size

Total Lines 50
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 10
eloc 26
nop 9
dl 0
loc 50
rs 7.6666
c 0
b 0
f 0

How to fix   Complexity    Many Parameters   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
/* Copyright (C) 2004-2014  Laurent Destailleur         <[email protected]>
4
 * Copyright (C) 2005-2012  Regis Houssin               <[email protected]>
5
 * Copyright (C) 2008       Raphael Bertrand            <[email protected]>
6
 * Copyright (C) 2010-2014  Juanjo Menent               <[email protected]>
7
 * Copyright (C) 2012       Christophe Battarel         <[email protected]>
8
 * Copyright (C) 2012       Cédric Salvador             <[email protected]>
9
 * Copyright (C) 2012-2014  Raphaël Doursenaud          <[email protected]>
10
 * Copyright (C) 2015       Marcos García               <[email protected]>
11
 * Copyright (C) 2017       Ferran Marcet               <[email protected]>
12
 * Copyright (C) 2018-2024  Frédéric France             <[email protected]>
13
 * Copyright (C) 2022		Anthony Berton			    <[email protected]>
14
 * Copyright (C) 2022       Alexandre Spangaro          <[email protected]>
15
 * Copyright (C) 2024		MDW						    <[email protected]>
16
 * Copyright (C) 2024       Rafael San José             <[email protected]>
17
 *
18
 * This program is free software; you can redistribute it and/or modify
19
 * it under the terms of the GNU General Public License as published by
20
 * the Free Software Foundation; either version 3 of the License, or
21
 * (at your option) any later version.
22
 *
23
 * This program is distributed in the hope that it will be useful,
24
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
25
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
26
 * GNU General Public License for more details.
27
 *
28
 * You should have received a copy of the GNU General Public License
29
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
30
 * or see https://www.gnu.org/
31
 */
32
33
use Dolibarr\Code\Compta\Classes\Account;
34
use Dolibarr\Code\Compta\Classes\Facture;
35
use Dolibarr\Code\Core\Classes\HookManager;
36
use Dolibarr\Code\Core\Classes\Translate;
37
use Dolibarr\Code\Facture\Classes\ModelePDFFactures;
38
use Dolibarr\Code\Product\Classes\Product;
39
use Dolibarr\Code\Societe\Classes\CompanyBankAccount;
40
use Dolibarr\Code\Societe\Classes\Societe;
41
use Dolibarr\Code\User\Classes\User;
42
use Dolibarr\Lib\ViewMain;
43
44
/**
45
 *  \file       htdocs/core/modules/facture/doc/pdf_sponge.modules.php
46
 *  \ingroup    invoice
47
 *  \brief      File of class to generate customers invoices from sponge model
48
 */
49
50
require_once constant('DOL_DOCUMENT_ROOT') . '/core/lib/company.lib.php';
51
require_once constant('DOL_DOCUMENT_ROOT') . '/core/lib/functions2.lib.php';
52
require_once constant('DOL_DOCUMENT_ROOT') . '/core/lib/pdf.lib.php';
53
54
/**
55
 *  Class to manage PDF invoice template sponge
56
 */
57
class pdf_sponge extends ModelePDFFactures
58
{
59
    /**
60
     * @var DoliDB Database handler
61
     */
62
    public $db;
63
64
    /**
65
     * @var string model name
66
     */
67
    public $name;
68
69
    /**
70
     * @var string model description (short text)
71
     */
72
    public $description;
73
74
    /**
75
     * @var int     Save the name of generated file as the main doc when generating a doc with this template
76
     */
77
    public $update_main_doc_field;
78
79
    /**
80
     * @var string document type
81
     */
82
    public $type;
83
84
    /**
85
     * Dolibarr version of the loaded document
86
     * @var string
87
     */
88
    public $version = 'dolibarr';
89
90
    /**
91
     * @var int heightforinfotot
92
     */
93
    public $heightforinfotot;
94
95
    /**
96
     * @var int heightforfreetext
97
     */
98
    public $heightforfreetext;
99
100
    /**
101
     * @var int heightforfooter
102
     */
103
    public $heightforfooter;
104
105
    /**
106
     * @var int tab_top
107
     */
108
    public $tab_top;
109
110
    /**
111
     * @var int tab_top_newpage
112
     */
113
    public $tab_top_newpage;
114
115
    /**
116
     * @var bool Situation invoice type
117
     */
118
    public $situationinvoice;
119
120
121
    /**
122
     * @var array<string,array{rank:int,width:float|int,title:array{textkey:string,label:string,align:string,padding:array{0:float,1:float,2:float,3:float}},content:array{align:string,padding:array{0:float,1:float,2:float,3:float}}}>   Array of columns
123
     */
124
    public $cols;
125
126
    /**
127
     * @var int Category of operation
128
     */
129
    public $categoryOfOperation = -1; // unknown by default
130
131
132
    /**
133
     *  Constructor
134
     *
135
     * @param DoliDB $db Database handler
136
     */
137
    public function __construct($db)
138
    {
139
        global $conf, $langs, $mysoc;
140
141
        // Translations
142
        $langs->loadLangs(array("main", "bills"));
143
144
        $this->db = $db;
145
        $this->name = "sponge";
146
        $this->description = $langs->trans('PDFSpongeDescription');
147
        $this->update_main_doc_field = 1; // Save the name of generated file as the main doc when generating a doc with this template
148
149
        // Dimension page
150
        $this->type = 'pdf';
151
        $formatarray = pdf_getFormat();
152
        $this->page_largeur = $formatarray['width'];
153
        $this->page_hauteur = $formatarray['height'];
154
        $this->format = array($this->page_largeur, $this->page_hauteur);
155
        $this->marge_gauche = getDolGlobalInt('MAIN_PDF_MARGIN_LEFT', 10);
156
        $this->marge_droite = getDolGlobalInt('MAIN_PDF_MARGIN_RIGHT', 10);
157
        $this->marge_haute = getDolGlobalInt('MAIN_PDF_MARGIN_TOP', 10);
158
        $this->marge_basse = getDolGlobalInt('MAIN_PDF_MARGIN_BOTTOM', 10);
159
160
        $this->option_logo = 1; // Display logo
161
        $this->option_tva = 1; // Manage the vat option FACTURE_TVAOPTION
162
        $this->option_modereg = 1; // Display payment mode
163
        $this->option_condreg = 1; // Display payment terms
164
        $this->option_multilang = 1; // Available in several languages
165
        $this->option_escompte = 1; // Displays if there has been a discount
166
        $this->option_credit_note = 1; // Support credit notes
167
        $this->option_freetext = 1; // Support add of a personalised text
168
        $this->option_draft_watermark = 1; // Support add of a watermark on drafts
169
        $this->watermark = '';
170
171
        // Get source company
172
        $this->emetteur = $mysoc;
173
        if (empty($this->emetteur->country_code)) {
174
            $this->emetteur->country_code = substr($langs->defaultlang, -2); // By default, if was not defined
175
        }
176
177
        // Define position of columns
178
        $this->posxdesc = $this->marge_gauche + 1; // used for notes and other stuff
179
180
181
        $this->tabTitleHeight = 5; // default height
182
183
        //  Use new system for position of columns, view  $this->defineColumnField()
184
185
        $this->tva = array();
186
        $this->tva_array = array();
187
        $this->localtax1 = array();
188
        $this->localtax2 = array();
189
        $this->atleastoneratenotnull = 0;
190
        $this->atleastonediscount = 0;
191
        $this->situationinvoice = false;
192
    }
193
194
195
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
196
197
    /**
198
     *  Function to build pdf onto disk
199
     *
200
     * @param Facture $object Object to generate
201
     * @param Translate $outputlangs Lang output object
202
     * @param string $srctemplatepath Full path of source filename for generator using a template file
203
     * @param int $hidedetails Do not show line details
204
     * @param int $hidedesc Do not show desc
205
     * @param int $hideref Do not show ref
206
     * @return     int                             1=OK, 0=KO
207
     */
208
    public function write_file($object, $outputlangs, $srctemplatepath = '', $hidedetails = 0, $hidedesc = 0, $hideref = 0)
209
    {
210
        // phpcs:enable
211
        global $user, $langs, $conf, $mysoc, $db, $hookmanager, $nblines;
212
213
        dol_syslog("write_file outputlangs->defaultlang=" . (is_object($outputlangs) ? $outputlangs->defaultlang : 'null'));
214
215
        if (!is_object($outputlangs)) {
216
            $outputlangs = $langs;
217
        }
218
        // For backward compatibility with FPDF, force output charset to ISO, because FPDF expect text to be encoded in ISO
219
        if (getDolGlobalString('MAIN_USE_FPDF')) {
220
            $outputlangs->charset_output = 'ISO-8859-1';
221
        }
222
223
        // Load translation files required by the page
224
        $outputlangs->loadLangs(array("main", "bills", "products", "dict", "companies"));
225
226
        global $outputlangsbis;
227
        $outputlangsbis = null;
228
        if (getDolGlobalString('PDF_USE_ALSO_LANGUAGE_CODE') && $outputlangs->defaultlang != getDolGlobalString('PDF_USE_ALSO_LANGUAGE_CODE')) {
229
            $outputlangsbis = new Translate('', $conf);
230
            $outputlangsbis->setDefaultLang(getDolGlobalString('PDF_USE_ALSO_LANGUAGE_CODE'));
231
            $outputlangsbis->loadLangs(array("main", "bills", "products", "dict", "companies"));
232
        }
233
234
        // Show Draft Watermark
235
        if ($object->status == $object::STATUS_DRAFT && (getDolGlobalString('FACTURE_DRAFT_WATERMARK'))) {
236
            $this->watermark = getDolGlobalString('FACTURE_DRAFT_WATERMARK');
237
        }
238
239
        $nblines = count($object->lines);
240
241
        $hidetop = 0;
242
        if (getDolGlobalString('MAIN_PDF_DISABLE_COL_HEAD_TITLE')) {
243
            $hidetop = getDolGlobalString('MAIN_PDF_DISABLE_COL_HEAD_TITLE');
244
        }
245
246
        // Loop on each lines to detect if there is at least one image to show
247
        $realpatharray = array();
248
        $this->atleastonephoto = false;
249
        if (getDolGlobalString('MAIN_GENERATE_INVOICES_WITH_PICTURE')) {
250
            $objphoto = new Product($this->db);
251
252
            for ($i = 0; $i < $nblines; $i++) {
253
                if (empty($object->lines[$i]->fk_product)) {
254
                    continue;
255
                }
256
257
                $objphoto->fetch($object->lines[$i]->fk_product);
258
                //var_dump($objphoto->ref);exit;
259
                $pdir = array();
260
                if (getDolGlobalInt('PRODUCT_USE_OLD_PATH_FOR_PHOTO')) {
261
                    $pdir[0] = get_exdir($objphoto->id, 2, 0, 0, $objphoto, 'product') . $objphoto->id . "/photos/";
262
                    $pdir[1] = get_exdir(0, 0, 0, 0, $objphoto, 'product') . dol_sanitizeFileName($objphoto->ref) . '/';
263
                } else {
264
                    $pdir[0] = get_exdir(0, 0, 0, 0, $objphoto, 'product'); // default
265
                    $pdir[1] = get_exdir($objphoto->id, 2, 0, 0, $objphoto, 'product') . $objphoto->id . "/photos/"; // alternative
266
                }
267
268
                $arephoto = false;
269
                foreach ($pdir as $midir) {
270
                    if (!$arephoto) {
271
                        if ($conf->entity != $objphoto->entity) {
272
                            $dir = $conf->product->multidir_output[$objphoto->entity] . '/' . $midir; //Check repertories of current entities
273
                        } else {
274
                            $dir = $conf->product->dir_output . '/' . $midir; //Check repertory of the current product
275
                        }
276
277
                        foreach ($objphoto->liste_photos($dir, 1) as $key => $obj) {
278
                            if (!getDolGlobalInt('CAT_HIGH_QUALITY_IMAGES')) {      // If CAT_HIGH_QUALITY_IMAGES not defined, we use thumb if defined and then original photo
279
                                if ($obj['photo_vignette']) {
280
                                    $filename = $obj['photo_vignette'];
281
                                } else {
282
                                    $filename = $obj['photo'];
283
                                }
284
                            } else {
285
                                $filename = $obj['photo'];
286
                            }
287
288
                            $realpath = $dir . $filename;
289
                            $arephoto = true;
290
                            $this->atleastonephoto = true;
291
                        }
292
                    }
293
                }
294
295
                if ($realpath && $arephoto) {
296
                    $realpatharray[$i] = $realpath;
297
                }
298
            }
299
        }
300
301
        //if (count($realpatharray) == 0) $this->posxpicture=$this->posxtva;
302
303
        if ($conf->facture->multidir_output[$conf->entity]) {
304
            $object->fetch_thirdparty();
305
306
            $deja_regle = $object->getSommePaiement((isModEnabled("multicurrency") && $object->multicurrency_tx != 1) ? 1 : 0);
307
            $amount_credit_notes_included = $object->getSumCreditNotesUsed((isModEnabled("multicurrency") && $object->multicurrency_tx != 1) ? 1 : 0);
308
            $amount_deposits_included = $object->getSumDepositsUsed((isModEnabled("multicurrency") && $object->multicurrency_tx != 1) ? 1 : 0);
309
310
            // Definition of $dir and $file
311
            if ($object->specimen) {
312
                $dir = $conf->facture->multidir_output[$conf->entity];
313
                $file = $dir . "/SPECIMEN.pdf";
314
            } else {
315
                $objectref = dol_sanitizeFileName($object->ref);
316
                $dir = $conf->facture->multidir_output[$object->entity] . "/" . $objectref;
317
                $file = $dir . "/" . $objectref . ".pdf";
318
            }
319
            if (!file_exists($dir)) {
320
                if (dol_mkdir($dir) < 0) {
321
                    $this->error = $langs->transnoentities("ErrorCanNotCreateDir", $dir);
322
                    return 0;
323
                }
324
            }
325
326
            if (file_exists($dir)) {
327
                // Add pdfgeneration hook
328
                if (!is_object($hookmanager)) {
329
                    $hookmanager = new HookManager($this->db);
330
                }
331
                $hookmanager->initHooks(array('pdfgeneration'));
332
                $parameters = array('file' => $file, 'object' => $object, 'outputlangs' => $outputlangs);
333
                global $action;
334
                $reshook = $hookmanager->executeHooks('beforePDFCreation', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
335
336
                // Set nblines with the new facture lines content after hook
337
                $nblines = count($object->lines);
338
                $nbpayments = count($object->getListOfPayments());
339
340
                // Create pdf instance
341
                $pdf = pdf_getInstance($this->format);
342
                $default_font_size = pdf_getPDFFontSize($outputlangs); // Must be after pdf_getInstance
343
                $pdf->SetAutoPageBreak(1, 0);
344
345
                $this->heightforinfotot = 50 + (4 * $nbpayments); // Height reserved to output the info and total part and payment part
346
                $this->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
347
                $this->heightforfooter = $this->marge_basse + (!getDolGlobalString('MAIN_GENERATE_DOCUMENTS_SHOW_FOOT_DETAILS') ? 12 : 22); // Height reserved to output the footer (value include bottom margin)
348
349
                $heightforqrinvoice = $heightforqrinvoice_firstpage = 0;
350
                if (getDolGlobalString('INVOICE_ADD_SWISS_QR_CODE') == 'bottom') {
351
                    if ($this->getHeightForQRInvoice(1, $object, $langs) > 0) {
352
                        // Shrink infotot to a base 30
353
                        $this->heightforinfotot = 30 + (4 * $nbpayments); // Height reserved to output the info and total part and payment part
354
                    }
355
                }
356
357
                if (class_exists('TCPDF')) {
358
                    $pdf->setPrintHeader(false);
359
                    $pdf->setPrintFooter(false);
360
                }
361
                $pdf->SetFont(pdf_getPDFFont($outputlangs));
362
363
                // Set path to the background PDF File
364
                if (getDolGlobalString('MAIN_ADD_PDF_BACKGROUND')) {
365
                    $logodir = $conf->mycompany->dir_output;
366
                    if (!empty($conf->mycompany->multidir_output[$object->entity])) {
367
                        $logodir = $conf->mycompany->multidir_output[$object->entity];
368
                    }
369
                    $pagecount = $pdf->setSourceFile($logodir . '/' . getDolGlobalString('MAIN_ADD_PDF_BACKGROUND'));
370
                    $tplidx = $pdf->importPage(1);
371
                }
372
373
                $pdf->Open();
374
                $pagenb = 0;
375
                $pdf->SetDrawColor(128, 128, 128);
376
377
                $pdf->SetTitle($outputlangs->convToOutputCharset($object->ref));
378
                $pdf->SetSubject($outputlangs->transnoentities("PdfInvoiceTitle"));
379
                $pdf->SetCreator("Dolibarr " . DOL_VERSION);
380
                $pdf->SetAuthor($mysoc->name . ($user->id > 0 ? ' - ' . $outputlangs->convToOutputCharset($user->getFullName($outputlangs)) : ''));
381
                $pdf->SetKeyWords($outputlangs->convToOutputCharset($object->ref) . " " . $outputlangs->transnoentities("PdfInvoiceTitle") . " " . $outputlangs->convToOutputCharset($object->thirdparty->name));
382
                if (getDolGlobalString('MAIN_DISABLE_PDF_COMPRESSION')) {
383
                    $pdf->SetCompression(false);
384
                }
385
386
                // Set certificate
387
                $cert = empty($user->conf->CERTIFICATE_CRT) ? '' : $user->conf->CERTIFICATE_CRT;
388
                $certprivate = empty($user->conf->CERTIFICATE_CRT_PRIVATE) ? '' : $user->conf->CERTIFICATE_CRT_PRIVATE;
389
                // If user has no certificate, we try to take the company one
390
                if (!$cert) {
391
                    $cert = getDolGlobalString('CERTIFICATE_CRT');
392
                }
393
                if (!$certprivate) {
394
                    $certprivate = getDolGlobalString('CERTIFICATE_CRT_PRIVATE');
395
                }
396
                // If a certificate is found
397
                if ($cert) {
398
                    $info = array(
399
                        'Name' => $this->emetteur->name,
400
                        'Location' => getCountry($this->emetteur->country_code, 0),
401
                        'Reason' => 'INVOICE',
402
                        'ContactInfo' => $this->emetteur->email
403
                    );
404
                    $pdf->setSignature($cert, $certprivate, $this->emetteur->name, '', 2, $info);
405
                }
406
407
                // @phan-suppress-next-line PhanPluginSuspiciousParamOrder
408
                $pdf->SetMargins($this->marge_gauche, $this->marge_haute, $this->marge_droite); // Left, Top, Right
409
410
                // Set $this->atleastonediscount if you have at least one discount
411
                // and determine category of operation
412
                $categoryOfOperation = 0;
413
                $nbProduct = 0;
414
                $nbService = 0;
415
                for ($i = 0; $i < $nblines; $i++) {
416
                    if ($object->lines[$i]->remise_percent) {
417
                        $this->atleastonediscount++;
418
                    }
419
420
                    // determine category of operation
421
                    if ($categoryOfOperation < 2) {
422
                        $lineProductType = $object->lines[$i]->product_type;
423
                        if ($lineProductType == Product::TYPE_PRODUCT) {
424
                            $nbProduct++;
425
                        } elseif ($lineProductType == Product::TYPE_SERVICE) {
426
                            $nbService++;
427
                        }
428
                        if ($nbProduct > 0 && $nbService > 0) {
429
                            // mixed products and services
430
                            $categoryOfOperation = 2;
431
                        }
432
                    }
433
                }
434
                // determine category of operation
435
                if ($categoryOfOperation <= 0) {
436
                    // only services
437
                    if ($nbProduct == 0 && $nbService > 0) {
438
                        $categoryOfOperation = 1;
439
                    }
440
                }
441
                $this->categoryOfOperation = $categoryOfOperation;
442
443
                // Situation invoice handling
444
                if ($object->situation_cycle_ref) {
445
                    $this->situationinvoice = true;
446
                }
447
448
                // New page
449
                $pdf->AddPage();
450
                if (!empty($tplidx)) {
451
                    $pdf->useTemplate($tplidx);
452
                }
453
                $pagenb++;
454
455
                // Output header (logo, ref and address blocks). This is first call for first page.
456
                $pagehead = $this->_pagehead($pdf, $object, 1, $outputlangs, $outputlangsbis);
457
                $top_shift = $pagehead['top_shift'];
458
                $shipp_shift = $pagehead['shipp_shift'];
459
                $pdf->SetFont('', '', $default_font_size - 1);
460
                $pdf->MultiCell(0, 3, ''); // Set interline to 3
461
                $pdf->SetTextColor(0, 0, 0);
462
463
                // $pdf->GetY() here can't be used. It is bottom of the second address box but first one may be higher
464
465
                // $this->tab_top is y where we must continue content (90 = 42 + 48: 42 is height of logo and ref, 48 is address blocks)
466
                $this->tab_top = 90 + $top_shift + $shipp_shift;        // top_shift is an addition for linked objects or addons (0 in most cases)
467
                $this->tab_top_newpage = (!getDolGlobalInt('MAIN_PDF_DONOTREPEAT_HEAD') ? 42 + $top_shift : 10);
468
469
                // You can add more thing under header here, if you increase $extra_under_address_shift too.
470
                $extra_under_address_shift = 0;
471
                $qrcodestring = '';
472
                if (getDolGlobalString('INVOICE_ADD_ZATCA_QR_CODE')) {
473
                    $qrcodestring = $object->buildZATCAQRString();
474
                } elseif (getDolGlobalString('INVOICE_ADD_SWISS_QR_CODE') == '1' && (empty($object->mode_reglement_code) || $object->mode_reglement_code == 'VIR')) {
475
                    if ($object->fk_account > 0 || $object->fk_bank > 0 || getDolGlobalInt('FACTURE_RIB_NUMBER')) {
476
                        $qrcodestring = $object->buildSwitzerlandQRString();
477
                    }
478
                } elseif (getDolGlobalString('INVOICE_ADD_EPC_QR_CODE') == '1' && (empty($object->mode_reglement_code) || $object->mode_reglement_code == 'VIR')) {
479
                    if ($object->fk_account > 0 || $object->fk_bank > 0 || getDolGlobalInt('FACTURE_RIB_NUMBER')) {
480
                        $qrcodestring = $object->buildEPCQrCodeString();
481
                    }
482
                }
483
484
                if ($qrcodestring) {
485
                    $qrcodecolor = array('25', '25', '25');
486
                    // set style for QR-code
487
                    $styleQr = array(
488
                        'border' => false,
489
                        'padding' => 0,
490
                        'fgcolor' => $qrcodecolor,
491
                        'bgcolor' => false, //array(255,255,255)
492
                        'module_width' => 1, // width of a single module in points
493
                        'module_height' => 1 // height of a single module in points
494
                    );
495
                    $pdf->write2DBarcode($qrcodestring, 'QRCODE,M', $this->marge_gauche, $this->tab_top - 5, 25, 25, $styleQr, 'N');
496
497
                    if (getDolGlobalString('INVOICE_ADD_EPC_QR_CODE') == '1' && (empty($object->mode_reglement_code) || $object->mode_reglement_code == 'VIR')) {
498
                        if ($object->fk_account > 0 || $object->fk_bank > 0 || getDolGlobalInt('FACTURE_RIB_NUMBER')) {
499
                            $pdf->SetXY($this->marge_gauche + 30, $pdf->GetY() - 15);
500
                            $pdf->SetFont('', '', $default_font_size - 4);
501
                            $pdf->MultiCell(40, 3, $langs->transnoentitiesnoconv("INVOICE_ADD_EPC_QR_CODEPay"), 0, 'L', 0);
502
                        }
503
                    }
504
505
                    $extra_under_address_shift += 25;
506
                }
507
508
                // Call hook printUnderHeaderPDFline
509
                $parameters = array(
510
                    'object' => $object,
511
                    'i' => $i,
512
                    'pdf' => &$pdf,
513
                    'outputlangs' => $outputlangs,
514
                    'hidedetails' => $hidedetails
515
                );
516
                $reshook = $hookmanager->executeHooks('printUnderHeaderPDFline', $parameters, $this); // Note that $object may have been modified by hook
517
                if (!empty($hookmanager->resArray['extra_under_address_shift'])) {
518
                    $extra_under_address_shift += $hookmanager->resArray['extra_under_address_shift'];
519
                }
520
521
                $this->tab_top += $extra_under_address_shift;
522
                $this->tab_top_newpage += 0;
523
524
525
                // Define height of table for lines (for first page)
526
                $tab_height = $this->page_hauteur - $this->tab_top - $this->heightforfooter - $this->heightforfreetext - $this->getHeightForQRInvoice(1, $object, $langs);
527
528
                $nexY = $this->tab_top - 1;
529
530
                // Incoterm
531
                $height_incoterms = 0;
532
                if (isModEnabled('incoterm')) {
533
                    $desc_incoterms = $object->getIncotermsForPDF();
534
                    if ($desc_incoterms) {
535
                        $this->tab_top -= 2;
536
537
                        $pdf->SetFont('', '', $default_font_size - 1);
538
                        $pdf->writeHTMLCell(190, 3, $this->posxdesc - 1, $this->tab_top - 1, dol_htmlentitiesbr($desc_incoterms), 0, 1);
539
                        $nexY = max($pdf->GetY(), $nexY);
540
                        $height_incoterms = $nexY - $this->tab_top;
541
542
                        // Rect takes a length in 3rd parameter
543
                        $pdf->SetDrawColor(192, 192, 192);
544
                        $pdf->Rect($this->marge_gauche, $this->tab_top - 1, $this->page_largeur - $this->marge_gauche - $this->marge_droite, $height_incoterms + 1);
545
546
                        $this->tab_top = $nexY + 6;
547
                        $height_incoterms += 4;
548
                    }
549
                }
550
551
                // Displays notes. Here we are still on code eecuted only for the first page.
552
                $notetoshow = empty($object->note_public) ? '' : $object->note_public;
553
                if (getDolGlobalString('MAIN_ADD_SALE_REP_SIGNATURE_IN_NOTE')) {
554
                    // Get first sale rep
555
                    if (is_object($object->thirdparty)) {
556
                        $salereparray = $object->thirdparty->getSalesRepresentatives($user);
557
                        $salerepobj = new User($this->db);
558
                        $salerepobj->fetch($salereparray[0]['id']);
559
                        if (!empty($salerepobj->signature)) {
560
                            $notetoshow = dol_concatdesc($notetoshow, $salerepobj->signature);
561
                        }
562
                    }
563
                }
564
565
                // Extrafields in note
566
                $extranote = $this->getExtrafieldsInHtml($object, $outputlangs);
567
                if (!empty($extranote)) {
568
                    $notetoshow = dol_concatdesc($notetoshow, $extranote);
569
                }
570
571
                $pagenb = $pdf->getPage();
572
                if ($notetoshow) {
573
                    $this->tab_top -= 2;
574
575
                    $tab_width = $this->page_largeur - $this->marge_gauche - $this->marge_droite;
576
                    $pageposbeforenote = $pagenb;
577
578
                    $substitutionarray = pdf_getSubstitutionArray($outputlangs, null, $object);
579
                    complete_substitutions_array($substitutionarray, $outputlangs, $object);
580
                    $notetoshow = make_substitutions($notetoshow, $substitutionarray, $outputlangs);
581
                    $notetoshow = convertBackOfficeMediasLinksToPublicLinks($notetoshow);
582
583
                    $pdf->startTransaction();
584
585
                    $pdf->SetFont('', '', $default_font_size - 1);
586
                    $pdf->writeHTMLCell(190, 3, $this->posxdesc - 1, $this->tab_top, dol_htmlentitiesbr($notetoshow), 0, 1);
587
                    // Description
588
                    $pageposafternote = $pdf->getPage();
589
                    $posyafter = $pdf->GetY();
590
591
                    if ($pageposafternote > $pageposbeforenote) {
592
                        $pdf->rollbackTransaction(true);
593
594
                        // prepare pages to receive notes
595
                        while ($pagenb < $pageposafternote) {
596
                            $pdf->AddPage();
597
                            $pagenb++;
598
                            if (!empty($tplidx)) {
599
                                $pdf->useTemplate($tplidx);
600
                            }
601
                            if (!getDolGlobalInt('MAIN_PDF_DONOTREPEAT_HEAD')) {
602
                                $this->_pagehead($pdf, $object, 0, $outputlangs, $outputlangsbis);
603
                            }
604
                            $pdf->setTopMargin($this->tab_top_newpage);
605
                            // The only function to edit the bottom margin of current page to set it.
606
                            $pdf->setPageOrientation('', 1, $this->heightforfooter + $this->heightforfreetext);
607
                        }
608
609
                        // back to start
610
                        $pdf->setPage($pageposbeforenote);
611
                        $pdf->setPageOrientation('', 1, $this->heightforfooter + $this->heightforfreetext);
612
                        $pdf->SetFont('', '', $default_font_size - 1);
613
                        $pdf->writeHTMLCell(190, 3, $this->posxdesc - 1, $this->tab_top, dol_htmlentitiesbr($notetoshow), 0, 1);
614
                        $pageposafternote = $pdf->getPage();
615
616
                        $posyafter = $pdf->GetY();
617
618
                        if ($posyafter > ($this->page_hauteur - ($this->heightforfooter + $this->heightforfreetext + 20))) {    // There is no space left for total+free text
619
                            $pdf->AddPage('', '', true);
620
                            $pagenb++;
621
                            $pageposafternote++;
622
                            $pdf->setPage($pageposafternote);
623
                            $pdf->setTopMargin($this->tab_top_newpage);
624
                            // The only function to edit the bottom margin of current page to set it.
625
                            $pdf->setPageOrientation('', 1, $this->heightforfooter + $this->heightforfreetext);
626
                            //$posyafter = $this->tab_top_newpage;
627
                        }
628
629
630
                        // apply note frame to previous pages
631
                        $i = $pageposbeforenote;
632
                        while ($i < $pageposafternote) {
633
                            $pdf->setPage($i);
634
635
636
                            $pdf->SetDrawColor(128, 128, 128);
637
                            // Draw note frame
638
                            if ($i > $pageposbeforenote) {
639
                                $height_note = $this->page_hauteur - ($this->tab_top_newpage + $this->heightforfooter);
640
                                $pdf->Rect($this->marge_gauche, $this->tab_top_newpage - 1, $tab_width, $height_note + 1);
641
                            } else {
642
                                $height_note = $this->page_hauteur - ($this->tab_top + $this->heightforfooter);
643
                                $pdf->Rect($this->marge_gauche, $this->tab_top - 1, $tab_width, $height_note + 1);
644
                            }
645
646
                            // Add footer
647
                            $pdf->setPageOrientation('', 1, 0); // The only function to edit the bottom margin of current page to set it.
648
                            $this->_pagefoot($pdf, $object, $outputlangs, 1, $this->getHeightForQRInvoice($i, $object, $outputlangs));
649
650
                            $i++;
651
                        }
652
653
                        // apply note frame to last page
654
                        $pdf->setPage($pageposafternote);
655
                        if (!empty($tplidx)) {
656
                            $pdf->useTemplate($tplidx);
657
                        }
658
                        if (!getDolGlobalInt('MAIN_PDF_DONOTREPEAT_HEAD')) {
659
                            $this->_pagehead($pdf, $object, 0, $outputlangs, $outputlangsbis);
660
                        }
661
                        $height_note = $posyafter - $this->tab_top_newpage;
662
                        $pdf->Rect($this->marge_gauche, $this->tab_top_newpage - 1, $tab_width, $height_note + 1);
663
                    } else {
664
                        // No pagebreak
665
                        $pdf->commitTransaction();
666
                        $posyafter = $pdf->GetY();
667
                        $height_note = $posyafter - $this->tab_top;
668
                        $pdf->Rect($this->marge_gauche, $this->tab_top - 1, $tab_width, $height_note + 1);
669
670
671
                        if ($posyafter > ($this->page_hauteur - ($this->heightforfooter + $this->heightforfreetext + 20))) {
672
                            // not enough space, need to add page
673
                            $pdf->AddPage('', '', true);
674
                            $pagenb++;
675
                            $pageposafternote++;
676
                            $pdf->setPage($pageposafternote);
677
                            if (!empty($tplidx)) {
678
                                $pdf->useTemplate($tplidx);
679
                            }
680
                            if (!getDolGlobalInt('MAIN_PDF_DONOTREPEAT_HEAD')) {
681
                                $this->_pagehead($pdf, $object, 0, $outputlangs, $outputlangsbis);
682
                            }
683
684
                            $posyafter = $this->tab_top_newpage;
685
                        }
686
                    }
687
688
                    $tab_height = $tab_height - $height_note;
689
                    $this->tab_top = $posyafter + 6;
690
                } else {
691
                    $height_note = 0;
692
                }
693
694
                // Use new auto column system
695
                $this->prepareArrayColumnField($object, $outputlangs, $hidedetails, $hidedesc, $hideref);
696
697
                // Table simulation to know the height of the title line (this set this->tableTitleHeight)
698
                $pdf->startTransaction();
699
                $this->pdfTabTitles($pdf, $this->tab_top, $tab_height, $outputlangs, $hidetop);
700
                $pdf->rollbackTransaction(true);
701
702
                $nexY = $this->tab_top + $this->tabTitleHeight;
703
704
                // Loop on each lines
705
                $pageposbeforeprintlines = $pdf->getPage();
706
                $pagenb = $pageposbeforeprintlines;
707
                for ($i = 0; $i < $nblines; $i++) {
708
                    $curY = $nexY;
709
                    $pdf->SetFont('', '', $default_font_size - 1); // Into loop to work with multipage
710
                    $pdf->SetTextColor(0, 0, 0);
711
712
                    // Define size of image if we need it
713
                    $imglinesize = array();
714
                    if (!empty($realpatharray[$i])) {
715
                        $imglinesize = pdf_getSizeForImage($realpatharray[$i]);
716
                    }
717
718
                    $pdf->setTopMargin($this->tab_top_newpage);
719
                    $page_bottom_margin = $this->heightforfooter + $this->heightforfreetext + $this->heightforinfotot + $this->getHeightForQRInvoice($pdf->getPage(), $object, $langs);
720
                    $pdf->setPageOrientation('', 1, $page_bottom_margin);
721
                    $pageposbefore = $pdf->getPage();
722
723
                    $showpricebeforepagebreak = 1;
724
                    $posYAfterImage = 0;
725
                    $posYAfterDescription = 0;
726
727
                    if ($this->getColumnStatus('position')) {
728
                        $this->printStdColumnContent($pdf, $curY, 'position', $i + 1);
729
                    }
730
731
                    if ($this->getColumnStatus('photo')) {
732
                        // We start with Photo of product line
733
                        if (isset($imglinesize['width']) && isset($imglinesize['height']) && ($curY + $imglinesize['height']) > ($this->page_hauteur - $page_bottom_margin)) {  // If photo too high, we moved completely on new page
734
                            $pdf->AddPage('', '', true);
735
                            if (!empty($tplidx)) {
736
                                $pdf->useTemplate($tplidx);
737
                            }
738
                            $pdf->setPage($pageposbefore + 1);
739
740
                            $curY = $this->tab_top_newpage;
741
742
                            // Allows data in the first page if description is long enough to break in multiples pages
743
                            if (getDolGlobalString('MAIN_PDF_DATA_ON_FIRST_PAGE')) {
744
                                $showpricebeforepagebreak = 1;
745
                            } else {
746
                                $showpricebeforepagebreak = 0;
747
                            }
748
                        }
749
750
                        if (!empty($this->cols['photo']) && isset($imglinesize['width']) && isset($imglinesize['height'])) {
751
                            $pdf->Image($realpatharray[$i], $this->getColumnContentXStart('photo'), $curY + 1, $imglinesize['width'], $imglinesize['height'], '', '', '', 2, 300); // Use 300 dpi
752
                            // $pdf->Image does not increase value return by getY, so we save it manually
753
                            $posYAfterImage = $curY + $imglinesize['height'];
754
                        }
755
                    }
756
757
                    // Description of product line
758
                    if ($this->getColumnStatus('desc')) {
759
                        $pdf->startTransaction();
760
761
                        $this->printColDescContent($pdf, $curY, 'desc', $object, $i, $outputlangs, $hideref, $hidedesc);
762
                        $pageposafter = $pdf->getPage();
763
764
                        if ($pageposafter > $pageposbefore) {   // There is a pagebreak
765
                            $pdf->rollbackTransaction(true);
766
                            $pageposafter = $pageposbefore;
767
                            $pdf->setPageOrientation('', 1, $this->heightforfooter); // The only function to edit the bottom margin of current page to set it.
768
769
                            $this->printColDescContent($pdf, $curY, 'desc', $object, $i, $outputlangs, $hideref, $hidedesc);
770
771
                            $pageposafter = $pdf->getPage();
772
                            $posyafter = $pdf->GetY();
773
                            //var_dump($posyafter); var_dump(($this->page_hauteur - ($this->heightforfooter+$this->heightforfreetext+$this->heightforinfotot))); exit;
774
                            if ($posyafter > ($this->page_hauteur - $page_bottom_margin)) { // There is no space left for total+free text
775
                                if ($i == ($nblines - 1)) { // No more lines, and no space left to show total, so we create a new page
776
                                    $pdf->AddPage('', '', true);
777
                                    if (!empty($tplidx)) {
778
                                        $pdf->useTemplate($tplidx);
779
                                    }
780
                                    $pdf->setPage($pageposafter + 1);
781
                                }
782
                            } else {
783
                                // We found a page break
784
                                // Allows data in the first page if description is long enough to break in multiples pages
785
                                if (getDolGlobalString('MAIN_PDF_DATA_ON_FIRST_PAGE')) {
786
                                    $showpricebeforepagebreak = 1;
787
                                } else {
788
                                    $showpricebeforepagebreak = 0;
789
                                }
790
                            }
791
                        } else { // No pagebreak
792
                            $pdf->commitTransaction();
793
                        }
794
                        $posYAfterDescription = $pdf->GetY();
795
                    }
796
797
                    $nexY = max($pdf->GetY(), $posYAfterImage, $posYAfterDescription);
798
799
                    $pageposafter = $pdf->getPage();
800
                    $pdf->setPage($pageposbefore);
801
                    $pdf->setTopMargin($this->marge_haute);
802
                    $pdf->setPageOrientation('', 1, 0); // The only function to edit the bottom margin of current page to set it.
803
804
                    // We suppose that a too long description or photo were moved completely on next page
805
                    if ($pageposafter > $pageposbefore && empty($showpricebeforepagebreak)) {
806
                        $pdf->setPage($pageposafter);
807
                        $curY = $this->tab_top_newpage;
808
                    }
809
810
                    $pdf->SetFont('', '', $default_font_size - 1); // We reposition the default font
811
812
                    // VAT Rate
813
                    if ($this->getColumnStatus('vat')) {
814
                        $vat_rate = pdf_getlinevatrate($object, $i, $outputlangs, $hidedetails);
815
                        $this->printStdColumnContent($pdf, $curY, 'vat', $vat_rate);
816
                        $nexY = max($pdf->GetY(), $nexY);
817
                    }
818
819
                    // Unit price before discount
820
                    if ($this->getColumnStatus('subprice')) {
821
                        $up_excl_tax = pdf_getlineupexcltax($object, $i, $outputlangs, $hidedetails);
822
                        $this->printStdColumnContent($pdf, $curY, 'subprice', $up_excl_tax);
823
                        $nexY = max($pdf->GetY(), $nexY);
824
                    }
825
826
                    // Quantity
827
                    // Enough for 6 chars
828
                    if ($this->getColumnStatus('qty')) {
829
                        $qty = pdf_getlineqty($object, $i, $outputlangs, $hidedetails);
830
                        $this->printStdColumnContent($pdf, $curY, 'qty', $qty);
831
                        $nexY = max($pdf->GetY(), $nexY);
832
                    }
833
834
                    // Situation progress
835
                    if ($this->getColumnStatus('progress')) {
836
                        $progress = pdf_getlineprogress($object, $i, $outputlangs, $hidedetails);
837
                        $this->printStdColumnContent($pdf, $curY, 'progress', $progress);
838
                        $nexY = max($pdf->GetY(), $nexY);
839
                    }
840
841
                    // Unit
842
                    if ($this->getColumnStatus('unit')) {
843
                        $unit = pdf_getlineunit($object, $i, $outputlangs, $hidedetails);
844
                        $this->printStdColumnContent($pdf, $curY, 'unit', $unit);
845
                        $nexY = max($pdf->GetY(), $nexY);
846
                    }
847
848
                    // Discount on line
849
                    if ($this->getColumnStatus('discount') && $object->lines[$i]->remise_percent) {
850
                        $remise_percent = pdf_getlineremisepercent($object, $i, $outputlangs, $hidedetails);
851
                        $this->printStdColumnContent($pdf, $curY, 'discount', $remise_percent);
852
                        $nexY = max($pdf->GetY(), $nexY);
853
                    }
854
855
                    // Total excl tax line (HT)
856
                    if ($this->getColumnStatus('totalexcltax')) {
857
                        $total_excl_tax = pdf_getlinetotalexcltax($object, $i, $outputlangs, $hidedetails);
858
                        $this->printStdColumnContent($pdf, $curY, 'totalexcltax', $total_excl_tax);
859
                        $nexY = max($pdf->GetY(), $nexY);
860
                    }
861
862
                    // Total with tax line (TTC)
863
                    if ($this->getColumnStatus('totalincltax')) {
864
                        $total_incl_tax = pdf_getlinetotalwithtax($object, $i, $outputlangs, $hidedetails);
865
                        $this->printStdColumnContent($pdf, $curY, 'totalincltax', $total_incl_tax);
866
                        $nexY = max($pdf->GetY(), $nexY);
867
                    }
868
869
                    // Extrafields
870
                    if (!empty($object->lines[$i]->array_options)) {
871
                        foreach ($object->lines[$i]->array_options as $extrafieldColKey => $extrafieldValue) {
872
                            if ($this->getColumnStatus($extrafieldColKey)) {
873
                                $extrafieldValue = $this->getExtrafieldContent($object->lines[$i], $extrafieldColKey, $outputlangs);
874
                                $this->printStdColumnContent($pdf, $curY, $extrafieldColKey, $extrafieldValue);
875
                                $nexY = max($pdf->GetY(), $nexY);
876
                            }
877
                        }
878
                    }
879
880
881
                    $parameters = array(
882
                        'object' => $object,
883
                        'i' => $i,
884
                        'pdf' => & $pdf,
885
                        'curY' => & $curY,
886
                        'nexY' => & $nexY,
887
                        'outputlangs' => $outputlangs,
888
                        'hidedetails' => $hidedetails
889
                    );
890
                    $reshook = $hookmanager->executeHooks('printPDFline', $parameters, $this); // Note that $object may have been modified by hook
891
892
893
                    $sign = 1;
894
                    if (isset($object->type) && $object->type == 2 && getDolGlobalString('INVOICE_POSITIVE_CREDIT_NOTE')) {
895
                        $sign = -1;
896
                    }
897
                    // Collecte des totaux par valeur de tva dans $this->tva["taux"]=total_tva
898
                    $prev_progress = $object->lines[$i]->get_prev_progress($object->id);
899
                    if ($prev_progress > 0 && !empty($object->lines[$i]->situation_percent)) { // Compute progress from previous situation
900
                        if (isModEnabled("multicurrency") && $object->multicurrency_tx != 1) {
901
                            $tvaligne = $sign * $object->lines[$i]->multicurrency_total_tva * ($object->lines[$i]->situation_percent - $prev_progress) / $object->lines[$i]->situation_percent;
902
                        } else {
903
                            $tvaligne = $sign * $object->lines[$i]->total_tva * ($object->lines[$i]->situation_percent - $prev_progress) / $object->lines[$i]->situation_percent;
904
                        }
905
                    } else {
906
                        if (isModEnabled("multicurrency") && $object->multicurrency_tx != 1) {
907
                            $tvaligne = $sign * $object->lines[$i]->multicurrency_total_tva;
908
                        } else {
909
                            $tvaligne = $sign * $object->lines[$i]->total_tva;
910
                        }
911
                    }
912
913
                    $localtax1ligne = $object->lines[$i]->total_localtax1;
914
                    $localtax2ligne = $object->lines[$i]->total_localtax2;
915
                    $localtax1_rate = $object->lines[$i]->localtax1_tx;
916
                    $localtax2_rate = $object->lines[$i]->localtax2_tx;
917
                    $localtax1_type = $object->lines[$i]->localtax1_type;
918
                    $localtax2_type = $object->lines[$i]->localtax2_type;
919
920
                    // TODO remise_percent is an obsolete field for object parent
921
                    /*if ($object->remise_percent) {
922
                        $tvaligne -= ($tvaligne * $object->remise_percent) / 100;
923
                    }
924
                    if ($object->remise_percent) {
925
                        $localtax1ligne -= ($localtax1ligne * $object->remise_percent) / 100;
926
                    }
927
                    if ($object->remise_percent) {
928
                        $localtax2ligne -= ($localtax2ligne * $object->remise_percent) / 100;
929
                    }*/
930
931
                    $vatrate = (string)$object->lines[$i]->tva_tx;
932
933
                    // Retrieve type from database for backward compatibility with old records
934
                    if (
935
                        (!isset($localtax1_type) || $localtax1_type == '' || !isset($localtax2_type) || $localtax2_type == '') // if tax type not defined
936
                        && (!empty($localtax1_rate) || !empty($localtax2_rate))
937
                    ) { // and there is local tax
938
                        $localtaxtmp_array = getLocalTaxesFromRate($vatrate, 0, $object->thirdparty, $mysoc);
939
                        $localtax1_type = isset($localtaxtmp_array[0]) ? $localtaxtmp_array[0] : '';
940
                        $localtax2_type = isset($localtaxtmp_array[2]) ? $localtaxtmp_array[2] : '';
941
                    }
942
943
                    // retrieve global local tax
944
                    if ($localtax1_type && $localtax1ligne != 0) {
945
                        if (empty($this->localtax1[$localtax1_type][$localtax1_rate])) {
946
                            $this->localtax1[$localtax1_type][$localtax1_rate] = $localtax1ligne;
947
                        } else {
948
                            $this->localtax1[$localtax1_type][$localtax1_rate] += $localtax1ligne;
949
                        }
950
                    }
951
                    if ($localtax2_type && $localtax2ligne != 0) {
952
                        if (empty($this->localtax2[$localtax2_type][$localtax2_rate])) {
953
                            $this->localtax2[$localtax2_type][$localtax2_rate] = $localtax2ligne;
954
                        } else {
955
                            $this->localtax2[$localtax2_type][$localtax2_rate] += $localtax2ligne;
956
                        }
957
                    }
958
959
                    if (($object->lines[$i]->info_bits & 0x01) == 0x01) {
960
                        $vatrate .= '*';
961
                    }
962
963
                    // Fill $this->tva and $this->tva_array
964
                    if (!isset($this->tva[$vatrate])) {
965
                        $this->tva[$vatrate] = 0;
966
                    }
967
                    $this->tva[$vatrate] += $tvaligne;  // ->tva is abandoned, we use now ->tva_array that is more complete
968
                    $vatcode = $object->lines[$i]->vat_src_code;
969
                    if (empty($this->tva_array[$vatrate . ($vatcode ? ' (' . $vatcode . ')' : '')]['amount'])) {
970
                        $this->tva_array[$vatrate . ($vatcode ? ' (' . $vatcode . ')' : '')]['amount'] = 0;
971
                    }
972
                    $this->tva_array[$vatrate . ($vatcode ? ' (' . $vatcode . ')' : '')] = array('vatrate' => $vatrate, 'vatcode' => $vatcode, 'amount' => $this->tva_array[$vatrate . ($vatcode ? ' (' . $vatcode . ')' : '')]['amount'] + $tvaligne);
973
974
                    $nexY = max($nexY, $posYAfterImage);
975
976
                    // Add line
977
                    if (getDolGlobalString('MAIN_PDF_DASH_BETWEEN_LINES') && $i < ($nblines - 1)) {
978
                        $pdf->setPage($pageposafter);
979
                        $pdf->SetLineStyle(array('dash' => '1,1', 'color' => array(80, 80, 80)));
980
                        //$pdf->SetDrawColor(190,190,200);
981
                        $pdf->line($this->marge_gauche, $nexY, $this->page_largeur - $this->marge_droite, $nexY);
982
                        $pdf->SetLineStyle(array('dash' => 0));
983
                    }
984
985
                    // Detect if some page were added automatically and output _tableau for past pages
986
                    while ($pagenb < $pageposafter) {
987
                        $pdf->setPage($pagenb);
988
                        $heightforqrinvoice = $this->getHeightForQRInvoice($pagenb, $object, $langs);
989
                        if ($pagenb == $pageposbeforeprintlines) {
990
                            $this->_tableau($pdf, $this->tab_top, $this->page_hauteur - $this->tab_top - $this->heightforfooter - $heightforqrinvoice, 0, $outputlangs, $hidetop, 1, $object->multicurrency_code, $outputlangsbis);
991
                        } else {
992
                            $this->_tableau($pdf, $this->tab_top_newpage, $this->page_hauteur - $this->tab_top_newpage - $this->heightforfooter - $heightforqrinvoice, 0, $outputlangs, 1, 1, $object->multicurrency_code, $outputlangsbis);
993
                        }
994
                        $this->_pagefoot($pdf, $object, $outputlangs, 1, $this->getHeightForQRInvoice($pdf->getPage(), $object, $outputlangs));
995
                        $pagenb++;
996
                        $pdf->setPage($pagenb);
997
                        $pdf->setPageOrientation('', 1, 0); // The only function to edit the bottom margin of current page to set it.
998
                        if (!getDolGlobalInt('MAIN_PDF_DONOTREPEAT_HEAD')) {
999
                            $this->_pagehead($pdf, $object, 0, $outputlangs, $outputlangsbis);
1000
                        }
1001
                        if (!empty($tplidx)) {
1002
                            $pdf->useTemplate($tplidx);
1003
                        }
1004
                    }
1005
1006
                    if (isset($object->lines[$i + 1]->pagebreak) && $object->lines[$i + 1]->pagebreak) {
1007
                        $heightforqrinvoice = $this->getHeightForQRInvoice($pagenb, $object, $langs);
1008
                        if ($pagenb == $pageposafter) {
1009
                            $this->_tableau($pdf, $this->tab_top, $this->page_hauteur - $this->tab_top - $this->heightforfooter - $heightforqrinvoice, 0, $outputlangs, $hidetop, 1, $object->multicurrency_code, $outputlangsbis);
1010
                        } else {
1011
                            $this->_tableau($pdf, $this->tab_top_newpage, $this->page_hauteur - $this->tab_top_newpage - $this->heightforfooter - $heightforqrinvoice, 0, $outputlangs, 1, 1, $object->multicurrency_code, $outputlangsbis);
1012
                        }
1013
                        $this->_pagefoot($pdf, $object, $outputlangs, 1, $this->getHeightForQRInvoice($pdf->getPage(), $object, $outputlangs));
1014
                        // New page
1015
                        $pdf->AddPage();
1016
                        if (!empty($tplidx)) {
1017
                            $pdf->useTemplate($tplidx);
1018
                        }
1019
                        $pagenb++;
1020
                        if (!getDolGlobalInt('MAIN_PDF_DONOTREPEAT_HEAD')) {
1021
                            $this->_pagehead($pdf, $object, 0, $outputlangs, $outputlangsbis);
1022
                        }
1023
                    }
1024
                }
1025
1026
                // Show square
1027
                $heightforqrinvoice = $this->getHeightForQRInvoice($pagenb, $object, $langs);
1028
                if ($pagenb == $pageposbeforeprintlines) {
1029
                    $this->_tableau($pdf, $this->tab_top, $this->page_hauteur - $this->tab_top - $this->heightforinfotot - $this->heightforfreetext - $this->heightforfooter - $heightforqrinvoice, 0, $outputlangs, $hidetop, 0, $object->multicurrency_code, $outputlangsbis);
1030
                    $bottomlasttab = $this->page_hauteur - $this->heightforinfotot - $this->heightforfreetext - $this->heightforfooter - $heightforqrinvoice + 1;
1031
                } else {
1032
                    $this->_tableau($pdf, $this->tab_top_newpage, $this->page_hauteur - $this->tab_top_newpage - $this->heightforinfotot - $this->heightforfreetext - $this->heightforfooter - $heightforqrinvoice, 0, $outputlangs, 1, 0, $object->multicurrency_code, $outputlangsbis);
1033
                    $bottomlasttab = $this->page_hauteur - $this->heightforinfotot - $this->heightforfreetext - $this->heightforfooter - $heightforqrinvoice + 1;
1034
                }
1035
1036
                // Display infos area
1037
                $posy = $this->drawInfoTable($pdf, $object, $bottomlasttab, $outputlangs, $outputlangsbis);
1038
1039
                // Display total zone
1040
                $posy = $this->drawTotalTable($pdf, $object, $deja_regle, $bottomlasttab, $outputlangs, $outputlangsbis);
1041
1042
                // Display payment area
1043
                if (($deja_regle || $amount_credit_notes_included || $amount_deposits_included) && !getDolGlobalString('INVOICE_NO_PAYMENT_DETAILS')) {
1044
                    $posy = $this->drawPaymentsTable($pdf, $object, $posy, $outputlangs);
1045
                }
1046
1047
                // Pagefoot
1048
                $this->_pagefoot($pdf, $object, $outputlangs, 0, $this->getHeightForQRInvoice($pdf->getPage(), $object, $langs));
1049
                if (method_exists($pdf, 'AliasNbPages')) {
1050
                    $pdf->AliasNbPages();
1051
                }
1052
1053
                if (getDolGlobalString('INVOICE_ADD_SWISS_QR_CODE') == 'bottom') {
1054
                    $this->addBottomQRInvoice($pdf, $object, $outputlangs);
1055
                }
1056
                $pdf->Close();
1057
1058
                $pdf->Output($file, 'F');
1059
1060
                // Add pdfgeneration hook
1061
                $hookmanager->initHooks(array('pdfgeneration'));
1062
                $parameters = array('file' => $file, 'object' => $object, 'outputlangs' => $outputlangs);
1063
                global $action;
1064
                $reshook = $hookmanager->executeHooks('afterPDFCreation', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1065
                if ($reshook < 0) {
1066
                    $this->error = $hookmanager->error;
1067
                    $this->errors = $hookmanager->errors;
1068
                }
1069
1070
                dolChmod($file);
1071
1072
                $this->result = array('fullpath' => $file);
1073
1074
                return 1; // No error
1075
            } else {
1076
                $this->error = $langs->transnoentities("ErrorCanNotCreateDir", $dir);
1077
                return 0;
1078
            }
1079
        } else {
1080
            $this->error = $langs->transnoentities("ErrorConstantNotDefined", "FAC_OUTPUTDIR");
1081
            return 0;
1082
        }
1083
    }
1084
1085
1086
    /**
1087
     *  Show payments table
1088
     *
1089
     * @param TCPDF $pdf Object PDF
1090
     * @param Facture $object Object invoice
1091
     * @param int $posy Position y in PDF
1092
     * @param Translate $outputlangs Object langs for output
1093
     * @return int                         Return integer <0 if KO, >0 if OK
1094
     */
1095
    public function drawPaymentsTable(&$pdf, $object, $posy, $outputlangs)
1096
    {
1097
        global $conf;
1098
1099
        $sign = 1;
1100
        if ($object->type == 2 && getDolGlobalString('INVOICE_POSITIVE_CREDIT_NOTE')) {
1101
            $sign = -1;
1102
        }
1103
1104
        $tab3_posx = 120;
1105
        $tab3_top = $posy + 8;
1106
        $tab3_width = 80;
1107
        $tab3_height = 4;
1108
        if ($this->page_largeur < 210) { // To work with US executive format
1109
            $tab3_posx -= 15;
1110
        }
1111
1112
        $default_font_size = pdf_getPDFFontSize($outputlangs);
1113
1114
        $title = $outputlangs->transnoentities("PaymentsAlreadyDone");
1115
        if ($object->type == 2) {
1116
            $title = $outputlangs->transnoentities("PaymentsBackAlreadyDone");
1117
        }
1118
1119
        $pdf->SetFont('', '', $default_font_size - 3);
1120
        $pdf->SetXY($tab3_posx, $tab3_top - 4);
1121
        $pdf->MultiCell(60, 3, $title, 0, 'L', 0);
1122
1123
        $pdf->line($tab3_posx, $tab3_top, $tab3_posx + $tab3_width, $tab3_top);
1124
1125
        $pdf->SetFont('', '', $default_font_size - 4);
1126
        $pdf->SetXY($tab3_posx, $tab3_top);
1127
        $pdf->MultiCell(20, 3, $outputlangs->transnoentities("Payment"), 0, 'L', 0);
1128
        $pdf->SetXY($tab3_posx + 21, $tab3_top);
1129
        $pdf->MultiCell(20, 3, $outputlangs->transnoentities("Amount"), 0, 'L', 0);
1130
        $pdf->SetXY($tab3_posx + 40, $tab3_top);
1131
        $pdf->MultiCell(20, 3, $outputlangs->transnoentities("Type"), 0, 'L', 0);
1132
        $pdf->SetXY($tab3_posx + 58, $tab3_top);
1133
        $pdf->MultiCell(20, 3, $outputlangs->transnoentities("Num"), 0, 'L', 0);
1134
1135
        $pdf->line($tab3_posx, $tab3_top - 1 + $tab3_height, $tab3_posx + $tab3_width, $tab3_top - 1 + $tab3_height);
1136
1137
        $y = 0;
1138
1139
        $pdf->SetFont('', '', $default_font_size - 4);
1140
1141
1142
        // Loop on each discount available (deposits and credit notes and excess of payment included)
1143
        $sql = "SELECT re.rowid, re.amount_ht, re.multicurrency_amount_ht, re.amount_tva, re.multicurrency_amount_tva,  re.amount_ttc, re.multicurrency_amount_ttc,";
1144
        $sql .= " re.description, re.fk_facture_source,";
1145
        $sql .= " f.type, f.datef";
1146
        $sql .= " FROM " . MAIN_DB_PREFIX . "societe_remise_except as re, " . MAIN_DB_PREFIX . "facture as f";
1147
        $sql .= " WHERE re.fk_facture_source = f.rowid AND re.fk_facture = " . ((int)$object->id);
1148
        $resql = $this->db->query($sql);
1149
        if ($resql) {
1150
            $num = $this->db->num_rows($resql);
1151
            $i = 0;
1152
            $invoice = new Facture($this->db);
1153
            while ($i < $num) {
1154
                $y += 3;
1155
                $obj = $this->db->fetch_object($resql);
1156
1157
                if ($obj->type == 2) {
1158
                    $text = $outputlangs->transnoentities("CreditNote");
1159
                } elseif ($obj->type == 3) {
1160
                    $text = $outputlangs->transnoentities("Deposit");
1161
                } elseif ($obj->type == 0) {
1162
                    $text = $outputlangs->transnoentities("ExcessReceived");
1163
                } else {
1164
                    $text = $outputlangs->transnoentities("UnknownType");
1165
                }
1166
1167
                $invoice->fetch($obj->fk_facture_source);
1168
1169
                $pdf->SetXY($tab3_posx, $tab3_top + $y);
1170
                $pdf->MultiCell(20, 3, dol_print_date($this->db->jdate($obj->datef), 'day', false, $outputlangs, true), 0, 'L', 0);
1171
                $pdf->SetXY($tab3_posx + 21, $tab3_top + $y);
1172
                $pdf->MultiCell(20, 3, price((isModEnabled("multicurrency") && $object->multicurrency_tx != 1) ? $obj->multicurrency_amount_ttc : $obj->amount_ttc, 0, $outputlangs), 0, 'L', 0);
1173
                $pdf->SetXY($tab3_posx + 40, $tab3_top + $y);
1174
                $pdf->MultiCell(20, 3, $text, 0, 'L', 0);
1175
                $pdf->SetXY($tab3_posx + 58, $tab3_top + $y);
1176
                $pdf->MultiCell(20, 3, $invoice->ref, 0, 'L', 0);
1177
1178
                $pdf->line($tab3_posx, $tab3_top + $y + 3, $tab3_posx + $tab3_width, $tab3_top + $y + 3);
1179
1180
                $i++;
1181
            }
1182
        } else {
1183
            $this->error = $this->db->lasterror();
1184
            return -1;
1185
        }
1186
1187
        // Loop on each payment
1188
        // TODO Call getListOfPaymentsgetListOfPayments instead of hard coded sql
1189
        $sql = "SELECT p.datep as date, p.fk_paiement, p.num_paiement as num, pf.amount as amount, pf.multicurrency_amount,";
1190
        $sql .= " cp.code";
1191
        $sql .= " FROM " . MAIN_DB_PREFIX . "paiement_facture as pf, " . MAIN_DB_PREFIX . "paiement as p";
1192
        $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "c_paiement as cp ON p.fk_paiement = cp.id";
1193
        $sql .= " WHERE pf.fk_paiement = p.rowid AND pf.fk_facture = " . ((int)$object->id);
1194
        //$sql.= " WHERE pf.fk_paiement = p.rowid AND pf.fk_facture = 1";
1195
        $sql .= " ORDER BY p.datep";
1196
1197
        $resql = $this->db->query($sql);
1198
        if ($resql) {
1199
            $num = $this->db->num_rows($resql);
1200
            $i = 0;
1201
            $y += 3;
1202
            $maxY = $y;
1203
            while ($i < $num) {
1204
                $row = $this->db->fetch_object($resql);
1205
                $pdf->SetXY($tab3_posx, $tab3_top + $y);
1206
                $pdf->MultiCell(20, 3, dol_print_date($this->db->jdate($row->date), 'day', false, $outputlangs, true), 0, 'L', 0);
1207
                $pdf->SetXY($tab3_posx + 21, $tab3_top + $y);
1208
                $pdf->MultiCell(20, 3, price($sign * ((isModEnabled("multicurrency") && $object->multicurrency_tx != 1) ? $row->multicurrency_amount : $row->amount), 0, $outputlangs), 0, 'L', 0);
1209
                $pdf->SetXY($tab3_posx + 40, $tab3_top + $y);
1210
                $oper = $outputlangs->transnoentitiesnoconv("PaymentTypeShort" . $row->code);
1211
1212
                $pdf->MultiCell(20, 3, $oper, 0, 'L', 0);
1213
                $maxY = max($pdf->GetY() - $tab3_top - 3, $maxY);
1214
                $pdf->SetXY($tab3_posx + 58, $tab3_top + $y);
1215
                $pdf->MultiCell(30, 3, $row->num, 0, 'L', 0);
1216
                $y = $maxY = max($pdf->GetY() - 3 - $tab3_top, $maxY);
1217
                $pdf->line($tab3_posx, $tab3_top + $y + 3, $tab3_posx + $tab3_width, $tab3_top + $y + 3);
1218
                $y += 3;
1219
                $i++;
1220
            }
1221
1222
            return $tab3_top + $y + 3;
1223
        } else {
1224
            $this->error = $this->db->lasterror();
1225
            return -1;
1226
        }
1227
    }
1228
1229
1230
    /**
1231
     *   Show miscellaneous information (payment mode, payment term, ...)
1232
     *
1233
     * @param TCPDF $pdf Object PDF
1234
     * @param Facture $object Object to show
1235
     * @param int $posy Y
1236
     * @param Translate $outputlangs Langs object
1237
     * @param Translate $outputlangsbis Object lang for output bis
1238
     * @return    int                         Pos y
1239
     */
1240
    protected function drawInfoTable(&$pdf, $object, $posy, $outputlangs, $outputlangsbis)
1241
    {
1242
        global $conf, $mysoc, $hookmanager;
1243
1244
        $default_font_size = pdf_getPDFFontSize($outputlangs);
1245
1246
        $pdf->SetFont('', '', $default_font_size - 1);
1247
1248
        // If France, show VAT mention if not applicable
1249
        if ($this->emetteur->country_code == 'FR' && empty($mysoc->tva_assuj)) {
1250
            $pdf->SetFont('', '', $default_font_size - 2);
1251
            $pdf->SetXY($this->marge_gauche, $posy);
1252
            if ($mysoc->forme_juridique_code == 92) {
1253
                $pdf->MultiCell(100, 3, $outputlangs->transnoentities("VATIsNotUsedForInvoiceAsso"), 0, 'L', 0);
1254
            } else {
1255
                $pdf->MultiCell(100, 3, $outputlangs->transnoentities("VATIsNotUsedForInvoice"), 0, 'L', 0);
1256
            }
1257
1258
            $posy = $pdf->GetY() + 4;
1259
        }
1260
1261
        $posxval = 52;  // Position of values of properties shown on left side
1262
        $posxend = 110; // End of x for text on left side
1263
        if ($this->page_largeur < 210) { // To work with US executive format
1264
            $posxend -= 10;
1265
        }
1266
1267
        // Show payments conditions
1268
        if ($object->type != 2 && ($object->cond_reglement_code || $object->cond_reglement)) {
1269
            $pdf->SetFont('', '', $default_font_size - 2);
1270
            $pdf->SetXY($this->marge_gauche, $posy);
1271
            $titre = $outputlangs->transnoentities("PaymentConditions") . ':';
1272
            $pdf->MultiCell($posxval - $this->marge_gauche, 4, $titre, 0, 'L');
1273
1274
            $pdf->SetFont('', '', $default_font_size - 2);
1275
            $pdf->SetXY($posxval, $posy);
1276
            $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);
1277
            $lib_condition_paiement = str_replace('\n', "\n", $lib_condition_paiement);
1278
            $pdf->MultiCell($posxend - $posxval, 4, $lib_condition_paiement, 0, 'L');
1279
1280
            $posy = $pdf->GetY() + 3; // We need spaces for 2 lines payment conditions
1281
        }
1282
1283
        // Show category of operations
1284
        if (getDolGlobalInt('INVOICE_CATEGORY_OF_OPERATION') == 2 && $this->categoryOfOperation >= 0) {
1285
            $pdf->SetFont('', '', $default_font_size - 2);
1286
            $pdf->SetXY($this->marge_gauche, $posy);
1287
            $categoryOfOperationTitle = $outputlangs->transnoentities("MentionCategoryOfOperations") . ' : ';
1288
            $pdf->MultiCell($posxval - $this->marge_gauche, 4, $categoryOfOperationTitle, 0, 'L');
1289
1290
            $pdf->SetFont('', '', $default_font_size - 2);
1291
            $pdf->SetXY($posxval, $posy);
1292
            $categoryOfOperationLabel = $outputlangs->transnoentities("MentionCategoryOfOperations" . $this->categoryOfOperation);
1293
            $pdf->MultiCell($posxend - $posxval, 4, $categoryOfOperationLabel, 0, 'L');
1294
1295
            $posy = $pdf->GetY() + 3; // for 2 lines
1296
        }
1297
1298
        if ($object->type != 2) {
1299
            // Check a payment mode is defined
1300
            if (
1301
                empty($object->mode_reglement_code)
1302
                && !getDolGlobalInt('FACTURE_CHQ_NUMBER')
1303
                && !getDolGlobalInt('FACTURE_RIB_NUMBER')
1304
            ) {
1305
                $this->error = $outputlangs->transnoentities("ErrorNoPaiementModeConfigured");
1306
            } elseif (
1307
                ($object->mode_reglement_code == 'CHQ' && !getDolGlobalInt('FACTURE_CHQ_NUMBER') && empty($object->fk_account) && empty($object->fk_bank))
1308
                || ($object->mode_reglement_code == 'VIR' && !getDolGlobalInt('FACTURE_RIB_NUMBER') && empty($object->fk_account) && empty($object->fk_bank))
1309
            ) {
1310
                // Avoid having any valid PDF with setup that is not complete
1311
                $outputlangs->load("errors");
1312
1313
                $pdf->SetXY($this->marge_gauche, $posy);
1314
                $pdf->SetTextColor(200, 0, 0);
1315
                $pdf->SetFont('', '', $default_font_size - 2);
1316
                $this->error = $outputlangs->transnoentities("ErrorPaymentModeDefinedToWithoutSetup", $object->mode_reglement_code);
1317
                $pdf->MultiCell($posxend - $this->marge_gauche, 3, $this->error, 0, 'L', 0);
1318
                $pdf->SetTextColor(0, 0, 0);
1319
1320
                $posy = $pdf->GetY() + 1;
1321
            }
1322
1323
            // Show payment mode
1324
            if (
1325
                !empty($object->mode_reglement_code)
1326
                && $object->mode_reglement_code != 'CHQ'
1327
                && $object->mode_reglement_code != 'VIR'
1328
            ) {
1329
                $pdf->SetFont('', '', $default_font_size - 2);
1330
                $pdf->SetXY($this->marge_gauche, $posy);
1331
                $titre = $outputlangs->transnoentities("PaymentMode") . ':';
1332
                $pdf->MultiCell($posxend - $this->marge_gauche, 5, $titre, 0, 'L');
1333
1334
                $pdf->SetFont('', '', $default_font_size - 2);
1335
                $pdf->SetXY($posxval, $posy);
1336
                $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);
1337
1338
                //#21654: add account number used for the debit
1339
                if ($object->mode_reglement_code == "PRE") {
1340
                    $bac = new CompanyBankAccount($this->db);
1341
                    // @phan-suppress-next-line PhanPluginSuspiciousParamPosition
1342
                    $bac->fetch(0, '', $object->thirdparty->id);
1343
                    $iban = $bac->iban . (($bac->iban && $bac->bic) ? ' / ' : '') . $bac->bic;
1344
                    $lib_mode_reg .= ' ' . $outputlangs->trans("PaymentTypePREdetails", dol_trunc($iban, 6, 'right', 'UTF-8', 1));
1345
                }
1346
1347
                $pdf->MultiCell($posxend - $posxval, 5, $lib_mode_reg, 0, 'L');
1348
1349
                $posy = $pdf->GetY();
1350
            }
1351
1352
            // Show if Option VAT debit option is on also if transmitter is french
1353
            // Decret n°2099-1299 2022-10-07
1354
            // French mention : "Option pour le paiement de la taxe d'après les débits"
1355
            if ($this->emetteur->country_code == 'FR') {
1356
                if (getDolGlobalInt('TAX_MODE') == 1) {
1357
                    $pdf->SetXY($this->marge_gauche, $posy);
1358
                    $pdf->writeHTMLCell(80, 5, '', '', $outputlangs->transnoentities("MentionVATDebitOptionIsOn"), 0, 1);
1359
1360
                    $posy = $pdf->GetY() + 1;
1361
                }
1362
            }
1363
1364
            // Show online payment link
1365
            if (empty($object->mode_reglement_code) || $object->mode_reglement_code == 'CB' || $object->mode_reglement_code == 'VAD') {
1366
                $useonlinepayment = 0;
1367
                if (getDolGlobalString('PDF_SHOW_LINK_TO_ONLINE_PAYMENT')) {
1368
                    // Show online payment link
1369
                    // The list can be complete by the hook 'doValidatePayment' executed inside getValidOnlinePaymentMethods()
1370
                    include_once DOL_DOCUMENT_ROOT . '/core/lib/payments.lib.php';
1371
                    $validpaymentmethod = getValidOnlinePaymentMethods('');
1372
                    $useonlinepayment = count($validpaymentmethod);
1373
                }
1374
1375
1376
                if ($object->statut != Facture::STATUS_DRAFT && $useonlinepayment) {
1377
                    require_once constant('DOL_DOCUMENT_ROOT') . '/core/lib/payments.lib.php';
1378
                    global $langs;
1379
1380
                    $langs->loadLangs(array('payment', 'paybox', 'stripe'));
1381
                    $servicename = $langs->transnoentities('Online');
1382
                    $paiement_url = getOnlinePaymentUrl('', 'invoice', $object->ref, '', '', '');
1383
                    $linktopay = $langs->trans("ToOfferALinkForOnlinePayment", $servicename) . ' <a href="' . $paiement_url . '">' . $outputlangs->transnoentities("ClickHere") . '</a>';
1384
1385
                    $pdf->SetXY($this->marge_gauche, $posy);
1386
                    $pdf->writeHTMLCell($posxend - $this->marge_gauche, 5, '', '', dol_htmlentitiesbr($linktopay), 0, 1);
1387
1388
                    $posy = $pdf->GetY() + 1;
1389
                }
1390
            }
1391
1392
            // Show payment mode CHQ
1393
            if (empty($object->mode_reglement_code) || $object->mode_reglement_code == 'CHQ') {
1394
                // If payment mode unregulated or payment mode forced to CHQ
1395
                if (getDolGlobalInt('FACTURE_CHQ_NUMBER')) {
1396
                    $diffsizetitle = (!getDolGlobalString('PDF_DIFFSIZE_TITLE') ? 3 : $conf->global->PDF_DIFFSIZE_TITLE);
1397
1398
                    if (getDolGlobalInt('FACTURE_CHQ_NUMBER') > 0) {
1399
                        $account = new Account($this->db);
1400
                        $account->fetch(getDolGlobalInt('FACTURE_CHQ_NUMBER'));
1401
1402
                        $pdf->SetXY($this->marge_gauche, $posy);
1403
                        $pdf->SetFont('', 'B', $default_font_size - $diffsizetitle);
1404
                        $pdf->MultiCell($posxend - $this->marge_gauche, 3, $outputlangs->transnoentities('PaymentByChequeOrderedTo', $account->owner_name), 0, 'L', 0);
1405
                        $posy = $pdf->GetY() + 1;
1406
1407
                        if (!getDolGlobalString('MAIN_PDF_HIDE_CHQ_ADDRESS')) {
1408
                            $pdf->SetXY($this->marge_gauche, $posy);
1409
                            $pdf->SetFont('', '', $default_font_size - $diffsizetitle);
1410
                            $pdf->MultiCell($posxend - $this->marge_gauche, 3, $outputlangs->convToOutputCharset($account->owner_address), 0, 'L', 0);
1411
                            $posy = $pdf->GetY() + 2;
1412
                        }
1413
                    }
1414
                    if ($conf->global->FACTURE_CHQ_NUMBER == -1) {
1415
                        $pdf->SetXY($this->marge_gauche, $posy);
1416
                        $pdf->SetFont('', 'B', $default_font_size - $diffsizetitle);
1417
                        $pdf->MultiCell($posxend - $this->marge_gauche, 3, $outputlangs->transnoentities('PaymentByChequeOrderedTo', $this->emetteur->name), 0, 'L', 0);
1418
                        $posy = $pdf->GetY() + 1;
1419
1420
                        if (!getDolGlobalString('MAIN_PDF_HIDE_CHQ_ADDRESS')) {
1421
                            $pdf->SetXY($this->marge_gauche, $posy);
1422
                            $pdf->SetFont('', '', $default_font_size - $diffsizetitle);
1423
                            $pdf->MultiCell($posxend - $this->marge_gauche, 3, $outputlangs->convToOutputCharset($this->emetteur->getFullAddress()), 0, 'L', 0);
1424
                            $posy = $pdf->GetY() + 2;
1425
                        }
1426
                    }
1427
                }
1428
            }
1429
1430
            // If payment mode not forced or forced to VIR, show payment with BAN
1431
            if (empty($object->mode_reglement_code) || $object->mode_reglement_code == 'VIR') {
1432
                if ($object->fk_account > 0 || $object->fk_bank > 0 || getDolGlobalInt('FACTURE_RIB_NUMBER')) {
1433
                    $bankid = ($object->fk_account <= 0 ? getDolGlobalInt('FACTURE_RIB_NUMBER') : $object->fk_account);
1434
                    if ($object->fk_bank > 0) {
1435
                        $bankid = $object->fk_bank; // For backward compatibility when object->fk_account is forced with object->fk_bank
1436
                    }
1437
                    $account = new Account($this->db);
1438
                    $account->fetch($bankid);
1439
1440
                    $curx = $this->marge_gauche;
1441
                    $cury = $posy;
1442
1443
                    $posy = pdf_bank($pdf, $outputlangs, $curx, $cury, $account, 0, $default_font_size);
1444
1445
                    $posy += 2;
1446
1447
                    // SHOW EPC QR CODE
1448
                    if (getDolGlobalString('INVOICE_ADD_EPC_QR_CODE') == 'bottom') {
1449
                        $qrPosX = 120;
1450
                        $qrPosY = $posy;
1451
                        $qrCodeColor = array('25', '25', '25');
1452
                        $styleQr = array(
1453
                            'border' => false,
1454
                            'padding' => 0,
1455
                            'fgcolor' => $qrCodeColor,
1456
                            'bgcolor' => false, //array(255,255,255)
1457
                            'module_width' => 1, // width of a single module in points
1458
                            'module_height' => 1 // height of a single module in points
1459
                        );
1460
1461
                        $EPCQrCodeString = $object->buildEPCQrCodeString();
1462
                        $pdf->write2DBarcode($EPCQrCodeString, 'QRCODE,M', $qrPosX, $qrPosY, 25, 25, $styleQr, 'N');
1463
1464
                        $pdf->SetXY($qrPosX + 5, $posy);
1465
                        $pdf->SetFont('', '', $default_font_size - 5);
1466
                        $pdf->MultiCell(30, 3, $langs->trans("INVOICE_ADD_EPC_QR_CODEPay"), 0, 'L', 0);
1467
                        $posy = $pdf->GetY() + 2;
1468
                    }
1469
                }
1470
            }
1471
        }
1472
1473
        return $posy;
1474
    }
1475
1476
1477
    /**
1478
     *  Show total to pay
1479
     *
1480
     * @param TCPDF $pdf Object PDF
1481
     * @param Facture $object Object invoice
1482
     * @param int $deja_regle Amount already paid (in the currency of invoice)
1483
     * @param int $posy Position depart
1484
     * @param Translate $outputlangs Object langs
1485
     * @param Translate $outputlangsbis Object lang for output bis
1486
     * @return int                         Position pour suite
1487
     */
1488
    protected function drawTotalTable(&$pdf, $object, $deja_regle, $posy, $outputlangs, $outputlangsbis)
1489
    {
1490
        global $conf, $mysoc, $hookmanager;
1491
1492
        $sign = 1;
1493
        if ($object->type == 2 && getDolGlobalString('INVOICE_POSITIVE_CREDIT_NOTE')) {
1494
            $sign = -1;
1495
        }
1496
1497
        $default_font_size = pdf_getPDFFontSize($outputlangs);
1498
1499
        $tab2_top = $posy;
1500
        $tab2_hl = 4;
1501
        if (is_object($outputlangsbis)) {   // When we show 2 languages we need more room for text, so we use a smaller font.
1502
            $pdf->SetFont('', '', $default_font_size - 2);
1503
        } else {
1504
            $pdf->SetFont('', '', $default_font_size - 1);
1505
        }
1506
1507
        // Total table
1508
        $col1x = 120;
1509
        $col2x = 170;
1510
        if ($this->page_largeur < 210) { // To work with US executive format
1511
            $col1x -= 15;
1512
            $col2x -= 10;
1513
        }
1514
        $largcol2 = ($this->page_largeur - $this->marge_droite - $col2x);
1515
1516
        $useborder = 0;
1517
        $index = 0;
1518
1519
        // Add trigger to allow to edit $object
1520
        $parameters = array(
1521
            'object' => &$object,
1522
            'outputlangs' => $outputlangs,
1523
        );
1524
        $hookmanager->executeHooks('beforePercentCalculation', $parameters, $this); // Note that $object may have been modified by hook
1525
1526
        // overall percentage of advancement
1527
        $percent = 0;
1528
        $i = 0;
1529
        foreach ($object->lines as $line) {
1530
            if ($line->product_type != 9) {
1531
                $percent += $line->situation_percent;
1532
                $i++;
1533
            }
1534
        }
1535
1536
        if (!empty($i)) {
1537
            $avancementGlobal = $percent / $i;
1538
        } else {
1539
            $avancementGlobal = 0;
1540
        }
1541
1542
        $object->fetchPreviousNextSituationInvoice();
1543
        $TPreviousIncoice = $object->tab_previous_situation_invoice;
1544
1545
        $total_a_payer = 0;
1546
        $total_a_payer_ttc = 0;
1547
        foreach ($TPreviousIncoice as &$fac) {
1548
            $total_a_payer += $fac->total_ht;
1549
            $total_a_payer_ttc += $fac->total_ttc;
1550
        }
1551
        $total_a_payer += $object->total_ht;
1552
        $total_a_payer_ttc += $object->total_ttc;
1553
1554
        if (!empty($avancementGlobal)) {
1555
            $total_a_payer = $total_a_payer * 100 / $avancementGlobal;
1556
            $total_a_payer_ttc = $total_a_payer_ttc * 100 / $avancementGlobal;
1557
        } else {
1558
            $total_a_payer = 0;
1559
            $total_a_payer_ttc = 0;
1560
        }
1561
1562
        $i = 1;
1563
        if (!empty($TPreviousIncoice)) {
1564
            $pdf->setY($tab2_top);
1565
            $posy = $pdf->GetY();
1566
1567
            foreach ($TPreviousIncoice as &$fac) {
1568
                if ($posy > $this->page_hauteur - 4 - $this->heightforfooter) {
1569
                    $this->_pagefoot($pdf, $object, $outputlangs, 1, $this->getHeightForQRInvoice($pdf->getPage(), $object, $outputlangs));
1570
                    $pdf->addPage();
1571
                    if (!getDolGlobalInt('MAIN_PDF_DONOTREPEAT_HEAD')) {
1572
                        $this->_pagehead($pdf, $object, 0, $outputlangs, $outputlangsbis);
1573
                        $pdf->setY($this->tab_top_newpage);
1574
                    } else {
1575
                        $pdf->setY($this->marge_haute);
1576
                    }
1577
                    $posy = $pdf->GetY();
1578
                }
1579
1580
                // Cumulate preceding VAT
1581
                $index++;
1582
                $pdf->SetFillColor(255, 255, 255);
1583
                $pdf->SetXY($col1x, $posy);
1584
                $pdf->MultiCell($col2x - $col1x, $tab2_hl, $outputlangs->transnoentities("PDFSituationTitle", $fac->situation_counter) . ' ' . $outputlangs->transnoentities("TotalHT"), 0, 'L', 1);
1585
1586
                $pdf->SetXY($col2x, $posy);
1587
1588
                $facSign = '';
1589
                if ($i > 1) {
1590
                    $facSign = $fac->total_ht >= 0 ? '+' : '';
1591
                }
1592
1593
                $displayAmount = ' ' . $facSign . ' ' . price($fac->total_ht, 0, $outputlangs);
1594
1595
                $pdf->MultiCell($largcol2, $tab2_hl, $displayAmount, 0, 'R', 1);
1596
1597
                $i++;
1598
                $posy += $tab2_hl;
1599
1600
                $pdf->setY($posy);
1601
            }
1602
1603
            // Display current total
1604
            $pdf->SetFillColor(255, 255, 255);
1605
            $pdf->SetXY($col1x, $posy);
1606
            $pdf->MultiCell($col2x - $col1x, $tab2_hl, $outputlangs->transnoentities("PDFSituationTitle", $object->situation_counter) . ' ' . $outputlangs->transnoentities("TotalHT"), 0, 'L', 1);
1607
1608
            $pdf->SetXY($col2x, $posy);
1609
            $facSign = '';
1610
            if ($i > 1) {
1611
                $facSign = $object->total_ht >= 0 ? '+' : ''; // management of a particular customer case
1612
            }
1613
1614
            if ($fac->type === Facture::TYPE_CREDIT_NOTE) {
1615
                $facSign = '-'; // les avoirs
1616
            }
1617
1618
1619
            $displayAmount = ' ' . $facSign . ' ' . price($object->total_ht, 0, $outputlangs);
1620
            $pdf->MultiCell($largcol2, $tab2_hl, $displayAmount, 0, 'R', 1);
1621
1622
            $posy += $tab2_hl;
1623
1624
            // Display all total
1625
            $pdf->SetFont('', '', $default_font_size - 1);
1626
            $pdf->SetFillColor(255, 255, 255);
1627
            $pdf->SetXY($col1x, $posy);
1628
            $pdf->MultiCell($col2x - $col1x, $tab2_hl, $outputlangs->transnoentities("SituationTotalProgress", $avancementGlobal), 0, 'L', 1);
1629
1630
            $pdf->SetXY($col2x, $posy);
1631
            $pdf->MultiCell($largcol2, $tab2_hl, price($total_a_payer * $avancementGlobal / 100, 0, $outputlangs), 0, 'R', 1);
1632
            $pdf->SetFont('', '', $default_font_size - 2);
1633
1634
            $posy += $tab2_hl;
1635
1636
            if ($posy > $this->page_hauteur - 4 - $this->heightforfooter) {
1637
                $pdf->addPage();
1638
                if (!getDolGlobalInt('MAIN_PDF_DONOTREPEAT_HEAD')) {
1639
                    $this->_pagehead($pdf, $object, 0, $outputlangs, $outputlangsbis);
1640
                    $pdf->setY($this->tab_top_newpage);
1641
                } else {
1642
                    $pdf->setY($this->marge_haute);
1643
                }
1644
1645
                $posy = $pdf->GetY();
1646
            }
1647
1648
            $tab2_top = $posy;
1649
            $index = 0;
1650
1651
            $tab2_top += 3;
1652
        }
1653
1654
1655
        // Get Total HT
1656
        $total_ht = (isModEnabled("multicurrency") && $object->multicurrency_tx != 1 ? $object->multicurrency_total_ht : $object->total_ht);
1657
1658
        // Total remise
1659
        $total_line_remise = 0;
1660
        foreach ($object->lines as $i => $line) {
1661
            $resdiscount = pdfGetLineTotalDiscountAmount($object, $i, $outputlangs, 2);
1662
            $total_line_remise += (is_numeric($resdiscount) ? $resdiscount : 0);
1663
            // Gestion remise sous forme de ligne négative
1664
            if ($line->total_ht < 0) {
1665
                $total_line_remise += -$line->total_ht;
1666
            }
1667
        }
1668
        if ($total_line_remise > 0) {
1669
            if (getDolGlobalString('MAIN_SHOW_AMOUNT_DISCOUNT')) {
1670
                $pdf->SetFillColor(255, 255, 255);
1671
                $pdf->SetXY($col1x, $tab2_top + $tab2_hl);
1672
                $pdf->MultiCell($col2x - $col1x, $tab2_hl, $outputlangs->transnoentities("TotalDiscount") . (is_object($outputlangsbis) ? ' / ' . $outputlangsbis->transnoentities("TotalDiscount") : ''), 0, 'L', 1);
1673
                $pdf->SetXY($col2x, $tab2_top + $tab2_hl);
1674
                $pdf->MultiCell($largcol2, $tab2_hl, price($total_line_remise, 0, $outputlangs), 0, 'R', 1);
1675
1676
                $index++;
1677
            }
1678
            // Show total NET before discount
1679
            if (getDolGlobalString('MAIN_SHOW_AMOUNT_BEFORE_DISCOUNT')) {
1680
                $pdf->SetFillColor(255, 255, 255);
1681
                $pdf->SetXY($col1x, $tab2_top);
1682
                $pdf->MultiCell($col2x - $col1x, $tab2_hl, $outputlangs->transnoentities("TotalHTBeforeDiscount") . (is_object($outputlangsbis) ? ' / ' . $outputlangsbis->transnoentities("TotalHTBeforeDiscount") : ''), 0, 'L', 1);
1683
                $pdf->SetXY($col2x, $tab2_top);
1684
                $pdf->MultiCell($largcol2, $tab2_hl, price($total_line_remise + $total_ht, 0, $outputlangs), 0, 'R', 1);
1685
1686
                $index++;
1687
            }
1688
        }
1689
1690
        // Total HT
1691
        $pdf->SetFillColor(255, 255, 255);
1692
        $pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index);
1693
        $pdf->MultiCell($col2x - $col1x, $tab2_hl, $outputlangs->transnoentities(!getDolGlobalString('MAIN_GENERATE_DOCUMENTS_WITHOUT_VAT') ? "TotalHT" : "Total") . (is_object($outputlangsbis) ? ' / ' . $outputlangsbis->transnoentities(!getDolGlobalString('MAIN_GENERATE_DOCUMENTS_WITHOUT_VAT') ? "TotalHT" : "Total") : ''), 0, 'L', 1);
1694
1695
        $total_ht = ((isModEnabled("multicurrency") && isset($object->multicurrency_tx) && $object->multicurrency_tx != 1) ? $object->multicurrency_total_ht : $object->total_ht);
1696
        $pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index);
1697
        $pdf->MultiCell($largcol2, $tab2_hl, price($sign * ($total_ht + (!empty($object->remise) ? $object->remise : 0)), 0, $outputlangs), 0, 'R', 1);
1698
1699
        // Show VAT by rates and total
1700
        $pdf->SetFillColor(248, 248, 248);
1701
1702
        $total_ttc = (isModEnabled("multicurrency") && $object->multicurrency_tx != 1) ? $object->multicurrency_total_ttc : $object->total_ttc;
1703
1704
        $this->atleastoneratenotnull = 0;
1705
        if (!getDolGlobalString('MAIN_GENERATE_DOCUMENTS_WITHOUT_VAT')) {
1706
            $tvaisnull = ((!empty($this->tva) && count($this->tva) == 1 && isset($this->tva['0.000']) && is_float($this->tva['0.000'])) ? true : false);
1707
            if (getDolGlobalString('MAIN_GENERATE_DOCUMENTS_WITHOUT_VAT_IFNULL') && $tvaisnull) {
1708
                // Nothing to do
1709
            } else {
1710
                //Local tax 1 before VAT
1711
                //if (!empty($conf->global->FACTURE_LOCAL_TAX1_OPTION) && $conf->global->FACTURE_LOCAL_TAX1_OPTION=='localtax1on')
1712
                //{
1713
                foreach ($this->localtax1 as $localtax_type => $localtax_rate) {
1714
                    if (in_array((string)$localtax_type, array('1', '3', '5'))) {
1715
                        continue;
1716
                    }
1717
1718
                    foreach ($localtax_rate as $tvakey => $tvaval) {
1719
                        if ($tvakey != 0) {    // On affiche pas taux 0
1720
                            //$this->atleastoneratenotnull++;
1721
1722
                            $index++;
1723
                            $pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index);
1724
1725
                            $tvacompl = '';
1726
                            if (preg_match('/\*/', (string)$tvakey)) {
1727
                                $tvakey = str_replace('*', '', (string)$tvakey);
1728
                                $tvacompl = " (" . $outputlangs->transnoentities("NonPercuRecuperable") . ")";
1729
                            }
1730
1731
                            $totalvat = $outputlangs->transcountrynoentities("TotalLT1", $mysoc->country_code) . (is_object($outputlangsbis) ? ' / ' . $outputlangsbis->transcountrynoentities("TotalLT1", $mysoc->country_code) : '');
1732
                            $totalvat .= ' ';
1733
                            $totalvat .= vatrate(abs($tvakey), 1) . $tvacompl;
1734
                            $pdf->MultiCell($col2x - $col1x, $tab2_hl, $totalvat, 0, 'L', 1);
1735
1736
                            $total_localtax = ((isModEnabled("multicurrency") && isset($object->multicurrency_tx) && $object->multicurrency_tx != 1) ? price2num($tvaval * $object->multicurrency_tx, 'MT') : $tvaval);
1737
1738
                            $pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index);
1739
                            $pdf->MultiCell($largcol2, $tab2_hl, price($total_localtax, 0, $outputlangs), 0, 'R', 1);
1740
                        }
1741
                    }
1742
                }
1743
                //}
1744
                //Local tax 2 before VAT
1745
                //if (!empty($conf->global->FACTURE_LOCAL_TAX2_OPTION) && $conf->global->FACTURE_LOCAL_TAX2_OPTION=='localtax2on')
1746
                //{
1747
                foreach ($this->localtax2 as $localtax_type => $localtax_rate) {
1748
                    if (in_array((string)$localtax_type, array('1', '3', '5'))) {
1749
                        continue;
1750
                    }
1751
1752
                    foreach ($localtax_rate as $tvakey => $tvaval) {
1753
                        if ($tvakey != 0) {    // On affiche pas taux 0
1754
                            //$this->atleastoneratenotnull++;
1755
1756
                            $index++;
1757
                            $pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index);
1758
1759
                            $tvacompl = '';
1760
                            if (preg_match('/\*/', (string)$tvakey)) {
1761
                                $tvakey = str_replace('*', '', (string)$tvakey);
1762
                                $tvacompl = " (" . $outputlangs->transnoentities("NonPercuRecuperable") . ")";
1763
                            }
1764
                            $totalvat = $outputlangs->transcountrynoentities("TotalLT2", $mysoc->country_code) . (is_object($outputlangsbis) ? ' / ' . $outputlangsbis->transcountrynoentities("TotalLT2", $mysoc->country_code) : '');
1765
                            $totalvat .= ' ';
1766
                            $totalvat .= vatrate(abs($tvakey), 1) . $tvacompl;
1767
                            $pdf->MultiCell($col2x - $col1x, $tab2_hl, $totalvat, 0, 'L', 1);
1768
1769
                            $total_localtax = ((isModEnabled("multicurrency") && isset($object->multicurrency_tx) && $object->multicurrency_tx != 1) ? price2num($tvaval * $object->multicurrency_tx, 'MT') : $tvaval);
1770
1771
                            $pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index);
1772
                            $pdf->MultiCell($largcol2, $tab2_hl, price($total_localtax, 0, $outputlangs), 0, 'R', 1);
1773
                        }
1774
                    }
1775
                }
1776
                //}
1777
1778
                // Situations totals might be wrong on huge amounts with old mode 1
1779
                if (getDolGlobalInt('INVOICE_USE_SITUATION') == 1 && $object->situation_cycle_ref && $object->situation_counter > 1) {
1780
                    $sum_pdf_tva = 0;
1781
                    foreach ($this->tva as $tvakey => $tvaval) {
1782
                        $sum_pdf_tva += $tvaval; // sum VAT amounts to compare to object
1783
                    }
1784
1785
                    if ($sum_pdf_tva != $object->total_tva) { // apply coef to recover the VAT object amount (the good one)
1786
                        if (!empty($sum_pdf_tva)) {
1787
                            $coef_fix_tva = $object->total_tva / $sum_pdf_tva;
1788
                        } else {
1789
                            $coef_fix_tva = 1;
1790
                        }
1791
1792
1793
                        foreach ($this->tva as $tvakey => $tvaval) {
1794
                            $this->tva[$tvakey] = $tvaval * $coef_fix_tva;
1795
                        }
1796
                        foreach ($this->tva_array as $tvakey => $tvaval) {
1797
                            $this->tva_array[$tvakey]['amount'] = $tvaval['amount'] * $coef_fix_tva;
1798
                        }
1799
                    }
1800
                }
1801
1802
                // VAT
1803
                foreach ($this->tva_array as $tvakey => $tvaval) {
1804
                    if ($tvakey != 0) {    // On affiche pas taux 0
1805
                        $this->atleastoneratenotnull++;
1806
1807
                        $index++;
1808
                        $pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index);
1809
1810
                        $tvacompl = '';
1811
                        if (preg_match('/\*/', $tvakey)) {
1812
                            $tvakey = str_replace('*', '', $tvakey);
1813
                            $tvacompl = " (" . $outputlangs->transnoentities("NonPercuRecuperable") . ")";
1814
                        }
1815
                        $totalvat = $outputlangs->transcountrynoentities("TotalVAT", $mysoc->country_code) . (is_object($outputlangsbis) ? ' / ' . $outputlangsbis->transcountrynoentities("TotalVAT", $mysoc->country_code) : '');
1816
                        $totalvat .= ' ';
1817
                        if (getDolGlobalString('PDF_VAT_LABEL_IS_CODE_OR_RATE') == 'rateonly') {
1818
                            $totalvat .= vatrate($tvaval['vatrate'], 1) . $tvacompl;
1819
                        } elseif (getDolGlobalString('PDF_VAT_LABEL_IS_CODE_OR_RATE') == 'codeonly') {
1820
                            $totalvat .= ($tvaval['vatcode'] ? $tvaval['vatcode'] : vatrate($tvaval['vatrate'], 1)) . $tvacompl;
1821
                        } else {
1822
                            $totalvat .= vatrate($tvaval['vatrate'], 1) . ($tvaval['vatcode'] ? ' (' . $tvaval['vatcode'] . ')' : '') . $tvacompl;
1823
                        }
1824
                        $pdf->MultiCell($col2x - $col1x, $tab2_hl, $totalvat, 0, 'L', 1);
1825
1826
                        $pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index);
1827
                        $pdf->MultiCell($largcol2, $tab2_hl, price(price2num($tvaval['amount'], 'MT'), 0, $outputlangs), 0, 'R', 1);
1828
                    }
1829
                }
1830
1831
                //Local tax 1 after VAT
1832
                //if (!empty($conf->global->FACTURE_LOCAL_TAX1_OPTION) && $conf->global->FACTURE_LOCAL_TAX1_OPTION=='localtax1on')
1833
                //{
1834
                foreach ($this->localtax1 as $localtax_type => $localtax_rate) {
1835
                    if (in_array((string)$localtax_type, array('2', '4', '6'))) {
1836
                        continue;
1837
                    }
1838
1839
                    foreach ($localtax_rate as $tvakey => $tvaval) {
1840
                        if ($tvakey != 0) {    // On affiche pas taux 0
1841
                            //$this->atleastoneratenotnull++;
1842
1843
                            $index++;
1844
                            $pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index);
1845
1846
                            $tvacompl = '';
1847
                            if (preg_match('/\*/', (string)$tvakey)) {
1848
                                $tvakey = str_replace('*', '', (string)$tvakey);
1849
                                $tvacompl = " (" . $outputlangs->transnoentities("NonPercuRecuperable") . ")";
1850
                            }
1851
                            $totalvat = $outputlangs->transcountrynoentities("TotalLT1", $mysoc->country_code) . (is_object($outputlangsbis) ? ' / ' . $outputlangsbis->transcountrynoentities("TotalLT1", $mysoc->country_code) : '');
1852
                            $totalvat .= ' ';
1853
                            $totalvat .= vatrate(abs($tvakey), 1) . $tvacompl;
1854
1855
                            $pdf->MultiCell($col2x - $col1x, $tab2_hl, $totalvat, 0, 'L', 1);
1856
1857
                            $total_localtax = ((isModEnabled("multicurrency") && isset($object->multicurrency_tx) && $object->multicurrency_tx != 1) ? price2num($tvaval * $object->multicurrency_tx, 'MT') : $tvaval);
1858
1859
                            $pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index);
1860
                            $pdf->MultiCell($largcol2, $tab2_hl, price($total_localtax, 0, $outputlangs), 0, 'R', 1);
1861
                        }
1862
                    }
1863
                }
1864
                //}
1865
                //Local tax 2 after VAT
1866
                //if (!empty($conf->global->FACTURE_LOCAL_TAX2_OPTION) && $conf->global->FACTURE_LOCAL_TAX2_OPTION=='localtax2on')
1867
                //{
1868
                foreach ($this->localtax2 as $localtax_type => $localtax_rate) {
1869
                    if (in_array((string)$localtax_type, array('2', '4', '6'))) {
1870
                        continue;
1871
                    }
1872
1873
                    foreach ($localtax_rate as $tvakey => $tvaval) {
1874
                        // retrieve global local tax
1875
                        if ($tvakey != 0) {    // On affiche pas taux 0
1876
                            //$this->atleastoneratenotnull++;
1877
1878
                            $index++;
1879
                            $pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index);
1880
1881
                            $tvacompl = '';
1882
                            if (preg_match('/\*/', (string)$tvakey)) {
1883
                                $tvakey = str_replace('*', '', (string)$tvakey);
1884
                                $tvacompl = " (" . $outputlangs->transnoentities("NonPercuRecuperable") . ")";
1885
                            }
1886
                            $totalvat = $outputlangs->transcountrynoentities("TotalLT2", $mysoc->country_code) . (is_object($outputlangsbis) ? ' / ' . $outputlangsbis->transcountrynoentities("TotalLT2", $mysoc->country_code) : '');
1887
                            $totalvat .= ' ';
1888
1889
                            $totalvat .= vatrate(abs($tvakey), 1) . $tvacompl;
1890
                            $pdf->MultiCell($col2x - $col1x, $tab2_hl, $totalvat, 0, 'L', 1);
1891
1892
                            $total_localtax = ((isModEnabled("multicurrency") && isset($object->multicurrency_tx) && $object->multicurrency_tx != 1) ? price2num($tvaval * $object->multicurrency_tx, 'MT') : $tvaval);
1893
1894
                            $pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index);
1895
                            $pdf->MultiCell($largcol2, $tab2_hl, price($total_localtax, 0, $outputlangs), 0, 'R', 1);
1896
                        }
1897
                    }
1898
                }
1899
1900
1901
                // Revenue stamp
1902
                if (price2num($object->revenuestamp) != 0) {
1903
                    $index++;
1904
                    $pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index);
1905
                    $pdf->MultiCell($col2x - $col1x, $tab2_hl, $outputlangs->transnoentities("RevenueStamp") . (is_object($outputlangsbis) ? ' / ' . $outputlangsbis->transnoentities("RevenueStamp", $mysoc->country_code) : ''), $useborder, 'L', 1);
1906
1907
                    $pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index);
1908
                    $pdf->MultiCell($largcol2, $tab2_hl, price($sign * $object->revenuestamp), $useborder, 'R', 1);
1909
                }
1910
1911
                // Total TTC
1912
                $index++;
1913
                $pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index);
1914
                $pdf->SetTextColor(0, 0, 60);
1915
                $pdf->SetFillColor(224, 224, 224);
1916
                $pdf->MultiCell($col2x - $col1x, $tab2_hl, $outputlangs->transnoentities("TotalTTC") . (is_object($outputlangsbis) ? ' / ' . $outputlangsbis->transnoentities("TotalTTC") : ''), $useborder, 'L', 1);
1917
1918
                $pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index);
1919
                $pdf->MultiCell($largcol2, $tab2_hl, price($sign * $total_ttc, 0, $outputlangs), $useborder, 'R', 1);
1920
1921
1922
                // Retained warranty
1923
                if ($object->displayRetainedWarranty()) {
1924
                    $pdf->SetTextColor(40, 40, 40);
1925
                    $pdf->SetFillColor(255, 255, 255);
1926
1927
                    $retainedWarranty = $object->getRetainedWarrantyAmount();
1928
                    $billedWithRetainedWarranty = $object->total_ttc - $retainedWarranty;
1929
1930
                    // Billed - retained warranty
1931
                    $index++;
1932
                    $pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index);
1933
                    $pdf->MultiCell($col2x - $col1x, $tab2_hl, $outputlangs->transnoentities("ToPayOn", dol_print_date($object->date_lim_reglement, 'day')), $useborder, 'L', 1);
1934
1935
                    $pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index);
1936
                    $pdf->MultiCell($largcol2, $tab2_hl, price($billedWithRetainedWarranty), $useborder, 'R', 1);
1937
1938
                    // retained warranty
1939
                    $index++;
1940
                    $pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index);
1941
1942
                    $retainedWarrantyToPayOn = $outputlangs->transnoentities("RetainedWarranty") . (is_object($outputlangsbis) ? ' / ' . $outputlangsbis->transnoentities("RetainedWarranty") : '') . ' (' . $object->retained_warranty . '%)';
1943
                    $retainedWarrantyToPayOn .= !empty($object->retained_warranty_date_limit) ? ' ' . $outputlangs->transnoentities("toPayOn", dol_print_date($object->retained_warranty_date_limit, 'day')) : '';
1944
1945
                    $pdf->MultiCell($col2x - $col1x, $tab2_hl, $retainedWarrantyToPayOn, $useborder, 'L', 1);
1946
                    $pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index);
1947
                    $pdf->MultiCell($largcol2, $tab2_hl, price($retainedWarranty), $useborder, 'R', 1);
1948
                }
1949
            }
1950
        }
1951
1952
        $pdf->SetTextColor(0, 0, 0);
1953
1954
        $creditnoteamount = $object->getSumCreditNotesUsed((isModEnabled("multicurrency") && $object->multicurrency_tx != 1) ? 1 : 0); // Warning, this also include excess received
1955
        $depositsamount = $object->getSumDepositsUsed((isModEnabled("multicurrency") && $object->multicurrency_tx != 1) ? 1 : 0);
1956
1957
        $resteapayer = price2num($total_ttc - $deja_regle - $creditnoteamount - $depositsamount, 'MT');
1958
        if (!empty($object->paye)) {
1959
            $resteapayer = 0;
1960
        }
1961
1962
        if (($deja_regle > 0 || $creditnoteamount > 0 || $depositsamount > 0) && !getDolGlobalString('INVOICE_NO_PAYMENT_DETAILS')) {
1963
            // Already paid + Deposits
1964
            $index++;
1965
            $pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index);
1966
            $pdf->MultiCell($col2x - $col1x, $tab2_hl, $outputlangs->transnoentities("Paid") . (is_object($outputlangsbis) ? ' / ' . $outputlangsbis->transnoentities("Paid") : ''), 0, 'L', 0);
1967
            $pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index);
1968
            $pdf->MultiCell($largcol2, $tab2_hl, price($deja_regle + $depositsamount, 0, $outputlangs), 0, 'R', 0);
1969
1970
            // Credit note
1971
            if ($creditnoteamount) {
1972
                $labeltouse = ($outputlangs->transnoentities("CreditNotesOrExcessReceived") != "CreditNotesOrExcessReceived") ? $outputlangs->transnoentities("CreditNotesOrExcessReceived") : $outputlangs->transnoentities("CreditNotes");
1973
                $labeltouse .= (is_object($outputlangsbis) ? (' / ' . (($outputlangsbis->transnoentities("CreditNotesOrExcessReceived") != "CreditNotesOrExcessReceived") ? $outputlangsbis->transnoentities("CreditNotesOrExcessReceived") : $outputlangsbis->transnoentities("CreditNotes"))) : '');
1974
                $index++;
1975
                $pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index);
1976
                $pdf->MultiCell($col2x - $col1x, $tab2_hl, $labeltouse, 0, 'L', 0);
1977
                $pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index);
1978
                $pdf->MultiCell($largcol2, $tab2_hl, price($creditnoteamount, 0, $outputlangs), 0, 'R', 0);
1979
            }
1980
1981
            if ($object->close_code == Facture::CLOSECODE_DISCOUNTVAT) {
1982
                $index++;
1983
                $pdf->SetFillColor(255, 255, 255);
1984
1985
                $pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index);
1986
                $pdf->MultiCell($col2x - $col1x, $tab2_hl, $outputlangs->transnoentities("EscompteOfferedShort") . (is_object($outputlangsbis) ? ' / ' . $outputlangsbis->transnoentities("EscompteOfferedShort") : ''), $useborder, 'L', 1);
1987
                $pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index);
1988
                $pdf->MultiCell($largcol2, $tab2_hl, price(price2num($object->total_ttc - $deja_regle - $creditnoteamount - $depositsamount, 'MT'), 0, $outputlangs), $useborder, 'R', 1);
1989
1990
                $resteapayer = 0;
1991
            }
1992
1993
            $index++;
1994
            $pdf->SetTextColor(0, 0, 60);
1995
            $pdf->SetFillColor(224, 224, 224);
1996
            $pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index);
1997
            $pdf->MultiCell($col2x - $col1x, $tab2_hl, $outputlangs->transnoentities("RemainderToPay") . (is_object($outputlangsbis) ? ' / ' . $outputlangsbis->transnoentities("RemainderToPay") : ''), $useborder, 'L', 1);
1998
            $pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index);
1999
            $pdf->MultiCell($largcol2, $tab2_hl, price($resteapayer, 0, $outputlangs), $useborder, 'R', 1);
2000
2001
            $pdf->SetFont('', '', $default_font_size - 1);
2002
            $pdf->SetTextColor(0, 0, 0);
2003
        }
2004
2005
        $parameters = array('pdf' => &$pdf, 'object' => &$object, 'outputlangs' => $outputlangs, 'index' => &$index);
2006
2007
        $reshook = $hookmanager->executeHooks('afterPDFTotalTable', $parameters, $this); // Note that $action and $object may have been modified by some hooks
2008
        if ($reshook < 0) {
2009
            $this->error = $hookmanager->error;
2010
            $this->errors = $hookmanager->errors;
2011
        }
2012
2013
        $index++;
2014
        return ($tab2_top + ($tab2_hl * $index));
2015
    }
2016
2017
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2018
2019
    /**
2020
     *  Return list of active generation modules
2021
     *
2022
     * @param DoliDB $db Database handler
2023
     * @param integer $maxfilenamelength Max length of value to show
2024
     * @return array                       List of templates
2025
     */
2026
    public static function liste_modeles($db, $maxfilenamelength = 0)
2027
    {
2028
        // phpcs:enable
2029
        return parent::liste_modeles($db, $maxfilenamelength); // TODO: Change the autogenerated stub
2030
    }
2031
2032
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
2033
2034
    /**
2035
     *   Show table for lines
2036
     *
2037
     * @param TCPDF $pdf Object PDF
2038
     * @param float|int $tab_top Top position of table
2039
     * @param float|int $tab_height Height of table (rectangle)
2040
     * @param int $nexY Y (not used)
2041
     * @param Translate $outputlangs Langs object
2042
     * @param int $hidetop 1=Hide top bar of array and title, 0=Hide nothing, -1=Hide only title
2043
     * @param int $hidebottom Hide bottom bar of array
2044
     * @param string $currency Currency code
2045
     * @param Translate $outputlangsbis Langs object bis
2046
     * @return    void
2047
     */
2048
    protected function _tableau(&$pdf, $tab_top, $tab_height, $nexY, $outputlangs, $hidetop = 0, $hidebottom = 0, $currency = '', $outputlangsbis = null)
2049
    {
2050
        global $conf;
2051
2052
        // Force to disable hidetop and hidebottom
2053
        $hidebottom = 0;
2054
        if ($hidetop) {
2055
            $hidetop = -1;
2056
        }
2057
2058
        $currency = !empty($currency) ? $currency : $conf->currency;
2059
        $default_font_size = pdf_getPDFFontSize($outputlangs);
2060
2061
        // Amount in (at tab_top - 1)
2062
        $pdf->SetTextColor(0, 0, 0);
2063
        $pdf->SetFont('', '', $default_font_size - 2);
2064
2065
        if (empty($hidetop)) {
2066
            // Show category of operations
2067
            if (getDolGlobalInt('INVOICE_CATEGORY_OF_OPERATION') == 1 && $this->categoryOfOperation >= 0) {
2068
                $categoryOfOperations = $outputlangs->transnoentities("MentionCategoryOfOperations") . ' : ' . $outputlangs->transnoentities("MentionCategoryOfOperations" . $this->categoryOfOperation);
2069
                $pdf->SetXY($this->marge_gauche, $tab_top - 4);
2070
                $pdf->MultiCell(($pdf->GetStringWidth($categoryOfOperations)) + 4, 2, $categoryOfOperations);
2071
            }
2072
2073
            $titre = $outputlangs->transnoentities("AmountInCurrency", $outputlangs->transnoentitiesnoconv("Currency" . $currency));
2074
            if (getDolGlobalString('PDF_USE_ALSO_LANGUAGE_CODE') && is_object($outputlangsbis)) {
2075
                $titre .= ' - ' . $outputlangsbis->transnoentities("AmountInCurrency", $outputlangsbis->transnoentitiesnoconv("Currency" . $currency));
2076
            }
2077
2078
            $pdf->SetXY($this->page_largeur - $this->marge_droite - ($pdf->GetStringWidth($titre) + 3), $tab_top - 4);
2079
            $pdf->MultiCell(($pdf->GetStringWidth($titre) + 3), 2, $titre);
2080
2081
            //$conf->global->MAIN_PDF_TITLE_BACKGROUND_COLOR='230,230,230';
2082
            if (getDolGlobalString('MAIN_PDF_TITLE_BACKGROUND_COLOR')) {
2083
                $pdf->Rect($this->marge_gauche, $tab_top, $this->page_largeur - $this->marge_droite - $this->marge_gauche, $this->tabTitleHeight, 'F', null, explode(',', getDolGlobalString('MAIN_PDF_TITLE_BACKGROUND_COLOR')));
2084
            }
2085
        }
2086
2087
        $pdf->SetDrawColor(128, 128, 128);
2088
        $pdf->SetFont('', '', $default_font_size - 1);
2089
2090
        // Output Rect
2091
        $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
2092
2093
2094
        $this->pdfTabTitles($pdf, $tab_top, $tab_height, $outputlangs, $hidetop);
2095
2096
        if (empty($hidetop)) {
2097
            $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
2098
        }
2099
    }
2100
2101
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
2102
2103
    /**
2104
     *  Show top header of page. This include the logo, ref and address blocks
2105
     *
2106
     * @param TCPDF $pdf Object PDF
2107
     * @param Facture $object Object to show
2108
     * @param int $showaddress 0=no, 1=yes (usually set to 1 for first page, and 0 for next pages)
2109
     * @param Translate $outputlangs Object lang for output
2110
     * @param Translate $outputlangsbis Object lang for output bis
2111
     * @return array                       top shift of linked object lines
2112
     */
2113
    protected function _pagehead(&$pdf, $object, $showaddress, $outputlangs, $outputlangsbis = null)
2114
    {
2115
        // phpcs:enable
2116
        global $conf, $langs;
2117
2118
        $ltrdirection = 'L';
2119
        if ($outputlangs->trans("DIRECTION") == 'rtl') {
2120
            $ltrdirection = 'R';
2121
        }
2122
2123
        // Load traductions files required by page
2124
        $outputlangs->loadLangs(array("main", "bills", "propal", "companies"));
2125
2126
        $default_font_size = pdf_getPDFFontSize($outputlangs);
2127
2128
        pdf_pagehead($pdf, $outputlangs, $this->page_hauteur);
2129
2130
        $pdf->SetTextColor(0, 0, 60);
2131
        $pdf->SetFont('', 'B', $default_font_size + 3);
2132
2133
        $w = 110;
2134
2135
        $posy = $this->marge_haute;
2136
        $posx = $this->page_largeur - $this->marge_droite - $w;
2137
2138
        $pdf->SetXY($this->marge_gauche, $posy);
2139
2140
        // Logo
2141
        if (!getDolGlobalInt('PDF_DISABLE_MYCOMPANY_LOGO')) {
2142
            if ($this->emetteur->logo) {
2143
                $logodir = $conf->mycompany->dir_output;
2144
                if (!empty($conf->mycompany->multidir_output[$object->entity])) {
2145
                    $logodir = $conf->mycompany->multidir_output[$object->entity];
2146
                }
2147
                if (!getDolGlobalInt('MAIN_PDF_USE_LARGE_LOGO')) {
2148
                    $logo = $logodir . '/logos/thumbs/' . $this->emetteur->logo_small;
2149
                } else {
2150
                    $logo = $logodir . '/logos/' . $this->emetteur->logo;
2151
                }
2152
                if (is_readable($logo)) {
2153
                    $height = pdf_getHeightForLogo($logo);
2154
                    $pdf->Image($logo, $this->marge_gauche, $posy, 0, $height); // width=0 (auto)
2155
                } else {
2156
                    $pdf->SetTextColor(200, 0, 0);
2157
                    $pdf->SetFont('', 'B', $default_font_size - 2);
2158
                    $pdf->MultiCell($w, 3, $outputlangs->transnoentities("ErrorLogoFileNotFound", $logo), 0, 'L');
2159
                    $pdf->MultiCell($w, 3, $outputlangs->transnoentities("ErrorGoToGlobalSetup"), 0, 'L');
2160
                }
2161
            } else {
2162
                $text = $this->emetteur->name;
2163
                $pdf->MultiCell($w, 4, $outputlangs->convToOutputCharset($text), 0, $ltrdirection);
2164
            }
2165
        }
2166
2167
        $pdf->SetFont('', 'B', $default_font_size + 3);
2168
        $pdf->SetXY($posx, $posy);
2169
        $pdf->SetTextColor(0, 0, 60);
2170
        $title = $outputlangs->transnoentities("PdfInvoiceTitle");
2171
        if ($object->type == 1) {
2172
            $title = $outputlangs->transnoentities("InvoiceReplacement");
2173
        }
2174
        if ($object->type == 2) {
2175
            $title = $outputlangs->transnoentities("InvoiceAvoir");
2176
        }
2177
        if ($object->type == 3) {
2178
            $title = $outputlangs->transnoentities("InvoiceDeposit");
2179
        }
2180
        if ($object->type == 4) {
2181
            $title = $outputlangs->transnoentities("InvoiceProForma");
2182
        }
2183
        if ($this->situationinvoice) {
2184
            $langs->loadLangs(array("other"));
2185
            $title = $outputlangs->transnoentities("PDFInvoiceSituation") . " " . $outputlangs->transnoentities("NumberingShort") . $object->situation_counter . " -";
2186
        }
2187
        if (getDolGlobalString('PDF_USE_ALSO_LANGUAGE_CODE') && is_object($outputlangsbis)) {
2188
            $title .= ' - ';
2189
            if ($object->type == 0) {
2190
                if ($this->situationinvoice) {
2191
                    $title .= $outputlangsbis->transnoentities("PDFInvoiceSituation");
2192
                }
2193
                $title .= $outputlangsbis->transnoentities("PdfInvoiceTitle");
2194
            } elseif ($object->type == 1) {
2195
                $title .= $outputlangsbis->transnoentities("InvoiceReplacement");
2196
            } elseif ($object->type == 2) {
2197
                $title .= $outputlangsbis->transnoentities("InvoiceAvoir");
2198
            } elseif ($object->type == 3) {
2199
                $title .= $outputlangsbis->transnoentities("InvoiceDeposit");
2200
            } elseif ($object->type == 4) {
2201
                $title .= $outputlangsbis->transnoentities("InvoiceProForma");
2202
            }
2203
        }
2204
        $title .= ' ' . $outputlangs->convToOutputCharset($object->ref);
2205
        if ($object->status == $object::STATUS_DRAFT) {
2206
            $pdf->SetTextColor(128, 0, 0);
2207
            $title .= ' - ' . $outputlangs->transnoentities("NotValidated");
2208
        }
2209
2210
        $pdf->MultiCell($w, 3, $title, '', 'R');
2211
2212
        $pdf->SetFont('', 'B', $default_font_size);
2213
2214
        /*
2215
         $posy += 5;
2216
         $pdf->SetXY($posx, $posy);
2217
         $pdf->SetTextColor(0, 0, 60);
2218
         $textref = $outputlangs->transnoentities("Ref")." : ".$outputlangs->convToOutputCharset($object->ref);
2219
         if ($object->statut == $object::STATUS_DRAFT) {
2220
         $pdf->SetTextColor(128, 0, 0);
2221
         $textref .= ' - '.$outputlangs->transnoentities("NotValidated");
2222
         }
2223
         $pdf->MultiCell($w, 4, $textref, '', 'R');*/
2224
2225
        $posy += 3;
2226
        $pdf->SetFont('', '', $default_font_size - 2);
2227
2228
        if ($object->ref_customer) {
2229
            $posy += 4;
2230
            $pdf->SetXY($posx, $posy);
2231
            $pdf->SetTextColor(0, 0, 60);
2232
            $pdf->MultiCell($w, 3, $outputlangs->transnoentities("RefCustomer") . " : " . dol_trunc($outputlangs->convToOutputCharset($object->ref_customer), 65), '', 'R');
2233
        }
2234
2235
        if (getDolGlobalString('PDF_SHOW_PROJECT_TITLE')) {
2236
            $object->fetch_projet();
2237
            if (!empty($object->project->ref)) {
2238
                $posy += 3;
2239
                $pdf->SetXY($posx, $posy);
2240
                $pdf->SetTextColor(0, 0, 60);
2241
                $pdf->MultiCell($w, 3, $outputlangs->transnoentities("Project") . " : " . (empty($object->project->title) ? '' : $object->project->title), '', 'R');
2242
            }
2243
        }
2244
2245
        if (getDolGlobalString('PDF_SHOW_PROJECT')) {
2246
            $object->fetch_projet();
2247
            if (!empty($object->project->ref)) {
2248
                $outputlangs->load("projects");
2249
                $posy += 3;
2250
                $pdf->SetXY($posx, $posy);
2251
                $pdf->SetTextColor(0, 0, 60);
2252
                $pdf->MultiCell($w, 3, $outputlangs->transnoentities("RefProject") . " : " . (empty($object->project->ref) ? '' : $object->project->ref), '', 'R');
2253
            }
2254
        }
2255
2256
        $objectidnext = $object->getIdReplacingInvoice('validated');
2257
        if ($object->type == 0 && $objectidnext) {
2258
            $objectreplacing = new Facture($this->db);
2259
            $objectreplacing->fetch($objectidnext);
2260
2261
            $posy += 3;
2262
            $pdf->SetXY($posx, $posy);
2263
            $pdf->SetTextColor(0, 0, 60);
2264
            $pdf->MultiCell($w, 3, $outputlangs->transnoentities("ReplacementByInvoice") . ' : ' . $outputlangs->convToOutputCharset($objectreplacing->ref), '', 'R');
2265
        }
2266
        if ($object->type == 1) {
2267
            $objectreplaced = new Facture($this->db);
2268
            $objectreplaced->fetch($object->fk_facture_source);
2269
2270
            $posy += 4;
2271
            $pdf->SetXY($posx, $posy);
2272
            $pdf->SetTextColor(0, 0, 60);
2273
            $pdf->MultiCell($w, 3, $outputlangs->transnoentities("ReplacementInvoice") . ' : ' . $outputlangs->convToOutputCharset($objectreplaced->ref), '', 'R');
2274
        }
2275
        if ($object->type == 2 && !empty($object->fk_facture_source)) {
2276
            $objectreplaced = new Facture($this->db);
2277
            $objectreplaced->fetch($object->fk_facture_source);
2278
2279
            $posy += 3;
2280
            $pdf->SetXY($posx, $posy);
2281
            $pdf->SetTextColor(0, 0, 60);
2282
            $pdf->MultiCell($w, 3, $outputlangs->transnoentities("CorrectionInvoice") . ' : ' . $outputlangs->convToOutputCharset($objectreplaced->ref), '', 'R');
2283
        }
2284
2285
        $posy += 4;
2286
        $pdf->SetXY($posx, $posy);
2287
        $pdf->SetTextColor(0, 0, 60);
2288
2289
        $title = $outputlangs->transnoentities("DateInvoice");
2290
        if (getDolGlobalString('PDF_USE_ALSO_LANGUAGE_CODE') && is_object($outputlangsbis)) {
2291
            $title .= ' - ' . $outputlangsbis->transnoentities("DateInvoice");
2292
        }
2293
        $pdf->MultiCell($w, 3, $title . " : " . dol_print_date($object->date, "day", false, $outputlangs, true), '', 'R');
2294
2295
        if (getDolGlobalString('INVOICE_POINTOFTAX_DATE')) {
2296
            $posy += 4;
2297
            $pdf->SetXY($posx, $posy);
2298
            $pdf->SetTextColor(0, 0, 60);
2299
            $pdf->MultiCell($w, 3, $outputlangs->transnoentities("DatePointOfTax") . " : " . dol_print_date($object->date_pointoftax, "day", false, $outputlangs), '', 'R');
2300
        }
2301
2302
        if ($object->type != 2) {
2303
            $posy += 3;
2304
            $pdf->SetXY($posx, $posy);
2305
            $pdf->SetTextColor(0, 0, 60);
2306
            $title = $outputlangs->transnoentities("DateDue");
2307
            if (getDolGlobalString('PDF_USE_ALSO_LANGUAGE_CODE') && is_object($outputlangsbis)) {
2308
                $title .= ' - ' . $outputlangsbis->transnoentities("DateDue");
2309
            }
2310
            $pdf->MultiCell($w, 3, $title . " : " . dol_print_date($object->date_lim_reglement, "day", false, $outputlangs, true), '', 'R');
2311
        }
2312
2313
        if (!getDolGlobalString('MAIN_PDF_HIDE_CUSTOMER_CODE') && $object->thirdparty->code_client) {
2314
            $posy += 3;
2315
            $pdf->SetXY($posx, $posy);
2316
            $pdf->SetTextColor(0, 0, 60);
2317
            $pdf->MultiCell($w, 3, $outputlangs->transnoentities("CustomerCode") . " : " . $outputlangs->transnoentities($object->thirdparty->code_client), '', 'R');
2318
        }
2319
2320
        // Get contact
2321
        if (getDolGlobalString('DOC_SHOW_FIRST_SALES_REP')) {
2322
            $arrayidcontact = $object->getIdContact('internal', 'SALESREPFOLL');
2323
            if (count($arrayidcontact) > 0) {
2324
                $usertmp = new User($this->db);
2325
                $usertmp->fetch($arrayidcontact[0]);
2326
                $posy += 4;
2327
                $pdf->SetXY($posx, $posy);
2328
                $pdf->SetTextColor(0, 0, 60);
2329
                $pdf->MultiCell($w, 3, $langs->transnoentities("SalesRepresentative") . " : " . $usertmp->getFullName($langs), '', 'R');
2330
            }
2331
        }
2332
2333
        $posy += 1;
2334
2335
        $top_shift = 0;
2336
        $shipp_shift = 0;
2337
        // Show list of linked objects
2338
        $current_y = $pdf->getY();
2339
        $posy = pdf_writeLinkedObjects($pdf, $object, $outputlangs, $posx, $posy, $w, 3, 'R', $default_font_size);
2340
        if ($current_y < $pdf->getY()) {
2341
            $top_shift = $pdf->getY() - $current_y;
2342
        }
2343
2344
        if ($showaddress) {
2345
            // Sender properties
2346
            $carac_emetteur = '';
2347
            // Add internal contact of object if defined
2348
            $arrayidcontact = $object->getIdContact('internal', 'BILLING');
2349
            if (count($arrayidcontact) > 0) {
2350
                $object->fetch_user($arrayidcontact[0]);
2351
                $labelbeforecontactname = ($outputlangs->transnoentities("FromContactName") != 'FromContactName' ? $outputlangs->transnoentities("FromContactName") : $outputlangs->transnoentities("Name"));
2352
                $carac_emetteur .= ($carac_emetteur ? "\n" : '') . $labelbeforecontactname . " " . $outputlangs->convToOutputCharset($object->user->getFullName($outputlangs));
2353
                $carac_emetteur .= "\n";
2354
            }
2355
2356
            $carac_emetteur .= pdf_build_address($outputlangs, $this->emetteur, $object->thirdparty, '', 0, 'source', $object);
2357
2358
            // Show sender
2359
            $posy = getDolGlobalString('MAIN_PDF_USE_ISO_LOCATION') ? 40 : 42;
2360
            $posy += $top_shift;
2361
            $posx = $this->marge_gauche;
2362
            if (getDolGlobalString('MAIN_INVERT_SENDER_RECIPIENT')) {
2363
                $posx = $this->page_largeur - $this->marge_droite - 80;
2364
            }
2365
2366
            $hautcadre = getDolGlobalString('MAIN_PDF_USE_ISO_LOCATION') ? 38 : 40;
2367
            $widthrecbox = getDolGlobalString('MAIN_PDF_USE_ISO_LOCATION') ? 92 : 82;
2368
2369
            // Show sender frame
2370
            if (!getDolGlobalString('MAIN_PDF_NO_SENDER_FRAME')) {
2371
                $pdf->SetTextColor(0, 0, 0);
2372
                $pdf->SetFont('', '', $default_font_size - 2);
2373
                $pdf->SetXY($posx, $posy - 5);
2374
                $pdf->MultiCell($widthrecbox, 5, $outputlangs->transnoentities("BillFrom"), 0, $ltrdirection);
2375
                $pdf->SetXY($posx, $posy);
2376
                $pdf->SetFillColor(230, 230, 230);
2377
                $pdf->MultiCell($widthrecbox, $hautcadre, "", 0, 'R', 1);
2378
                $pdf->SetTextColor(0, 0, 60);
2379
            }
2380
2381
            // Show sender name
2382
            if (!getDolGlobalString('MAIN_PDF_HIDE_SENDER_NAME')) {
2383
                $pdf->SetXY($posx + 2, $posy + 3);
2384
                $pdf->SetFont('', 'B', $default_font_size);
2385
                $pdf->MultiCell($widthrecbox - 2, 4, $outputlangs->convToOutputCharset($this->emetteur->name), 0, $ltrdirection);
2386
                $posy = $pdf->getY();
2387
            }
2388
2389
            // Show sender information
2390
            $pdf->SetXY($posx + 2, $posy);
2391
            $pdf->SetFont('', '', $default_font_size - 1);
2392
            $pdf->MultiCell($widthrecbox - 2, 4, $carac_emetteur, 0, $ltrdirection);
2393
2394
            // If BILLING contact defined on invoice, we use it
2395
            $usecontact = false;
2396
            $arrayidcontact = $object->getIdContact('external', 'BILLING');
2397
            if (count($arrayidcontact) > 0) {
2398
                $usecontact = true;
2399
                $result = $object->fetch_contact($arrayidcontact[0]);
2400
            }
2401
2402
            // Recipient name
2403
            if ($usecontact && ($object->contact->socid != $object->thirdparty->id && (!isset($conf->global->MAIN_USE_COMPANY_NAME_OF_CONTACT) || getDolGlobalString('MAIN_USE_COMPANY_NAME_OF_CONTACT')))) {
2404
                $thirdparty = $object->contact;
2405
            } else {
2406
                $thirdparty = $object->thirdparty;
2407
            }
2408
2409
            $carac_client_name = pdfBuildThirdpartyName($thirdparty, $outputlangs);
2410
2411
            $mode = 'target';
2412
            $carac_client = pdf_build_address($outputlangs, $this->emetteur, $object->thirdparty, ($usecontact ? $object->contact : ''), $usecontact, $mode, $object);
2413
2414
            // Show recipient
2415
            $widthrecbox = getDolGlobalString('MAIN_PDF_USE_ISO_LOCATION') ? 92 : 100;
2416
            if ($this->page_largeur < 210) {
2417
                $widthrecbox = 84; // To work with US executive format
2418
            }
2419
            $posy = getDolGlobalString('MAIN_PDF_USE_ISO_LOCATION') ? 40 : 42;
2420
            $posy += $top_shift;
2421
            $posx = $this->page_largeur - $this->marge_droite - $widthrecbox;
2422
            if (getDolGlobalString('MAIN_INVERT_SENDER_RECIPIENT')) {
2423
                $posx = $this->marge_gauche;
2424
            }
2425
2426
            // Show recipient frame
2427
            if (!getDolGlobalString('MAIN_PDF_NO_RECIPENT_FRAME')) {
2428
                $pdf->SetTextColor(0, 0, 0);
2429
                $pdf->SetFont('', '', $default_font_size - 2);
2430
                $pdf->SetXY($posx + 2, $posy - 5);
2431
                $pdf->MultiCell($widthrecbox - 2, 5, $outputlangs->transnoentities("BillTo"), 0, $ltrdirection);
2432
                $pdf->Rect($posx, $posy, $widthrecbox, $hautcadre);
2433
            }
2434
2435
            // Show recipient name
2436
            $pdf->SetXY($posx + 2, $posy + 3);
2437
            $pdf->SetFont('', 'B', $default_font_size);
2438
            // @phan-suppress-next-line PhanPluginSuspiciousParamOrder
2439
            $pdf->MultiCell($widthrecbox - 2, 2, $carac_client_name, 0, $ltrdirection);
2440
2441
            $posy = $pdf->getY();
2442
2443
            // Show recipient information
2444
            $pdf->SetFont('', '', $default_font_size - 1);
2445
            $pdf->SetXY($posx + 2, $posy);
2446
            // @phan-suppress-next-line PhanPluginSuspiciousParamOrder
2447
            $pdf->MultiCell($widthrecbox - 2, 4, $carac_client, 0, $ltrdirection);
2448
2449
            // Show shipping address
2450
            if (getDolGlobalInt('INVOICE_SHOW_SHIPPING_ADDRESS')) {
2451
                $idaddressshipping = $object->getIdContact('external', 'SHIPPING');
2452
2453
                if (!empty($idaddressshipping)) {
2454
                    $contactshipping = $object->fetch_Contact($idaddressshipping[0]);
2455
                    $companystatic = new Societe($this->db);
2456
                    $companystatic->fetch($object->contact->fk_soc);
2457
                    $carac_client_name_shipping = pdfBuildThirdpartyName($object->contact, $outputlangs);
2458
                    $carac_client_shipping = pdf_build_address($outputlangs, $this->emetteur, $companystatic, $object->contact, $usecontact, 'target', $object);
2459
                } else {
2460
                    $carac_client_name_shipping = pdfBuildThirdpartyName($object->thirdparty, $outputlangs);
2461
                    $carac_client_shipping = pdf_build_address($outputlangs, $this->emetteur, $object->thirdparty, '', 0, 'target', $object);
2462
                }
2463
                if (!empty($carac_client_shipping)) {
2464
                    $posy += $hautcadre;
2465
2466
                    // Show shipping frame
2467
                    $pdf->SetXY($posx + 2, $posy - 5);
2468
                    $pdf->SetFont('', '', $default_font_size - 2);
2469
                    $pdf->MultiCell($widthrecbox, '', $outputlangs->transnoentities('ShippingTo'), 0, 'L', 0);
2470
                    $pdf->Rect($posx, $posy, $widthrecbox, $hautcadre);
2471
2472
                    // Show shipping name
2473
                    $pdf->SetXY($posx + 2, $posy + 3);
2474
                    $pdf->SetFont('', 'B', $default_font_size);
2475
                    $pdf->MultiCell($widthrecbox - 2, 2, $carac_client_name_shipping, '', 'L');
2476
2477
                    $posy = $pdf->getY();
2478
2479
                    // Show shipping information
2480
                    $pdf->SetXY($posx + 2, $posy);
2481
                    $pdf->SetFont('', '', $default_font_size - 1);
2482
                    $pdf->MultiCell($widthrecbox - 2, 2, $carac_client_shipping, '', 'L');
2483
                    $shipp_shift += $hautcadre;
2484
                }
2485
            }
2486
        }
2487
2488
        $pdf->SetTextColor(0, 0, 0);
2489
2490
        $pagehead = array('top_shift' => $top_shift, 'shipp_shift' => $shipp_shift);
2491
2492
        return $pagehead;
2493
    }
2494
2495
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
2496
2497
    /**
2498
     *      Show footer of page. Need this->emetteur object
2499
     *
2500
     * @param TCPDF $pdf PDF
2501
     * @param Facture $object Object to show
2502
     * @param Translate $outputlangs Object lang for output
2503
     * @param int $hidefreetext 1=Hide free text
2504
     * @param int $heightforqrinvoice Height for QR invoices
2505
     * @return int                             Return height of bottom margin including footer text
2506
     */
2507
    protected function _pagefoot(&$pdf, $object, $outputlangs, $hidefreetext = 0, $heightforqrinvoice = 0)
2508
    {
2509
        $showdetails = getDolGlobalInt('MAIN_GENERATE_DOCUMENTS_SHOW_FOOT_DETAILS', 0);
2510
        return pdf_pagefoot($pdf, $outputlangs, 'INVOICE_FREE_TEXT', $this->emetteur, $heightforqrinvoice + $this->marge_basse, $this->marge_gauche, $this->page_hauteur, $object, $showdetails, $hidefreetext, $this->page_largeur, $this->watermark);
2511
    }
2512
2513
    /**
2514
     *  Define Array Column Field
2515
     *
2516
     * @param Facture $object common object
2517
     * @param Translate $outputlangs langs
2518
     * @param int $hidedetails Do not show line details
2519
     * @param int $hidedesc Do not show desc
2520
     * @param int $hideref Do not show ref
2521
     * @return void
2522
     */
2523
    public function defineColumnField($object, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0)
2524
    {
2525
        global $hookmanager;
2526
2527
        // Default field style for content
2528
        $this->defaultContentsFieldsStyle = array(
2529
            'align' => 'R', // R,C,L
2530
            'padding' => array(1, 0.5, 1, 0.5), // Like css 0 => top , 1 => right, 2 => bottom, 3 => left
2531
        );
2532
2533
        // Default field style for content
2534
        $this->defaultTitlesFieldsStyle = array(
2535
            'align' => 'C', // R,C,L
2536
            'padding' => array(0.5, 0, 0.5, 0), // Like css 0 => top , 1 => right, 2 => bottom, 3 => left
2537
        );
2538
2539
        /*
2540
         * For example
2541
         $this->cols['theColKey'] = array(
2542
         'rank' => $rank, // int : use for ordering columns
2543
         'width' => 20, // the column width in mm
2544
         'title' => array(
2545
         'textkey' => 'yourLangKey', // if there is no label, yourLangKey will be translated to replace label
2546
         'label' => ' ', // the final label : used fore final generated text
2547
         'align' => 'L', // text alignment :  R,C,L
2548
         'padding' => array(0.5,0.5,0.5,0.5), // Like css 0 => top , 1 => right, 2 => bottom, 3 => left
2549
         ),
2550
         'content' => array(
2551
         'align' => 'L', // text alignment :  R,C,L
2552
         'padding' => array(0.5,0.5,0.5,0.5), // Like css 0 => top , 1 => right, 2 => bottom, 3 => left
2553
         ),
2554
         );
2555
         */
2556
2557
        $rank = 0; // do not use negative rank
2558
        $this->cols['position'] = array(
2559
            'rank' => $rank,
2560
            'width' => 10,
2561
            'status' => getDolGlobalInt('PDF_SPONGE_ADD_POSITION') ? true : (getDolGlobalInt('PDF_ADD_POSITION') ? true : false),
2562
            'title' => array(
2563
                'textkey' => '#', // use lang key is useful in somme case with module
2564
                'align' => 'C',
2565
                // 'textkey' => 'yourLangKey', // if there is no label, yourLangKey will be translated to replace label
2566
                // 'label' => ' ', // the final label
2567
                'padding' => array(0.5, 0.5, 0.5, 0.5), // Like css 0 => top , 1 => right, 2 => bottom, 3 => left
2568
            ),
2569
            'content' => array(
2570
                'align' => 'C',
2571
                'padding' => array(1, 0.5, 1, 1.5), // Like css 0 => top , 1 => right, 2 => bottom, 3 => left
2572
            ),
2573
        );
2574
2575
        $rank = 5; // do not use negative rank
2576
        $this->cols['desc'] = array(
2577
            'rank' => $rank,
2578
            'width' => false, // only for desc
2579
            'status' => true,
2580
            'title' => array(
2581
                'textkey' => 'Designation', // use lang key is useful in somme case with module
2582
                'align' => 'L',
2583
                // 'textkey' => 'yourLangKey', // if there is no label, yourLangKey will be translated to replace label
2584
                // 'label' => ' ', // the final label
2585
                'padding' => array(0.5, 0.5, 0.5, 0.5), // Like css 0 => top , 1 => right, 2 => bottom, 3 => left
2586
            ),
2587
            'content' => array(
2588
                'align' => 'L',
2589
                'padding' => array(1, 0.5, 1, 1.5), // Like css 0 => top , 1 => right, 2 => bottom, 3 => left
2590
            ),
2591
        );
2592
2593
        // Image of product
2594
        $rank = $rank + 10;
2595
        $this->cols['photo'] = array(
2596
            'rank' => $rank,
2597
            'width' => getDolGlobalInt('MAIN_DOCUMENTS_WITH_PICTURE_WIDTH', 20), // in mm
2598
            'status' => false,
2599
            'title' => array(
2600
                'textkey' => 'Photo',
2601
                'label' => ' '
2602
            ),
2603
            'content' => array(
2604
                'padding' => array(0, 0, 0, 0), // Like css 0 => top , 1 => right, 2 => bottom, 3 => left
2605
            ),
2606
            'border-left' => false, // remove left line separator
2607
        );
2608
2609
        if (getDolGlobalString('MAIN_GENERATE_INVOICES_WITH_PICTURE') && !empty($this->atleastonephoto)) {
2610
            $this->cols['photo']['status'] = true;
2611
        }
2612
2613
2614
        $rank = $rank + 10;
2615
        $this->cols['vat'] = array(
2616
            'rank' => $rank,
2617
            'status' => false,
2618
            'width' => 16, // in mm
2619
            'title' => array(
2620
                'textkey' => 'VAT'
2621
            ),
2622
            'border-left' => true, // add left line separator
2623
        );
2624
2625
        if (!getDolGlobalString('MAIN_GENERATE_DOCUMENTS_WITHOUT_VAT') && !getDolGlobalString('MAIN_GENERATE_DOCUMENTS_WITHOUT_VAT_COLUMN')) {
2626
            $this->cols['vat']['status'] = true;
2627
        }
2628
2629
        $rank = $rank + 10;
2630
        $this->cols['subprice'] = array(
2631
            'rank' => $rank,
2632
            'width' => 19, // in mm
2633
            'status' => true,
2634
            'title' => array(
2635
                'textkey' => 'PriceUHT'
2636
            ),
2637
            'border-left' => true, // add left line separator
2638
        );
2639
2640
        // Adapt dynamically the width of subprice, if text is too long.
2641
        $tmpwidth = 0;
2642
        $nblines = count($object->lines);
2643
        for ($i = 0; $i < $nblines; $i++) {
2644
            $tmpwidth2 = dol_strlen(dol_string_nohtmltag(pdf_getlineupexcltax($object, $i, $outputlangs, $hidedetails)));
2645
            $tmpwidth = max($tmpwidth, $tmpwidth2);
2646
        }
2647
        if ($tmpwidth > 10) {
2648
            $this->cols['subprice']['width'] += (2 * ($tmpwidth - 10));
2649
        }
2650
2651
        $rank = $rank + 10;
2652
        $this->cols['qty'] = array(
2653
            'rank' => $rank,
2654
            'width' => 16, // in mm
2655
            'status' => true,
2656
            'title' => array(
2657
                'textkey' => 'Qty'
2658
            ),
2659
            'border-left' => true, // add left line separator
2660
        );
2661
2662
        $rank = $rank + 10;
2663
        $this->cols['progress'] = array(
2664
            'rank' => $rank,
2665
            'width' => 19, // in mm
2666
            'status' => false,
2667
            'title' => array(
2668
                'textkey' => 'ProgressShort'
2669
            ),
2670
            'border-left' => true, // add left line separator
2671
        );
2672
2673
        if ($this->situationinvoice) {
2674
            $this->cols['progress']['status'] = true;
2675
        }
2676
2677
        $rank = $rank + 10;
2678
        $this->cols['unit'] = array(
2679
            'rank' => $rank,
2680
            'width' => 11, // in mm
2681
            'status' => false,
2682
            'title' => array(
2683
                'textkey' => 'Unit'
2684
            ),
2685
            'border-left' => true, // add left line separator
2686
        );
2687
        if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
2688
            $this->cols['unit']['status'] = true;
2689
        }
2690
2691
        $rank = $rank + 10;
2692
        $this->cols['discount'] = array(
2693
            'rank' => $rank,
2694
            'width' => 13, // in mm
2695
            'status' => false,
2696
            'title' => array(
2697
                'textkey' => 'ReductionShort'
2698
            ),
2699
            'border-left' => true, // add left line separator
2700
        );
2701
        if ($this->atleastonediscount) {
2702
            $this->cols['discount']['status'] = true;
2703
        }
2704
2705
        $rank = $rank + 1000; // add a big offset to be sure is the last col because default extrafield rank is 100
2706
        $this->cols['totalexcltax'] = array(
2707
            'rank' => $rank,
2708
            'width' => 26, // in mm
2709
            'status' => !getDolGlobalString('PDF_PROPAL_HIDE_PRICE_EXCL_TAX') ? true : false,
2710
            'title' => array(
2711
                'textkey' => 'TotalHTShort'
2712
            ),
2713
            'border-left' => true, // add left line separator
2714
        );
2715
2716
        $rank = $rank + 1010; // add a big offset to be sure is the last col because default extrafield rank is 100
2717
        $this->cols['totalincltax'] = array(
2718
            'rank' => $rank,
2719
            'width' => 26, // in mm
2720
            'status' => !getDolGlobalString('PDF_PROPAL_SHOW_PRICE_INCL_TAX') ? false : true,
2721
            'title' => array(
2722
                'textkey' => 'TotalTTCShort'
2723
            ),
2724
            'border-left' => true, // add left line separator
2725
        );
2726
2727
        // Add extrafields cols
2728
        if (!empty($object->lines)) {
2729
            $line = reset($object->lines);
2730
            $this->defineColumnExtrafield($line, $outputlangs, $hidedetails);
2731
        }
2732
2733
        $parameters = array(
2734
            'object' => $object,
2735
            'outputlangs' => $outputlangs,
2736
            'hidedetails' => $hidedetails,
2737
            'hidedesc' => $hidedesc,
2738
            'hideref' => $hideref
2739
        );
2740
2741
        $reshook = $hookmanager->executeHooks('defineColumnField', $parameters, $this); // Note that $object may have been modified by hook
2742
        if ($reshook < 0) {
2743
            setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
2744
        } elseif (empty($reshook)) {
2745
            // @phan-suppress-next-line PhanPluginSuspiciousParamOrderInternal
2746
            $this->cols = array_replace($this->cols, $hookmanager->resArray); // array_replace is used to preserve keys
2747
        } else {
2748
            $this->cols = $hookmanager->resArray;
2749
        }
2750
    }
2751
}
2752