pdf_octopus   F
last analyzed

Complexity

Total Complexity 547

Size/Duplication

Total Lines 3673
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 2042
dl 0
loc 3673
rs 0.8
c 0
b 0
f 0
wmc 547

18 Methods

Rating   Name   Duplication   Size   Complexity  
C _tableau() 0 72 12
F _pagehead() 0 377 65
F drawTotalTable() 0 357 80
B displayRetainedWarranty() 0 35 11
A getInfosLineLastSituation() 0 25 5
A _pagefoot() 0 4 1
C getDataSituation() 0 122 16
A btpGetInvoiceAmounts() 0 68 5
B __construct() 0 76 6
F resumeLastPage() 0 417 59
A sumSituation() 0 19 5
F drawInfoTable() 0 223 58
F write_file() 0 891 166
D drawPaymentsTable() 0 131 16
A liste_modeles() 0 4 1
A printRectBtp() 0 10 4
F _tableFirstPage() 0 201 19
F defineColumnField() 0 275 18

How to fix   Complexity   

Complex Class

Complex classes like pdf_octopus often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use pdf_octopus, and based on these observations, apply Extract Interface, too.

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 Garcia               <[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-2024  Alexandre Spangaro          <[email protected]>
15
 * Copyright (C) 2022-2024  Eric Seigne                 <[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\Compta\Classes\FactureLigne;
36
use Dolibarr\Code\Core\Classes\DiscountAbsolute;
37
use Dolibarr\Code\Core\Classes\Form;
38
use Dolibarr\Code\Core\Classes\HookManager;
39
use Dolibarr\Code\Core\Classes\Translate;
40
use Dolibarr\Code\Facture\Classes\ModelePDFFactures;
41
use Dolibarr\Code\Product\Classes\Product;
42
use Dolibarr\Code\Societe\Classes\CompanyBankAccount;
43
use Dolibarr\Code\Societe\Classes\Societe;
44
use Dolibarr\Code\User\Classes\User;
45
use Dolibarr\Lib\ViewMain;
46
47
/**
48
 *  \file       htdocs/core/modules/facture/doc/pdf_octopus.modules.php
49
 *  \ingroup    facture
50
 *  \brief      File of class to generate customers invoices from octopus model
51
 */
52
53
require_once constant('DOL_DOCUMENT_ROOT') . '/core/lib/company.lib.php';
54
require_once constant('DOL_DOCUMENT_ROOT') . '/core/lib/functions2.lib.php';
55
require_once constant('DOL_DOCUMENT_ROOT') . '/core/lib/pdf.lib.php';
56
require_once constant('DOL_DOCUMENT_ROOT') . '/core/lib/price.lib.php';
57
58
59
/**
60
 *  Class to manage PDF invoice template octopus
61
 */
62
class pdf_octopus extends ModelePDFFactures
63
{
64
    /**
65
     * @var DoliDB Database handler
66
     */
67
    public $db;
68
69
    /**
70
     * @var string model name
71
     */
72
    public $name;
73
74
    /**
75
     * @var string model description (short text)
76
     */
77
    public $description;
78
79
    /**
80
     * @var int     Save the name of generated file as the main doc when generating a doc with this template
81
     */
82
    public $update_main_doc_field;
83
84
    /**
85
     * @var string document type
86
     */
87
    public $type;
88
89
    /**
90
     * Dolibarr version of the loaded document
91
     * @var string
92
     */
93
    public $version = 'disabled';   // Disabled by default. Enabled in constructor if option INVOICE_USE_SITUATION is 2.
94
95
    /**
96
     * @var int height for info total
97
     */
98
    public $heightforinfotot;
99
100
    /**
101
     * @var int height for free text
102
     */
103
    public $heightforfreetext;
104
105
    /**
106
     * @var int height for footer
107
     */
108
    public $heightforfooter;
109
110
    /**
111
     * @var int tab_top
112
     */
113
    public $tab_top;
114
115
    /**
116
     * @var int tab_top_newpage
117
     */
118
    public $tab_top_newpage;
119
120
    /**
121
     * Issuer
122
     * @var Societe Object that emits
123
     */
124
    public $emetteur;
125
126
    /**
127
     * @var bool Situation invoice type
128
     */
129
    public $situationinvoice;
130
131
132
    /**
133
     * @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
134
     */
135
    public $cols;
136
137
    /**
138
     * @var int Category of operation
139
     */
140
    public $categoryOfOperation = -1; // unknown by default
141
142
    /**
143
     * Situation invoices
144
     *
145
     * @var array{derniere_situation:Facture,date_derniere_situation:int,current:array} Data of situation
146
     */
147
    public $TDataSituation;
148
149
    /**
150
     * @var int posx cumul anterieur
151
     */
152
    public $posx_cumul_anterieur;
153
154
    /**
155
     * @var int posx new cumul
156
     */
157
    public $posx_new_cumul;
158
159
    /**
160
     * @var int posx current
161
     */
162
    public $posx_current;
163
164
    /**
165
     * @var int tabTitleHeight
166
     */
167
    public $tabTitleHeight;
168
169
    /**
170
     * @var int is rg
171
     */
172
    public $is_rg;
173
174
    /**
175
     * @var bool franchise
176
     */
177
    public $franchise;
178
179
    /**
180
     * @var int
181
     */
182
    public $tplidx;
183
184
    /**
185
     *  Constructor
186
     *
187
     * @param DoliDB $db Database handler
188
     */
189
    public function __construct($db)
190
    {
191
        global $conf, $langs, $mysoc, $object;
192
193
        // for retro compatibility
194
        if (getDolGlobalString('INVOICE_USE_SITUATION_RETAINED_WARRANTY') && !getDolGlobalString('INVOICE_USE_RETAINED_WARRANTY')) {
195
            // before it was only for final situation invoice
196
            $conf->global->INVOICE_USE_RETAINED_WARRANTY = $conf->global->INVOICE_USE_SITUATION_RETAINED_WARRANTY;
197
            $conf->global->USE_RETAINED_WARRANTY_ONLY_FOR_SITUATION_FINAL = 1;
198
        }
199
200
        // If hidden option INVOICE_USE_SITUATION is set to 2, we can show the invoice situation template
201
        if (getDolGlobalString('INVOICE_USE_SITUATION') == 2) {
202
            $this->version = 'dolibarr';
203
        }
204
205
        // Translations
206
        $langs->loadLangs(array("main", "bills"));
207
208
        $this->db = $db;
209
        $this->name = "octopus";
210
        $this->description = $langs->trans('PDFOctopusDescription');
211
        $this->update_main_doc_field = 1; // Save the name of generated file as the main doc when generating a doc with this template
212
213
        // Dimension page
214
        $this->type = 'pdf';
215
        $formatarray = pdf_getFormat();
216
        $this->page_largeur = $formatarray['width'];
217
        $this->page_hauteur = $formatarray['height'];
218
        $this->format = array($this->page_largeur, $this->page_hauteur);
219
        $this->marge_gauche = getDolGlobalInt('MAIN_PDF_MARGIN_LEFT', 10);
220
        $this->marge_droite = getDolGlobalInt('MAIN_PDF_MARGIN_RIGHT', 10);
221
        $this->marge_haute = getDolGlobalInt('MAIN_PDF_MARGIN_TOP', 10);
222
        $this->marge_basse = getDolGlobalInt('MAIN_PDF_MARGIN_BOTTOM', 10);
223
224
        $this->posx_cumul_anterieur = 94;
225
        $this->posx_new_cumul = 130;
226
        $this->posx_current = 166;
227
228
        $this->option_logo = 1; // Display logo
229
        $this->option_tva = 1; // Manage the vat option FACTURE_TVAOPTION
230
        $this->option_modereg = 1; // Display payment mode
231
        $this->option_condreg = 1; // Display payment terms
232
        $this->option_multilang = 1; // Available in several languages
233
        $this->option_escompte = 1; // Displays if there has been a discount
234
        $this->option_credit_note = 1; // Support credit notes
235
        $this->option_freetext = 1; // Support add of a personalised text
236
        $this->option_draft_watermark = 1; // Support add of a watermark on drafts
237
        $this->watermark = '';
238
        $this->franchise = !$mysoc->tva_assuj; // not used ?
239
240
        // Get source company
241
        $this->emetteur = $mysoc;
242
        if (empty($this->emetteur->country_code)) {
243
            $this->emetteur->country_code = substr($langs->defaultlang, -2); // By default, if was not defined
244
        }
245
246
        // Define position of columns
247
        $this->posxdesc = $this->marge_gauche + 1; // used for notes and other stuff
248
249
250
        $this->tabTitleHeight = 8; // default height (2 lines due to overtitle)
251
252
        //  Use new system for position of columns, view  $this->defineColumnField()
253
254
        $this->tva = array();
255
        $this->tva_array = array();
256
        $this->localtax1 = array();
257
        $this->localtax2 = array();
258
        $this->atleastoneratenotnull = 0;
259
        $this->atleastonediscount = 0;
260
        $this->situationinvoice = true;
261
        if (!empty($object)) {
262
            $this->TDataSituation = $this->getDataSituation($object);
263
        } else {
264
            dol_syslog("object is empty, do not call getDataSituation...");
265
        }
266
    }
267
268
269
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
270
271
    /**
272
     *  Function to build pdf onto disk
273
     *
274
     * @param Facture $object Object to generate
275
     * @param Translate $outputlangs Lang output object
276
     * @param string $srctemplatepath Full path of source filename for generator using a template file
277
     * @param int $hidedetails Do not show line details
278
     * @param int $hidedesc Do not show desc
279
     * @param int $hideref Do not show ref
280
     * @return     int                             1=OK, 0=KO
281
     */
282
    public function write_file($object, $outputlangs, $srctemplatepath = '', $hidedetails = 0, $hidedesc = 0, $hideref = 0)
283
    {
284
        // phpcs:enable
285
        global $user, $langs, $conf, $mysoc, $db, $hookmanager, $nblines;
286
287
        dol_syslog("write_file outputlangs->defaultlang=" . (is_object($outputlangs) ? $outputlangs->defaultlang : 'null'));
288
289
        if (!is_object($outputlangs)) {
290
            $outputlangs = $langs;
291
        }
292
        // For backward compatibility with FPDF, force output charset to ISO, because FPDF expect text to be encoded in ISO
293
        if (getDolGlobalString('MAIN_USE_FPDF')) {
294
            $outputlangs->charset_output = 'ISO-8859-1';
295
        }
296
297
        // Load translation files required by the page
298
        $outputlangs->loadLangs(array("main", "bills", "products", "dict", "companies"));
299
300
        global $outputlangsbis;
301
        $outputlangsbis = null;
302
        if (getDolGlobalString('PDF_USE_ALSO_LANGUAGE_CODE') && $outputlangs->defaultlang != getDolGlobalString('PDF_USE_ALSO_LANGUAGE_CODE')) {
303
            $outputlangsbis = new Translate('', $conf);
304
            $outputlangsbis->setDefaultLang(getDolGlobalString('PDF_USE_ALSO_LANGUAGE_CODE'));
305
            $outputlangsbis->loadLangs(array("main", "bills", "products", "dict", "companies"));
306
        }
307
308
        if (empty($object) || ($object->type != Facture::TYPE_SITUATION && ($object->type != Facture::TYPE_CREDIT_NOTE && !empty($object->situation_cycle_ref)))) {
309
            setEventMessage($langs->trans('WarningsObjectIsNotASituation'), 'warnings');
310
            return 1;
311
        }
312
        // Show Draft Watermark
313
        if ($object->status == $object::STATUS_DRAFT && (getDolGlobalString('FACTURE_DRAFT_WATERMARK'))) {
314
            $this->watermark = getDolGlobalString('FACTURE_DRAFT_WATERMARK');
315
        }
316
317
        $nblines = count($object->lines);
318
319
        $hidetop = 0;
320
        if (getDolGlobalString('MAIN_PDF_DISABLE_COL_HEAD_TITLE')) {
321
            $hidetop = getDolGlobalString('MAIN_PDF_DISABLE_COL_HEAD_TITLE');
322
        }
323
324
        // Loop on each lines to detect if there is at least one image to show
325
        $realpatharray = array();
326
        $this->atleastonephoto = false;
327
        if (getDolGlobalString('MAIN_GENERATE_INVOICES_WITH_PICTURE')) {
328
            $objphoto = new Product($this->db);
329
330
            for ($i = 0; $i < $nblines; $i++) {
331
                if (empty($object->lines[$i]->fk_product)) {
332
                    continue;
333
                }
334
335
                $objphoto->fetch($object->lines[$i]->fk_product);
336
                //var_dump($objphoto->ref);exit;
337
                $pdir = array();
338
                if (getDolGlobalInt('PRODUCT_USE_OLD_PATH_FOR_PHOTO')) {
339
                    $pdir[0] = get_exdir($objphoto->id, 2, 0, 0, $objphoto, 'product') . $objphoto->id . "/photos/";
340
                    $pdir[1] = get_exdir(0, 0, 0, 0, $objphoto, 'product') . dol_sanitizeFileName($objphoto->ref) . '/';
341
                } else {
342
                    $pdir[0] = get_exdir(0, 0, 0, 0, $objphoto, 'product'); // default
343
                    $pdir[1] = get_exdir($objphoto->id, 2, 0, 0, $objphoto, 'product') . $objphoto->id . "/photos/"; // alternative
344
                }
345
346
                $arephoto = false;
347
                foreach ($pdir as $midir) {
348
                    if (!$arephoto) {
349
                        if ($conf->entity != $objphoto->entity) {
350
                            $dir = $conf->product->multidir_output[$objphoto->entity] . '/' . $midir; //Check repertories of current entities
351
                        } else {
352
                            $dir = $conf->product->dir_output . '/' . $midir; //Check repertory of the current product
353
                        }
354
355
                        foreach ($objphoto->liste_photos($dir, 1) as $key => $obj) {
356
                            if (!getDolGlobalInt('CAT_HIGH_QUALITY_IMAGES')) {      // If CAT_HIGH_QUALITY_IMAGES not defined, we use thumb if defined and then original photo
357
                                if ($obj['photo_vignette']) {
358
                                    $filename = $obj['photo_vignette'];
359
                                } else {
360
                                    $filename = $obj['photo'];
361
                                }
362
                            } else {
363
                                $filename = $obj['photo'];
364
                            }
365
366
                            $realpath = $dir . $filename;
367
                            $arephoto = true;
368
                            $this->atleastonephoto = true;
369
                        }
370
                    }
371
                }
372
373
                if ($realpath && $arephoto) {
374
                    $realpatharray[$i] = $realpath;
375
                }
376
            }
377
        }
378
379
        //if (count($realpatharray) == 0) $this->posxpicture=$this->posxtva;
380
381
        if ($conf->facture->multidir_output[$conf->entity]) {
382
            $object->fetch_thirdparty();
383
384
            $deja_regle = $object->getSommePaiement((isModEnabled("multicurrency") && $object->multicurrency_tx != 1) ? 1 : 0);
385
            $amount_credit_notes_included = $object->getSumCreditNotesUsed((isModEnabled("multicurrency") && $object->multicurrency_tx != 1) ? 1 : 0);
386
            $amount_deposits_included = $object->getSumDepositsUsed((isModEnabled("multicurrency") && $object->multicurrency_tx != 1) ? 1 : 0);
387
388
            // Definition of $dir and $file
389
            if ($object->specimen) {
390
                $dir = $conf->facture->multidir_output[$conf->entity];
391
                $file = $dir . "/SPECIMEN.pdf";
392
            } else {
393
                $objectref = dol_sanitizeFileName($object->ref);
394
                $dir = $conf->facture->multidir_output[$object->entity] . "/" . $objectref;
395
                $file = $dir . "/" . $objectref . ".pdf";
396
            }
397
            if (!file_exists($dir)) {
398
                if (dol_mkdir($dir) < 0) {
399
                    $this->error = $langs->transnoentities("ErrorCanNotCreateDir", $dir);
400
                    return 0;
401
                }
402
            }
403
404
            if (file_exists($dir)) {
405
                // Add pdfgeneration hook
406
                if (!is_object($hookmanager)) {
407
                    $hookmanager = new HookManager($this->db);
408
                }
409
                $hookmanager->initHooks(array('pdfgeneration'));
410
                $parameters = array('file' => $file, 'object' => $object, 'outputlangs' => $outputlangs);
411
                global $action;
412
                $reshook = $hookmanager->executeHooks('beforePDFCreation', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
413
414
                // Set nblines with the new facture lines content after hook
415
                $nblines = count($object->lines);
416
                $nbpayments = count($object->getListOfPayments());
417
                $nbprevsituation = is_array($object->tab_previous_situation_invoice) ? count($object->tab_previous_situation_invoice) : 0;
418
419
                // Create pdf instance
420
                $pdf = pdf_getInstance($this->format);
421
                //$default_font_size = pdf_getPDFFontSize($outputlangs); // Must be after pdf_getInstance
422
                $default_font_size = 9;
423
                $pdf->SetAutoPageBreak(1, 0);
424
425
                // compute height for situation invoices
426
                $this->heightforinfotot = 45;   // Height reserved to output the info and total part and payment part
427
                if (!getDolGlobalString('INVOICE_NO_PAYMENT_DETAILS') && $nbpayments > 0) {
428
                    $this->heightforinfotot += 4 * ($nbpayments + 3);
429
                }
430
                if ($nbprevsituation > 0) {
431
                    $this->heightforinfotot += 4 * ($nbprevsituation + 3);
432
                }
433
                $this->heightforfreetext = (getDolGlobalInt('MAIN_PDF_FREETEXT_HEIGHT', 5)); // Height reserved to output the free text on last page
434
                $this->heightforfooter = $this->marge_basse + (!getDolGlobalString('MAIN_GENERATE_DOCUMENTS_SHOW_FOOT_DETAILS') ? 12 : 22); // Height reserved to output the footer (value include bottom margin)
435
436
                if (class_exists('TCPDF')) {
437
                    $pdf->setPrintHeader(false);
438
                    $pdf->setPrintFooter(false);
439
                }
440
                $pdf->SetFont(pdf_getPDFFont($outputlangs));
441
442
                // Set path to the background PDF File
443
                if (getDolGlobalString('MAIN_ADD_PDF_BACKGROUND')) {
444
                    $logodir = $conf->mycompany->dir_output;
445
                    if (!empty($conf->mycompany->multidir_output[$object->entity])) {
446
                        $logodir = $conf->mycompany->multidir_output[$object->entity];
447
                    }
448
                    $pagecount = $pdf->setSourceFile($logodir . '/' . getDolGlobalString('MAIN_ADD_PDF_BACKGROUND'));
449
                    $this->tplidx = $pdf->importPage(1);
450
                }
451
452
                $pdf->Open();
453
                $pagenb = 0;
454
                $pdf->SetDrawColor(128, 128, 128);
455
456
                $pdf->SetTitle($outputlangs->convToOutputCharset($object->ref));
457
                $pdf->SetSubject($outputlangs->transnoentities("PdfInvoiceSituationTitle"));
458
                $pdf->SetCreator("Dolibarr " . DOL_VERSION);
459
                $pdf->SetAuthor($mysoc->name . ($user->id > 0 ? ' - ' . $outputlangs->convToOutputCharset($user->getFullName($outputlangs)) : ''));
460
                $pdf->SetKeyWords($outputlangs->convToOutputCharset($object->ref) . " " . $outputlangs->transnoentities("PdfInvoiceTitle") . " " . $outputlangs->convToOutputCharset($object->thirdparty->name));
461
                if (getDolGlobalString('MAIN_DISABLE_PDF_COMPRESSION')) {
462
                    $pdf->SetCompression(false);
463
                }
464
465
                // Set certificate
466
                $cert = empty($user->conf->CERTIFICATE_CRT) ? '' : $user->conf->CERTIFICATE_CRT;
467
                $certprivate = empty($user->conf->CERTIFICATE_CRT_PRIVATE) ? '' : $user->conf->CERTIFICATE_CRT_PRIVATE;
468
                // If user has no certificate, we try to take the company one
469
                if (!$cert) {
470
                    $cert = getDolGlobalString('CERTIFICATE_CRT', '');
471
                }
472
                if (!$certprivate) {
473
                    $certprivate = getDolGlobalString('CERTIFICATE_CRT_PRIVATE', '');
474
                }
475
                // If a certificate is found
476
                if ($cert) {
477
                    $info = array(
478
                        'Name' => $this->emetteur->name,
479
                        'Location' => getCountry($this->emetteur->country_code, 0),
480
                        'Reason' => 'INVOICE',
481
                        'ContactInfo' => $this->emetteur->email
482
                    );
483
                    $pdf->setSignature($cert, $certprivate, $this->emetteur->name, '', 2, $info);
484
                }
485
486
                // @phan-suppress-next-line PhanPluginSuspiciousParamOrder
487
                $pdf->SetMargins($this->marge_gauche, $this->marge_haute, $this->marge_droite); // Left, Top, Right
488
489
                // Set $this->atleastonediscount if you have at least one discount
490
                // and determine category of operation
491
                $categoryOfOperation = 0;
492
                $nbProduct = 0;
493
                $nbService = 0;
494
                for ($i = 0; $i < $nblines; $i++) {
495
                    if ($object->lines[$i]->remise_percent) {
496
                        $this->atleastonediscount++;
497
                    }
498
499
                    // determine category of operation
500
                    if ($categoryOfOperation < 2) {
501
                        $lineProductType = $object->lines[$i]->product_type;
502
                        if ($lineProductType == Product::TYPE_PRODUCT) {
503
                            $nbProduct++;
504
                        } elseif ($lineProductType == Product::TYPE_SERVICE) {
505
                            $nbService++;
506
                        }
507
                        if ($nbProduct > 0 && $nbService > 0) {
508
                            // mixed products and services
509
                            $categoryOfOperation = 2;
510
                        }
511
                    }
512
                }
513
                // determine category of operation
514
                if ($categoryOfOperation <= 0) {
515
                    // only services
516
                    if ($nbProduct == 0 && $nbService > 0) {
517
                        $categoryOfOperation = 1;
518
                    }
519
                }
520
                $this->categoryOfOperation = $categoryOfOperation;
521
522
                // New page
523
                $pdf->AddPage();
524
                if (!empty($this->tplidx)) {
525
                    $pdf->useTemplate($this->tplidx);
526
                }
527
                $pagenb++;
528
529
                // Output header (logo, ref and address blocks). This is first call for first page.
530
                $pagehead = $this->_pagehead($pdf, $object, 1, $outputlangs, $outputlangsbis);
531
                $top_shift = $pagehead['top_shift'];
532
                $shipp_shift = $pagehead['shipp_shift'];
533
                $pdf->SetFont('', '', $default_font_size - 1);
534
                $pdf->MultiCell(0, 3, ''); // Set interline to 3
535
                $pdf->SetTextColor(0, 0, 0);
536
537
                // $pdf->GetY() here can't be used. It is bottom of the second address box but first one may be higher
538
539
                // $this->tab_top is y where we must continue content (90 = 42 + 48: 42 is height of logo and ref, 48 is address blocks)
540
                $this->tab_top = 90 + $top_shift + $shipp_shift;        // top_shift is an addition for linked objects or addons (0 in most cases)
541
                $this->tab_top_newpage = (!getDolGlobalInt('MAIN_PDF_DONOTREPEAT_HEAD') ? 42 + $top_shift : 10);
542
543
                // You can add more thing under header here, if you increase $extra_under_address_shift too.
544
                $extra_under_address_shift = 0;
545
                $qrcodestring = '';
546
                if (getDolGlobalString('INVOICE_ADD_ZATCA_QR_CODE')) {
547
                    $qrcodestring = $object->buildZATCAQRString();
548
                } elseif (getDolGlobalString('INVOICE_ADD_SWISS_QR_CODE') == '1') {
549
                    $qrcodestring = $object->buildSwitzerlandQRString();
550
                }
551
                if ($qrcodestring) {
552
                    $qrcodecolor = array('25', '25', '25');
553
                    // set style for QR-code
554
                    $styleQr = array(
555
                        'border' => false,
556
                        'padding' => 0,
557
                        'fgcolor' => $qrcodecolor,
558
                        'bgcolor' => false, //array(255,255,255)
559
                        'module_width' => 1, // width of a single module in points
560
                        'module_height' => 1 // height of a single module in points
561
                    );
562
                    $pdf->write2DBarcode($qrcodestring, 'QRCODE,M', $this->marge_gauche, $this->tab_top - 5, 25, 25, $styleQr, 'N');
563
                    $extra_under_address_shift += 25;
564
                }
565
566
                // Call hook printUnderHeaderPDFline
567
                $parameters = array(
568
                    'object' => $object,
569
                    'i' => $i,
570
                    'pdf' => &$pdf,
571
                    'outputlangs' => $outputlangs,
572
                    'hidedetails' => $hidedetails
573
                );
574
                $reshook = $hookmanager->executeHooks('printUnderHeaderPDFline', $parameters, $this); // Note that $object may have been modified by hook
575
                if (!empty($hookmanager->resArray['extra_under_address_shift'])) {
576
                    $extra_under_address_shift += $hookmanager->resArray['extra_under_address_shift'];
577
                }
578
579
                $this->tab_top += $extra_under_address_shift;
580
                $this->tab_top_newpage += 0;
581
582
583
                // Define height of table for lines (for first page)
584
                $tab_height = $this->page_hauteur - $this->tab_top - $this->heightforfooter - $this->heightforfreetext - $this->getHeightForQRInvoice(1, $object, $langs);
585
586
                $nexY = $this->tab_top - 1;
587
588
                // Specific stuff for situations invoices first page
589
                $tab_top = 90;
590
                $tab_height = 130;
591
                $tab_height_newpage = 150;
592
593
                $this->_tableFirstPage($pdf, $tab_top, $this->page_hauteur - 100 - $this->heightforfreetext - $this->heightforfooter, 0, $outputlangs, 0, 0, $object->multicurrency_code);
594
595
                $bottomlasttab = $this->page_hauteur - $this->heightforinfotot - $this->heightforfreetext - $this->heightforfooter + 1;
596
597
                $this->_pagefoot($pdf, $object, $outputlangs, 1);
598
599
                $pdf->AddPage();
600
                $pdf->setPage(2);
601
                $pagenb++;
602
                $this->_pagehead($pdf, $object, 0, $outputlangs, $outputlangsbis);
603
                $pdf->setTopMargin($this->tab_top_newpage);
604
605
                // Incoterm
606
                $height_incoterms = 0;
607
                if (isModEnabled('incoterm')) {
608
                    $desc_incoterms = $object->getIncotermsForPDF();
609
                    if ($desc_incoterms) {
610
                        $this->tab_top -= 2;
611
612
                        $pdf->SetFont('', '', $default_font_size - 1);
613
                        $pdf->writeHTMLCell(190, 3, $this->posxdesc - 1, $this->tab_top - 1, dol_htmlentitiesbr($desc_incoterms), 0, 1);
614
                        $nexY = max($pdf->GetY(), $nexY);
615
                        $height_incoterms = $nexY - $this->tab_top;
616
617
                        // Rect takes a length in 3rd parameter
618
                        $pdf->SetDrawColor(192, 192, 192);
619
                        $pdf->Rect($this->marge_gauche, $this->tab_top - 1, $this->page_largeur - $this->marge_gauche - $this->marge_droite, $height_incoterms + 1);
620
621
                        $this->tab_top = $nexY + 6;
622
                        $height_incoterms += 4;
623
                    }
624
                }
625
626
                // Displays notes. Here we are still on code eecuted only for the first page.
627
                $notetoshow = empty($object->note_public) ? '' : $object->note_public;
628
                if (getDolGlobalString('MAIN_ADD_SALE_REP_SIGNATURE_IN_NOTE')) {
629
                    // Get first sale rep
630
                    if (is_object($object->thirdparty)) {
631
                        $salereparray = $object->thirdparty->getSalesRepresentatives($user);
632
                        $salerepobj = new User($this->db);
633
                        $salerepobj->fetch($salereparray[0]['id']);
634
                        if (!empty($salerepobj->signature)) {
635
                            $notetoshow = dol_concatdesc($notetoshow, $salerepobj->signature);
636
                        }
637
                    }
638
                }
639
640
                // Extrafields in note
641
                $extranote = $this->getExtrafieldsInHtml($object, $outputlangs);
642
                if (!empty($extranote)) {
643
                    $notetoshow = dol_concatdesc($notetoshow, $extranote);
644
                }
645
646
                $pagenb = $pdf->getPage();
647
                if ($notetoshow) {
648
                    $this->tab_top -= 2;
649
650
                    $tab_width = $this->page_largeur - $this->marge_gauche - $this->marge_droite;
651
                    $pageposbeforenote = $pagenb;
652
653
                    $substitutionarray = pdf_getSubstitutionArray($outputlangs, null, $object);
654
                    complete_substitutions_array($substitutionarray, $outputlangs, $object);
655
                    $notetoshow = make_substitutions($notetoshow, $substitutionarray, $outputlangs);
656
                    $notetoshow = convertBackOfficeMediasLinksToPublicLinks($notetoshow);
657
658
                    $pdf->startTransaction();
659
660
                    $pdf->SetFont('', '', $default_font_size - 1);
661
                    $pdf->writeHTMLCell(190, 3, $this->posxdesc - 1, $this->tab_top, dol_htmlentitiesbr($notetoshow), 0, 1);
662
                    // Description
663
                    $pageposafternote = $pdf->getPage();
664
                    $posyafter = $pdf->GetY();
665
666
                    if ($pageposafternote > $pageposbeforenote) {
667
                        $pdf->rollbackTransaction(true);
668
669
                        // prepare pages to receive notes
670
                        while ($pagenb < $pageposafternote) {
671
                            $pdf->AddPage();
672
                            $pagenb++;
673
                            if (!empty($this->tplidx)) {
674
                                $pdf->useTemplate($this->tplidx);
675
                            }
676
                            if (!getDolGlobalInt('MAIN_PDF_DONOTREPEAT_HEAD')) {
677
                                $this->_pagehead($pdf, $object, 0, $outputlangs, $outputlangsbis);
678
                            }
679
                            $pdf->setTopMargin($this->tab_top_newpage);
680
                            // The only function to edit the bottom margin of current page to set it.
681
                            $pdf->setPageOrientation('', 1, $this->heightforfooter + $this->heightforfreetext);
682
                        }
683
684
                        // back to start
685
                        $pdf->setPage($pageposbeforenote);
686
                        $pdf->setPageOrientation('', 1, $this->heightforfooter + $this->heightforfreetext);
687
                        $pdf->SetFont('', '', $default_font_size - 1);
688
                        $pdf->writeHTMLCell(190, 3, $this->posxdesc - 1, $this->tab_top, dol_htmlentitiesbr($notetoshow), 0, 1);
689
                        $pageposafternote = $pdf->getPage();
690
691
                        $posyafter = $pdf->GetY();
692
693
                        if ($posyafter > ($this->page_hauteur - ($this->heightforfooter + $this->heightforfreetext + 20))) {    // There is no space left for total+free text
694
                            $pdf->AddPage('', '', true);
695
                            $pagenb++;
696
                            $pageposafternote++;
697
                            $pdf->setPage($pageposafternote);
698
                            $pdf->setTopMargin($this->tab_top_newpage);
699
                            // The only function to edit the bottom margin of current page to set it.
700
                            $pdf->setPageOrientation('', 1, $this->heightforfooter + $this->heightforfreetext);
701
                            //$posyafter = $this->tab_top_newpage;
702
                        }
703
704
705
                        // apply note frame to previous pages
706
                        $i = $pageposbeforenote;
707
                        while ($i < $pageposafternote) {
708
                            $pdf->setPage($i);
709
710
711
                            $pdf->SetDrawColor(128, 128, 128);
712
                            // Draw note frame
713
                            if ($i > $pageposbeforenote) {
714
                                $height_note = $this->page_hauteur - ($this->tab_top_newpage + $this->heightforfooter);
715
                                $pdf->Rect($this->marge_gauche, $this->tab_top_newpage - 1, $tab_width, $height_note + 1);
716
                            } else {
717
                                $height_note = $this->page_hauteur - ($this->tab_top + $this->heightforfooter);
718
                                $pdf->Rect($this->marge_gauche, $this->tab_top - 1, $tab_width, $height_note + 1);
719
                            }
720
721
                            // Add footer
722
                            $pdf->setPageOrientation('', 1, 0); // The only function to edit the bottom margin of current page to set it.
723
                            $this->_pagefoot($pdf, $object, $outputlangs, 1);
724
725
                            $i++;
726
                        }
727
728
                        // apply note frame to last page
729
                        $pdf->setPage($pageposafternote);
730
                        if (!empty($this->tplidx)) {
731
                            $pdf->useTemplate($this->tplidx);
732
                        }
733
                        if (!getDolGlobalInt('MAIN_PDF_DONOTREPEAT_HEAD')) {
734
                            $this->_pagehead($pdf, $object, 0, $outputlangs, $outputlangsbis);
735
                        }
736
                        $height_note = $posyafter - $this->tab_top_newpage;
737
                        $pdf->Rect($this->marge_gauche, $this->tab_top_newpage - 1, $tab_width, $height_note + 1);
738
                    } else {
739
                        // No pagebreak
740
                        $pdf->commitTransaction();
741
                        $posyafter = $pdf->GetY();
742
                        $height_note = $posyafter - $this->tab_top;
743
                        $pdf->Rect($this->marge_gauche, $this->tab_top - 1, $tab_width, $height_note + 1);
744
745
746
                        if ($posyafter > ($this->page_hauteur - ($this->heightforfooter + $this->heightforfreetext + 20))) {
747
                            // not enough space, need to add page
748
                            $pdf->AddPage('', '', true);
749
                            $pagenb++;
750
                            $pageposafternote++;
751
                            $pdf->setPage($pageposafternote);
752
                            if (!empty($this->tplidx)) {
753
                                $pdf->useTemplate($this->tplidx);
754
                            }
755
                            if (!getDolGlobalInt('MAIN_PDF_DONOTREPEAT_HEAD')) {
756
                                $this->_pagehead($pdf, $object, 0, $outputlangs, $outputlangsbis);
757
                            }
758
759
                            $posyafter = $this->tab_top_newpage;
760
                        }
761
                    }
762
763
                    $tab_height = $tab_height - $height_note;
764
                    $this->tab_top = $posyafter + 6;
765
                } else {
766
                    $height_note = 0;
767
                }
768
769
                // Use new auto column system
770
                $this->prepareArrayColumnField($object, $outputlangs, $hidedetails, $hidedesc, $hideref);
771
772
                // Table simulation to know the height of the title line (this set this->tableTitleHeight)
773
                // don't need it in situation invoices
774
                // $pdf->startTransaction();
775
                // $this->pdfTabTitles($pdf, $this->tab_top_newpage + ($this->tabTitleHeight/2), $tab_height, $outputlangs, $hidetop);
776
                // $pdf->rollbackTransaction(true);
777
778
                $nexY = $this->tab_top_newpage + $this->tabTitleHeight;
779
780
                // Loop on each lines
781
                $pageposbeforeprintlines = $pdf->getPage();
782
                $pagenb = $pageposbeforeprintlines;
783
                for ($i = 0; $i < $nblines; $i++) {
784
                    $posy = $nexY;
785
                    $pdf->SetFont('', '', $default_font_size - 1); // Into loop to work with multipage
786
                    $pdf->SetTextColor(0, 0, 0);
787
788
                    // Define size of image if we need it
789
                    $imglinesize = array();
790
                    if (!empty($realpatharray[$i])) {
791
                        $imglinesize = pdf_getSizeForImage($realpatharray[$i]);
792
                    }
793
794
                    $pdf->setTopMargin($this->tab_top_newpage);
795
                    $pdf->setPageOrientation('', 1, $this->heightforfooter + $this->heightforfreetext + $this->heightforinfotot); // The only function to edit the bottom margin of current page to set it.
796
                    $pageposbefore = $pdf->getPage();
797
798
                    $showpricebeforepagebreak = 1;
799
                    $posYAfterImage = 0;
800
                    $posYAfterDescription = 0;
801
802
                    if ($this->getColumnStatus('photo')) {
803
                        // We start with Photo of product line
804
                        if (isset($imglinesize['width']) && isset($imglinesize['height']) && ($posy + $imglinesize['height']) > ($this->page_hauteur - ($this->heightforfooter + $this->heightforfreetext + $this->heightforinfotot))) {    // If photo too high, we moved completely on new page
805
                            $pdf->AddPage('', '', true);
806
                            if (!empty($this->tplidx)) {
807
                                $pdf->useTemplate($this->tplidx);
808
                            }
809
                            $pdf->setPage($pageposbefore + 1);
810
811
                            $posy = $this->tab_top_newpage;
812
813
                            // Allows data in the first page if description is long enough to break in multiples pages
814
                            if (getDolGlobalString('MAIN_PDF_DATA_ON_FIRST_PAGE')) {
815
                                $showpricebeforepagebreak = 1;
816
                            } else {
817
                                $showpricebeforepagebreak = 0;
818
                            }
819
                        }
820
821
                        if (!empty($this->cols['photo']) && isset($imglinesize['width']) && isset($imglinesize['height'])) {
822
                            $pdf->Image($realpatharray[$i], $this->getColumnContentXStart('photo'), $posy + 1, $imglinesize['width'], $imglinesize['height'], '', '', '', 2, 300); // Use 300 dpi
823
                            // $pdf->Image does not increase value return by getY, so we save it manually
824
                            $posYAfterImage = $posy + $imglinesize['height'];
825
                        }
826
                    }
827
828
                    // Description of product line
829
                    if ($this->getColumnStatus('desc')) {
830
                        $pdf->startTransaction();
831
832
                        $this->printColDescContent($pdf, $posy, 'desc', $object, $i, $outputlangs, $hideref, $hidedesc);
833
                        $pageposafter = $pdf->getPage();
834
835
                        if ($pageposafter > $pageposbefore) {   // There is a pagebreak
836
                            $pdf->rollbackTransaction(true);
837
                            $pageposafter = $pageposbefore;
838
                            $pdf->setPageOrientation('', 1, $this->heightforfooter); // The only function to edit the bottom margin of current page to set it.
839
840
                            $this->printColDescContent($pdf, $posy, 'desc', $object, $i, $outputlangs, $hideref, $hidedesc);
841
842
                            $pageposafter = $pdf->getPage();
843
                            $posyafter = $pdf->GetY();
844
                            //var_dump($posyafter); var_dump(($this->page_hauteur - ($this->heightforfooter+$this->heightforfreetext+$this->heightforinfotot))); exit;
845
                            if ($posyafter > ($this->page_hauteur - ($this->heightforfooter + $this->heightforfreetext + $this->heightforinfotot))) {   // There is no space left for total+free text
846
                                if ($i == ($nblines - 1)) { // No more lines, and no space left to show total, so we create a new page
847
                                    $pdf->AddPage('', '', true);
848
                                    if (!empty($this->tplidx)) {
849
                                        $pdf->useTemplate($this->tplidx);
850
                                    }
851
                                    $pdf->setPage($pageposafter + 1);
852
                                }
853
                            } else {
854
                                // We found a page break
855
                                // Allows data in the first page if description is long enough to break in multiples pages
856
                                if (getDolGlobalString('MAIN_PDF_DATA_ON_FIRST_PAGE')) {
857
                                    $showpricebeforepagebreak = 1;
858
                                } else {
859
                                    $showpricebeforepagebreak = 0;
860
                                }
861
                            }
862
                        } else { // No pagebreak
863
                            $pdf->commitTransaction();
864
                        }
865
                        $posYAfterDescription = $pdf->GetY();
866
                    }
867
868
                    $nexY = max($pdf->GetY(), $posYAfterImage, $posYAfterDescription);
869
870
                    $pageposafter = $pdf->getPage();
871
                    $pdf->setPage($pageposbefore);
872
                    $pdf->setTopMargin($this->marge_haute);
873
                    $pdf->setPageOrientation('', 1, 0); // The only function to edit the bottom margin of current page to set it.
874
875
                    // We suppose that a too long description or photo were moved completely on next page
876
                    if ($pageposafter > $pageposbefore && empty($showpricebeforepagebreak)) {
877
                        $pdf->setPage($pageposafter);
878
                        $posy = $this->tab_top_newpage;
879
                    }
880
881
                    $pdf->SetFont('', '', $default_font_size - 1); // We reposition the default font
882
883
                    // VAT Rate
884
                    if ($this->getColumnStatus('vat')) {
885
                        $vat_rate = pdf_getlinevatrate($object, $i, $outputlangs, $hidedetails);
886
                        $this->printStdColumnContent($pdf, $posy, 'vat', $vat_rate);
887
                        $nexY = max($pdf->GetY(), $nexY);
888
                    }
889
890
                    // Unit price before discount
891
                    if ($this->getColumnStatus('subprice')) {
892
                        $up_excl_tax = pdf_getlineupexcltax($object, $i, $outputlangs, $hidedetails);
893
                        $this->printStdColumnContent($pdf, $posy, 'subprice', $up_excl_tax);
894
                        $nexY = max($pdf->GetY(), $nexY);
895
                    }
896
897
                    // Quantity
898
                    // Enough for 6 chars
899
                    if ($this->getColumnStatus('qty')) {
900
                        $qty = pdf_getlineqty($object, $i, $outputlangs, $hidedetails);
901
                        $this->printStdColumnContent($pdf, $posy, 'qty', $qty);
902
                        $nexY = max($pdf->GetY(), $nexY);
903
                    }
904
905
                    // Situation progress
906
                    if ($this->getColumnStatus('progress')) {
907
                        $progress = pdf_getlineprogress($object, $i, $outputlangs, $hidedetails);
908
                        $this->printStdColumnContent($pdf, $posy, 'progress', $progress);
909
                        $nexY = max($pdf->GetY(), $nexY);
910
                    }
911
912
                    // Unit
913
                    if ($this->getColumnStatus('unit')) {
914
                        $unit = pdf_getlineunit($object, $i, $outputlangs, $hidedetails);
915
                        $this->printStdColumnContent($pdf, $posy, 'unit', $unit);
916
                        $nexY = max($pdf->GetY(), $nexY);
917
                    }
918
919
                    // Discount on line
920
                    if ($this->getColumnStatus('discount') && $object->lines[$i]->remise_percent) {
921
                        $remise_percent = pdf_getlineremisepercent($object, $i, $outputlangs, $hidedetails);
922
                        $this->printStdColumnContent($pdf, $posy, 'discount', $remise_percent);
923
                        $nexY = max($pdf->GetY(), $nexY);
924
                    }
925
926
                    // Total excl tax line (HT)
927
                    if ($this->getColumnStatus('totalexcltax')) {
928
                        $total_excl_tax = pdf_getlinetotalexcltax($object, $i, $outputlangs, $hidedetails);
929
                        $this->printStdColumnContent($pdf, $posy, 'totalexcltax', $total_excl_tax);
930
                        $nexY = max($pdf->GetY(), $nexY);
931
                    }
932
933
                    // Retrieving information from the previous line
934
                    $TInfosLigneSituationPrecedente = $this->getInfosLineLastSituation($object, $object->lines[$i]);
935
936
                    // Sum
937
                    $columkey = 'btpsomme';
938
                    if ($this->getColumnStatus($columkey)) {
939
                        $printval = price($TInfosLigneSituationPrecedente['total_ht_without_progress'], 0, '', 1, -1, 2);
940
                        $this->printStdColumnContent($pdf, $posy, $columkey, $printval);
941
                        $nexY = max($pdf->GetY(), $nexY);
942
                    }
943
944
                    // Current progress
945
                    $columkey = 'progress_amount';
946
                    if ($this->getColumnStatus($columkey)) {
947
                        $printval = price($object->lines[$i]->total_ht, 0, '', 1, -1, 2);
948
                        $this->printStdColumnContent($pdf, $posy, $columkey, $printval);
949
                        $nexY = max($pdf->GetY(), $nexY);
950
                    }
951
                    // Previous progress line
952
                    $columkey = 'prev_progress';
953
                    if ($this->getColumnStatus($columkey)) {
954
                        $printval = $TInfosLigneSituationPrecedente['progress_prec'] . '%';
955
                        $this->printStdColumnContent($pdf, $posy, $columkey, $printval);
956
                        $nexY = max($pdf->GetY(), $nexY);
957
                    }
958
                    // Previous progress amount
959
                    $columkey = 'prev_progress_amount';
960
                    if ($this->getColumnStatus($columkey)) {
961
                        $printval = price($TInfosLigneSituationPrecedente['total_ht'], 0, '', 1, -1, 2);
962
                        $this->printStdColumnContent($pdf, $posy, $columkey, $printval);
963
                        $nexY = max($pdf->GetY(), $nexY);
964
                    }
965
966
                    $parameters = array(
967
                        'object' => $object,
968
                        'i' => $i,
969
                        'pdf' => & $pdf,
970
                        'curY' => & $posy,
971
                        'nexY' => & $nexY,
972
                        'outputlangs' => $outputlangs,
973
                        'hidedetails' => $hidedetails
974
                    );
975
                    $reshook = $hookmanager->executeHooks('printPDFline', $parameters, $this); // Note that $object may have been modified by hook
976
977
978
                    $sign = 1;
979
                    if (isset($object->type) && $object->type == 2 && getDolGlobalString('INVOICE_POSITIVE_CREDIT_NOTE')) {
980
                        $sign = -1;
981
                    }
982
                    // Collecte des totaux par valeur de tva dans $this->tva["taux"]=total_tva
983
                    $prev_progress = $object->lines[$i]->get_prev_progress($object->id);
984
                    if ($prev_progress > 0 && !empty($object->lines[$i]->situation_percent)) { // Compute progress from previous situation
985
                        if (isModEnabled("multicurrency") && $object->multicurrency_tx != 1) {
986
                            $tvaligne = $sign * $object->lines[$i]->multicurrency_total_tva * ($object->lines[$i]->situation_percent - $prev_progress) / $object->lines[$i]->situation_percent;
987
                        } else {
988
                            $tvaligne = $sign * $object->lines[$i]->total_tva * ($object->lines[$i]->situation_percent - $prev_progress) / $object->lines[$i]->situation_percent;
989
                        }
990
                    } else {
991
                        if (isModEnabled("multicurrency") && $object->multicurrency_tx != 1) {
992
                            $tvaligne = $sign * $object->lines[$i]->multicurrency_total_tva;
993
                        } else {
994
                            $tvaligne = $sign * $object->lines[$i]->total_tva;
995
                        }
996
                    }
997
998
                    $localtax1ligne = $object->lines[$i]->total_localtax1;
999
                    $localtax2ligne = $object->lines[$i]->total_localtax2;
1000
                    $localtax1_rate = $object->lines[$i]->localtax1_tx;
1001
                    $localtax2_rate = $object->lines[$i]->localtax2_tx;
1002
                    $localtax1_type = $object->lines[$i]->localtax1_type;
1003
                    $localtax2_type = $object->lines[$i]->localtax2_type;
1004
1005
                    // TODO remise_percent is an obsolete field for object parent
1006
                    /*if ($object->remise_percent) {
1007
                        $tvaligne -= ($tvaligne * $object->remise_percent) / 100;
1008
                    }
1009
                    if ($object->remise_percent) {
1010
                        $localtax1ligne -= ($localtax1ligne * $object->remise_percent) / 100;
1011
                    }
1012
                    if ($object->remise_percent) {
1013
                        $localtax2ligne -= ($localtax2ligne * $object->remise_percent) / 100;
1014
                    }*/
1015
1016
                    $vatrate = (string)$object->lines[$i]->tva_tx;
1017
1018
                    // Retrieve type from database for backward compatibility with old records
1019
                    if (
1020
                        (!isset($localtax1_type) || $localtax1_type == '' || !isset($localtax2_type) || $localtax2_type == '') // if tax type not defined
1021
                        && (!empty($localtax1_rate) || !empty($localtax2_rate))
1022
                    ) { // and there is local tax
1023
                        $localtaxtmp_array = getLocalTaxesFromRate($vatrate, 0, $object->thirdparty, $mysoc);
1024
                        $localtax1_type = isset($localtaxtmp_array[0]) ? $localtaxtmp_array[0] : '';
1025
                        $localtax2_type = isset($localtaxtmp_array[2]) ? $localtaxtmp_array[2] : '';
1026
                    }
1027
1028
                    // retrieve global local tax
1029
                    if ($localtax1_type && $localtax1ligne != 0) {
1030
                        if (empty($this->localtax1[$localtax1_type][$localtax1_rate])) {
1031
                            $this->localtax1[$localtax1_type][$localtax1_rate] = $localtax1ligne;
1032
                        } else {
1033
                            $this->localtax1[$localtax1_type][$localtax1_rate] += $localtax1ligne;
1034
                        }
1035
                    }
1036
                    if ($localtax2_type && $localtax2ligne != 0) {
1037
                        if (empty($this->localtax2[$localtax2_type][$localtax2_rate])) {
1038
                            $this->localtax2[$localtax2_type][$localtax2_rate] = $localtax2ligne;
1039
                        } else {
1040
                            $this->localtax2[$localtax2_type][$localtax2_rate] += $localtax2ligne;
1041
                        }
1042
                    }
1043
1044
                    if (($object->lines[$i]->info_bits & 0x01) == 0x01) {
1045
                        $vatrate .= '*';
1046
                    }
1047
1048
                    // Fill $this->tva and $this->tva_array
1049
                    if (!isset($this->tva[$vatrate])) {
1050
                        $this->tva[$vatrate] = 0;
1051
                    }
1052
                    $this->tva[$vatrate] += $tvaligne;  // ->tva is abandoned, we use now ->tva_array that is more complete
1053
                    $vatcode = $object->lines[$i]->vat_src_code;
1054
                    if (empty($this->tva_array[$vatrate . ($vatcode ? ' (' . $vatcode . ')' : '')]['amount'])) {
1055
                        $this->tva_array[$vatrate . ($vatcode ? ' (' . $vatcode . ')' : '')]['amount'] = 0;
1056
                    }
1057
                    $this->tva_array[$vatrate . ($vatcode ? ' (' . $vatcode . ')' : '')] = array('vatrate' => $vatrate, 'vatcode' => $vatcode, 'amount' => $this->tva_array[$vatrate . ($vatcode ? ' (' . $vatcode . ')' : '')]['amount'] + $tvaligne);
1058
1059
                    $nexY = max($nexY, $posYAfterImage);
1060
1061
                    // Add line
1062
                    if (getDolGlobalString('MAIN_PDF_DASH_BETWEEN_LINES') && $i < ($nblines - 1)) {
1063
                        $pdf->setPage($pageposafter);
1064
                        $pdf->SetLineStyle(array('dash' => '1,1', 'color' => array(80, 80, 80)));
1065
                        //$pdf->SetDrawColor(190,190,200);
1066
                        $pdf->line($this->marge_gauche, $nexY, $this->page_largeur - $this->marge_droite, $nexY);
1067
                        $pdf->SetLineStyle(array('dash' => 0));
1068
                    }
1069
1070
                    // Detect if some page were added automatically and output _tableau for past pages
1071
                    while ($pagenb < $pageposafter) {
1072
                        $pdf->setPage($pagenb);
1073
                        $tabtop = $this->tab_top;
1074
                        $tabhauteur = $this->page_hauteur - $tabtop - $this->heightforfooter;
1075
                        if ($pagenb != $pageposbeforeprintlines) {
1076
                            $tabtop = $this->tab_top_newpage;
1077
                            $tabhauteur = $this->page_hauteur - $tabtop - $this->heightforfooter;
1078
                            $hidetop = 1;
1079
                        }
1080
                        $this->_tableau($pdf, $tabtop, $tabhauteur, 0, $outputlangs, $hidetop, 1, $object->multicurrency_code, $outputlangsbis);
1081
1082
                        $this->_pagefoot($pdf, $object, $outputlangs, 1);
1083
                        $pagenb++;
1084
                        $pdf->setPage($pagenb);
1085
                        $pdf->setPageOrientation('', 1, 0); // The only function to edit the bottom margin of current page to set it.
1086
                        if (!getDolGlobalInt('MAIN_PDF_DONOTREPEAT_HEAD')) {
1087
                            $this->_pagehead($pdf, $object, 0, $outputlangs, $outputlangsbis);
1088
                        }
1089
                        if (!empty($this->tplidx)) {
1090
                            $pdf->useTemplate($this->tplidx);
1091
                        }
1092
                    }
1093
1094
                    if (isset($object->lines[$i + 1]->pagebreak) && $object->lines[$i + 1]->pagebreak) {
1095
                        $tabtop = $this->tab_top;
1096
                        $tabhauteur = $this->page_hauteur - $tabtop - $this->heightforfooter;
1097
                        if ($pagenb != $pageposbeforeprintlines) {
1098
                            $tabtop = $this->tab_top_newpage;
1099
                            $tabhauteur = $this->page_hauteur - $tabtop - $this->heightforfooter;
1100
                            $hidetop = 1;
1101
                        }
1102
                        $this->_tableau($pdf, $tabtop, $tabhauteur, 0, $outputlangs, $hidetop, 1, $object->multicurrency_code, $outputlangsbis);
1103
1104
                        $this->_pagefoot($pdf, $object, $outputlangs, 1);
1105
                        // New page
1106
                        $pdf->AddPage();
1107
                        if (!empty($this->tplidx)) {
1108
                            $pdf->useTemplate($this->tplidx);
1109
                        }
1110
                        $pagenb++;
1111
                        if (!getDolGlobalInt('MAIN_PDF_DONOTREPEAT_HEAD')) {
1112
                            $this->_pagehead($pdf, $object, 0, $outputlangs, $outputlangsbis);
1113
                        }
1114
                    }
1115
                }
1116
1117
                // Show square
1118
                // special for situation invoices
1119
                $tabtop = $this->tab_top_newpage;
1120
                $tabhauteur = $this->page_hauteur - $tabtop - $this->heightforfooter - $this->heightforinfotot - $this->heightforfreetext;
1121
                $tabTitleHeight = 0;
1122
                $this->_tableau($pdf, $tabtop, $tabhauteur, 0, $outputlangs, $hidetop, 1, $object->multicurrency_code, $outputlangsbis);
1123
1124
                $bottomlasttab = $tabtop + $tabhauteur + $tabTitleHeight + 10;
1125
1126
                // Display infos area
1127
                $posy = $this->drawInfoTable($pdf, $object, $bottomlasttab, $outputlangs, $outputlangsbis);
1128
1129
                // Display total zone
1130
                $posy = $this->drawTotalTable($pdf, $object, $deja_regle, $bottomlasttab, $outputlangs, $outputlangsbis);
1131
1132
                // Display payment area
1133
                if (($deja_regle || $amount_credit_notes_included || $amount_deposits_included) && !getDolGlobalString('INVOICE_NO_PAYMENT_DETAILS')) {
1134
                    $posy = $this->drawPaymentsTable($pdf, $object, $posy, $outputlangs);
1135
                }
1136
1137
                // Pagefoot
1138
                $this->_pagefoot($pdf, $object, $outputlangs);
1139
                if (method_exists($pdf, 'AliasNbPages')) {
1140
                    $pdf->AliasNbPages();
1141
                }
1142
1143
                $this->resumeLastPage($pdf, $object, 0, $tab_top, $outputlangs, $outputlangsbis);
1144
                $bottomlasttab = $this->page_hauteur - $this->heightforinfotot - $this->heightforfreetext - $this->heightforfooter + 1;
1145
                $this->_pagefoot($pdf, $object, $outputlangs, 1);
1146
1147
                $pdf->Close();
1148
1149
                $pdf->Output($file, 'F');
1150
1151
                // Add pdfgeneration hook
1152
                $hookmanager->initHooks(array('pdfgeneration'));
1153
                $parameters = array('file' => $file, 'object' => $object, 'outputlangs' => $outputlangs);
1154
                global $action;
1155
                $reshook = $hookmanager->executeHooks('afterPDFCreation', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1156
                if ($reshook < 0) {
1157
                    $this->error = $hookmanager->error;
1158
                    $this->errors = $hookmanager->errors;
1159
                }
1160
1161
                dolChmod($file);
1162
1163
                $this->result = array('fullpath' => $file);
1164
1165
                return 1; // No error
1166
            } else {
1167
                $this->error = $langs->transnoentities("ErrorCanNotCreateDir", $dir);
1168
                return 0;
1169
            }
1170
        } else {
1171
            $this->error = $langs->transnoentities("ErrorConstantNotDefined", "FAC_OUTPUTDIR");
1172
            return 0;
1173
        }
1174
    }
1175
1176
1177
    /**
1178
     *  Show payments table
1179
     *
1180
     * @param TCPDF $pdf Object PDF
1181
     * @param Facture $object Object invoice
1182
     * @param int $posy Position y in PDF
1183
     * @param Translate $outputlangs Object langs for output
1184
     * @return int                         Return integer <0 if KO, >0 if OK
1185
     */
1186
    public function drawPaymentsTable(&$pdf, $object, $posy, $outputlangs)
1187
    {
1188
        global $conf;
1189
1190
        $sign = 1;
1191
        if ($object->type == 2 && getDolGlobalString('INVOICE_POSITIVE_CREDIT_NOTE')) {
1192
            $sign = -1;
1193
        }
1194
1195
        $tab3_posx = 120;
1196
        $tab3_top = $posy + 8;
1197
        $tab3_width = 80;
1198
        $tab3_height = 4;
1199
        if ($this->page_largeur < 210) { // To work with US executive format
1200
            $tab3_posx -= 15;
1201
        }
1202
1203
        $default_font_size = pdf_getPDFFontSize($outputlangs);
1204
1205
        $title = $outputlangs->transnoentities("PaymentsAlreadyDone");
1206
        if ($object->type == 2) {
1207
            $title = $outputlangs->transnoentities("PaymentsBackAlreadyDone");
1208
        }
1209
1210
        $pdf->SetFont('', '', $default_font_size - 3);
1211
        $pdf->SetXY($tab3_posx, $tab3_top - 4);
1212
        $pdf->MultiCell(60, 3, $title, 0, 'L', 0);
1213
1214
        $pdf->line($tab3_posx, $tab3_top, $tab3_posx + $tab3_width, $tab3_top);
1215
1216
        $pdf->SetFont('', '', $default_font_size - 4);
1217
        $pdf->SetXY($tab3_posx, $tab3_top);
1218
        $pdf->MultiCell(20, 3, $outputlangs->transnoentities("Payment"), 0, 'L', 0);
1219
        $pdf->SetXY($tab3_posx + 21, $tab3_top);
1220
        $pdf->MultiCell(20, 3, $outputlangs->transnoentities("Amount"), 0, 'L', 0);
1221
        $pdf->SetXY($tab3_posx + 40, $tab3_top);
1222
        $pdf->MultiCell(20, 3, $outputlangs->transnoentities("Type"), 0, 'L', 0);
1223
        $pdf->SetXY($tab3_posx + 58, $tab3_top);
1224
        $pdf->MultiCell(20, 3, $outputlangs->transnoentities("Num"), 0, 'L', 0);
1225
1226
        $pdf->line($tab3_posx, $tab3_top - 1 + $tab3_height, $tab3_posx + $tab3_width, $tab3_top - 1 + $tab3_height);
1227
1228
        $y = 0;
1229
1230
        $pdf->SetFont('', '', $default_font_size - 4);
1231
1232
1233
        // Loop on each discount available (deposits and credit notes and excess of payment included)
1234
        $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,";
1235
        $sql .= " re.description, re.fk_facture_source,";
1236
        $sql .= " f.type, f.datef";
1237
        $sql .= " FROM " . MAIN_DB_PREFIX . "societe_remise_except as re, " . MAIN_DB_PREFIX . "facture as f";
1238
        $sql .= " WHERE re.fk_facture_source = f.rowid AND re.fk_facture = " . ((int)$object->id);
1239
        $resql = $this->db->query($sql);
1240
        if ($resql) {
1241
            $num = $this->db->num_rows($resql);
1242
            $i = 0;
1243
            $invoice = new Facture($this->db);
1244
            while ($i < $num) {
1245
                $y += 3;
1246
                $obj = $this->db->fetch_object($resql);
1247
1248
                if ($obj->type == 2) {
1249
                    $text = $outputlangs->transnoentities("CreditNote");
1250
                } elseif ($obj->type == 3) {
1251
                    $text = $outputlangs->transnoentities("Deposit");
1252
                } elseif ($obj->type == 0) {
1253
                    $text = $outputlangs->transnoentities("ExcessReceived");
1254
                } else {
1255
                    $text = $outputlangs->transnoentities("UnknownType");
1256
                }
1257
1258
                $invoice->fetch($obj->fk_facture_source);
1259
1260
                $pdf->SetXY($tab3_posx, $tab3_top + $y);
1261
                $pdf->MultiCell(20, 3, dol_print_date($this->db->jdate($obj->datef), 'day', false, $outputlangs, true), 0, 'L', 0);
1262
                $pdf->SetXY($tab3_posx + 21, $tab3_top + $y);
1263
                $pdf->MultiCell(20, 3, price((isModEnabled("multicurrency") && $object->multicurrency_tx != 1) ? $obj->multicurrency_amount_ttc : $obj->amount_ttc, 0, $outputlangs), 0, 'L', 0);
1264
                $pdf->SetXY($tab3_posx + 40, $tab3_top + $y);
1265
                $pdf->MultiCell(20, 3, $text, 0, 'L', 0);
1266
                $pdf->SetXY($tab3_posx + 58, $tab3_top + $y);
1267
                $pdf->MultiCell(20, 3, $invoice->ref, 0, 'L', 0);
1268
1269
                $pdf->line($tab3_posx, $tab3_top + $y + 3, $tab3_posx + $tab3_width, $tab3_top + $y + 3);
1270
1271
                $i++;
1272
            }
1273
        } else {
1274
            $this->error = $this->db->lasterror();
1275
            return -1;
1276
        }
1277
1278
        // Loop on each payment
1279
        // TODO Call getListOfPaymentsgetListOfPayments instead of hard coded sql
1280
        $sql = "SELECT p.datep as date, p.fk_paiement, p.num_paiement as num, pf.amount as amount, pf.multicurrency_amount,";
1281
        $sql .= " cp.code";
1282
        $sql .= " FROM " . MAIN_DB_PREFIX . "paiement_facture as pf, " . MAIN_DB_PREFIX . "paiement as p";
1283
        $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "c_paiement as cp ON p.fk_paiement = cp.id";
1284
        $sql .= " WHERE pf.fk_paiement = p.rowid AND pf.fk_facture = " . ((int)$object->id);
1285
        //$sql.= " WHERE pf.fk_paiement = p.rowid AND pf.fk_facture = 1";
1286
        $sql .= " ORDER BY p.datep";
1287
1288
        $resql = $this->db->query($sql);
1289
        if ($resql) {
1290
            $num = $this->db->num_rows($resql);
1291
            $i = 0;
1292
            $y += 3;
1293
            $maxY = $y;
1294
            while ($i < $num) {
1295
                $row = $this->db->fetch_object($resql);
1296
                $pdf->SetXY($tab3_posx, $tab3_top + $y);
1297
                $pdf->MultiCell(20, 3, dol_print_date($this->db->jdate($row->date), 'day', false, $outputlangs, true), 0, 'L', 0);
1298
                $pdf->SetXY($tab3_posx + 21, $tab3_top + $y);
1299
                $pdf->MultiCell(20, 3, price($sign * ((isModEnabled("multicurrency") && $object->multicurrency_tx != 1) ? $row->multicurrency_amount : $row->amount), 0, $outputlangs), 0, 'L', 0);
1300
                $pdf->SetXY($tab3_posx + 40, $tab3_top + $y);
1301
                $oper = $outputlangs->transnoentitiesnoconv("PaymentTypeShort" . $row->code);
1302
1303
                $pdf->MultiCell(20, 3, $oper, 0, 'L', 0);
1304
                $maxY = max($pdf->GetY() - $tab3_top - 3, $maxY);
1305
                $pdf->SetXY($tab3_posx + 58, $tab3_top + $y);
1306
                $pdf->MultiCell(30, 3, $row->num, 0, 'L', 0);
1307
                $y = $maxY = max($pdf->GetY() - 3 - $tab3_top, $maxY);
1308
                $pdf->line($tab3_posx, $tab3_top + $y + 3, $tab3_posx + $tab3_width, $tab3_top + $y + 3);
1309
                $y += 3;
1310
                $i++;
1311
            }
1312
1313
            return $tab3_top + $y + 3;
1314
        } else {
1315
            $this->error = $this->db->lasterror();
1316
            return -1;
1317
        }
1318
    }
1319
1320
1321
    /**
1322
     *   Show miscellaneous information (payment mode, payment term, ...)
1323
     *
1324
     * @param TCPDF $pdf Object PDF
1325
     * @param Facture $object Object to show
1326
     * @param int $posy Y
1327
     * @param Translate $outputlangs Langs object
1328
     * @param Translate $outputlangsbis Object lang for output bis
1329
     * @return    int                         Pos y
1330
     */
1331
    protected function drawInfoTable(&$pdf, $object, $posy, $outputlangs, $outputlangsbis)
1332
    {
1333
        global $conf, $mysoc, $hookmanager;
1334
1335
        $default_font_size = pdf_getPDFFontSize($outputlangs);
1336
1337
        $pdf->SetFont('', '', $default_font_size - 1);
1338
1339
        // If France, show VAT mention if not applicable
1340
        if ($this->emetteur->country_code == 'FR' && empty($mysoc->tva_assuj)) {
1341
            $pdf->SetFont('', 'B', $default_font_size - 2);
1342
            $pdf->SetXY($this->marge_gauche, $posy);
1343
            if ($mysoc->forme_juridique_code == 92) {
1344
                $pdf->MultiCell(100, 3, $outputlangs->transnoentities("VATIsNotUsedForInvoiceAsso"), 0, 'L', 0);
1345
            } else {
1346
                $pdf->MultiCell(100, 3, $outputlangs->transnoentities("VATIsNotUsedForInvoice"), 0, 'L', 0);
1347
            }
1348
1349
            $posy = $pdf->GetY() + 4;
1350
        }
1351
1352
        $posxval = 52;  // Position of values of properties shown on left side
1353
        $posxend = 110; // End of x for text on left side
1354
        if ($this->page_largeur < 210) { // To work with US executive format
1355
            $posxend -= 10;
1356
        }
1357
1358
        // Show payments conditions
1359
        if ($object->type != 2 && ($object->cond_reglement_code || $object->cond_reglement)) {
1360
            $pdf->SetFont('', 'B', $default_font_size - 2);
1361
            $pdf->SetXY($this->marge_gauche, $posy);
1362
            $titre = $outputlangs->transnoentities("PaymentConditions") . ':';
1363
            $pdf->MultiCell($posxval - $this->marge_gauche, 4, $titre, 0, 'L');
1364
1365
            $pdf->SetFont('', '', $default_font_size - 2);
1366
            $pdf->SetXY($posxval, $posy);
1367
            $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);
1368
            $lib_condition_paiement = str_replace('\n', "\n", $lib_condition_paiement);
1369
            $pdf->MultiCell($posxend - $posxval, 4, $lib_condition_paiement, 0, 'L');
1370
1371
            $posy = $pdf->GetY() + 3; // We need spaces for 2 lines payment conditions
1372
        }
1373
1374
        // Show category of operations
1375
        if (getDolGlobalInt('INVOICE_CATEGORY_OF_OPERATION') == 2 && $this->categoryOfOperation >= 0) {
1376
            $pdf->SetFont('', 'B', $default_font_size - 2);
1377
            $pdf->SetXY($this->marge_gauche, $posy);
1378
            $categoryOfOperationTitle = $outputlangs->transnoentities("MentionCategoryOfOperations") . ' : ';
1379
            $pdf->MultiCell($posxval - $this->marge_gauche, 4, $categoryOfOperationTitle, 0, 'L');
1380
1381
            $pdf->SetFont('', '', $default_font_size - 2);
1382
            $pdf->SetXY($posxval, $posy);
1383
            $categoryOfOperationLabel = $outputlangs->transnoentities("MentionCategoryOfOperations" . $this->categoryOfOperation);
1384
            $pdf->MultiCell($posxend - $posxval, 4, $categoryOfOperationLabel, 0, 'L');
1385
1386
            $posy = $pdf->GetY() + 3; // for 2 lines
1387
        }
1388
1389
        if ($object->type != 2) {
1390
            // Check a payment mode is defined
1391
            if (
1392
                empty($object->mode_reglement_code)
1393
                && !getDolGlobalInt('FACTURE_CHQ_NUMBER')
1394
                && !getDolGlobalInt('FACTURE_RIB_NUMBER')
1395
            ) {
1396
                $this->error = $outputlangs->transnoentities("ErrorNoPaiementModeConfigured");
1397
            } elseif (
1398
                ($object->mode_reglement_code == 'CHQ' && !getDolGlobalInt('FACTURE_CHQ_NUMBER') && empty($object->fk_account) && empty($object->fk_bank))
1399
                || ($object->mode_reglement_code == 'VIR' && !getDolGlobalInt('FACTURE_RIB_NUMBER') && empty($object->fk_account) && empty($object->fk_bank))
1400
            ) {
1401
                // Avoid having any valid PDF with setup that is not complete
1402
                $outputlangs->load("errors");
1403
1404
                $pdf->SetXY($this->marge_gauche, $posy);
1405
                $pdf->SetTextColor(200, 0, 0);
1406
                $pdf->SetFont('', 'B', $default_font_size - 2);
1407
                $this->error = $outputlangs->transnoentities("ErrorPaymentModeDefinedToWithoutSetup", $object->mode_reglement_code);
1408
                $pdf->MultiCell($posxend - $this->marge_gauche, 3, $this->error, 0, 'L', 0);
1409
                $pdf->SetTextColor(0, 0, 0);
1410
1411
                $posy = $pdf->GetY() + 1;
1412
            }
1413
1414
            // Show payment mode
1415
            if (
1416
                !empty($object->mode_reglement_code)
1417
                && $object->mode_reglement_code != 'CHQ'
1418
                && $object->mode_reglement_code != 'VIR'
1419
            ) {
1420
                $pdf->SetFont('', 'B', $default_font_size - 2);
1421
                $pdf->SetXY($this->marge_gauche, $posy);
1422
                $titre = $outputlangs->transnoentities("PaymentMode") . ':';
1423
                $pdf->MultiCell($posxend - $this->marge_gauche, 5, $titre, 0, 'L');
1424
1425
                $pdf->SetFont('', '', $default_font_size - 2);
1426
                $pdf->SetXY($posxval, $posy);
1427
                $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);
1428
1429
                //#21654: add account number used for the debit
1430
                if ($object->mode_reglement_code == "PRE") {
1431
                    $bac = new CompanyBankAccount($this->db);
1432
                    // @phan-suppress-next-line PhanPluginSuspiciousParamPosition
1433
                    $bac->fetch(0, $object->thirdparty->id);
1434
                    $iban = $bac->iban . (($bac->iban && $bac->bic) ? ' / ' : '') . $bac->bic;
1435
                    $lib_mode_reg .= ' ' . $outputlangs->trans("PaymentTypePREdetails", dol_trunc($iban, 6, 'right', 'UTF-8', 1));
1436
                }
1437
1438
                $pdf->MultiCell($posxend - $posxval, 5, $lib_mode_reg, 0, 'L');
1439
1440
                $posy = $pdf->GetY();
1441
            }
1442
1443
            // Show if Option VAT debit option is on also if transmitter is french
1444
            // Decret n°2099-1299 2022-10-07
1445
            // French mention : "Option pour le paiement de la taxe d'après les débits"
1446
            if ($this->emetteur->country_code == 'FR') {
1447
                if (getDolGlobalInt('TAX_MODE') == 1) {
1448
                    $pdf->SetXY($this->marge_gauche, $posy);
1449
                    $pdf->writeHTMLCell(80, 5, '', '', $outputlangs->transnoentities("MentionVATDebitOptionIsOn"), 0, 1);
1450
1451
                    $posy = $pdf->GetY() + 1;
1452
                }
1453
            }
1454
1455
            // Show online payment link
1456
            if (empty($object->mode_reglement_code) || $object->mode_reglement_code == 'CB' || $object->mode_reglement_code == 'VAD') {
1457
                $useonlinepayment = 0;
1458
                if (getDolGlobalString('PDF_SHOW_LINK_TO_ONLINE_PAYMENT')) {
1459
                    if (isModEnabled('paypal')) {
1460
                        $useonlinepayment++;
1461
                    }
1462
                    if (isModEnabled('stripe')) {
1463
                        $useonlinepayment++;
1464
                    }
1465
                    if (isModEnabled('paybox')) {
1466
                        $useonlinepayment++;
1467
                    }
1468
                    $parameters = array();
1469
                    $action = '';
1470
                    $reshook = $hookmanager->executeHooks('doShowOnlinePaymentUrl', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
1471
                    if ($reshook > 0) {
1472
                        if (isset($hookmanager->resArray['showonlinepaymenturl'])) {
1473
                            $useonlinepayment += $hookmanager->resArray['showonlinepaymenturl'];
1474
                        }
1475
                    }
1476
                }
1477
1478
1479
                if ($object->statut != Facture::STATUS_DRAFT && $useonlinepayment) {
1480
                    require_once constant('DOL_DOCUMENT_ROOT') . '/core/lib/payments.lib.php';
1481
                    global $langs;
1482
1483
                    $langs->loadLangs(array('payment', 'paybox', 'stripe'));
1484
                    $servicename = $langs->transnoentities('Online');
1485
                    $paiement_url = getOnlinePaymentUrl('', 'invoice', $object->ref, '', '', '');
1486
                    $linktopay = $langs->trans("ToOfferALinkForOnlinePayment", $servicename) . ' <a href="' . $paiement_url . '">' . $outputlangs->transnoentities("ClickHere") . '</a>';
1487
1488
                    $pdf->SetXY($this->marge_gauche, $posy);
1489
                    $pdf->writeHTMLCell($posxend - $this->marge_gauche, 5, '', '', dol_htmlentitiesbr($linktopay), 0, 1);
1490
1491
                    $posy = $pdf->GetY() + 1;
1492
                }
1493
            }
1494
1495
            // Show payment mode CHQ
1496
            if (empty($object->mode_reglement_code) || $object->mode_reglement_code == 'CHQ') {
1497
                // If payment mode unregulated or payment mode forced to CHQ
1498
                if (getDolGlobalInt('FACTURE_CHQ_NUMBER')) {
1499
                    $diffsizetitle = getDolGlobalInt('PDF_DIFFSIZE_TITLE', 3);
1500
1501
                    if (getDolGlobalInt('FACTURE_CHQ_NUMBER') > 0) {
1502
                        $account = new Account($this->db);
1503
                        $account->fetch(getDolGlobalInt('FACTURE_CHQ_NUMBER'));
1504
1505
                        $pdf->SetXY($this->marge_gauche, $posy);
1506
                        $pdf->SetFont('', 'B', $default_font_size - $diffsizetitle);
1507
                        $pdf->MultiCell($posxend - $this->marge_gauche, 3, $outputlangs->transnoentities('PaymentByChequeOrderedTo', $account->proprio), 0, 'L', 0);
1508
                        $posy = $pdf->GetY() + 1;
1509
1510
                        if (!getDolGlobalString('MAIN_PDF_HIDE_CHQ_ADDRESS')) {
1511
                            $pdf->SetXY($this->marge_gauche, $posy);
1512
                            $pdf->SetFont('', '', $default_font_size - $diffsizetitle);
1513
                            $pdf->MultiCell($posxend - $this->marge_gauche, 3, $outputlangs->convToOutputCharset($account->owner_address), 0, 'L', 0);
1514
                            $posy = $pdf->GetY() + 2;
1515
                        }
1516
                    }
1517
                    if (getDolGlobalString('FACTURE_CHQ_NUMBER') == -1) {
1518
                        $pdf->SetXY($this->marge_gauche, $posy);
1519
                        $pdf->SetFont('', 'B', $default_font_size - $diffsizetitle);
1520
                        $pdf->MultiCell($posxend - $this->marge_gauche, 3, $outputlangs->transnoentities('PaymentByChequeOrderedTo', $this->emetteur->name), 0, 'L', 0);
1521
                        $posy = $pdf->GetY() + 1;
1522
1523
                        if (!getDolGlobalString('MAIN_PDF_HIDE_CHQ_ADDRESS')) {
1524
                            $pdf->SetXY($this->marge_gauche, $posy);
1525
                            $pdf->SetFont('', '', $default_font_size - $diffsizetitle);
1526
                            $pdf->MultiCell($posxend - $this->marge_gauche, 3, $outputlangs->convToOutputCharset($this->emetteur->getFullAddress()), 0, 'L', 0);
1527
                            $posy = $pdf->GetY() + 2;
1528
                        }
1529
                    }
1530
                }
1531
            }
1532
1533
            // If payment mode not forced or forced to VIR, show payment with BAN
1534
            if (empty($object->mode_reglement_code) || $object->mode_reglement_code == 'VIR') {
1535
                if ($object->fk_account > 0 || $object->fk_bank > 0 || getDolGlobalInt('FACTURE_RIB_NUMBER')) {
1536
                    $bankid = ($object->fk_account <= 0 ? getDolGlobalInt('FACTURE_RIB_NUMBER') : $object->fk_account);
1537
                    if ($object->fk_bank > 0) {
1538
                        $bankid = $object->fk_bank; // For backward compatibility when object->fk_account is forced with object->fk_bank
1539
                    }
1540
                    $account = new Account($this->db);
1541
                    $account->fetch($bankid);
1542
1543
                    $curx = $this->marge_gauche;
1544
                    $cury = $posy;
1545
1546
                    $posy = pdf_bank($pdf, $outputlangs, $curx, $cury, $account, 0, $default_font_size);
1547
1548
                    $posy += 2;
1549
                }
1550
            }
1551
        }
1552
1553
        return $posy;
1554
    }
1555
1556
1557
    /**
1558
     *  Show total to pay
1559
     *
1560
     * @param TCPDF $pdf Object PDF
1561
     * @param Facture $object Object invoice
1562
     * @param int $deja_regle Amount already paid (in the currency of invoice)
1563
     * @param int $posy Position depart
1564
     * @param Translate $outputlangs Object langs
1565
     * @param Translate $outputlangsbis Object lang for output bis
1566
     * @return int                         Position pour suite
1567
     */
1568
    protected function drawTotalTable(&$pdf, $object, $deja_regle, $posy, $outputlangs, $outputlangsbis)
1569
    {
1570
        global $conf, $mysoc, $hookmanager;
1571
1572
        $sign = 1;
1573
        if ($object->type == 2 && getDolGlobalString('INVOICE_POSITIVE_CREDIT_NOTE')) {
1574
            $sign = -1;
1575
        }
1576
1577
        $default_font_size = pdf_getPDFFontSize($outputlangs);
1578
1579
        if (getDolGlobalString('PDF_USE_ALSO_LANGUAGE_CODE') && $outputlangs->defaultlang != getDolGlobalString('PDF_USE_ALSO_LANGUAGE_CODE')) {
1580
            $outputlangsbis = new Translate('', $conf);
1581
            $outputlangsbis->setDefaultLang(getDolGlobalString('PDF_USE_ALSO_LANGUAGE_CODE'));
1582
            $outputlangsbis->loadLangs(array("main", "dict", "companies", "bills", "products", "propal"));
1583
            $default_font_size--;
1584
        }
1585
1586
        $tab2_top = $posy - 4;
1587
        $tab2_hl = 4;
1588
        $pdf->SetFont('', '', $default_font_size - 1);
1589
1590
        // Total table
1591
        $col1x = 120;
1592
        $col2x = 170;
1593
        if ($this->page_largeur < 210) { // To work with US executive format
1594
            $col2x -= 20;
1595
        }
1596
        $largcol2 = ($this->page_largeur - $this->marge_droite - $col2x);
1597
1598
        $useborder = 0;
1599
        $index = 0;
1600
1601
        // Total HT
1602
        $pdf->SetFillColor(255, 255, 255);
1603
        $pdf->SetXY($col1x, $tab2_top + 0);
1604
        $pdf->MultiCell($col2x - $col1x, $tab2_hl, $outputlangs->transnoentities("TotalHT") . (is_object($outputlangsbis) ? ' / ' . $outputlangsbis->transnoentities("TotalHT") : ''), 0, 'L', 1);
1605
1606
        $total_ht = ((!empty($conf->multicurrency->enabled) && isset($object->multicurrency_tx) && $object->multicurrency_tx != 1) ? $object->multicurrency_total_ht : $object->total_ht);
1607
        $pdf->SetXY($col2x, $tab2_top + 0);
1608
        $pdf->MultiCell($largcol2, $tab2_hl, price($total_ht, 0, $outputlangs), 0, 'R', 1);
1609
1610
        $remise = !empty($object->remise) ? $object->remise : 0;
1611
        if ($remise > 0) {
1612
            $index++;
1613
            $pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index);
1614
            $pdf->MultiCell($col2x - $col1x, $tab2_hl, $outputlangs->transnoentities("DiscountHT") . (is_object($outputlangsbis) ? ' / ' . $outputlangsbis->transnoentities("DiscountHT") : ''), 0, 'L', 1);
1615
            $pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index);
1616
            $pdf->MultiCell($largcol2, $tab2_hl, price($remise, 0, $outputlangs), 0, 'R', 1);
1617
1618
            $index++;
1619
            $pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index);
1620
            $pdf->MultiCell($col2x - $col1x, $tab2_hl, $outputlangs->transnoentities("TotalHTWithDiscount") . (is_object($outputlangsbis) ? ' / ' . $outputlangsbis->transnoentities("TotalHTWithDiscount") : ''), 0, 'L', 1);
1621
            $pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index);
1622
            $pdf->MultiCell($largcol2, $tab2_hl, price($total_ht - $remise, 0, $outputlangs), 0, 'R', 1);
1623
        }
1624
1625
        // Show VAT by rates and total
1626
        $pdf->SetFillColor(248, 248, 248);
1627
1628
        $total_ttc = (isModEnabled("multicurrency") && $object->multicurrency_tx != 1) ? $object->multicurrency_total_ttc : $object->total_ttc;
1629
1630
        $this->atleastoneratenotnull = 0;
1631
        if (!getDolGlobalString('MAIN_GENERATE_DOCUMENTS_WITHOUT_VAT')) {
1632
            $tvaisnull = ((!empty($this->tva) && count($this->tva) == 1 && isset($this->tva['0.000']) && is_float($this->tva['0.000'])) ? true : false);
1633
            if (getDolGlobalString('MAIN_GENERATE_DOCUMENTS_WITHOUT_VAT_IFNULL') && $tvaisnull) {
1634
                // Nothing to do
1635
            } else {
1636
                //Local tax 1 before VAT
1637
                //if (!empty($conf->global->FACTURE_LOCAL_TAX1_OPTION) && $conf->global->FACTURE_LOCAL_TAX1_OPTION=='localtax1on')
1638
                //{
1639
                foreach ($this->localtax1 as $localtax_type => $localtax_rate) {
1640
                    if (in_array((string)$localtax_type, array('1', '3', '5'))) {
1641
                        continue;
1642
                    }
1643
1644
                    foreach ($localtax_rate as $tvakey => $tvaval) {
1645
                        if ($tvakey != 0) {    // On affiche pas taux 0
1646
                            //$this->atleastoneratenotnull++;
1647
1648
                            $index++;
1649
                            $pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index);
1650
1651
                            $tvacompl = '';
1652
                            if (preg_match('/\*/', $tvakey)) {
1653
                                $tvakey = str_replace('*', '', $tvakey);
1654
                                $tvacompl = " (" . $outputlangs->transnoentities("NonPercuRecuperable") . ")";
1655
                            }
1656
1657
                            $totalvat = $outputlangs->transcountrynoentities("TotalLT1", $mysoc->country_code) . (is_object($outputlangsbis) ? ' / ' . $outputlangsbis->transcountrynoentities("TotalLT1", $mysoc->country_code) : '');
1658
                            $totalvat .= ' ';
1659
                            $totalvat .= vatrate(abs($tvakey), 1) . $tvacompl;
1660
                            $pdf->MultiCell($col2x - $col1x, $tab2_hl, $totalvat, 0, 'L', 1);
1661
1662
                            $total_localtax = ((isModEnabled("multicurrency") && isset($object->multicurrency_tx) && $object->multicurrency_tx != 1) ? price2num($tvaval * $object->multicurrency_tx, 'MT') : $tvaval);
1663
1664
                            $pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index);
1665
                            $pdf->MultiCell($largcol2, $tab2_hl, price($total_localtax, 0, $outputlangs), 0, 'R', 1);
1666
                        }
1667
                    }
1668
                }
1669
                //}
1670
                //Local tax 2 before VAT
1671
                //if (!empty($conf->global->FACTURE_LOCAL_TAX2_OPTION) && $conf->global->FACTURE_LOCAL_TAX2_OPTION=='localtax2on')
1672
                //{
1673
                foreach ($this->localtax2 as $localtax_type => $localtax_rate) {
1674
                    if (in_array((string)$localtax_type, array('1', '3', '5'))) {
1675
                        continue;
1676
                    }
1677
1678
                    foreach ($localtax_rate as $tvakey => $tvaval) {
1679
                        if ($tvakey != 0) {    // On affiche pas taux 0
1680
                            //$this->atleastoneratenotnull++;
1681
1682
                            $index++;
1683
                            $pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index);
1684
1685
                            $tvacompl = '';
1686
                            if (preg_match('/\*/', $tvakey)) {
1687
                                $tvakey = str_replace('*', '', $tvakey);
1688
                                $tvacompl = " (" . $outputlangs->transnoentities("NonPercuRecuperable") . ")";
1689
                            }
1690
                            $totalvat = $outputlangs->transcountrynoentities("TotalLT2", $mysoc->country_code) . (is_object($outputlangsbis) ? ' / ' . $outputlangsbis->transcountrynoentities("TotalLT2", $mysoc->country_code) : '');
1691
                            $totalvat .= ' ';
1692
                            $totalvat .= vatrate(abs($tvakey), 1) . $tvacompl;
1693
                            $pdf->MultiCell($col2x - $col1x, $tab2_hl, $totalvat, 0, 'L', 1);
1694
1695
                            $total_localtax = ((isModEnabled("multicurrency") && isset($object->multicurrency_tx) && $object->multicurrency_tx != 1) ? price2num($tvaval * $object->multicurrency_tx, 'MT') : $tvaval);
1696
1697
                            $pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index);
1698
                            $pdf->MultiCell($largcol2, $tab2_hl, price($total_localtax, 0, $outputlangs), 0, 'R', 1);
1699
                        }
1700
                    }
