pdf_octopus::_tableau()   C
last analyzed

Complexity

Conditions 12

Size

Total Lines 72
Code Lines 41

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 12
eloc 41
nop 9
dl 0
loc 72
rs 6.9666
c 0
b 0
f 0

How to fix   Long Method    Complexity    Many Parameters   

Long Method

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

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

Commonly applied refactorings include:

Many Parameters

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

There are several approaches to avoid long parameter lists:

1
<?php
2
3
/* Copyright (C) 2004-2014  Laurent Destailleur         <[email protected]>
4
 * Copyright (C) 2005-2012  Regis Houssin               <[email protected]>
5
 * Copyright (C) 2008       Raphael Bertrand            <[email protected]>
6
 * Copyright (C) 2010-2014  Juanjo Menent               <[email protected]>
7
 * Copyright (C) 2012       Christophe Battarel         <[email protected]>
8
 * Copyright (C) 2012       Cédric Salvador             <[email protected]>
9
 * Copyright (C) 2012-2014  Raphaël Doursenaud          <[email protected]>
10
 * Copyright (C) 2015       Marcos 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