1701
                }
1702
                //}
1703
1704
                // VAT
1705
                $tvas = array();
1706
                $nblines = count($object->lines);
1707
                for ($i = 0; $i < $nblines; $i++) {
1708
                    $tvaligne = $object->lines[$i]->total_tva;
1709
                    $vatrate = (string)$object->lines[$i]->tva_tx;
1710
1711
                    if (($object->lines[$i]->info_bits & 0x01) == 0x01) {
1712
                        $vatrate .= '*';
1713
                    }
1714
                    if (!isset($tvas[$vatrate])) {
1715
                        $tvas[$vatrate] = 0;
1716
                    }
1717
                    $tvas[$vatrate] += $tvaligne;
1718
                }
1719
1720
                foreach ($tvas as $tvakey => $tvaval) {
1721
                    if ($tvakey != 0) { // On affiche pas taux 0
1722
                        $this->atleastoneratenotnull++;
1723
1724
                        $index++;
1725
                        $pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index);
1726
1727
                        $tvacompl = '';
1728
                        if (preg_match('/\*/', $tvakey)) {
1729
                            $tvakey = str_replace('*', '', $tvakey);
1730
                            $tvacompl = " (" . $outputlangs->transnoentities("NonPercuRecuperable") . ")";
1731
                        }
1732
                        $totalvat = $outputlangs->transcountrynoentities("TotalVAT", $mysoc->country_code) . (is_object($outputlangsbis) ? ' / ' . $outputlangsbis->transcountrynoentities("TotalVAT", $mysoc->country_code) : '');
1733
                        $totalvat .= ' ';
1734
                        if (getDolGlobalString('PDF_VAT_LABEL_IS_CODE_OR_RATE') == 'rateonly') {
1735
                            $totalvat .= vatrate($tvaval['vatrate'], 1) . $tvacompl;
1736
                        } elseif (getDolGlobalString('PDF_VAT_LABEL_IS_CODE_OR_RATE') == 'codeonly') {
1737
                            $totalvat .= ($tvaval['vatcode'] ? $tvaval['vatcode'] : vatrate($tvaval['vatrate'], 1)) . $tvacompl;
1738
                        } else {
1739
                            $totalvat .= vatrate($tvaval['vatrate'], 1) . ($tvaval['vatcode'] ? ' (' . $tvaval['vatcode'] . ')' : '') . $tvacompl;
1740
                        }
1741
                        $pdf->MultiCell($col2x - $col1x, $tab2_hl, $totalvat, 0, 'L', 1);
1742
1743
                        $pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index);
1744
                        $pdf->MultiCell($largcol2, $tab2_hl, price(price2num($tvaval['amount'], 'MT'), 0, $outputlangs), 0, 'R', 1);
1745
                    }
1746
                }
1747
1748
                //Local tax 1 after VAT
1749
                //if (!empty($conf->global->FACTURE_LOCAL_TAX1_OPTION) && $conf->global->FACTURE_LOCAL_TAX1_OPTION=='localtax1on')
1750
                //{
1751
                foreach ($this->localtax1 as $localtax_type => $localtax_rate) {
1752
                    if (in_array((string)$localtax_type, array('2', '4', '6'))) {
1753
                        continue;
1754
                    }
1755
1756
                    foreach ($localtax_rate as $tvakey => $tvaval) {
1757
                        if ($tvakey != 0) {    // On affiche pas taux 0
1758
                            //$this->atleastoneratenotnull++;
1759
1760
                            $index++;
1761
                            $pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index);
1762
1763
                            $tvacompl = '';
1764
                            if (preg_match('/\*/', $tvakey)) {
1765
                                $tvakey = str_replace('*', '', $tvakey);
1766
                                $tvacompl = " (" . $outputlangs->transnoentities("NonPercuRecuperable") . ")";
1767
                            }
1768
                            $totalvat = $outputlangs->transcountrynoentities("TotalLT1", $mysoc->country_code) . (is_object($outputlangsbis) ? ' / ' . $outputlangsbis->transcountrynoentities("TotalLT1", $mysoc->country_code) : '');
1769
                            $totalvat .= ' ';
1770
                            $totalvat .= vatrate(abs($tvakey), 1) . $tvacompl;
1771
1772
                            $pdf->MultiCell($col2x - $col1x, $tab2_hl, $totalvat, 0, 'L', 1);
1773
1774
                            $total_localtax = ((isModEnabled("multicurrency") && isset($object->multicurrency_tx) && $object->multicurrency_tx != 1) ? price2num($tvaval * $object->multicurrency_tx, 'MT') : $tvaval);
1775
1776
                            $pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index);
1777
                            $pdf->MultiCell($largcol2, $tab2_hl, price($total_localtax, 0, $outputlangs), 0, 'R', 1);
1778
                        }
1779
                    }
1780
                }
1781
                //}
1782
                //Local tax 2 after VAT
1783
                //if (!empty($conf->global->FACTURE_LOCAL_TAX2_OPTION) && $conf->global->FACTURE_LOCAL_TAX2_OPTION=='localtax2on')
1784
                //{
1785
                foreach ($this->localtax2 as $localtax_type => $localtax_rate) {
1786
                    if (in_array((string)$localtax_type, array('2', '4', '6'))) {
1787
                        continue;
1788
                    }
1789
1790
                    foreach ($localtax_rate as $tvakey => $tvaval) {
1791
                        // retrieve global local tax
1792
                        if ($tvakey != 0) {    // On affiche pas taux 0
1793
                            //$this->atleastoneratenotnull++;
1794
1795
                            $index++;
1796
                            $pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index);
1797
1798
                            $tvacompl = '';
1799
                            if (preg_match('/\*/', $tvakey)) {
1800
                                $tvakey = str_replace('*', '', $tvakey);
1801
                                $tvacompl = " (" . $outputlangs->transnoentities("NonPercuRecuperable") . ")";
1802
                            }
1803
                            $totalvat = $outputlangs->transcountrynoentities("TotalLT2", $mysoc->country_code) . (is_object($outputlangsbis) ? ' / ' . $outputlangsbis->transcountrynoentities("TotalLT2", $mysoc->country_code) : '');
1804
                            $totalvat .= ' ';
1805
1806
                            $totalvat .= vatrate(abs($tvakey), 1) . $tvacompl;
1807
                            $pdf->MultiCell($col2x - $col1x, $tab2_hl, $totalvat, 0, 'L', 1);
1808
1809
                            $total_localtax = ((isModEnabled("multicurrency") && isset($object->multicurrency_tx) && $object->multicurrency_tx != 1) ? price2num($tvaval * $object->multicurrency_tx, 'MT') : $tvaval);
1810
1811
                            $pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index);
1812
                            $pdf->MultiCell($largcol2, $tab2_hl, price($total_localtax, 0, $outputlangs), 0, 'R', 1);
1813
                        }
1814
                    }
1815
                }
1816
1817
1818
                // Revenue stamp
1819
                if (price2num($object->revenuestamp) != 0) {
1820
                    $index++;
1821
                    $pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index);
1822
                    $pdf->MultiCell($col2x - $col1x, $tab2_hl, $outputlangs->transnoentities("RevenueStamp") . (is_object($outputlangsbis) ? ' / ' . $outputlangsbis->transnoentities("RevenueStamp", $mysoc->country_code) : ''), $useborder, 'L', 1);
1823
1824
                    $pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index);
1825
                    $pdf->MultiCell($largcol2, $tab2_hl, price($sign * $object->revenuestamp), $useborder, 'R', 1);
1826
                }
1827
1828
                // Total TTC
1829
                $index++;
1830
                $pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index);
1831
                $pdf->SetTextColor(0, 0, 60);
1832
                $pdf->SetFillColor(224, 224, 224);
1833
                $pdf->MultiCell($col2x - $col1x, $tab2_hl, $outputlangs->transnoentities("TotalTTC") . (is_object($outputlangsbis) ? ' / ' . $outputlangsbis->transnoentities("TotalTTC") : ''), $useborder, 'L', 1);
1834
1835
                $pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index);
1836
                $pdf->MultiCell($largcol2, $tab2_hl, price($sign * $total_ttc, 0, $outputlangs), $useborder, 'R', 1);
1837
1838
1839
                // Retained warranty
1840
                if ($object->displayRetainedWarranty()) {
1841
                    $pdf->SetTextColor(40, 40, 40);
1842
                    $pdf->SetFillColor(255, 255, 255);
1843
1844
                    $retainedWarranty = $object->getRetainedWarrantyAmount();
1845
                    $billedWithRetainedWarranty = $object->total_ttc - $retainedWarranty;
1846
1847
                    // Billed - retained warranty
1848
                    $index++;
1849
                    $pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index);
1850
                    $pdf->MultiCell($col2x - $col1x, $tab2_hl, $outputlangs->transnoentities("ToPayOn", dol_print_date($object->date_lim_reglement, 'day')), $useborder, 'L', 1);
1851
1852
                    $pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index);
1853
                    $pdf->MultiCell($largcol2, $tab2_hl, price($billedWithRetainedWarranty), $useborder, 'R', 1);
1854
1855
                    // retained warranty
1856
                    $index++;
1857
                    $pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index);
1858
1859
                    $retainedWarrantyToPayOn = $outputlangs->transnoentities("RetainedWarranty") . (is_object($outputlangsbis) ? ' / ' . $outputlangsbis->transnoentities("RetainedWarranty") : '') . ' (' . $object->retained_warranty . '%)';
1860
                    $retainedWarrantyToPayOn .= !empty($object->retained_warranty_date_limit) ? ' ' . $outputlangs->transnoentities("toPayOn", dol_print_date($object->retained_warranty_date_limit, 'day')) : '';
1861
1862
                    $pdf->MultiCell($col2x - $col1x, $tab2_hl, $retainedWarrantyToPayOn, $useborder, 'L', 1);
1863
                    $pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index);
1864
                    $pdf->MultiCell($largcol2, $tab2_hl, price($retainedWarranty), $useborder, 'R', 1);
1865
                }
1866
            }
1867
        }
1868
1869
        $pdf->SetTextColor(0, 0, 0);
1870
1871
        $resteapayer = 0;
1872
        /*
1873
        $resteapayer = $object->total_ttc - $deja_regle;
1874
        if (! empty($object->paye)) $resteapayer=0;
1875
        */
1876
1877
        if ($deja_regle > 0) {
1878
            // Already paid + Deposits
1879
            $index++;
1880
1881
            $pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index);
1882
            $pdf->MultiCell($col2x - $col1x, $tab2_hl, $outputlangs->transnoentities("AlreadyPaid") . (is_object($outputlangsbis) ? ' / ' . $outputlangsbis->transnoentities("AlreadyPaid") : ''), 0, 'L', 0);
1883
1884
            $pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index);
1885
            $pdf->MultiCell($largcol2, $tab2_hl, price($deja_regle, 0, $outputlangs), 0, 'R', 0);
1886
1887
            /*
1888
            if ($object->close_code == 'discount_vat')
1889
            {
1890
                $index++;
1891
                $pdf->SetFillColor(255,255,255);
1892
1893
                $pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index);
1894
                $pdf->MultiCell($col2x - $col1x, $tab2_hl, $outputlangs->transnoentities("EscompteOfferedShort"), $useborder, 'L', 1);
1895
1896
                $pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index);
1897
                $pdf->MultiCell($largcol2, $tab2_hl, price($object->total_ttc - $deja_regle, 0, $outputlangs), $useborder, 'R', 1);
1898
1899
                $resteapayer=0;
1900
            }
1901
            */
1902
1903
            $index++;
1904
            $pdf->SetTextColor(0, 0, 60);
1905
            $pdf->SetFillColor(224, 224, 224);
1906
            $pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index);
1907
            $pdf->MultiCell($col2x - $col1x, $tab2_hl, $outputlangs->transnoentities("RemainderToPay") . (is_object($outputlangsbis) ? ' / ' . $outputlangsbis->transnoentities("RemainderToPay") : ''), $useborder, 'L', 1);
1908
            $pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index);
1909
            $pdf->MultiCell($largcol2, $tab2_hl, price($resteapayer, 0, $outputlangs), $useborder, 'R', 1);
1910
1911
            $pdf->SetFont('', '', $default_font_size - 1);
1912
            $pdf->SetTextColor(0, 0, 0);
1913
        }
1914
1915
        $parameters = array('pdf' => &$pdf, 'object' => &$object, 'outputlangs' => $outputlangs, 'index' => &$index);
1916
1917
        $reshook = $hookmanager->executeHooks('afterPDFTotalTable', $parameters, $this); // Note that $action and $object may have been modified by some hooks
1918
        if ($reshook < 0) {
1919
            $this->error = $hookmanager->error;
1920
            $this->errors = $hookmanager->errors;
1921
        }
1922
1923
        $index++;
1924
        return ($tab2_top + ($tab2_hl * $index));
1925
    }
1926
1927
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1928
1929
    /**
1930
     *  Return list of active generation modules
1931
     *
1932
     * @param DoliDB $db Database handler
1933
     * @param integer $maxfilenamelength Max length of value to show
1934
     * @return array                       List of templates
1935
     */
1936
    public static function liste_modeles($db, $maxfilenamelength = 0)
1937
    {
1938
        // phpcs:enable
1939
        return parent::liste_modeles($db, $maxfilenamelength); // TODO: Change the autogenerated stub
1940
    }
1941
1942
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
1943
1944
    /**
1945
     *   Show table for lines
1946
     *
1947
     * @param TCPDF $pdf Object PDF
1948
     * @param int $tab_top Top position of table
1949
     * @param int $tab_height Height of table (rectangle)
1950
     * @param int $nexY Y (not used)
1951
     * @param Translate $outputlangs Langs object
1952
     * @param int $hidetop 1=Hide top bar of array and title, 0=Hide nothing, -1=Hide only title
1953
     * @param int $hidebottom Hide bottom bar of array
1954
     * @param string $currency Currency code
1955
     * @param Translate $outputlangsbis Langs object bis
1956
     * @return    void
1957
     */
1958
    protected function _tableau(&$pdf, $tab_top, $tab_height, $nexY, $outputlangs, $hidetop = 0, $hidebottom = 0, $currency = '', $outputlangsbis = null)
1959
    {
1960
        global $conf;
1961
1962
        // Force to disable hidetop and hidebottom
1963
        $hidebottom = 0;
1964
        $hidetop = 0;
1965
1966
        $currency = !empty($currency) ? $currency : $conf->currency;
1967
        $default_font_size = pdf_getPDFFontSize($outputlangs);
1968
1969
        // Amount in (at tab_top - 1)
1970
        $pdf->SetTextColor(0, 0, 0);
1971
        $pdf->SetFont('', '', $default_font_size - 2);
1972
1973
        if (empty($hidetop)) {
1974
            $titre = $outputlangs->transnoentities("AmountInCurrency", $outputlangs->transnoentitiesnoconv("Currency" . $currency));
1975
            $pdf->SetXY($this->page_largeur - $this->marge_droite - ($pdf->GetStringWidth($titre) + 3), $tab_top - 4);
1976
            $pdf->MultiCell(($pdf->GetStringWidth($titre) + 3), 2, $titre);
1977
1978
            // MAIN_PDF_TITLE_BACKGROUND_COLOR='230,230,230';
1979
            if (getDolGlobalString('MAIN_PDF_TITLE_BACKGROUND_COLOR')) {
1980
                $pdf->Rect($this->marge_gauche, $tab_top, $this->page_largeur - $this->marge_droite - $this->marge_gauche, 5, 'F', null, explode(',', getDolGlobalString('MAIN_PDF_TITLE_BACKGROUND_COLOR')));
1981
            }
1982
            $tab_top += 4;
1983
        }
1984
1985
        $pdf->SetDrawColor(128, 128, 128);
1986
        $pdf->SetFont('', '', $default_font_size - 1);
1987
1988
        // Output Rect
1989
        $this->printRect($pdf, $this->marge_gauche, $tab_top, $this->page_largeur - $this->marge_gauche - $this->marge_droite, $tab_height, $hidetop, $hidebottom); // Rect prend une longueur en 3eme param et 4eme param
1990
1991
        // situation invoice
1992
        $pdf->SetFont('', '', $default_font_size - 2);
1993
1994
        foreach ($this->cols as $colKey => $colDef) {
1995
            if (!$this->getColumnStatus($colKey)) {
1996
                continue;
1997
            }
1998
            $xstartpos = (int)($colDef['xStartPos'] ?? 0);
1999
            //is there any overtitle ?
2000
            if (!empty($colDef['overtitle']) && is_array($colDef['overtitle'])) {
2001
                $overtitle_top = $tab_top - 4;
2002
                $overtitle = $colDef['overtitle']['textkey'] ?? '';
2003
                $textWidth = $colDef['overtitle']['width'] ?? 0;
2004
                $pdf->SetXY($xstartpos + $colDef['overtitle']['padding'][3], $overtitle_top);
2005
                $pdf->MultiCell($textWidth, 2, $overtitle, '', $colDef['overtitle']['align']);
2006
                $pdf->line($xstartpos, $overtitle_top, $xstartpos, $overtitle_top + 4); //left
2007
                $pdf->line($xstartpos, $overtitle_top, $xstartpos + $textWidth, $overtitle_top); //top
2008
                $pdf->line($xstartpos + $textWidth, $overtitle_top, $xstartpos + $textWidth, $overtitle_top + 4); //right
2009
            }
2010
2011
            // get title label
2012
            $colDef['title']['label'] = !empty($colDef['title']['label']) ? $colDef['title']['label'] : $outputlangs->transnoentities($colDef['title']['textkey']);
2013
2014
            // Add column separator
2015
            if (!empty($colDef['border-left'])) {
2016
                $pdf->line($xstartpos, $tab_top, $xstartpos, $tab_top + $tab_height);
2017
            }
2018
2019
            if (empty($hidetop)) {
2020
                $pdf->SetXY($xstartpos + $colDef['title']['padding'][3], $tab_top + $colDef['title']['padding'][0]);
2021
2022
                $textWidth = $colDef['width'] - $colDef['title']['padding'][3] - $colDef['title']['padding'][1];
2023
                $pdf->MultiCell($textWidth, 2, $colDef['title']['label'], '', $colDef['title']['align']);
2024
            }
2025
        }
2026
        $pdf->SetFont('', '', $default_font_size - 1);
2027
2028
        if (empty($hidetop)) {
2029
            $pdf->line($this->marge_gauche, $tab_top + 5, $this->page_largeur - $this->marge_droite, $tab_top + 5);   // line prend une position y en 2eme param et 4eme param
2030
        }
2031
    }
2032
2033
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
2034
2035
    /**
2036
     *  Show top header of page. This include the logo, ref and address blocks
2037
     *
2038
     * @param TCPDF $pdf Object PDF
2039
     * @param Facture $object Object to show
2040
     * @param int $showaddress 0=no, 1=yes (usually set to 1 for first page, and 0 for next pages)
2041
     * @param Translate $outputlangs Object lang for output
2042
     * @param Translate $outputlangsbis Object lang for output bis
2043
     * @return array                       top shift of linked object lines
2044
     */
2045
    protected function _pagehead(&$pdf, $object, $showaddress, $outputlangs, $outputlangsbis = null)
2046
    {
2047
        // phpcs:enable
2048
        global $conf, $langs;
2049
2050
        $ltrdirection = 'L';
2051
        if ($outputlangs->trans("DIRECTION") == 'rtl') {
2052
            $ltrdirection = 'R';
2053
        }
2054
2055
        // Load traductions files required by page
2056
        $outputlangs->loadLangs(array("main", "bills", "propal", "companies"));
2057
2058
        $default_font_size = pdf_getPDFFontSize($outputlangs);
2059
2060
        pdf_pagehead($pdf, $outputlangs, $this->page_hauteur);
2061
2062
        $pdf->SetTextColor(0, 0, 60);
2063
        $pdf->SetFont('', 'B', $default_font_size + 3);
2064
2065
        $w = 110;
2066
2067
        $posy = $this->marge_haute;
2068
        $posx = $this->page_largeur - $this->marge_droite - $w;
2069
2070
        $pdf->SetXY($this->marge_gauche, $posy);
2071
2072
        // Logo
2073
        if (!getDolGlobalInt('PDF_DISABLE_MYCOMPANY_LOGO')) {
2074
            if ($this->emetteur->logo) {
2075
                $logodir = $conf->mycompany->dir_output;
2076
                if (!empty($conf->mycompany->multidir_output[$object->entity])) {
2077
                    $logodir = $conf->mycompany->multidir_output[$object->entity];
2078
                }
2079
                if (!getDolGlobalInt('MAIN_PDF_USE_LARGE_LOGO')) {
2080
                    $logo = $logodir . '/logos/thumbs/' . $this->emetteur->logo_small;
2081
                } else {
2082
                    $logo = $logodir . '/logos/' . $this->emetteur->logo;
2083
                }
2084
                if (is_readable($logo)) {
2085
                    $height = pdf_getHeightForLogo($logo);
2086
                    $pdf->Image($logo, $this->marge_gauche, $posy, 0, $height); // width=0 (auto)
2087
                } else {
2088
                    $pdf->SetTextColor(200, 0, 0);
2089
                    $pdf->SetFont('', 'B', $default_font_size - 2);
2090
                    $pdf->MultiCell($w, 3, $outputlangs->transnoentities("ErrorLogoFileNotFound", $logo), 0, 'L');
2091
                    $pdf->MultiCell($w, 3, $outputlangs->transnoentities("ErrorGoToGlobalSetup"), 0, 'L');
2092
                }
2093
            } else {
2094
                $text = $this->emetteur->name;
2095
                $pdf->MultiCell($w, 4, $outputlangs->convToOutputCharset($text), 0, $ltrdirection);
2096
            }
2097
        }
2098
2099
        $pdf->SetFont('', 'B', $default_font_size + 3);
2100
        $pdf->SetXY($posx, $posy);
2101
        $pdf->SetTextColor(0, 0, 60);
2102
        $subtitle = "";
2103
        $title = $outputlangs->transnoentities("PdfInvoiceTitle");
2104
        if ($object->type == 1) {
2105
            $title = $outputlangs->transnoentities("InvoiceReplacement");
2106
        }
2107
        if ($object->type == 2) {
2108
            $title = $outputlangs->transnoentities("InvoiceAvoir");
2109
        }
2110
        if ($object->type == 3) {
2111
            $title = $outputlangs->transnoentities("InvoiceDeposit");
2112
        }
2113
        if ($object->type == 4) {
2114
            $title = $outputlangs->transnoentities("InvoiceProForma");
2115
        }
2116
        if ($this->situationinvoice) {
2117
            $title = $outputlangs->transnoentities("PDFInvoiceSituation");
2118
            $subtitle = $outputlangs->transnoentities("PDFSituationTitle", $object->situation_counter);
2119
        }
2120
        if (getDolGlobalString('PDF_USE_ALSO_LANGUAGE_CODE') && is_object($outputlangsbis)) {
2121
            $title .= ' - ';
2122
            if ($object->type == 0) {
2123
                if ($this->situationinvoice) {
2124
                    $title .= $outputlangsbis->transnoentities("PDFInvoiceSituation");
2125
                }
2126
                $title .= $outputlangsbis->transnoentities("PdfInvoiceTitle");
2127
            } elseif ($object->type == 1) {
2128
                $title .= $outputlangsbis->transnoentities("InvoiceReplacement");
2129
            } elseif ($object->type == 2) {
2130
                $title .= $outputlangsbis->transnoentities("InvoiceAvoir");
2131
            } elseif ($object->type == 3) {
2132
                $title .= $outputlangsbis->transnoentities("InvoiceDeposit");
2133
            } elseif ($object->type == 4) {
2134
                $title .= $outputlangsbis->transnoentities("InvoiceProForma");
2135
            }
2136
        }
2137
        $title .= ' ' . $outputlangs->convToOutputCharset($object->ref);
2138
        // if ($object->statut == $object::STATUS_DRAFT) {
2139
        //  $pdf->SetTextColor(128, 0, 0);
2140
        //  $title .= ' - '.$outputlangs->transnoentities("NotValidated");
2141
        // }
2142
2143
        $pdf->MultiCell($w, 3, $title, '', 'R');
2144
        if (!empty($subtitle)) {
2145
            $pdf->SetFont('', 'B', $default_font_size);
2146
            $pdf->SetXY($posx, $posy + 5);
2147
            $pdf->MultiCell($w, 6, $subtitle, '', 'R');
2148
            $posy += 2;
2149
        }
2150
2151
        $pdf->SetFont('', 'B', $default_font_size);
2152
2153
        /*
2154
         $posy += 5;
2155
         $pdf->SetXY($posx, $posy);
2156
         $pdf->SetTextColor(0, 0, 60);
2157
         $textref = $outputlangs->transnoentities("Ref")." : ".$outputlangs->convToOutputCharset($object->ref);
2158
         if ($object->statut == $object::STATUS_DRAFT) {
2159
         $pdf->SetTextColor(128, 0, 0);
2160
         $textref .= ' - '.$outputlangs->transnoentities("NotValidated");
2161
         }
2162
         $pdf->MultiCell($w, 4, $textref, '', 'R');*/
2163
2164
        $posy += 3;
2165
        $pdf->SetFont('', '', $default_font_size - 2);
2166
2167
        if ($object->ref_customer) {
2168
            $posy += 4;
2169
            $pdf->SetXY($posx, $posy);
2170
            $pdf->SetTextColor(0, 0, 60);
2171
            $pdf->MultiCell($w, 3, $outputlangs->transnoentities("RefCustomer") . " : " . dol_trunc($outputlangs->convToOutputCharset($object->ref_customer), 65), '', 'R');
2172
        }
2173
2174
        if (getDolGlobalString('PDF_SHOW_PROJECT_TITLE')) {
2175
            $object->fetch_projet();
2176
            if (!empty($object->project->ref)) {
2177
                $posy += 3;
2178
                $pdf->SetXY($posx, $posy);
2179
                $pdf->SetTextColor(0, 0, 60);
2180
                $pdf->MultiCell($w, 3, $outputlangs->transnoentities("Project") . " : " . (empty($object->project->title) ? '' : $object->project->title), '', 'R');
2181
            }
2182
        }
2183
2184
        if (getDolGlobalString('PDF_SHOW_PROJECT')) {
2185
            $object->fetch_projet();
2186
            if (!empty($object->project->ref)) {
2187
                $outputlangs->load("projects");
2188
                $posy += 3;
2189
                $pdf->SetXY($posx, $posy);
2190
                $pdf->SetTextColor(0, 0, 60);
2191
                $pdf->MultiCell($w, 3, $outputlangs->transnoentities("RefProject") . " : " . (empty($object->project->ref) ? '' : $object->project->ref), '', 'R');
2192
            }
2193
        }
2194
2195
        $objectidnext = $object->getIdReplacingInvoice('validated');
2196
        if ($object->type == 0 && $objectidnext) {
2197
            $objectreplacing = new Facture($this->db);
2198
            $objectreplacing->fetch($objectidnext);
2199
2200
            $posy += 3;
2201
            $pdf->SetXY($posx, $posy);
2202
            $pdf->SetTextColor(0, 0, 60);
2203
            $pdf->MultiCell($w, 3, $outputlangs->transnoentities("ReplacementByInvoice") . ' : ' . $outputlangs->convToOutputCharset($objectreplacing->ref), '', 'R');
2204
        }
2205
        if ($object->type == 1) {
2206
            $objectreplaced = new Facture($this->db);
2207
            $objectreplaced->fetch($object->fk_facture_source);
2208
2209
            $posy += 4;
2210
            $pdf->SetXY($posx, $posy);
2211
            $pdf->SetTextColor(0, 0, 60);
2212
            $pdf->MultiCell($w, 3, $outputlangs->transnoentities("ReplacementInvoice") . ' : ' . $outputlangs->convToOutputCharset($objectreplaced->ref), '', 'R');
2213
        }
2214
        if ($object->type == 2 && !empty($object->fk_facture_source)) {
2215
            $objectreplaced = new Facture($this->db);
2216
            $objectreplaced->fetch($object->fk_facture_source);
2217
2218
            $posy += 3;
2219
            $pdf->SetXY($posx, $posy);
2220
            $pdf->SetTextColor(0, 0, 60);
2221
            $pdf->MultiCell($w, 3, $outputlangs->transnoentities("CorrectionInvoice") . ' : ' . $outputlangs->convToOutputCharset($objectreplaced->ref), '', 'R');
2222
        }
2223
2224
        $posy += 4;
2225
        $pdf->SetXY($posx, $posy);
2226
        $pdf->SetTextColor(0, 0, 60);
2227
2228
        $title = $outputlangs->transnoentities("DateInvoice");
2229
        if (getDolGlobalString('PDF_USE_ALSO_LANGUAGE_CODE') && is_object($outputlangsbis)) {
2230
            $title .= ' - ' . $outputlangsbis->transnoentities("DateInvoice");
2231
        }
2232
        $pdf->MultiCell($w, 3, $title . " : " . dol_print_date($object->date, "day", false, $outputlangs, true), '', 'R');
2233
2234
        if (getDolGlobalString('INVOICE_POINTOFTAX_DATE')) {
2235
            $posy += 4;
2236
            $pdf->SetXY($posx, $posy);
2237
            $pdf->SetTextColor(0, 0, 60);
2238
            $pdf->MultiCell($w, 3, $outputlangs->transnoentities("DatePointOfTax") . " : " . dol_print_date($object->date_pointoftax, "day", false, $outputlangs), '', 'R');
2239
        }
2240
2241
        if ($object->type != 2) {
2242
            $posy += 3;
2243
            $pdf->SetXY($posx, $posy);
2244
            $pdf->SetTextColor(0, 0, 60);
2245
            $title = $outputlangs->transnoentities("DateDue");
2246
            if (getDolGlobalString('PDF_USE_ALSO_LANGUAGE_CODE') && is_object($outputlangsbis)) {
2247
                $title .= ' - ' . $outputlangsbis->transnoentities("DateDue");
2248
            }
2249
            $pdf->MultiCell($w, 3, $title . " : " . dol_print_date($object->date_lim_reglement, "day", false, $outputlangs, true), '', 'R');
2250
        }
2251
2252
        if (!getDolGlobalString('MAIN_PDF_HIDE_CUSTOMER_CODE') && $object->thirdparty->code_client) {
2253
            $posy += 3;
2254
            $pdf->SetXY($posx, $posy);
2255
            $pdf->SetTextColor(0, 0, 60);
2256
            $pdf->MultiCell($w, 3, $outputlangs->transnoentities("CustomerCode") . " : " . $outputlangs->transnoentities($object->thirdparty->code_client), '', 'R');
2257
        }
2258
2259
        // Get contact
2260
        if (getDolGlobalString('DOC_SHOW_FIRST_SALES_REP')) {
2261
            $arrayidcontact = $object->getIdContact('internal', 'SALESREPFOLL');
2262
            if (count($arrayidcontact) > 0) {
2263
                $usertmp = new User($this->db);
2264
                $usertmp->fetch($arrayidcontact[0]);
2265
                $posy += 4;
2266
                $pdf->SetXY($posx, $posy);
2267
                $pdf->SetTextColor(0, 0, 60);
2268
                $pdf->MultiCell($w, 3, $langs->transnoentities("SalesRepresentative") . " : " . $usertmp->getFullName($langs), '', 'R');
2269
            }
2270
        }
2271
2272
        $posy += 1;
2273
2274
        $top_shift = 0;
2275
        $shipp_shift = 0;
2276
        // Show list of linked objects
2277
        $current_y = $pdf->getY();
2278
        $posy = pdf_writeLinkedObjects($pdf, $object, $outputlangs, $posx, $posy, $w, 3, 'R', $default_font_size);
2279
        if ($current_y < $pdf->getY()) {
2280
            $top_shift = $pdf->getY() - $current_y;
2281
        }
2282
2283
        if ($showaddress) {
2284
            // Sender properties
2285
            $carac_emetteur = pdf_build_address($outputlangs, $this->emetteur, $object->thirdparty, '', 0, 'source', $object);
2286
2287
            // Show sender
2288
            $posy = getDolGlobalString('MAIN_PDF_USE_ISO_LOCATION') ? 40 : 42;
2289
            $posy += $top_shift;
2290
            $posx = $this->marge_gauche;
2291
            if (getDolGlobalString('MAIN_INVERT_SENDER_RECIPIENT')) {
2292
                $posx = $this->page_largeur - $this->marge_droite - 80;
2293
            }
2294
2295
            $hautcadre = getDolGlobalString('MAIN_PDF_USE_ISO_LOCATION') ? 38 : 40;
2296
            $widthrecbox = getDolGlobalString('MAIN_PDF_USE_ISO_LOCATION') ? 92 : 82;
2297
2298
            // Show sender frame
2299
            if (!getDolGlobalString('MAIN_PDF_NO_SENDER_FRAME')) {
2300
                $pdf->SetTextColor(0, 0, 0);
2301
                $pdf->SetFont('', '', $default_font_size - 2);
2302
                $pdf->SetXY($posx, $posy - 5);
2303
                $pdf->MultiCell($widthrecbox, 5, $outputlangs->transnoentities("BillFrom"), 0, $ltrdirection);
2304
                $pdf->SetXY($posx, $posy);
2305
                $pdf->SetFillColor(230, 230, 230);
2306
                $pdf->MultiCell($widthrecbox, $hautcadre, "", 0, 'R', 1);
2307
                $pdf->SetTextColor(0, 0, 60);
2308
            }
2309
2310
            // Show sender name
2311
            if (!getDolGlobalString('MAIN_PDF_HIDE_SENDER_NAME')) {
2312
                $pdf->SetXY($posx + 2, $posy + 3);
2313
                $pdf->SetFont('', 'B', $default_font_size);
2314
                $pdf->MultiCell($widthrecbox - 2, 4, $outputlangs->convToOutputCharset($this->emetteur->name), 0, $ltrdirection);
2315
                $posy = $pdf->getY();
2316
            }
2317
2318
            // Show sender information
2319
            $pdf->SetXY($posx + 2, $posy);
2320
            $pdf->SetFont('', '', $default_font_size - 1);
2321
            $pdf->MultiCell($widthrecbox - 2, 4, $carac_emetteur, 0, $ltrdirection);
2322
2323
            // If BILLING contact defined on invoice, we use it
2324
            $usecontact = false;
2325
            $arrayidcontact = $object->getIdContact('external', 'BILLING');
2326
            if (count($arrayidcontact) > 0) {
2327
                $usecontact = true;
2328
                $result = $object->fetch_contact($arrayidcontact[0]);
2329
            }
2330
2331
            // Recipient name
2332
            if ($usecontact && ($object->contact->socid != $object->thirdparty->id && (!isset($conf->global->MAIN_USE_COMPANY_NAME_OF_CONTACT) || getDolGlobalString('MAIN_USE_COMPANY_NAME_OF_CONTACT')))) {
2333
                $thirdparty = $object->contact;
2334
            } else {
2335
                $thirdparty = $object->thirdparty;
2336
            }
2337
2338
            $carac_client_name = pdfBuildThirdpartyName($thirdparty, $outputlangs);
2339
2340
            $mode = 'target';
2341
            $carac_client = pdf_build_address($outputlangs, $this->emetteur, $object->thirdparty, ($usecontact ? $object->contact : ''), $usecontact, $mode, $object);
2342
2343
            // Show recipient
2344
            $widthrecbox = getDolGlobalString('MAIN_PDF_USE_ISO_LOCATION') ? 92 : 100;
2345
            if ($this->page_largeur < 210) {
2346
                $widthrecbox = 84; // To work with US executive format
2347
            }
2348
            $posy = getDolGlobalString('MAIN_PDF_USE_ISO_LOCATION') ? 40 : 42;
2349
            $posy += $top_shift;
2350
            $posx = $this->page_largeur - $this->marge_droite - $widthrecbox;
2351
            if (getDolGlobalString('MAIN_INVERT_SENDER_RECIPIENT')) {
2352
                $posx = $this->marge_gauche;
2353
            }
2354
2355
            // Show recipient frame
2356
            if (!getDolGlobalString('MAIN_PDF_NO_RECIPENT_FRAME')) {
2357
                $pdf->SetTextColor(0, 0, 0);
2358
                $pdf->SetFont('', '', $default_font_size - 2);
2359
                $pdf->SetXY($posx + 2, $posy - 5);
2360
                $pdf->MultiCell($widthrecbox - 2, 5, $outputlangs->transnoentities("BillTo"), 0, $ltrdirection);
2361
                $pdf->Rect($posx, $posy, $widthrecbox, $hautcadre);
2362
            }
2363
2364
            // Show recipient name
2365
            $pdf->SetXY($posx + 2, $posy + 3);
2366
            $pdf->SetFont('', 'B', $default_font_size);
2367
            // @phan-suppress-next-line PhanPluginSuspiciousParamOrder
2368
            $pdf->MultiCell($widthrecbox - 2, 2, $carac_client_name, 0, $ltrdirection);
2369
2370
            $posy = $pdf->getY();
2371
2372
            // Show recipient information
2373
            $pdf->SetFont('', '', $default_font_size - 1);
2374
            $pdf->SetXY($posx + 2, $posy);
2375
            // @phan-suppress-next-line PhanPluginSuspiciousParamOrder
2376
            $pdf->MultiCell($widthrecbox - 2, 4, $carac_client, 0, $ltrdirection);
2377
2378
            // Show shipping address
2379
            if (getDolGlobalInt('INVOICE_SHOW_SHIPPING_ADDRESS')) {
2380
                $idaddressshipping = $object->getIdContact('external', 'SHIPPING');
2381
2382
                if (!empty($idaddressshipping)) {
2383
                    $contactshipping = $object->fetch_Contact($idaddressshipping[0]);
2384
                    $companystatic = new Societe($this->db);
2385
                    $companystatic->fetch($object->contact->fk_soc);
2386
                    $carac_client_name_shipping = pdfBuildThirdpartyName($object->contact, $outputlangs);
2387
                    $carac_client_shipping = pdf_build_address($outputlangs, $this->emetteur, $companystatic, $object->contact, $usecontact, 'target', $object);
2388
                } else {
2389
                    $carac_client_name_shipping = pdfBuildThirdpartyName($object->thirdparty, $outputlangs);
2390
                    $carac_client_shipping = pdf_build_address($outputlangs, $this->emetteur, $object->thirdparty, '', 0, 'target', $object);
2391
                }
2392
                if (!empty($carac_client_shipping)) {
2393
                    $posy += $hautcadre;
2394
2395
                    // Show shipping frame
2396
                    $pdf->SetXY($posx + 2, $posy - 5);
2397
                    $pdf->SetFont('', '', $default_font_size - 2);
2398
                    $pdf->MultiCell($widthrecbox, '', $outputlangs->transnoentities('ShippingTo'), 0, 'L', 0);
2399
                    $pdf->Rect($posx, $posy, $widthrecbox, $hautcadre);
2400
2401
                    // Show shipping name
2402
                    $pdf->SetXY($posx + 2, $posy + 3);
2403
                    $pdf->SetFont('', 'B', $default_font_size);
2404
                    $pdf->MultiCell($widthrecbox - 2, 2, $carac_client_name_shipping, '', 'L');
2405
2406
                    $posy = $pdf->getY();
2407
2408
                    // Show shipping information
2409
                    $pdf->SetXY($posx + 2, $posy);
2410
                    $pdf->SetFont('', '', $default_font_size - 1);
2411
                    $pdf->MultiCell($widthrecbox - 2, 2, $carac_client_shipping, '', 'L');
2412
                    $shipp_shift += $hautcadre;
2413
                }
2414
            }
2415
        }
2416
2417
        $pdf->SetTextColor(0, 0, 0);
2418
2419
        $pagehead = array('top_shift' => $top_shift, 'shipp_shift' => $shipp_shift);
2420
2421
        return $pagehead;
2422
    }
2423
2424
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
2425
2426
    /**
2427
     *      Show footer of page. Need this->emetteur object
2428
     *
2429
     * @param TCPDF $pdf PDF
2430
     * @param Facture $object Object to show
2431
     * @param Translate $outputlangs Object lang for output
2432
     * @param int $hidefreetext 1=Hide free text
2433
     * @return int                             Return height of bottom margin including footer text
2434
     */
2435
    protected function _pagefoot(&$pdf, $object, $outputlangs, $hidefreetext = 0)
2436
    {
2437
        $showdetails = getDolGlobalInt('MAIN_GENERATE_DOCUMENTS_SHOW_FOOT_DETAILS', 0);
2438
        return pdf_pagefoot($pdf, $outputlangs, 'INVOICE_FREE_TEXT', $this->emetteur, $this->marge_basse, $this->marge_gauche, $this->page_hauteur, $object, $showdetails, $hidefreetext, $this->page_largeur, $this->watermark);
2439
    }
2440
2441
    /**
2442
     *  Define Array Column Field
2443
     *
2444
     * @param Facture $object common object
2445
     * @param Translate $outputlangs langs
2446
     * @param int $hidedetails Do not show line details
2447
     * @param int $hidedesc Do not show desc
2448
     * @param int $hideref Do not show ref
2449
     * @return void
2450
     */
2451
    public function defineColumnField($object, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0)
2452
    {
2453
        global $conf, $hookmanager;
2454
2455
        // Default field style for content
2456
        $this->defaultContentsFieldsStyle = array(
2457
            'align' => 'R', // R,C,L
2458
            'padding' => array(1, 0.5, 1, 0.5), // Like css 0 => top , 1 => right, 2 => bottom, 3 => left
2459
        );
2460
2461
        // Default field style for content
2462
        $this->defaultTitlesFieldsStyle = array(
2463
            'align' => 'C', // R,C,L
2464
            'padding' => array(0.5, 0, 0.5, 0), // Like css 0 => top , 1 => right, 2 => bottom, 3 => left
2465
        );
2466
2467
        /*
2468
         * For example
2469
         $this->cols['theColKey'] = array(
2470
         'rank' => $rank, // int : use for ordering columns
2471
         'width' => 20, // the column width in mm
2472
         'title' => array(
2473
         'textkey' => 'yourLangKey', // if there is no label, yourLangKey will be translated to replace label
2474
         'label' => ' ', // the final label : used fore final generated text
2475
         'align' => 'L', // text alignment :  R,C,L
2476
         'padding' => array(0.5,0.5,0.5,0.5), // Like css 0 => top , 1 => right, 2 => bottom, 3 => left
2477
         ),
2478
         'content' => array(
2479
         'align' => 'L', // text alignment :  R,C,L
2480
         'padding' => array(0.5,0.5,0.5,0.5), // Like css 0 => top , 1 => right, 2 => bottom, 3 => left
2481
         ),
2482
         );
2483
         */
2484
2485
        $rank = 0; // do not use negative rank
2486
        $this->cols['desc'] = array(
2487
            'rank' => $rank,
2488
            'width' => false, // only for desc
2489
            'status' => true,
2490
            'title' => array(
2491
                'textkey' => 'Designation', // use lang key is useful in somme case with module
2492
                'align' => 'L',
2493
                // 'textkey' => 'yourLangKey', // if there is no label, yourLangKey will be translated to replace label
2494
                // 'label' => ' ', // the final label
2495
                'padding' => array(0.5, 0.5, 0.5, 0.5), // Like css 0 => top , 1 => right, 2 => bottom, 3 => left
2496
            ),
2497
            'content' => array(
2498
                'align' => 'L',
2499
                'padding' => array(1, 0.5, 1, 1.5), // Like css 0 => top , 1 => right, 2 => bottom, 3 => left
2500
            ),
2501
        );
2502
2503
        // Image of product
2504
        $rank = $rank + 10;
2505
        $this->cols['photo'] = array(
2506
            'rank' => $rank,
2507
            'width' => getDolGlobalString('MAIN_DOCUMENTS_WITH_PICTURE_WIDTH', 20), // in mm
2508
            'status' => false,
2509
            'title' => array(
2510
                'textkey' => 'Photo',
2511
                'label' => ' '
2512
            ),
2513
            'content' => array(
2514
                'padding' => array(0, 0, 0, 0), // Like css 0 => top , 1 => right, 2 => bottom, 3 => left
2515
            ),
2516
            'border-left' => false, // remove left line separator
2517
        );
2518
2519
        if (getDolGlobalString('MAIN_GENERATE_INVOICES_WITH_PICTURE') && !empty($this->atleastonephoto)) {
2520
            $this->cols['photo']['status'] = true;
2521
        }
2522
2523
2524
        $rank = $rank + 10;
2525
        $this->cols['vat'] = array(
2526
            'rank' => $rank,
2527
            'status' => false,
2528
            'width' => 10, // in mm
2529
            'title' => array(
2530
                'textkey' => 'VAT'
2531
            ),
2532
            'border-left' => true, // add left line separator
2533
        );
2534
2535
        if (!getDolGlobalString('MAIN_GENERATE_DOCUMENTS_WITHOUT_VAT') && !getDolGlobalString('MAIN_GENERATE_DOCUMENTS_WITHOUT_VAT_COLUMN')) {
2536
            $this->cols['vat']['status'] = true;
2537
        }
2538
2539
        $rank = $rank + 10;
2540
        $this->cols['unit'] = array(
2541
            'rank' => $rank,
2542
            'width' => 11, // in mm
2543
            'status' => false,
2544
            'title' => array(
2545
                'textkey' => 'Unit'
2546
            ),
2547
            'border-left' => true, // add left line separator
2548
        );
2549
        if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
2550
            $this->cols['unit']['status'] = false;
2551
        }
2552
2553
        $rank = $rank + 10;
2554
        $this->cols['subprice'] = array(
2555
            'rank' => $rank,
2556
            'width' => 17, // in mm
2557
            'status' => true,
2558
            'title' => array(
2559
                'textkey' => 'PriceUHT'
2560
            ),
2561
            'border-left' => true, // add left line separator
2562
        );
2563
2564
        // Adapt dynamically the width of subprice, if text is too long.
2565
        $tmpwidth = 0;
2566
        $nblines = count($object->lines);
2567
        for ($i = 0; $i < $nblines; $i++) {
2568
            $tmpwidth2 = dol_strlen(dol_string_nohtmltag(pdf_getlineupexcltax($object, $i, $outputlangs, $hidedetails)));
2569
            $tmpwidth = max($tmpwidth, $tmpwidth2);
2570
        }
2571
        if ($tmpwidth > 10) {
2572
            $this->cols['subprice']['width'] += (2 * ($tmpwidth - 10));
2573
        }
2574
2575
        $rank = $rank + 10;
2576
        $this->cols['qty'] = array(
2577
            'rank' => $rank,
2578
            'width' => 10, // in mm
2579
            'status' => true,
2580
            'title' => array(
2581
                'textkey' => 'Qty'
2582
            ),
2583
            'border-left' => true, // add left line separator
2584
        );
2585
        //situation invoices
2586
        $this->cols['qty']['status'] = true;
2587
2588
        //sum column
2589
        $rank = $rank + 10;
2590
        $this->cols['btpsomme'] = array(
2591
            'rank' => $rank,
2592
            'width' => 18, // in mm
2593
            'status' => false,
2594
            'title' => array(
2595
                'textkey' => 'Chantier'
2596
            ),
2597
            'border-left' => true, // add left line separator
2598
            'overtitle' => array(
2599
                'textkey' => 'Chantier', // use lang key is useful in somme case with module
2600
                'align' => 'C',
2601
                'padding' => array(0.5, 0.5, 0.5, 0.5), // Like css 0 => top , 1 => right, 2 => bottom, 3 => left
2602
                'width' => 18
2603
            ),
2604
        );
2605
        if (!empty($this->TDataSituation['date_derniere_situation'])) {
2606
            $this->cols['btpsomme']['status'] = true;
2607
        }
2608
2609
        $derniere_situation = $this->TDataSituation['derniere_situation'];
2610
2611
        if (empty($derniere_situation)) {
2612
            $derniere_situation = 0;
2613
        }
2614
2615
        // Colonne "Pourcentage Progression précédente"
2616
        $rank = $rank + 10;
2617
        $this->cols['prev_progress'] = array(
2618
            'rank' => $rank,
2619
            'width' => 10, // in mm
2620
            'status' => false,
2621
            'title' => array(
2622
                'textkey' => $outputlangs->transnoentities('SituationInvoiceProgressColTitle', $derniere_situation->situation_counter)
2623
            ),
2624
            'border-left' => true, // add left line separator
2625
            'overtitle' => array(
2626
                'textkey' => $outputlangs->transnoentities('SituationInvoiceDate', $derniere_situation->situation_counter, dol_print_date($derniere_situation->date, "%d/%m/%Y")), // use lang key is useful in somme case with module
2627
                'align' => 'C',
2628
                'padding' => array(0.5, 0.2, 0.5, 0.2), // Like css 0 => top , 1 => right, 2 => bottom, 3 => left
2629
                'width' => 10 + 15 //current width + amount cell width
2630
            ),
2631
        );
2632
        if ($this->situationinvoice && !empty($this->TDataSituation['date_derniere_situation'])) {
2633
            $this->cols['prev_progress']['status'] = true;
2634
        }
2635
2636
        // Column 'Previous progression'
2637
        $rank = $rank + 10;
2638
        $this->cols['prev_progress_amount'] = array(
2639
            'rank' => $rank,
2640
            'width' => 15, // in mm
2641
            'status' => false,
2642
            'title' => array(
2643
                'textkey' => $outputlangs->transnoentities('SituationInvoiceAmountColTitle', $derniere_situation->situation_counter)
2644
            ),
2645
            'border-left' => true, // add left line separator
2646
        );
2647
        if ($this->situationinvoice && !empty($this->TDataSituation['date_derniere_situation'])) {
2648
            $this->cols['prev_progress_amount']['status'] = true;
2649
        }
2650
2651
        // Column 'Current percent progress'
2652
        $rank = $rank + 10;
2653
        $this->cols['progress'] = array(
2654
            'rank' => $rank,
2655
            'width' => 10, // in mm
2656
            'status' => true,
2657
            'title' => array(
2658
                'textkey' => $outputlangs->transnoentities('SituationInvoiceProgressColTitle', $object->situation_counter)
2659
            ),
2660
            'border-left' => true, // add left line separator
2661
            'overtitle' => array(
2662
                'textkey' => $outputlangs->transnoentities('SituationInvoiceDate', $object->situation_counter, dol_print_date($object->date, "%d/%m/%Y")), // use lang key is useful in somme case with module
2663
                'align' => 'C',
2664
                'padding' => array(0.5, 0.2, 0.5, 0.2), // Like css 0 => top , 1 => right, 2 => bottom, 3 => left
2665
                'width' => 10 + 15
2666
            ),
2667
        );
2668
2669
        // Column 'Current progress'
2670
        $rank = $rank + 10;
2671
        $this->cols['progress_amount'] = array(
2672
            'rank' => $rank,
2673
            'width' => 15, // in mm
2674
            'status' => true,
2675
            'title' => array(
2676
                'textkey' => $outputlangs->transnoentities('SituationInvoiceAmountColTitle', $object->situation_counter)
2677
            ),
2678
            'border-left' => true, // add left line separator
2679
        );
2680
        if ($this->situationinvoice) {
2681
            $this->cols['progress_amount']['status'] = true;
2682
        }
2683
2684
        // FIN BTP SITUATION
2685
2686
        $rank = $rank + 10;
2687
        $this->cols['discount'] = array(
2688
            'rank' => $rank,
2689
            'width' => 10, // in mm
2690
            'status' => false,
2691
            'title' => array(
2692
                'textkey' => 'ReductionShort'
2693
            ),
2694
            'border-left' => true, // add left line separator
2695
        );
2696
        if ($this->atleastonediscount) {
2697
            $this->cols['discount']['status'] = true;
2698
        }
2699
        $rank = $rank + 10;
2700
        $this->cols['totalexcltax'] = array(
2701
            'rank' => $rank,
2702
            'width' => 18, // in mm
2703
            'status' => true,
2704
            'title' => array(
2705
                'textkey' => $outputlangs->transnoentities('TotalHT')
2706
            ),
2707
            'border-left' => true, // add left line separator
2708
        );
2709
2710
        $parameters = array(
2711
            'object' => $object,
2712
            'outputlangs' => $outputlangs,
2713
            'hidedetails' => $hidedetails,
2714
            'hidedesc' => $hidedesc,
2715
            'hideref' => $hideref
2716
        );
2717
2718
        $reshook = $hookmanager->executeHooks('defineColumnField', $parameters, $this); // Note that $object may have been modified by hook
2719
        if ($reshook < 0) {
2720
            setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
2721
        } elseif (empty($reshook)) {
2722
            // @phan-suppress-next-line PhanPluginSuspiciousParamOrderInternal
2723
            $this->cols = array_replace($this->cols, $hookmanager->resArray); // array_replace is used to preserve keys
2724
        } else {
2725
            $this->cols = $hookmanager->resArray;
2726
        }
2727
    }
2728
2729
    /**
2730
     *   Show table for lines
2731
     *
2732
     * @param TCPDF $pdf Object PDF
2733
     * @param int $tab_top Top position of table
2734
     * @param int $tab_height Height of table (rectangle)
2735
     * @param int $nexY Y (not used)
2736
     * @param Translate $outputlangs Langs object
2737
     * @param int $hidetop 1=Hide top bar of array and title, 0=Hide nothing, -1=Hide only title
2738
     * @param int $hidebottom Hide bottom bar of array
2739
     * @param string $currency Currency code
2740
     * @return    void
2741
     */
2742
    public function _tableFirstPage(&$pdf, $tab_top, $tab_height, $nexY, $outputlangs, $hidetop = 0, $hidebottom = 0, $currency = '')
2743
    {
2744
        global $conf, $object, $db;
2745
2746
        $form = new Form($db);
2747
2748
        $tab_height -= 29; // Réduction de la hauteur global du tableau
2749
        $displayWarranty = $this->displayRetainedWarranty($object);
2750
        if (!$displayWarranty) {
2751
            $tab_height -= 19; // Réduction de la hauteur global du tableau
2752
        }
2753
2754
2755
        // Force to disable hidetop and hidebottom
2756
        $hidebottom = 0;
2757
        if ($hidetop) {
2758
            $hidetop = -1;
2759
        }
2760
2761
        $currency = !empty($currency) ? $currency : $conf->currency;
2762
        $default_font_size = pdf_getPDFFontSize($outputlangs);
2763
2764
        // Amount in (at tab_top - 1)
2765
        $pdf->SetTextColor(0, 0, 0);
2766
        $pdf->SetFont('', '', $default_font_size - 2);
2767
2768
        if (empty($hidetop)) {
2769
            $titre = $outputlangs->transnoentities("AmountInCurrency", $outputlangs->transnoentitiesnoconv("Currency" . $currency));
2770
            $pdf->SetXY($this->page_largeur - $this->marge_droite - ($pdf->GetStringWidth($titre) + 3), $tab_top - 8);
2771
            $pdf->MultiCell(($pdf->GetStringWidth($titre) + 3), 2, $titre);
2772
2773
            $width = $this->page_largeur - $this->marge_gauche - $this->marge_droite - 83;
2774
2775
            //$conf->global->MAIN_PDF_TITLE_BACKGROUND_COLOR='230,230,230';
2776
            if (getDolGlobalString('MAIN_PDF_TITLE_BACKGROUND_COLOR')) {
2777
                $pdf->Rect($this->posx_cumul_anterieur - 1, $tab_top, $width, 5, 'F', null, explode(',', getDolGlobalString('MAIN_PDF_TITLE_BACKGROUND_COLOR')));
2778
                $pdf->Rect($this->marge_gauche, $tab_top + 92.5, $this->page_largeur - $this->marge_gauche - $this->marge_droite, 5, 'F', null, explode(',', getDolGlobalString('MAIN_PDF_TITLE_BACKGROUND_COLOR')));
2779
            }
2780
        }
2781
2782
        $pdf->SetDrawColor(128, 128, 128);
2783
        $pdf->SetFont('', '', $default_font_size - 1);
2784
2785
        // Output Rect
2786
        // KEEPTHIS => Affiche les bords extérieurs
2787
        $this->printRectBtp($pdf, $this->marge_gauche, $tab_top, $this->page_largeur - $this->marge_gauche - $this->marge_droite, $tab_height, $hidetop, $hidebottom);  // Rect prend une longueur en 3eme param et 4eme param
2788
2789
        $pdf->line($this->posx_cumul_anterieur - 1, $tab_top, $this->posx_cumul_anterieur - 1, $tab_top + $tab_height);
2790
        if (empty($hidetop)) {
2791
            $pdf->SetXY($this->posx_cumul_anterieur - 1, $tab_top + 0.5);
2792
            $pdf->MultiCell(35, 2, $outputlangs->transnoentities("SituationInvoiceOldCumulation"), '', 'C');
2793
        }
2794
2795
        // PRINT COLUMNS TITLES
2796
        $pdf->line($this->posx_new_cumul - 1, $tab_top, $this->posx_new_cumul - 1, $tab_top + $tab_height);
2797
        if (empty($hidetop)) {
2798
            $pdf->SetXY($this->posx_new_cumul - 1, $tab_top + 0.5);
2799
            $pdf->MultiCell(35, 2, $outputlangs->transnoentities("SituationInvoiceNewCumulation"), '', 'C');
2800
        }
2801
2802
        $pdf->line($this->posx_current - 1, $tab_top, $this->posx_current - 1, $tab_top + $tab_height);
2803
        if (empty($hidetop)) {
2804
            $pdf->SetXY($this->posx_current - 1, $tab_top + 0.5);
2805
            $pdf->MultiCell(36, 2, $outputlangs->transnoentities("CurrentSituationTotal", $object->situation_counter), '', 'C');
2806
        }
2807
2808
        // ADD HORIZONTAL LINES
2809
        $pdf->line($this->posx_cumul_anterieur - 1, $tab_top + 5, $this->page_largeur - $this->marge_droite, $tab_top + 5);
2810
2811
        $pdf->line($this->posx_cumul_anterieur - 1, $tab_top + 24, $this->page_largeur - $this->marge_droite, $tab_top + 24);
2812
2813
        $pdf->line($this->marge_gauche, $tab_top + 55, $this->page_largeur - $this->marge_droite, $tab_top + 55);
2814
2815
        $pdf->line($this->marge_gauche, $tab_top + 65, $this->page_largeur - $this->marge_droite, $tab_top + 65);
2816
2817
        if ($displayWarranty) {
2818
            $pdf->line($this->marge_gauche, $tab_top + 85, $this->page_largeur - $this->marge_droite, $tab_top + 85);
2819
        }
2820
2821
2822
        // ADD TEXT INTO CELL
2823
        /********************** Titles ******************************/
2824
        $pdf->SetXY($this->marge_gauche + 2, $tab_top + 8);
2825
        $pdf->MultiCell(60, 2, $outputlangs->transnoentities("SituationInvoiceMainTask"), '', 'L');
2826
2827
        $pdf->SetXY($this->marge_gauche + 2, $tab_top + 12);
2828
        $pdf->MultiCell(60, 2, $outputlangs->transnoentities("SituationInvoiceAdditionalTask"), '', 'L');
2829
2830
        $form->load_cache_vatrates("'" . $object->thirdparty->country_code . "'");
2831
2832
        $i = -8;
2833
        foreach ($form->cache_vatrates as $TVatInfo) {
2834
            $tva_tx_formated = sprintf("%01.3f", (float)$TVatInfo['txtva']);
2835
            // print "<p>Un taux de tva ... $tva_tx_formated :: " . json_encode($this->TDataSituation['current'][$tva_tx_formated]) . "</p>";
2836
            if (empty($this->TDataSituation['current'][$tva_tx_formated])) {
2837
                continue;
2838
            }
2839
            $i += 8;
2840
2841
            $pdf->SetXY($this->marge_gauche + 10, $tab_top + 24 + $i);
2842
            $pdf->MultiCell(80, 2, $outputlangs->transnoentities("TotalHT") . ' ' . $TVatInfo['label'], '', 'L');
2843
2844
            if (!empty($this->TDataSituation['current'][$tva_tx_formated]['TVA'])) {
2845
                $pdf->SetXY($this->marge_gauche + 10, $tab_top + 28 + $i);
2846
                $pdf->MultiCell(80, 2, $outputlangs->transnoentities("VAT") . ' ' . $TVatInfo['label'], '', 'L');
2847
            } else {
2848
                $i -= 4;
2849
            }
2850
        }
2851
2852
        $pdf->SetXY($this->marge_gauche + 2, $tab_top + 33 + $i);
2853
        $pdf->MultiCell(80, 2, $outputlangs->transnoentities("TotalTTC"), '', 'L');
2854
2855
2856
        $pdf->SetFont('', 'B', $default_font_size - 1);
2857
        $pdf->SetXY($this->marge_gauche + 2, $tab_top + 58);
2858
        $pdf->MultiCell(80, 2, $outputlangs->transnoentities("TotalSituationInvoice"), '', 'L');
2859
        $pdf->SetFont('', '', $default_font_size - 2);
2860
2861
        if ($displayWarranty) {
2862
            $pdf->SetXY($this->marge_gauche + 2, $tab_top + 74);
2863
            $pdf->MultiCell(80, 2, $outputlangs->trans("TotalSituationInvoiceWithRetainedWarranty", $object->retained_warranty), '', 'L');
2864
            $nextY = $tab_top + 93;
2865
        } else {
2866
            $nextY = $tab_top + 74;
2867
        }
2868
2869
        $pdf->SetFont('', 'B', $default_font_size - 1);
2870
        $pdf->SetXY($this->marge_gauche + 2, $nextY);
2871
        $pdf->MultiCell(80, 2, $outputlangs->transnoentities("SituationTotalRayToRest"), '', 'L');
2872
        $pdf->SetFont('', '', $default_font_size - 2);
2873
        /***********************************************************/
2874
2875
        /********************** Data *******************************/
2876
        $TToDisplay = array(
2877
            'cumul_anterieur',
2878
            'nouveau_cumul',
2879
            'current'
2880
        );
2881
2882
        $x = $this->marge_gauche + 85;
2883
        // unset($this->TDataSituation['derniere_situation']);
2884
        // print json_encode($object->lines);exit;
2885
        // print json_encode($this->TDataSituation);exit;
2886
        foreach ($TToDisplay as $col) {
2887
            // Travaux principaux
2888
            $pdf->SetXY($x, $tab_top + 8);
2889
            $pdf->MultiCell(32, 2, price($this->TDataSituation[$col]['HT'], 0, '', 1, -1, 2), '', 'R');
2890
2891
            // Travaux supplémentaires
2892
            $pdf->SetXY($x, $tab_top + 12);
2893
            $pdf->MultiCell(32, 2, price($this->TDataSituation[$col]['travaux_sup'], 0, '', 1, -1, 2), '', 'R');
2894
2895
            $i = -8;
2896
            foreach ($form->cache_vatrates as $TVatInfo) {
2897
                $tva_tx_formated = sprintf("%01.3f", (float)$TVatInfo['txtva']);
2898
                if (empty($this->TDataSituation['current'][$tva_tx_formated])) {
2899
                    continue;
2900
                }
2901
                $i += 8;
2902
2903
                // Total HT
2904
                $pdf->SetXY($x, $tab_top + 24 + $i);
2905
                $pdf->MultiCell(32, 2, price($this->TDataSituation[$col][$tva_tx_formated]['HT'], 0, '', 1, -1, 2), '', 'R');
2906
2907
                // Total TVA
2908
                if (!empty($this->TDataSituation['current'][$tva_tx_formated]['TVA'])) {
2909
                    $pdf->SetXY($x, $tab_top + 28 + $i);
2910
                    $pdf->MultiCell(32, 2, price($this->TDataSituation[$col][$tva_tx_formated]['TVA'], 0, '', 1, -1, 2), '', 'R');
2911
                } else {
2912
                    $i -= 4;
2913
                }
2914
            }
2915
2916
            // Total TTC
2917
            $pdf->SetXY($x, $tab_top + 33 + $i);
2918
            $pdf->MultiCell(32, 2, price($this->TDataSituation[$col]['TTC'], 0, '', 1, -1, 2), '', 'R');
2919
2920
            // Total situation
2921
            $pdf->SetFont('', 'B', $default_font_size - 1);
2922
            $pdf->SetXY($x, $tab_top + 58);
2923
            $pdf->MultiCell(32, 2, price($this->TDataSituation[$col]['TTC'], 0, '', 1, -1, 2), '', 'R');
2924
            $pdf->SetFont('', '', $default_font_size - 2);
2925
2926
2927
            if ($displayWarranty) {
2928
                // Retained warranty
2929
                $pdf->SetXY($x, $tab_top + 74);
2930
                $pdf->MultiCell(32, 2, price($this->TDataSituation[$col]['retenue_garantie'], 0, '', 1, -1, 2), '', 'R');
2931
                $nextY = $tab_top + 93;
2932
            } else {
2933
                $nextY = $tab_top + 74;
2934
            }
2935
2936
            // Amount payable incl. VAT
2937
            $pdf->SetFont('', 'B', $default_font_size - 1);
2938
            $pdf->SetXY($x, $nextY);
2939
            $pdf->MultiCell(32, 2, price($this->TDataSituation[$col]['total_a_payer'], 0, '', 1, -1, 2), '', 'R');
2940
            $pdf->SetFont('', '', $default_font_size - 2);
2941
2942
            $x += 36;
2943
        }
2944
        /************************************************************/
2945
    }
2946
2947
2948
    /**
2949
     * Recovers data from situation invoices
2950
     *
2951
     * NOTE :
2952
     *  Main work: lines on the status invoice that were already present on the previous invoice
2953
     *  Additional work: lines on the status invoice that have been added to the previous invoice
2954
     *  Example : S1 with l1 (tp), l2 (tp)
2955
     *            S2 with l1 (tp), l2 (tp), l3 (ts)
2956
     *            S3 with l1 (tp), l2 (tp), l3 (tp), l4 (ts)
2957
     *
2958
     * @param Facture $object Facture
2959
     *
2960
     * @return  array
2961
     *
2962
     * Details of returned table
2963
     *
2964
     * cumul_anterieur: data from previous status invoice
2965
     * nouveau_cumul: Cumulative data from all invoices up to the current one
2966
     * current: current status invoice data
2967
     *
2968
     */
2969
    public function getDataSituation(&$object)
2970
    {
2971
        global $conf, $db;
2972
2973
        // Fetch previous and next situations invoices.
2974
        // Return all previous and next invoices (both standard and credit notes)
2975
        $object->fetchPreviousNextSituationInvoice();
2976
        /** @var Facture[] $TPreviousInvoices */
2977
        $TPreviousInvoices = $object->tab_previous_situation_invoice;
2978
        unset($object->tab_previous_situation_invoice);
2979
2980
        // liste de toutes les factures précédentes
2981
        // print json_encode($TPreviousInvoices); exit;
2982
2983
        $TPreviousInvoices = array_reverse($TPreviousInvoices);
2984
        $facDerniereSituation = $TPreviousInvoices[0];
2985
2986
        $TDataSituation = array();
2987
2988
        if (!empty($facDerniereSituation)) {
2989
            $TDataSituation['derniere_situation'] = $facDerniereSituation;
2990
            $TDataSituation['date_derniere_situation'] = $facDerniereSituation->date;
2991
        }
2992
2993
        $retenue_garantie = 0;
2994
        $retenue_garantie_anterieure = 0;
2995
        // Init tous les champs à 0
2996
        $TDataSituation['cumul_anterieur'] = array(
2997
            'HT' => 0,  //montant HT normal
2998
            'TVA' => 0,   //montant de la TVA sur le HTnet
2999
            'TTC' => 0,   //montant TTC (HTnet + TVA)
3000
            'retenue_garantie' => 0,
3001
            'travaux_sup' => 0,
3002
            'HTnet' => 0, //montant HT
3003
            'total_a_payer' => 0 //montant "a payer" sur la facture
3004
        );
3005
3006
        //S'il y a des factures de situations précédentes
3007
        if (!empty($TPreviousInvoices)) {
3008
            //calcul des cumuls -- plus necessaire ?
3009
            foreach ($TPreviousInvoices as $i => $previousInvoice) {
3010
                $TDataSituation['cumul_anterieur']['HT'] += $previousInvoice->total_ht;
3011
                // $TDataSituation['cumul_anterieur']['TTC'] += $previousInvoice->total_ttc;
3012
                $TDataSituation['cumul_anterieur']['TVA'] += $previousInvoice->total_tva;
3013
3014
                //lecture de chaque ligne pour
3015
                // 1. recalculer le total_ht pour chaque taux de TVA
3016
                // 2. recalculer la TVA associée à ce montant HT
3017
                // 3. le cas échéant stocker cette information comme travaux_sup si cette ligne n'est pas liée à une ligne de la situation précédente
3018
                foreach ($previousInvoice->lines as $k => $l) {
3019
                    $total_ht = floatval($l->total_ht);
3020
                    if (empty($total_ht)) {
3021
                        continue;
3022
                    }
3023
3024
                    // Si $prevSituationPercent vaut 0 c'est que la ligne $l est un travail supplémentaire
3025
                    $prevSituationPercent = 0;
3026
                    $isFirstSituation = false;
3027
                    if (!empty($l->fk_prev_id)) {
3028
                        $prevSituationPercent = $l->get_prev_progress($previousInvoice->id, true);
3029
                    } elseif (!array_key_exists($i + 1, $TPreviousInvoices)) {
3030
                        $isFirstSituation = true;
3031
                    }
3032
3033
                    $calc_ht = $l->total_ht;
3034
                    //modification du format de TVA, cas particulier des imports ou autres qui peuvent avoir des 20.0000
3035
                    $ltvatx = (float)sprintf("%01.3f", $l->tva_tx);
3036
3037
                    //1ere ligne
3038
                    $amounttva = $calc_ht * ($ltvatx / 100);
3039
                    if (!isset($TDataSituation['cumul_anterieur'][$ltvatx])) {
3040
                        $TDataSituation['cumul_anterieur'][$ltvatx]['HT'] = $calc_ht;
3041
                        $TDataSituation['cumul_anterieur'][$ltvatx]['TVA'] = $amounttva;
3042
                    } else {
3043
                        //lignes suivantes
3044
                        $TDataSituation['cumul_anterieur'][$ltvatx]['HT'] += ($calc_ht);
3045
                        $TDataSituation['cumul_anterieur'][$ltvatx]['TVA'] += $amounttva;
3046
                    }
3047
3048
                    //le grand total de TVA
3049
                    // $TDataSituation['cumul_anterieur']['TVA'] += $amounttva;
3050
3051
                    if (empty($l->fk_prev_id) && !$isFirstSituation) {
3052
                        // TODO: à clarifier, mais pour moi, un facture de situation précédente qui a des progressions à 0% c'est pas logique
3053
                        $TDataSituation['cumul_anterieur']['travaux_sup'] += $calc_ht;
3054
                    }
3055
                }
3056
            }
3057
3058
            if (!empty($previousInvoice->retained_warranty) && !getDolGlobalString('USE_RETAINED_WARRANTY_ONLY_FOR_SITUATION_FINAL')) {
3059
                $retenue_garantie_anterieure += $previousInvoice->getRetainedWarrantyAmount();
3060
            }
3061
3062
            //les cumuls
3063
            $TDataSituation['cumul_anterieur']['HT'] -= $TDataSituation['cumul_anterieur']['travaux_sup'];
3064
            $TDataSituation['cumul_anterieur']['retenue_garantie'] = $retenue_garantie_anterieure;
3065
            $TDataSituation['cumul_anterieur']['TTC'] = $TDataSituation['cumul_anterieur']['HT'] + $TDataSituation['cumul_anterieur']['TVA'];
3066
            $TDataSituation['cumul_anterieur']['total_a_payer'] = $TDataSituation['cumul_anterieur']['TTC'] - $retenue_garantie_anterieure;
3067
        }
3068
3069
        // print json_encode($facDerniereSituation->lines);exit;
3070
        $TDataSituation['current'] = $this->btpGetInvoiceAmounts($object->id);
3071
3072
        if (!empty($facDerniereSituation->lines)) {
3073
            $TFacLinesKey = array_keys($facDerniereSituation->lines);
3074
            $TObjectLinesKey = array_keys($object->lines);
3075
            $TDiffKey = array_diff($TObjectLinesKey, $TFacLinesKey);
3076
3077
            // print json_encode($TDiffKey);exit;
3078
3079
            foreach ($TDiffKey as $i) {
3080
                if (empty($object->lines[$i]->fk_prev_id)) {
3081
                    $TDataSituation['nouveau_cumul']['travaux_sup'] += $object->lines[$i]->total_ht;
3082
                    $TDataSituation['current']['travaux_sup'] += $object->lines[$i]->total_ht;
3083
                }
3084
            }
3085
        }
3086
3087
        //Le nouveau cumul = cumul antérieur + current
3088
        $TDataSituation['nouveau_cumul'] = $this->sumSituation($TDataSituation['current'], $TDataSituation['cumul_anterieur']);
3089
3090
        return $TDataSituation;
3091
    }
3092
3093
    /**
3094
     * Calculates the sum of two arrays, key by key, taking into account nested arrays
3095
     *
3096
     * @param array $a [$a description]
3097
     * @param array $b [$b description]
3098
     *
3099
     * @return  array     [return description]
3100
     */
3101
    public function sumSituation($a, $b)
3102
    {
3103
        $ret = array();
3104
        if (is_array($a)) {
3105
            foreach ($a as $k => $v) {
3106
                if (is_array($v)) {
3107
                    $ret[$k] = $this->sumSituation($v, $b[$k]);
3108
                } else {
3109
                    $ret[$k] = $a[$k];
3110
                    if (isset($b[$k])) {
3111
                        $ret[$k] += $b[$k];
3112
                    }
3113
                }
3114
            }
3115
        } else {
3116
            dol_syslog("sumSituation first arg is not an array");
3117
        }
3118
3119
        return $ret;
3120
    }
3121
3122
    /**
3123
     * Display retained Warranty
3124
     *
3125
     * @param Facture $object Facture
3126
     * @return  bool
3127
     */
3128
    public function displayRetainedWarranty($object)
3129
    {
3130
        if (is_callable(array($object, 'displayRetainedWarranty'))) {
3131
            return $object->displayRetainedWarranty();
3132
        } else {
3133
            // FOR RETROCOMPATIBILITY
3134
            global $conf;
3135
3136
            // TODO : add a flag on invoices to store this conf USE_RETAINED_WARRANTY_ONLY_FOR_SITUATION_FINAL
3137
3138
            // note : we don't need to test USE_RETAINED_WARRANTY_ONLY_FOR_SITUATION because if $object->retained_warranty is not empty it's because it was set when this conf was active
3139
3140
            $displayWarranty = false;
3141
            if (!empty($object->retained_warranty)) {
3142
                $displayWarranty = true;
3143
3144
                if ($object->type == Facture::TYPE_SITUATION && getDolGlobalString('USE_RETAINED_WARRANTY_ONLY_FOR_SITUATION_FINAL')) {
3145
                    // Check if this situation invoice is 100% for real
3146
                    $displayWarranty = false;
3147
                    if (!empty($object->situation_final)) {
3148
                        $displayWarranty = true;
3149
                    } elseif (!empty($object->lines) && $object->status == Facture::STATUS_DRAFT) {
3150
                        // $object->situation_final need validation to be done so this test is need for draft
3151
                        $displayWarranty = true;
3152
3153
                        foreach ($object->lines as $i => $line) {
3154
                            if ($line->product_type < 2 && $line->situation_percent < 100) {
3155
                                $displayWarranty = false;
3156
                                break;
3157
                            }
3158
                        }
3159
                    }
3160
                }
3161
            }
3162
            return $displayWarranty;
3163
        }
3164
    }
3165
3166
    /**
3167
     * Get info line of the last situation
3168
     *
3169
     * @param Facture $object Object
3170
     * @param FactureLigne $current_line current line
3171
     * @return void|array{progress_prec:float,total_ht_without_progress:float,total_ht:float}
3172
     */
3173
    public function getInfosLineLastSituation(&$object, &$current_line)
3174
    {
3175
        if (empty($object->situation_cycle_ref) || $object->situation_counter <= 1) {
3176
            return;
3177
        }
3178
3179
        $facDerniereSituation = &$this->TDataSituation['derniere_situation'];
3180
3181
        // Find the previous line of the line you are on
3182
        foreach ($facDerniereSituation->lines as $l) {
3183
            if ($l->rowid == $current_line->fk_prev_id) {
3184
                // Recovery of total_ht without taking progress into account (for the "sums" column)
3185
                $ltvatx = (float)sprintf("%01.3f", $l->tva_tx);
3186
                $tabprice = calcul_price_total($l->qty, $l->subprice, $l->remise_percent, $ltvatx, $l->localtax1_tx, $l->localtax2_tx, 0, 'HT', $l->info_bits, $l->product_type);
3187
                $total_ht = $tabprice[0];
3188
                $total_tva = $tabprice[1];
3189
                $total_ttc = $tabprice[2];
3190
                $total_localtax1 = $tabprice[9];
3191
                $total_localtax2 = $tabprice[10];
3192
                $pu_ht = $tabprice[3];
3193
3194
                return array(
3195
                    'progress_prec' => $l->situation_percent,
3196
                    'total_ht_without_progress' => $total_ht,
3197
                    'total_ht' => $l->total_ht,
3198
                );
3199
            }
3200
        }
3201
    }
3202
3203
    /**
3204
     * Rect pdf
3205
     *
3206
     * @param TCPDF $pdf Object PDF
3207
     * @param float $x Abscissa of first point
3208
     * @param float $y Ordinate of first point
3209
     * @param float $l ??
3210
     * @param float $h ??
3211
     * @param int $hidetop 1=Hide top bar of array and title, 0=Hide nothing, -1=Hide only title
3212
     * @param int $hidebottom Hide bottom
3213
     * @return  void
3214
     */
3215
    public function printRectBtp(&$pdf, $x, $y, $l, $h, $hidetop = 0, $hidebottom = 0)
3216
    {
3217
        if (empty($hidetop) || $hidetop == -1) {
3218
            $pdf->line($x, $y, $x + $l, $y);
3219
        }
3220
        $pdf->line($x + $l, $y, $x + $l, $y + $h);
3221
        if (empty($hidebottom)) {
3222
            $pdf->line($x + $l, $y + $h, $x, $y + $h);
3223
        }
3224
        $pdf->line($x, $y + $h, $x, $y);
3225
    }
3226
3227
3228
    /**
3229
     * Get data about invoice
3230
     *
3231
     * @param int $id invoice id
3232
     * @param boolean $forceReadFromDB set to true if you want to force refresh data from SQL
3233
     *
3234
     * @return  array      [return description]
3235
     */
3236
    public function btpGetInvoiceAmounts($id, $forceReadFromDB = false)
3237
    {
3238
        global $user, $langs, $conf, $mysoc, $db, $hookmanager, $nblignes;
3239
3240
        $object = new Facture($db);
3241
        $object->fetch($id);
3242
3243
        /* from dolibarr core
3244
        * Fetch previous and next situations invoices.
3245
        * Return all previous and next invoices (both standard and credit notes).
3246
        */
3247
        $object->fetchPreviousNextSituationInvoice();
3248
        /** @var Facture[] $TPreviousInvoices */
3249
        $TPreviousInvoices = $object->tab_previous_situation_invoice;
3250
        unset($object->tab_previous_situation_invoice);
3251
3252
        $TPreviousInvoices = array_reverse($TPreviousInvoices);
3253
        $facDerniereSituation = $TPreviousInvoices[0];
3254
3255
        $ret = array(
3256
            'HT' => 0,  //montant HT normal
3257
            'HTnet' => 0, //montant HT
3258
            'TVA' => 0,   //montant de la TVA sur le HTnet
3259
            'TTC' => 0,   //montant TTC (HTnet + TVA)
3260
            'retenue_garantie' => 0,
3261
            'travaux_sup' => 0,
3262
            'total_a_payer' => 0 //montant "a payer" sur la facture
3263
        );
3264
3265
        if (!empty($facDerniereSituation)) {
3266
            $ret['derniere_situation'] = $facDerniereSituation;
3267
            $ret['date_derniere_situation'] = $facDerniereSituation->date;
3268
        }
3269
3270
        // Scroll through the lines of the current invoice to retrieve all data
3271
        foreach ($object->lines as $k => $l) {
3272
            $total_ht = floatval($l->total_ht);
3273
            if (empty($total_ht)) {
3274
                continue;
3275
            }
3276
3277
            // Modification of VAT format, special case of imports or others which may have 20.0000
3278
            $ltvatx = (float)sprintf("%01.3f", $l->tva_tx);
3279
3280
            $ret[$ltvatx]['TVA'] += $l->total_tva;
3281
            $ret[$ltvatx]['HT'] += $l->total_ht;
3282
        }
3283
3284
        // Retained warranty
3285
        $retenue_garantie = $object->getRetainedWarrantyAmount();
3286
        if ($retenue_garantie == -1) {
3287
            $retenue_garantie = 0;
3288
        }
3289
3290
        //les cumuls
3291
        $ret['TTC'] = $object->total_ttc;
3292
        $ret['TVA'] = $object->total_tva;
3293
        $ret['HT'] = $object->total_ht - $ret['travaux_sup'];
3294
        $ret['total_a_payer'] = $ret['TTC'] - $retenue_garantie;
3295
        $ret['retenue_garantie'] = $retenue_garantie;
3296
3297
        //Clean up before keep in "cache"
3298
        unset($ret['derniere_situation']->db);
3299
        unset($ret['derniere_situation']->fields);
3300
        unset($ret['derniere_situation']->lines);
3301
3302
        // print "<p>Store to cache $id : " . json_encode($_cache_btpProrataGetInvoiceAmounts[$id]) . "</p>";
3303
        return $ret;
3304
    }
3305
3306
3307
    /**
3308
     *  Show last page with a resume of all invoices
3309
     *
3310
     * @param TCPDF $pdf Object PDF
3311
     * @param Facture $object Object invoice
3312
     * @param int $deja_regle Amount already paid (in the currency of invoice)
3313
     * @param int $posy Position depart
3314
     * @param Translate $outputlangs Object langs
3315
     * @param Translate $outputlangsbis Object lang for output bis
3316
     * @return void
3317
     */
3318
    public function resumeLastPage(&$pdf, $object, $deja_regle, $posy, $outputlangs, $outputlangsbis)
3319
    {
3320
        global $conf, $mysoc, $hookmanager;
3321
        $default_font_size = pdf_getPDFFontSize($outputlangs);
3322
3323
        $sign = 1;
3324
        if ($object->type == 2 && getDolGlobalString('INVOICE_POSITIVE_CREDIT_NOTE')) {
3325
            $sign = -1;
3326
        }
3327
3328
        $pdf->AddPage();
3329
        if (!empty($this->tplidx)) {
3330
            $pdf->useTemplate($this->tplidx);
3331
        }
3332
3333
        $pagehead = $this->_pagehead($pdf, $object, 0, $outputlangs, $outputlangsbis);
3334
3335
        $tab2_top = $this->tab_top_newpage - 4;
3336
        $posy = $tab2_top;
3337
        $posx = $this->marge_gauche;
3338
        $index = 1;
3339
        $outputlangs->load('orders');
3340
        $outputlangs->load('propal');
3341
        $width = 70;
3342
        $width2 = $this->page_largeur - $posx - $width - $this->marge_droite;
3343
3344
        $pdf->SetFont('', '', $default_font_size - 1);
3345
        $pdf->MultiCell(0, 3, ''); // Set interline to 3
3346
        $pdf->SetTextColor(0, 0, 0);
3347
        $pdf->setY($tab2_top);
3348
3349
        if (is_object($outputlangsbis)) {   // When we show 2 languages we need more room for text, so we use a smaller font.
3350
            $pdf->SetFont('', '', $default_font_size - 2);
3351
        } else {
3352
            $pdf->SetFont('', '', $default_font_size - 1);
3353
        }
3354
3355
        if (empty($object->tab_previous_situation_invoice)) {
3356
            $object->fetchPreviousNextSituationInvoice();
3357
        }
3358
3359
        $previousinvoices = count($object->tab_previous_situation_invoice) ? $object->tab_previous_situation_invoice : array();
3360
3361
        $remain_to_pay = 0;
3362
3363
        // Proposal total
3364
        $propals = array();
3365
        $orders = array();
3366
3367
        if (count($previousinvoices)) {
3368
            foreach ($previousinvoices as $invoice) {
3369
                if ($invoice->is_first()) {
3370
                    $invoice->fetchObjectLinked();
3371
3372
                    $propals = isset($invoice->linkedObjects['propal']) ? $invoice->linkedObjects['propal'] : array();
3373
                    $orders = isset($invoice->linkedObjects['commande']) ? $invoice->linkedObjects['commande'] : array();
3374
                }
3375
            }
3376
        } else {
3377
            if ($object->is_first()) {
3378
                $object->fetchObjectLinked();
3379
3380
                $propals = isset($object->linkedObjects['propal']) ? $object->linkedObjects['propal'] : array();
3381
                $orders = isset($object->linkedObjects['commande']) ? $object->linkedObjects['commande'] : array();
3382
            }
3383
        }
3384
3385
        if (count($propals)) {
3386
            $propal = array_pop($propals);
3387
3388
            $total_ht = ($conf->multicurrency->enabled && $propal->mylticurrency_tx != 1) ? $propal->multicurrency_total_ht : $propal->total_ht;
3389
            $remain_to_pay = $total_ht;
3390
3391
            $pdf->SetTextColor(0, 0, 60);
3392
            $pdf->SetFont('', '', $default_font_size - 1);
3393
3394
            $label = $outputlangs->transnoentities("SituationInvoiceTotalProposal");
3395
            $pdf->MultiCell($this->page_largeur - ($this->marge_droite + $this->marge_gauche), 3, $label, 0, 'L', 0, 1, $posx, $posy + 1);
3396
3397
            $amount = price($sign * ($total_ht + (!empty($propal->remise) ? $propal->remise : 0)));
3398
            $pdf->MultiCell($width2, 3, $amount, 0, 'R', 0, 1, $posx + $width, $posy + 1);
3399
3400
            $pdf->SetFont('', '', $default_font_size - 1);
3401
3402
            // Output Rect
3403
            $pdf->SetDrawColor(128, 128, 128);
3404
            $this->printRect($pdf, $posx, $posy, $this->page_largeur - $this->marge_gauche - $this->marge_droite, 6);   // Rect prend une longueur en 3eme param et 4eme param
3405
3406
            $posy += 4;
3407
        } elseif (count($orders)) {
3408
            $order = array_pop($orders);
3409
3410
            $total_ht = ($conf->multicurrency->enabled && $order->mylticurrency_tx != 1 ? $order->multicurrency_total_ht : $order->total_ht);
3411
            $remain_to_pay = $total_ht;
3412
        }
3413
3414
        $useborder = 0;
3415
        $index = 0;
3416
3417
        $height = 4;
3418
3419
        $sign = 1;
3420
        if ($object->type == 2 && getDolGlobalString('INVOICE_POSITIVE_CREDIT_NOTE')) {
3421
            $sign = -1;
3422
        }
3423
        $pdf->SetTextColor(0, 0, 60);
3424
        $pdf->SetFont('', 'B', $default_font_size + 3);
3425
3426
3427
        $pdf->SetXY($posx, $posy);
3428
3429
3430
        $depositsamount = $object->getSumDepositsUsed();
3431
        $deja_regle = $object->getSommePaiement();
3432
3433
        $tot_deja_regle = ($depositsamount + $deja_regle);
3434
3435
        $previousinvoices[] = $object;
3436
3437
        $force_to_zero = false;
3438
3439
        $idinv = 0;//count($previousinvoices);
3440
        while ($idinv < count($previousinvoices)) {
3441
            $invoice = $previousinvoices[$idinv];
3442
3443
            $posy += 7;
3444
            $index = 0;
3445
3446
            $pdf->SetTextColor(0, 0, 60);
3447
            $pdf->SetFont('', 'B', $default_font_size + 3);
3448
3449
            $pageposbefore = $pdf->getPage();
3450
            $pdf->startTransaction();
3451
3452
            $pdf->SetXY($posx, $posy);
3453
3454
            $ref = $outputlangs->transnoentities("InvoiceSituation") . $outputlangs->convToOutputCharset(" n°" . $invoice->situation_counter);
3455
3456
            if ($invoice->situation_final) {
3457
                $ref .= ' - DGD';
3458
                $force_to_zero = true;
3459
            }
3460
3461
            $pdf->MultiCell($this->page_largeur - ($this->marge_droite + $this->marge_gauche), 3, $ref . ' ' . $invoice->ref . ' ' . $outputlangs->transnoentities("InvoiceDateUsed", dol_print_date($invoice->date, "%d/%m/%Y", false, $outputlangs)), 0, 'L', 0);
3462
3463
            $pdf->SetFont('', '', $default_font_size - 1);
3464
3465
            $sign = 1;
3466
            if ($invoice->type == 2 && getDolGlobalString('INVOICE_POSITIVE_CREDIT_NOTE')) {
3467
                $sign = -1;
3468
            }
3469
3470
            $posy += 7;
3471
            // Total HT
3472
            $pdf->SetFillColor(255, 255, 255);
3473
            $pdf->SetXY($posx, $posy);
3474
            $pdf->MultiCell($width, $height, $outputlangs->transnoentities("TotalHT"), 0, 'L', 1);
3475
3476
            $total_ht = ($conf->multicurrency->enabled && $invoice->mylticurrency_tx != 1 ? $invoice->multicurrency_total_ht : $invoice->total_ht);
3477
            $pdf->SetXY($posx + $width, $posy);
3478
            $pdf->MultiCell($width2, $height, price($sign * ($total_ht + (!empty($invoice->remise) ? $invoice->remise : 0)), 0, $outputlangs), 0, 'R', 1);
3479
3480
            $tvas = array();
3481
            $nblines = count($invoice->lines);
3482
            for ($i = 0; $i < $nblines; $i++) {
3483
                $tvaligne = $invoice->lines[$i]->total_tva;
3484
                $vatrate = (string)$invoice->lines[$i]->tva_tx;
3485
3486
                if (($invoice->lines[$i]->info_bits & 0x01) == 0x01) {
3487
                    $vatrate .= '*';
3488
                }
3489
                if (!isset($tvas[$vatrate])) {
3490
                    $tvas[$vatrate] = 0;
3491
                }
3492
                $tvas[$vatrate] += $tvaligne;
3493
            }
3494
3495
            // Show VAT by rates and total
3496
            $pdf->SetFillColor(248, 248, 248);
3497
            foreach ($tvas as $tvakey => $tvaval) {
3498
                if ($tvakey != 0) { // On affiche pas taux 0
3499
                    $index++;
3500
                    $pdf->SetXY($posx, $posy + $height * $index);
3501
3502
                    $tvacompl = '';
3503
                    if (preg_match('/\*/', $tvakey)) {
3504
                        $tvakey = str_replace('*', '', $tvakey);
3505
                        $tvacompl = " (" . $outputlangs->transnoentities("NonPercuRecuperable") . ")";
3506
                    }
3507
                    $totalvat = $outputlangs->transcountrynoentities("TotalVAT", $mysoc->country_code) . ' ';
3508
                    $totalvat .= vatrate($tvakey, 1) . $tvacompl;
3509
                    $pdf->MultiCell($width, $height, $totalvat, 0, 'L', 1);
3510
3511
                    $pdf->SetXY($posx + $width, $posy + $height * $index);
3512
                    $pdf->MultiCell($width2, $height, price($tvaval, 0, $outputlangs), 0, 'R', 1);
3513
                }
3514
            }
3515
3516
            $index++;
3517
3518
            $total_ht = ($conf->multicurrency->enabled && $invoice->multicurrency_tx != 1) ? $invoice->multicurrency_total_ht : $invoice->total_ht;
3519
            $total_ttc = ($conf->multicurrency->enabled && $invoice->multicurrency_tx != 1) ? $invoice->multicurrency_total_ttc : $invoice->total_ttc;
3520
3521
            // Total TTC
3522
            $pdf->SetXY($posx, $posy + $height * $index);
3523
            $pdf->SetTextColor(0, 0, 60);
3524
            $pdf->SetFillColor(224, 224, 224);
3525
            $pdf->MultiCell($width, $height, $outputlangs->transnoentities("TotalTTC"), $useborder, 'L', 1);
3526
3527
3528
            $pdf->SetXY($posx + $width, $posy + $height * $index);
3529
            $pdf->MultiCell($width2, $height, price($sign * $total_ttc, 0, $outputlangs), $useborder, 'R', 1);
3530
3531
            $retainedWarrantyRate = (float)($object->retained_warranty ? price2num($object->retained_warranty) : price2num(getDolGlobalString('INVOICE_SITUATION_DEFAULT_RETAINED_WARRANTY_PERCENT', 0)));
3532
3533
            $total_ht_rg = 0;
3534
            $total_ttc_rg = 0;
3535
3536
            if ($this->is_rg) {
3537
                $index++;
3538
3539
                $pdf->SetXY($posx, $posy + $height * $index);
3540
                $pdf->SetTextColor(0, 0, 60);
3541
                $pdf->SetFillColor(241, 241, 241);
3542
                $pdf->MultiCell($width, $height, $outputlangs->transnoentities("RetainedWarrantyShort", $retainedWarrantyRate), $useborder, 'L', 1);
3543
3544
                $total_ht_rg = (float)price2num(price($total_ht * $retainedWarrantyRate / 100), 'MT');
3545
                $total_ttc_rg = (float)price2num(price($total_ttc * $retainedWarrantyRate / 100), 'MT');
3546
3547
                $pdf->SetXY($posx + $width, $posy + $height * $index);
3548
                $pdf->MultiCell($width2, $height, price(-$sign * $total_ht_rg, 0, $outputlangs), $useborder, 'R', 1);
3549
3550
                $total_ht_with_rg = $total_ht - $total_ht_rg;
3551
                $total_ttc_with_rg = $total_ttc - $total_ttc_rg;
3552
3553
                $index++;
3554
3555
                // Total TTC
3556
                $pdf->SetXY($posx, $posy + $height * $index);
3557
                $pdf->SetTextColor(0, 0, 60);
3558
                $pdf->SetFillColor(224, 224, 224);
3559
                $pdf->MultiCell($width, $height, $outputlangs->transnoentities("TotalSituationInvoiceWithRetainedWarranty"), $useborder, 'L', 1);
3560
3561
                $pdf->SetXY($posx + $width, $posy + $height * $index);
3562
                $pdf->MultiCell($width2, $height, price($sign * $total_ttc_with_rg, 0, $outputlangs), $useborder, 'R', 1);
3563
            }
3564
3565
3566
            $index++;
3567
3568
            $pdf->SetTextColor(0, 0, 0);
3569
3570
            $creditnoteamount = $invoice->getSumCreditNotesUsed();
3571
            $depositsamount = $invoice->getSumDepositsUsed();
3572
            $deja_regle = $invoice->getSommePaiement();
3573
3574
            $resteapayer = price2num($invoice->total_ttc - $deja_regle - $total_ttc_rg - $creditnoteamount - $depositsamount, 'MT');
3575
            if ($invoice->paye) $resteapayer = 0;
3576
3577
            $y = 0;
3578
3579
3580
            // Already paid + Deposits
3581
            $tot_deja_regle += $deja_regle + $depositsamount;
3582
3583
            $pdf->SetXY($posx, $posy + $height * $index);
3584
            $pdf->MultiCell($width, $height, $outputlangs->transnoentities("Paid"), 0, 'L', 0);
3585
            $pdf->SetXY($posx + $width, $posy + $height * $index);
3586
            $pdf->MultiCell($width2, $height, price($deja_regle + $depositsamount, 0, $outputlangs), 0, 'R', 0);
3587
3588
            // Credit note
3589
            if ($creditnoteamount) {
3590
                $index++;
3591
                $pdf->SetXY($posx, $posy + $height * $index);
3592
                $pdf->MultiCell($width, $height, $outputlangs->transnoentities("CreditNotes"), 0, 'L', 0);
3593
                $pdf->SetXY($posx + $width, $posy + $height * $index);
3594
                $pdf->MultiCell($width2, $height, price($creditnoteamount, 0, $outputlangs), 0, 'R', 0);
3595
            }
3596
3597
            // Escompte
3598
            if ($invoice->close_code == Facture::CLOSECODE_DISCOUNTVAT) {
3599
                $index++;
3600
                $pdf->SetFillColor(255, 255, 255);
3601
3602
                $pdf->SetXY($posx, $posy + $height * $index);
3603
                $pdf->MultiCell($width, $height, $outputlangs->transnoentities("EscompteOfferedShort"), $useborder, 'L', 1);
3604
                $pdf->SetXY($posx + $width, $posy + $height * $index);
3605
                $pdf->MultiCell($width2, $height, price($invoice->total_ttc - $deja_regle - $creditnoteamount - $depositsamount, 0, $outputlangs), $useborder, 'R', 1);
3606
3607
                $resteapayer = 0;
3608
            }
3609
3610
            $index++;
3611
            $pdf->SetTextColor(0, 0, 60);
3612
            $pdf->SetFillColor(224, 224, 224);
3613
            $pdf->SetXY($posx, $posy + $height * $index);
3614
            $pdf->MultiCell($width, $height, $outputlangs->transnoentities("RemainderToPay"), $useborder, 'L', 1);
3615
            $pdf->SetXY($posx + $width, $posy + $height * $index);
3616
            $pdf->MultiCell($width2, $height, price($resteapayer, 0, $outputlangs), $useborder, 'R', 1);
3617
3618
            $pdf->SetFont('', '', $default_font_size - 1);
3619
            $pdf->SetTextColor(0, 0, 0);
3620
3621
            $index++;
3622
3623
            if ($deja_regle > 0) {
3624
                $title = $outputlangs->transnoentities("PaymentsAlreadyDone");
3625
                if ($invoice->type == 2) $title = $outputlangs->transnoentities("PaymentsBackAlreadyDone");
3626
3627
                $pdf->SetFont('', '', $default_font_size - 3);
3628
                $pdf->SetXY($posx, $posy + $height * $index);
3629
                $pdf->MultiCell($width, $height, $title, 0, 'L', 0);
3630
3631
                //$pdf->line($tab3_posx, $tab3_top, $tab3_posx+$tab3_width, $tab3_top);
3632
3633
                $index++;
3634
3635
                $width4 = ($this->page_largeur - $this->marge_droite - $posx) / 4;
3636
3637
                $pdf->SetFont('', '', $default_font_size - 4);
3638
                $pdf->SetXY($posx, $posy + $height * $index);
3639
                $pdf->MultiCell($width4, $height - 1, $outputlangs->transnoentities("Payment"), 0, 'L', 0);
3640
                $pdf->SetXY($posx + $width4, $posy + $height * $index);
3641
                $pdf->MultiCell($width4, $height - 1, $outputlangs->transnoentities("Amount"), 0, 'L', 0);
3642
                $pdf->SetXY($posx + $width4 * 2, $posy + $height * $index);
3643
                $pdf->MultiCell($width4, $height - 1, $outputlangs->transnoentities("Type"), 0, 'L', 0);
3644
                $pdf->SetXY($posx + $width4 * 3, $posy + $height * $index);
3645
                $pdf->MultiCell($width4, $height - 1, $outputlangs->transnoentities("Num"), 0, 'L', 0);
3646
3647
                //$pdf->line($tab3_posx, $tab3_top-1+$tab3_height, $tab3_posx+$tab3_width, $tab3_top-1+$tab3_height);
3648
3649
                $y = $height - 1;
3650
3651
                $pdf->SetFont('', '', $default_font_size - 4);
3652
                /** @var Facture $invoice */
3653
                $payments = $invoice->getListOfPayments();
3654
3655
                if (count($payments)) {
3656
                    foreach ($payments as $payment) {
3657
                        $pdf->SetXY($posx, $posy + $height * $index + $y);
3658
                        $pdf->MultiCell($width4, $height - 1, dol_print_date($this->db->jdate($payment['date']), 'day', false, $outputlangs, true), 0, 'L', 0);
3659
                        $pdf->SetXY($posx + $width4, $posy + $height * $index + $y);
3660
                        $pdf->MultiCell($width4, $height - 1, price($sign * $payment['amount'], 0, $outputlangs), 0, 'L', 0);
3661
                        $pdf->SetXY($posx + $width4 * 2, $posy + $height * $index + $y);
3662
                        $oper = $outputlangs->transnoentitiesnoconv("PaymentTypeShort" . $payment['type']);
3663
3664
                        $pdf->MultiCell($width4, $height - 1, $oper, 0, 'L', 0);
3665
                        $pdf->SetXY($posx + $width4 * 3, $posy + $height * $index + $y);
3666
                        $pdf->MultiCell($width4, $height - 1, $payment['num'], 0, 'L', 0);
3667
3668
                        //$pdf->line($tab3_posx, $tab3_top+$y+3, $tab3_posx+$tab3_width, $tab3_top+$y+3);
3669
                        $y += ($height - 1);
3670
                    }
3671
                }
3672
            }
3673
3674
            // Output Rect
3675
            $pdf->SetDrawColor(128, 128, 128);
3676
            $this->printRect($pdf, $this->marge_gauche, $posy, $this->page_largeur - $this->marge_gauche - $this->marge_droite, $height * $index + $y);
3677
            $posy += $height * $index + $y;
3678
3679
            $pageposafter = $pdf->getPage();
3680
            if ($pageposafter > $pageposbefore) {   // There is a pagebreak
3681
                $pdf->rollbackTransaction(true);
3682
3683
                $pageposafter = $pageposbefore;
3684
                $pdf->AddPage('', '', true);
3685
                if (!empty($this->tplidx)) {
3686
                    $pdf->useTemplate($this->tplidx);
3687
                }
3688
                if (!getDolGlobalInt('MAIN_PDF_DONOTREPEAT_HEAD')) $this->_pagehead($pdf, $object, 0, $outputlangs);
3689
                $pdf->setPage($pageposafter + 1);
3690
                $pdf->setPageOrientation('', 1, 0); // The only function to edit the bottom margin of current page to set it.
3691
3692
                $posy = $this->tab_top_newpage + 1;
3693
            } else {
3694
                $idinv++;
3695
                $remain_to_pay -= ($sign * ($total_ht + (!empty($invoice->remise) ? $invoice->remise : 0)));
3696
3697
                $rem = 0;
3698
                if (count($invoice->lines)) {
3699
                    foreach ($invoice->lines as $l) {
3700
                        if ($l->fk_remise_except > 0) {
3701
                            $discount = new DiscountAbsolute($this->db);
3702
                            $result = $discount->fetch($l->fk_remise_except);
3703
                            if ($result > 0) {
3704
                                $rem += $discount->amount_ht;
3705
                            }
3706
                        }
3707
                    }
3708
                }
3709
3710
                $remain_to_pay -= $rem;
3711
3712
                $pdf->commitTransaction();
3713
            }
3714
        }
3715
3716
        if ($force_to_zero) {
3717
            $remain_to_pay = 0;
3718
        }
3719
3720
        $posy += 10;
3721
3722
        $pdf->setPageOrientation('', 1, 0); // The only function to edit the bottom margin of current page to set it.
3723
3724
        $pdf->SetTextColor(0, 0, 60);
3725
        $pdf->SetFont('', '', $default_font_size - 1);
3726
        $pdf->SetXY($this->marge_gauche, $posy + 1);
3727
        $label = $outputlangs->transnoentities("SituationTotalRayToRest");
3728
        $pdf->MultiCell($this->page_largeur - ($this->marge_droite + $this->marge_gauche), 3, $label, 0, 'L', 0);
3729
3730
        $amount = price($remain_to_pay);
3731
        $pdf->MultiCell($width2, 3, $amount, 0, 'R', 0, 1, $posx + $width, $posy + 1);
3732
3733
        $pdf->SetDrawColor(128, 128, 128);
3734
        $this->printRect($pdf, $this->marge_gauche, $posy, $this->page_largeur - $this->marge_gauche - $this->marge_droite, 7);
3735
    }
3736
}
3737