Passed
Push — EXTRACT_CLASSES ( c25e41...9f3ede )
by Rafael
55:18
created

FactureFournisseur::fetch()   C

Complexity

Conditions 14

Size

Total Lines 164
Code Lines 140

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 14
eloc 140
nop 3
dl 0
loc 164
rs 5.0133
c 0
b 0
f 0

How to fix   Long Method    Complexity   

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:

1
<?php
2
3
/* Copyright (C) 2002-2004  Rodolphe Quiedeville        <[email protected]>
4
 * Copyright (C) 2004-2012	Laurent Destailleur		    <[email protected]>
5
 * Copyright (C) 2004		Christophe Combelles	    <[email protected]>
6
 * Copyright (C) 2005		Marc Barilley			    <[email protected]>
7
 * Copyright (C) 2005-2012	Regis Houssin			    <[email protected]>
8
 * Copyright (C) 2010-2020	Juanjo Menent			    <[email protected]>
9
 * Copyright (C) 2013-2019	Philippe Grand			    <[email protected]>
10
 * Copyright (C) 2013		Florian Henry			    <[email protected]>
11
 * Copyright (C) 2014-2016	Marcos García			    <[email protected]>
12
 * Copyright (C) 2015		Bahfir Abbes			    <[email protected]>
13
 * Copyright (C) 2015-2022	Ferran Marcet			    <[email protected]>
14
 * Copyright (C) 2016-2023	Alexandre Spangaro		    <[email protected]>
15
 * Copyright (C) 2018       Nicolas ZABOURI			    <[email protected]>
16
 * Copyright (C) 2018-2024  Frédéric France             <[email protected]>
17
 * Copyright (C) 2022      	Gauthier VERDOL     	    <[email protected]>
18
 * Copyright (C) 2023		Nick Fragoulis
19
 * Copyright (C) 2024		MDW							<[email protected]>
20
 * Copyright (C) 2024       Rafael San José             <[email protected]>
21
 *
22
 * This program is free software; you can redistribute it and/or modify
23
 * it under the terms of the GNU General Public License as published by
24
 * the Free Software Foundation; either version 3 of the License, or
25
 * (at your option) any later version.
26
 *
27
 * This program is distributed in the hope that it will be useful,
28
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
29
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
30
 * GNU General Public License for more details.
31
 *
32
 * You should have received a copy of the GNU General Public License
33
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
34
 */
35
36
namespace Dolibarr\Code\Fourn\Classes;
37
38
use Dolibarr\Code\Accountancy\Classes\AccountingAccount;
39
40
/**
41
 *  \file       htdocs/fourn/class/fournisseur.facture.class.php
42
 *  \ingroup    fournisseur,facture
43
 *  \brief      File of class to manage suppliers invoices
44
 */
45
46
use Dolibarr\Code\MultiCurrency\Classes\MultiCurrency;
47
48
require_once constant('DOL_DOCUMENT_ROOT') . '/core/class/commoninvoice.class.php';
49
use Dolibarr\Core\Base\CommonObjectLine;
50
require_once constant('DOL_DOCUMENT_ROOT') . '/fourn/class/fournisseur.product.class.php';
51
52
if (isModEnabled('accounting')) {
53
    require_once constant('DOL_DOCUMENT_ROOT') . '/core/class/html.formaccounting.class.php';
54
}
55
56
/**
57
 *  Class to manage suppliers invoices
58
 */
59
class FactureFournisseur extends CommonInvoice
60
{
61
    /**
62
     * @var string ID to identify managed object
63
     */
64
    public $element = 'invoice_supplier';
65
66
    /**
67
     * @var string Name of table without prefix where object is stored
68
     */
69
    public $table_element = 'facture_fourn';
70
71
    /**
72
     * @var string    Name of subtable line
73
     */
74
    public $table_element_line = 'facture_fourn_det';
75
76
    /**
77
     * @var string  Name of class line
78
     */
79
    public $class_element_line = 'SupplierInvoiceLine';
80
    /**
81
     * @var string Field with ID of parent key if this field has a parent
82
     */
83
    public $fk_element = 'fk_facture_fourn';
84
85
    /**
86
     * @var string String with name of icon for myobject. Must be the part after the 'object_' into object_myobject.png
87
     */
88
    public $picto = 'supplier_invoice';
89
90
    /**
91
     * 0=Default, 1=View may be restricted to sales representative only if no permission to see all or to company of external user if external user
92
     * @var integer
93
     */
94
    public $restrictiononfksoc = 1;
95
96
    /**
97
     * {@inheritdoc}
98
     */
99
    protected $table_ref_field = 'ref';
100
101
    /**
102
     * @var int ID
103
     */
104
    public $rowid;
105
106
    /**
107
     * @var string Ref
108
     */
109
    public $ref;
110
111
    /**
112
     * @var string Ref supplier
113
     */
114
    public $ref_supplier;
115
116
    /**
117
     * @var string  Label of invoice
118
     * @deprecated  Use $label
119
     */
120
    public $libelle;
121
    /**
122
     * @var string Label of invoice
123
     */
124
    public $label;
125
126
    //Check constants for types
127
    public $type = self::TYPE_STANDARD;
128
129
    /**
130
     * Supplier invoice status
131
     * @var int
132
     * @deprecated
133
     * @see $status
134
     */
135
    public $statut;
136
137
    /**
138
     * Supplier invoice status
139
     * @var int
140
     * @see FactureFournisseur::STATUS_DRAFT, FactureFournisseur::STATUS_VALIDATED, FactureFournisseur::STATUS_PAID, FactureFournisseur::STATUS_ABANDONED
141
     */
142
    public $status;
143
144
    /**
145
     * Supplier invoice status
146
     * @var int
147
     * @deprecated
148
     * @see $status
149
     */
150
    public $fk_statut;
151
152
    /**
153
     * Set to 1 if the invoice is completely paid, otherwise is 0
154
     * @var int<0,1>
155
     * @deprecated Use $paid
156
     */
157
    public $paye;
158
    /**
159
     * Set to 1 if the invoice is completely paid, otherwise is 0
160
     * @var int<0,1>
161
     */
162
    public $paid;
163
164
    /**
165
     * @var int
166
     * @deprecated  Use $user_creation_id
167
     */
168
    public $author;
169
170
    /**
171
     * Date creation record (datec)
172
     *
173
     * @var integer
174
     */
175
    public $datec;
176
177
    /**
178
     * Max payment date (date_echeance)
179
     *
180
     * @var integer
181
     */
182
    public $date_echeance;
183
184
    /**
185
     * @var double $amount
186
     * @deprecated See $total_ttc, $total_ht, $total_tva
187
     */
188
    public $amount = 0;
189
    /**
190
     * @var double $remise
191
     * @deprecated
192
     */
193
    public $remise = 0;
194
195
    /**
196
     * @var float tva
197
     * @deprecated Use $total_tva
198
     */
199
    public $tva;
200
201
    // Warning: Do not set default value into property definition. it must stay null.
202
    // For example to avoid to have substitution done when object is generic and not yet defined.
203
    /** @var ?string */
204
    public $localtax1;
205
    /** @var ?string */
206
    public $localtax2;
207
    /** @var float */
208
    public $total_ht;
209
    /** @var float */
210
    public $total_tva;
211
    /** @var float */
212
    public $total_localtax1;
213
    /** @var float */
214
    public $total_localtax2;
215
    /** @var float */
216
    public $total_ttc;
217
218
    /**
219
     * @deprecated
220
     * @see $note_private, $note_public
221
     * @var string
222
     */
223
    public $note;
224
    /** @var string */
225
    public $note_private;
226
    /** @var string */
227
    public $note_public;
228
    /** @var int */
229
    public $propalid;
230
231
    /**
232
     * @var int ID
233
     */
234
    public $fk_account;     // default bank account
235
236
    /**
237
     * @var int Transport mode id
238
     */
239
    public $transport_mode_id;
240
241
    /**
242
     * @var int<0,1>  VAT reverse charge can be used on the invoice
243
     */
244
    public $vat_reverse_charge;
245
246
    public $extraparams = array();
247
248
    /**
249
     * Invoice lines
250
     * @var CommonInvoiceLine[]
251
     */
252
    public $lines = array();
253
254
    /**
255
     * @deprecated
256
     * @var ?Fournisseur
257
     */
258
    public $fournisseur;
259
260
    //! id of source invoice if replacement invoice or credit note
261
    /**
262
     * @var int ID
263
     */
264
    public $fk_facture_source;
265
266
    /** @var int */
267
    public $fac_rec;
268
    /** @var int */
269
    public $fk_fac_rec_source;
270
271
    public $fields = array(
272
        'rowid' => array('type' => 'integer', 'label' => 'TechnicalID', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 10),
273
        'ref' => array('type' => 'varchar(255)', 'label' => 'Ref', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'showoncombobox' => 1, 'position' => 15),
274
        'ref_supplier' => array('type' => 'varchar(255)', 'label' => 'RefSupplier', 'enabled' => 1, 'visible' => -1, 'position' => 20),
275
        'entity' => array('type' => 'integer', 'label' => 'Entity', 'default' => '1', 'enabled' => 1, 'visible' => -2, 'notnull' => 1, 'position' => 25, 'index' => 1),
276
        'ref_ext' => array('type' => 'varchar(255)', 'label' => 'RefExt', 'enabled' => 1, 'visible' => 0, 'position' => 30),
277
        'type' => array('type' => 'smallint(6)', 'label' => 'Type', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 35),
278
        'subtype' => array('type' => 'smallint(6)', 'label' => 'InvoiceSubtype', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 36),
279
        'fk_soc' => array('type' => 'integer:Societe:societe/class/societe.class.php', 'label' => 'ThirdParty', 'enabled' => 'isModEnabled("societe")', 'visible' => -1, 'notnull' => 1, 'position' => 40),
280
        'datec' => array('type' => 'datetime', 'label' => 'DateCreation', 'enabled' => 1, 'visible' => -1, 'position' => 45),
281
        'datef' => array('type' => 'date', 'label' => 'Date', 'enabled' => 1, 'visible' => -1, 'position' => 50),
282
        'tms' => array('type' => 'timestamp', 'label' => 'DateModification', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 55),
283
        'libelle' => array('type' => 'varchar(255)', 'label' => 'Label', 'enabled' => 1, 'visible' => -1, 'position' => 60),
284
        'paye' => array('type' => 'smallint(6)', 'label' => 'Paye', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 65),
285
        'amount' => array('type' => 'double(24,8)', 'label' => 'Amount', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 70),
286
        'remise' => array('type' => 'double(24,8)', 'label' => 'Discount', 'enabled' => 1, 'visible' => -1, 'position' => 75),
287
        'close_code' => array('type' => 'varchar(16)', 'label' => 'CloseCode', 'enabled' => 1, 'visible' => -1, 'position' => 80),
288
        'close_note' => array('type' => 'varchar(128)', 'label' => 'CloseNote', 'enabled' => 1, 'visible' => -1, 'position' => 85),
289
        'tva' => array('type' => 'double(24,8)', 'label' => 'Tva', 'enabled' => 1, 'visible' => -1, 'position' => 90),
290
        'localtax1' => array('type' => 'double(24,8)', 'label' => 'Localtax1', 'enabled' => 1, 'visible' => -1, 'position' => 95),
291
        'localtax2' => array('type' => 'double(24,8)', 'label' => 'Localtax2', 'enabled' => 1, 'visible' => -1, 'position' => 100),
292
        'total_ht' => array('type' => 'double(24,8)', 'label' => 'TotalHT', 'enabled' => 1, 'visible' => -1, 'position' => 105),
293
        'total_tva' => array('type' => 'double(24,8)', 'label' => 'TotalVAT', 'enabled' => 1, 'visible' => -1, 'position' => 110),
294
        'total_ttc' => array('type' => 'double(24,8)', 'label' => 'TotalTTC', 'enabled' => 1, 'visible' => -1, 'position' => 115),
295
        'fk_user_author' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserAuthor', 'enabled' => 1, 'visible' => -1, 'position' => 125),
296
        'fk_user_modif' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserModif', 'enabled' => 1, 'visible' => -2, 'notnull' => -1, 'position' => 130),
297
        'fk_user_valid' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserValidation', 'enabled' => 1, 'visible' => -1, 'position' => 135),
298
        'fk_facture_source' => array('type' => 'integer', 'label' => 'Fk facture source', 'enabled' => 1, 'visible' => -1, 'position' => 140),
299
        'fk_projet' => array('type' => 'integer:Project:projet/class/project.class.php:1:fk_statut=1', 'label' => 'Project', 'enabled' => "isModEnabled('project')", 'visible' => -1, 'position' => 145),
300
        'fk_account' => array('type' => 'integer', 'label' => 'Account', 'enabled' => 'isModEnabled("bank")', 'visible' => -1, 'position' => 150),
301
        'fk_cond_reglement' => array('type' => 'integer', 'label' => 'PaymentTerm', 'enabled' => 1, 'visible' => -1, 'position' => 155),
302
        'fk_mode_reglement' => array('type' => 'integer', 'label' => 'PaymentMode', 'enabled' => 1, 'visible' => -1, 'position' => 160),
303
        'date_lim_reglement' => array('type' => 'date', 'label' => 'DateLimReglement', 'enabled' => 1, 'visible' => -1, 'position' => 165),
304
        'note_private' => array('type' => 'html', 'label' => 'NotePrivate', 'enabled' => 1, 'visible' => 0, 'position' => 170),
305
        'note_public' => array('type' => 'html', 'label' => 'NotePublic', 'enabled' => 1, 'visible' => 0, 'position' => 175),
306
        'model_pdf' => array('type' => 'varchar(255)', 'label' => 'ModelPdf', 'enabled' => 1, 'visible' => 0, 'position' => 180),
307
        'extraparams' => array('type' => 'varchar(255)', 'label' => 'Extraparams', 'enabled' => 1, 'visible' => -1, 'position' => 190),
308
        'fk_incoterms' => array('type' => 'integer', 'label' => 'IncotermCode', 'enabled' => 1, 'visible' => -1, 'position' => 195),
309
        'location_incoterms' => array('type' => 'varchar(255)', 'label' => 'IncotermLocation', 'enabled' => 1, 'visible' => -1, 'position' => 200),
310
        'fk_multicurrency' => array('type' => 'integer', 'label' => 'MulticurrencyId', 'enabled' => 1, 'visible' => -1, 'position' => 205),
311
        'multicurrency_code' => array('type' => 'varchar(255)', 'label' => 'MulticurrencyCode', 'enabled' => 1, 'visible' => -1, 'position' => 210),
312
        'multicurrency_tx' => array('type' => 'double(24,8)', 'label' => 'MulticurrencyRate', 'enabled' => 1, 'visible' => -1, 'position' => 215),
313
        'multicurrency_total_ht' => array('type' => 'double(24,8)', 'label' => 'MulticurrencyTotalHT', 'enabled' => 1, 'visible' => -1, 'position' => 220),
314
        'multicurrency_total_tva' => array('type' => 'double(24,8)', 'label' => 'MulticurrencyTotalVAT', 'enabled' => 1, 'visible' => -1, 'position' => 225),
315
        'multicurrency_total_ttc' => array('type' => 'double(24,8)', 'label' => 'MulticurrencyTotalTTC', 'enabled' => 1, 'visible' => -1, 'position' => 230),
316
        'date_pointoftax' => array('type' => 'date', 'label' => 'Date pointoftax', 'enabled' => 1, 'visible' => -1, 'position' => 235),
317
        'date_valid' => array('type' => 'date', 'label' => 'DateValidation', 'enabled' => 1, 'visible' => -1, 'position' => 240),
318
        'last_main_doc' => array('type' => 'varchar(255)', 'label' => 'Last main doc', 'enabled' => 1, 'visible' => -1, 'position' => 245),
319
        'fk_statut' => array('type' => 'smallint(6)', 'label' => 'Status', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 500),
320
        'import_key' => array('type' => 'varchar(14)', 'label' => 'ImportId', 'enabled' => 1, 'visible' => -2, 'position' => 900),
321
    );
322
323
324
    /**
325
     * Standard invoice
326
     */
327
    const TYPE_STANDARD = 0;
328
329
    /**
330
     * Replacement invoice
331
     */
332
    const TYPE_REPLACEMENT = 1;
333
334
    /**
335
     * Credit note invoice
336
     */
337
    const TYPE_CREDIT_NOTE = 2;
338
339
    /**
340
     * Deposit invoice
341
     */
342
    const TYPE_DEPOSIT = 3;
343
344
    /**
345
     * Draft
346
     */
347
    const STATUS_DRAFT = 0;
348
349
    /**
350
     * Validated (need to be paid)
351
     */
352
    const STATUS_VALIDATED = 1;
353
354
    /**
355
     * Classified paid.
356
     * If paid partially, $this->close_code can be:
357
     * - CLOSECODE_DISCOUNTVAT
358
     * - CLOSECODE_BADCREDIT
359
     * If paid completely, this->close_code will be null
360
     */
361
    const STATUS_CLOSED = 2;
362
363
    /**
364
     * Classified abandoned and no payment done.
365
     * $this->close_code can be:
366
     * - CLOSECODE_BADCREDIT
367
     * - CLOSECODE_ABANDONED
368
     * - CLOSECODE_REPLACED
369
     */
370
    const STATUS_ABANDONED = 3;
371
372
    const CLOSECODE_DISCOUNTVAT = 'discount_vat';
373
    const CLOSECODE_BADCREDIT = 'badsupplier';
374
    const CLOSECODE_ABANDONED = 'abandon';
375
    const CLOSECODE_REPLACED = 'replaced';
376
377
    /**
378
     *  Constructor
379
     *
380
     *  @param      DoliDB      $db      Database handler
0 ignored issues
show
Bug introduced by
The type Dolibarr\Code\Fourn\Classes\DoliDB was not found. Did you mean DoliDB? If so, make sure to prefix the type with \.
Loading history...
381
     */
382
    public function __construct($db)
383
    {
384
        $this->db = $db;
385
386
        $this->ismultientitymanaged = 1;
387
    }
388
389
    /**
390
     *    Create supplier invoice into database
391
     *
392
     *    @param      User      $user       user object that creates
0 ignored issues
show
Bug introduced by
The type Dolibarr\Code\Fourn\Classes\User was not found. Did you mean User? If so, make sure to prefix the type with \.
Loading history...
393
     *    @return     int                   Id invoice created if OK, < 0 if KO
394
     */
395
    public function create($user)
396
    {
397
        global $langs, $conf, $hookmanager;
398
399
        $error = 0;
400
        $now = dol_now();
401
402
        // Clean parameters
403
        if (isset($this->ref_supplier)) {
404
            $this->ref_supplier = trim($this->ref_supplier);
405
        }
406
        if (empty($this->type)) {
407
            $this->type = self::TYPE_STANDARD;
408
        }
409
        if (empty($this->date)) {
410
            $this->date = $now;
411
        }
412
413
        // Multicurrency (test on $this->multicurrency_tx because we should take the default rate only if not using origin rate)
414
        if (!empty($this->multicurrency_code) && empty($this->multicurrency_tx)) {
415
            list($this->fk_multicurrency, $this->multicurrency_tx) = MultiCurrency::getIdAndTxFromCode($this->db, $this->multicurrency_code, $this->date);
416
        } else {
417
            $this->fk_multicurrency = MultiCurrency::getIdFromCode($this->db, $this->multicurrency_code);
418
        }
419
        if (empty($this->fk_multicurrency)) {
420
            $this->multicurrency_code = $conf->currency;
421
            $this->fk_multicurrency = 0;
422
            $this->multicurrency_tx = 1;
423
        }
424
425
        $this->db->begin();
426
427
        // Create invoice from a template recurring invoice
428
        if ($this->fac_rec > 0) {
429
            $this->fk_fac_rec_source = $this->fac_rec;
430
431
            require_once constant('DOL_DOCUMENT_ROOT') . '/fourn/class/fournisseur.facture-rec.class.php';
432
            $_facrec = new FactureFournisseurRec($this->db);
433
            $result = $_facrec->fetch($this->fac_rec);
434
            $result = $_facrec->fetchObjectLinked(null, '', null, '', 'OR', 1, 'sourcetype', 0); // This load $_facrec->linkedObjectsIds
435
436
            // Define some dates
437
            if (!empty($_facrec->frequency)) {
438
                $originaldatewhen = $_facrec->date_when;
439
                $nextdatewhen = dol_time_plus_duree($originaldatewhen, $_facrec->frequency, $_facrec->unit_frequency);
440
                $previousdaynextdatewhen = dol_time_plus_duree($nextdatewhen, -1, 'd');
441
                $this->socid = $_facrec->socid;
442
            } else {
443
                $originaldatewhen = 0;
444
                $nextdatewhen = 0;
445
                $previousdaynextdatewhen = 0;
446
            }
447
448
            $this->entity = $_facrec->entity; // Invoice created in same entity than template
449
450
            // Fields coming from GUI
451
            // @TODO Value of template should be used as default value on the form on the GUI, and we should here always use the value from GUI
452
            // set by posted page with $object->xxx = ... and this section should be removed.
453
            $this->fk_project = GETPOSTINT('projectid') > 0 ? (GETPOSTINT('projectid')) : $_facrec->fk_project;
454
            $this->note_public = GETPOST('note_public', 'restricthtml') ? GETPOST('note_public', 'restricthtml') : $_facrec->note_public;
0 ignored issues
show
Documentation Bug introduced by
It seems like GETPOST('note_public', '...: $_facrec->note_public can also be of type array or array or array. However, the property $note_public is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
455
            $this->note_private = GETPOST('note_private', 'restricthtml') ? GETPOST('note_private', 'restricthtml') : $_facrec->note_private;
0 ignored issues
show
Documentation Bug introduced by
It seems like GETPOST('note_private', ... $_facrec->note_private can also be of type array or array or array. However, the property $note_private is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
456
            $this->model_pdf = GETPOST('model', 'alpha') ? GETPOST('model', 'alpha') : $_facrec->model_pdf;
457
            $this->cond_reglement_id = GETPOSTINT('cond_reglement_id') > 0 ? (GETPOSTINT('cond_reglement_id')) : $_facrec->cond_reglement_id;
458
            $this->mode_reglement_id = GETPOSTINT('mode_reglement_id') > 0 ? (GETPOSTINT('mode_reglement_id')) : $_facrec->mode_reglement_id;
459
            $this->fk_account = GETPOST('fk_account') > 0 ? ((int) GETPOST('fk_account')) : $_facrec->fk_account;
460
461
            // Set here to have this defined for substitution into notes, should be recalculated after adding lines to get same result
462
            $this->total_ht = $_facrec->total_ht;
463
            $this->total_ttc = $_facrec->total_ttc;
464
465
            // Fields always coming from template
466
            $this->fk_incoterms = $_facrec->fk_incoterms;
467
            $this->location_incoterms = $_facrec->location_incoterms;
468
469
            // Clean parameters
470
            if (! $this->type) {
471
                $this->type = self::TYPE_STANDARD;
472
            }
473
            if (!empty(GETPOST('ref_supplier'))) {
474
                $this->ref_supplier = trim($this->ref_supplier);
475
            } else {
476
                $this->ref_supplier = trim($this->ref_supplier . '_' . ($_facrec->nb_gen_done + 1));
477
            }
478
            $this->note_public = trim($this->note_public);
479
            $this->note_private = trim($this->note_private);
480
            $this->note_private = dol_concatdesc($this->note_private, $langs->trans("GeneratedFromRecurringInvoice", $_facrec->title));
481
482
            $this->array_options = $_facrec->array_options;
483
484
            if (! $this->mode_reglement_id) {
485
                $this->mode_reglement_id = 0;
486
            }
487
            $this->status = self::STATUS_DRAFT;
488
            $this->statut = self::STATUS_DRAFT; // deprecated
489
490
            $this->linked_objects = $_facrec->linkedObjectsIds;
491
            // We do not add link to template invoice or next invoice will be linked to all generated invoices
492
            //$this->linked_objects['facturerec'][0] = $this->fac_rec;
493
494
            $forceduedate = $this->calculate_date_lim_reglement();
495
496
            // For recurring invoices, update date and number of last generation of recurring template invoice, before inserting new invoice
497
            if ($_facrec->frequency > 0) {
498
                dol_syslog("This is a recurring invoice so we set date_last_gen and next date_when");
499
                if (empty($_facrec->date_when)) {
500
                    $_facrec->date_when = $now;
501
                }
502
                $next_date = $_facrec->getNextDate(); // Calculate next date
503
                $result = $_facrec->setValueFrom('date_last_gen', $now, '', 0, 'date', '', $user, '');
504
                //$_facrec->setValueFrom('nb_gen_done', $_facrec->nb_gen_done + 1);     // Not required, +1 already included into setNextDate when second param is 1.
505
                $result = $_facrec->setNextDate($next_date, 1);
506
            }
507
508
            // Define lang of customer
509
            $outputlangs = $langs;
510
            $newlang = '';
511
512
            if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang) && isset($this->thirdparty->default_lang)) {
513
                $newlang = $this->thirdparty->default_lang; // for proposal, order, invoice, ...
514
            }
515
            if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang) && isset($this->default_lang)) {
516
                $newlang = $this->default_lang; // for thirdparty
517
            }
518
            if (!empty($newlang)) {
519
                $outputlangs = new Translate("", $conf);
520
                $outputlangs->setDefaultLang($newlang);
521
            } // Array of possible substitutions (See also file mailing-send.php that should manage same substitutions)
522
            $substitutionarray = getCommonSubstitutionArray($outputlangs, 0, null, $this);
523
            $substitutionarray['__INVOICE_PREVIOUS_MONTH__'] = dol_print_date(dol_time_plus_duree($this->date, -1, 'm'), '%m');
524
            $substitutionarray['__INVOICE_MONTH__'] = dol_print_date($this->date, '%m');
525
            $substitutionarray['__INVOICE_NEXT_MONTH__'] = dol_print_date(dol_time_plus_duree($this->date, 1, 'm'), '%m');
526
            $substitutionarray['__INVOICE_PREVIOUS_MONTH_TEXT__'] = dol_print_date(dol_time_plus_duree($this->date, -1, 'm'), '%B');
527
            $substitutionarray['__INVOICE_MONTH_TEXT__'] = dol_print_date($this->date, '%B');
528
            $substitutionarray['__INVOICE_NEXT_MONTH_TEXT__'] = dol_print_date(dol_time_plus_duree($this->date, 1, 'm'), '%B');
529
            $substitutionarray['__INVOICE_PREVIOUS_YEAR__'] = dol_print_date(dol_time_plus_duree($this->date, -1, 'y'), '%Y');
530
            $substitutionarray['__INVOICE_YEAR__'] = dol_print_date($this->date, '%Y');
531
            $substitutionarray['__INVOICE_NEXT_YEAR__'] = dol_print_date(dol_time_plus_duree($this->date, 1, 'y'), '%Y'); // Only for template invoice
532
            $substitutionarray['__INVOICE_DATE_NEXT_INVOICE_BEFORE_GEN__'] = $originaldatewhen ? dol_print_date($originaldatewhen, 'dayhour') : '';
533
            $substitutionarray['__INVOICE_DATE_NEXT_INVOICE_AFTER_GEN__'] = $nextdatewhen ? dol_print_date($nextdatewhen, 'dayhour') : '';
534
            $substitutionarray['__INVOICE_PREVIOUS_DATE_NEXT_INVOICE_AFTER_GEN__'] = $previousdaynextdatewhen ? dol_print_date($previousdaynextdatewhen, 'dayhour') : '';
535
            $substitutionarray['__INVOICE_COUNTER_CURRENT__'] = $_facrec->nb_gen_done;
536
            $substitutionarray['__INVOICE_COUNTER_MAX__'] = $_facrec->nb_gen_max;
537
538
            complete_substitutions_array($substitutionarray, $outputlangs);
539
540
            $this->note_public = make_substitutions($this->note_public, $substitutionarray);
541
            $this->note_private = make_substitutions($this->note_private, $substitutionarray);
542
        }
543
544
        // Define due date if not already defined
545
        if (!empty($forceduedate)) {
546
            $this->date_echeance = $forceduedate;
547
        }
548
549
        $sql = "INSERT INTO " . MAIN_DB_PREFIX . "facture_fourn (";
550
        $sql .= "ref";
551
        $sql .= ", ref_supplier";
552
        $sql .= ", ref_ext";
553
        $sql .= ", entity";
554
        $sql .= ", type";
555
        $sql .= ", subtype";
556
        $sql .= ", libelle";
557
        $sql .= ", fk_soc";
558
        $sql .= ", datec";
559
        $sql .= ", datef";
560
        $sql .= ", vat_reverse_charge";
561
        $sql .= ", fk_projet";
562
        $sql .= ", fk_cond_reglement";
563
        $sql .= ", fk_mode_reglement";
564
        $sql .= ", fk_account";
565
        $sql .= ", note_private";
566
        $sql .= ", note_public";
567
        $sql .= ", fk_user_author";
568
        $sql .= ", date_lim_reglement";
569
        $sql .= ", fk_incoterms, location_incoterms";
570
        $sql .= ", fk_multicurrency";
571
        $sql .= ", multicurrency_code";
572
        $sql .= ", multicurrency_tx";
573
        $sql .= ", fk_facture_source";
574
        $sql .= ", fk_fac_rec_source";
575
        $sql .= ")";
576
        $sql .= " VALUES (";
577
        $sql .= "'(PROV)'";
578
        $sql .= ", '" . $this->db->escape($this->ref_supplier) . "'";
579
        $sql .= ", '" . $this->db->escape($this->ref_ext) . "'";
580
        $sql .= ", " . ((int) $conf->entity);
581
        $sql .= ", '" . $this->db->escape($this->type) . "'";
582
        $sql .= ", " . ((int) $this->subtype);
583
        $sql .= ", '" . $this->db->escape(isset($this->label) ? $this->label : (isset($this->libelle) ? $this->libelle : '')) . "'";
584
        $sql .= ", " . ((int) $this->socid);
585
        $sql .= ", '" . $this->db->idate($now) . "'";
586
        $sql .= ", '" . $this->db->idate($this->date) . "'";
587
        $sql .= ", " . ($this->vat_reverse_charge != '' ? ((int) $this->db->escape($this->vat_reverse_charge)) : 0);
588
        $sql .= ", " . ($this->fk_project > 0 ? ((int) $this->fk_project) : "null");
589
        $sql .= ", " . ($this->cond_reglement_id > 0 ? ((int) $this->cond_reglement_id) : "null");
590
        $sql .= ", " . ($this->mode_reglement_id > 0 ? ((int) $this->mode_reglement_id) : "null");
591
        $sql .= ", " . ($this->fk_account > 0 ? ((int) $this->fk_account) : 'NULL');
592
        $sql .= ", '" . $this->db->escape($this->note_private) . "'";
593
        $sql .= ", '" . $this->db->escape($this->note_public) . "'";
594
        $sql .= ", " . ((int) $user->id) . ",";
595
        $sql .= $this->date_echeance != '' ? "'" . $this->db->idate($this->date_echeance) . "'" : "null";
596
        $sql .= ", " . (int) $this->fk_incoterms;
597
        $sql .= ", '" . $this->db->escape($this->location_incoterms) . "'";
598
        $sql .= ", " . (int) $this->fk_multicurrency;
599
        $sql .= ", '" . $this->db->escape($this->multicurrency_code) . "'";
600
        $sql .= ", " . (float) $this->multicurrency_tx;
601
        $sql .= ", " . ($this->fk_facture_source ? ((int) $this->fk_facture_source) : "null");
602
        $sql .= ", " . (isset($this->fk_fac_rec_source) ? $this->fk_fac_rec_source : "NULL");
603
        $sql .= ")";
604
605
        dol_syslog(get_class($this) . "::create", LOG_DEBUG);
606
        $resql = $this->db->query($sql);
607
        if ($resql) {
608
            $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX . 'facture_fourn');
609
610
            // Update ref with new one
611
            $this->ref = '(PROV' . $this->id . ')';
612
            $sql = 'UPDATE ' . MAIN_DB_PREFIX . "facture_fourn SET ref='" . $this->db->escape($this->ref) . "' WHERE rowid=" . ((int) $this->id);
613
614
            dol_syslog(get_class($this) . "::create", LOG_DEBUG);
615
            $resql = $this->db->query($sql);
616
            if (!$resql) {
617
                $error++;
618
            }
619
620
            if (!empty($this->linkedObjectsIds) && empty($this->linked_objects)) {  // To use new linkedObjectsIds instead of old linked_objects
621
                $this->linked_objects = $this->linkedObjectsIds; // TODO Replace linked_objects with linkedObjectsIds
622
            }
623
624
            // Add object linked
625
            if (!$error && $this->id && !empty($this->linked_objects) && is_array($this->linked_objects)) {
626
                foreach ($this->linked_objects as $origin => $tmp_origin_id) {
627
                    if (is_array($tmp_origin_id)) {       // New behaviour, if linked_object can have several links per type, so is something like array('contract'=>array(id1, id2, ...))
628
                        foreach ($tmp_origin_id as $origin_id) {
629
                            $ret = $this->add_object_linked($origin, $origin_id);
630
                            if (!$ret) {
631
                                dol_print_error($this->db);
632
                                $error++;
633
                            }
634
                        }
635
                    } else { // Old behaviour, if linked_object has only one link per type, so is something like array('contract'=>id1))
636
                        $origin_id = $tmp_origin_id;
637
                        $ret = $this->add_object_linked($origin, $origin_id);
638
                        if (!$ret) {
639
                            dol_print_error($this->db);
640
                            $error++;
641
                        }
642
                    }
643
                }
644
            }
645
646
            if (!$error && empty($this->fac_rec) && count($this->lines) && is_object($this->lines[0])) {    // If this->lines is array of InvoiceLines (preferred mode)
647
                dol_syslog("There is " . count($this->lines) . " lines that are invoice lines objects");
648
                foreach ($this->lines as $i => $val) {
649
                    $sql = 'INSERT INTO ' . MAIN_DB_PREFIX . 'facture_fourn_det (fk_facture_fourn, special_code, fk_remise_except)';
650
                    $sql .= " VALUES (" . ((int) $this->id) . ", " . ((int) $this->lines[$i]->special_code) . ", " . ($this->lines[$i]->fk_remise_except > 0 ? ((int) $this->lines[$i]->fk_remise_except) : 'NULL') . ')';
651
652
                    $resql_insert = $this->db->query($sql);
653
                    if ($resql_insert) {
654
                        $idligne = $this->db->last_insert_id(MAIN_DB_PREFIX . 'facture_fourn_det');
655
656
                        $res = $this->updateline(
657
                            $idligne,
658
                            $this->lines[$i]->desc ? $this->lines[$i]->desc : $this->lines[$i]->description,
659
                            $this->lines[$i]->subprice,
660
                            $this->lines[$i]->tva_tx . ($this->lines[$i]->vat_src_code ? ' (' . $this->lines[$i]->vat_src_code . ')' : ''),
661
                            $this->lines[$i]->localtax1_tx,
662
                            $this->lines[$i]->localtax2_tx,
663
                            $this->lines[$i]->qty,
664
                            $this->lines[$i]->fk_product,
665
                            'HT',
666
                            (!empty($this->lines[$i]->info_bits) ? $this->lines[$i]->info_bits : ''),
667
                            $this->lines[$i]->product_type,
668
                            $this->lines[$i]->remise_percent,
669
                            false,
670
                            $this->lines[$i]->date_start,
671
                            $this->lines[$i]->date_end,
672
                            $this->lines[$i]->array_options,
673
                            $this->lines[$i]->fk_unit,
674
                            $this->lines[$i]->multicurrency_subprice,
675
                            $this->lines[$i]->ref_supplier
676
                        );
677
                    } else {
678
                        $this->error = $this->db->lasterror();
679
                        $this->db->rollback();
680
                        return -5;
681
                    }
682
                }
683
            } elseif (!$error && empty($this->fac_rec)) {   // If this->lines is an array of invoice line arrays
684
                dol_syslog("There is " . count($this->lines) . " lines that are array lines");
685
                foreach ($this->lines as $i => $val) {
686
                    $line = $this->lines[$i];
687
688
                    // Test and convert into object this->lines[$i]. When coming from REST API, we may still have an array
689
                    //if (! is_object($line)) $line=json_decode(json_encode($line), false);  // convert recursively array into object.
690
                    if (!is_object($line)) {
691
                        $line = (object) $line;
692
                    }
693
694
                    $sql = 'INSERT INTO ' . MAIN_DB_PREFIX . 'facture_fourn_det (fk_facture_fourn, special_code, fk_remise_except)';
695
                    $sql .= " VALUES (" . ((int) $this->id) . ", " . ((int) $this->lines[$i]->special_code) . ", " . ($this->lines[$i]->fk_remise_except > 0 ? ((int) $this->lines[$i]->fk_remise_except) : 'NULL') . ')';
696
697
                    $resql_insert = $this->db->query($sql);
698
                    if ($resql_insert) {
699
                        $idligne = $this->db->last_insert_id(MAIN_DB_PREFIX . 'facture_fourn_det');
700
701
                        $this->updateline(
702
                            $idligne,
703
                            $line->desc ? $line->desc : $line->description,
704
                            $line->subprice,
705
                            $line->tva_tx,
706
                            $line->localtax1_tx,
707
                            $line->localtax2_tx,
708
                            $line->qty,
709
                            $line->fk_product,
710
                            'HT',
711
                            (!empty($line->info_bits) ? $line->info_bits : ''),
712
                            $line->product_type,
713
                            $line->remise_percent,
714
                            0,
715
                            $line->date_start,
716
                            $line->date_end,
717
                            $line->array_options,
718
                            $line->fk_unit,
719
                            $line->multicurrency_subprice,
720
                            $line->ref_supplier
721
                        );
722
                    } else {
723
                        $this->error = $this->db->lasterror();
724
                        $this->db->rollback();
725
                        return -5;
726
                    }
727
                }
728
            }
729
730
            /*
731
             * Insert lines of template invoices
732
             */
733
            if (! $error && $this->fac_rec > 0 && $_facrec instanceof FactureFournisseurRec) {
734
                foreach ($_facrec->lines as $i => $val) {
735
                    if ($_facrec->lines[$i]->fk_product) {
736
                        $prod = new Product($this->db);
737
                        $res = $prod->fetch($_facrec->lines[$i]->fk_product);
738
                    }
739
740
                    // For line from template invoice, we use data from template invoice
741
                    /*
742
                    $tva_tx = get_default_tva($mysoc,$soc,$prod->id);
743
                    $tva_npr = get_default_npr($mysoc,$soc,$prod->id);
744
                    if (empty($tva_tx)) $tva_npr=0;
745
                    $localtax1_tx=get_localtax($tva_tx,1,$soc,$mysoc,$tva_npr);
746
                    $localtax2_tx=get_localtax($tva_tx,2,$soc,$mysoc,$tva_npr);
747
                    */
748
                    $tva_tx = $_facrec->lines[$i]->tva_tx . ($_facrec->lines[$i]->vat_src_code ? '(' . $_facrec->lines[$i]->vat_src_code . ')' : '');
749
                    $tva_npr = $_facrec->lines[$i]->info_bits;
750
                    if (empty($tva_tx)) {
751
                        $tva_npr = 0;
752
                    }
753
                    $localtax1_tx = $_facrec->lines[$i]->localtax1_tx;
754
                    $localtax2_tx = $_facrec->lines[$i]->localtax2_tx;
755
756
                    $fk_product_fournisseur_price = empty($_facrec->lines[$i]->fk_product_fournisseur_price) ? null : $_facrec->lines[$i]->fk_product_fournisseur_price;
757
                    $buyprice = empty($_facrec->lines[$i]->buyprice) ? 0 : $_facrec->lines[$i]->buyprice;
758
759
                    // If buyprice not defined from template invoice, we try to guess the best value
760
                    if (! $buyprice && $_facrec->lines[$i]->fk_product > 0) {
761
                        require_once constant('DOL_DOCUMENT_ROOT') . '/fourn/class/fournisseur.product.class.php';
762
                        $producttmp = new ProductFournisseur($this->db);
763
                        $producttmp->fetch($_facrec->lines[$i]->fk_product);
764
765
                        // If margin module defined on costprice, we try the costprice
766
                        // If not defined or if module margin defined and pmp and stock module enabled, we try pmp price
767
                        // else we get the best supplier price
768
                        if (getDolGlobalString('MARGIN_TYPE') == 'costprice' && !empty($producttmp->cost_price)) {
769
                            $buyprice = $producttmp->cost_price;
770
                        } elseif (isModEnabled('stock') && (getDolGlobalString('MARGIN_TYPE') == 'costprice' || getDolGlobalString('MARGIN_TYPE') == 'pmp') && !empty($producttmp->pmp)) {
771
                            $buyprice = $producttmp->pmp;
772
                        } else {
773
                            if ($producttmp->find_min_price_product_fournisseur($_facrec->lines[$i]->fk_product) > 0) {
774
                                if ($producttmp->product_fourn_price_id > 0) {
775
                                    $buyprice = price2num($producttmp->fourn_unitprice * (1 - $producttmp->fourn_remise_percent / 100) + $producttmp->fourn_remise, 'MU');
776
                                }
777
                            }
778
                        }
779
                    }
780
781
                    $result_insert = $this->addline(
782
                        $_facrec->lines[$i]->desc ? $_facrec->lines[$i]->desc : $_facrec->lines[$i]->description,
783
                        $_facrec->lines[$i]->pu_ht,
784
                        $tva_tx,
785
                        $localtax1_tx,
786
                        $localtax2_tx,
787
                        $_facrec->lines[$i]->qty,
788
                        $_facrec->lines[$i]->fk_product,
789
                        $_facrec->lines[$i]->remise_percent,
790
                        ($_facrec->lines[$i]->date_start == 1 && $this->date) ? $this->date : '',
791
                        ($_facrec->lines[$i]->date_end == 1 && $previousdaynextdatewhen) ? $previousdaynextdatewhen : '',
792
                        0,
793
                        $_facrec->lines[$i]->info_bits,
794
                        'HT',
795
                        0,
796
                        $_facrec->lines[$i]->rang,
797
                        false,
798
                        $_facrec->lines[$i]->array_options,
799
                        $_facrec->lines[$i]->fk_unit,
800
                        0,
801
                        0,
802
                        $_facrec->lines[$i]->ref_supplier,
803
                        $_facrec->lines[$i]->special_code,
804
                        0,
805
                        0
806
                    );
807
                    if ($result_insert < 0) {
808
                        $error++;
809
                        $this->error = $this->db->error();
810
                        break;
811
                    }
812
                }
813
            }
814
815
816
            // Update total price
817
            $result = $this->update_price(1);
818
            if ($result > 0) {
819
                // Actions on extra fields
820
                if (!$error) {
821
                    $result = $this->insertExtraFields(); // This also set $this->error or $this->errors if errors are found
822
                    if ($result < 0) {
823
                        $error++;
824
                    }
825
                }
826
827
                if (!$error) {
828
                    // Call trigger
829
                    $result = $this->call_trigger('BILL_SUPPLIER_CREATE', $user);
830
                    if ($result < 0) {
831
                        $error++;
832
                    }
833
                    // End call triggers
834
                }
835
836
                if (!$error) {
837
                    $this->db->commit();
838
                    return $this->id;
839
                } else {
840
                    $this->db->rollback();
841
                    return -4;
842
                }
843
            } else {
844
                $this->error = $langs->trans('FailedToUpdatePrice');
845
                $this->db->rollback();
846
                return -3;
847
            }
848
        } else {
849
            if ($this->db->errno() == 'DB_ERROR_RECORD_ALREADY_EXISTS') {
850
                $this->error = $langs->trans('ErrorRefAlreadyExists');
851
                $this->db->rollback();
852
                return -1;
853
            } else {
854
                $this->error = $this->db->lasterror();
855
                $this->db->rollback();
856
                return -2;
857
            }
858
        }
859
    }
860
861
    /**
862
     *  Load object in memory from database
863
     *
864
     *  @param  int     $id         Id supplier invoice
865
     *  @param  string  $ref        Ref supplier invoice
866
     *  @param  string  $ref_ext    External reference of invoice
867
     *  @return int                 Return integer <0 if KO, >0 if OK, 0 if not found
868
     */
869
    public function fetch($id = 0, $ref = '', $ref_ext = '')
870
    {
871
        if (empty($id) && empty($ref) && empty($ref_ext)) {
872
            return -1;
873
        }
874
875
        $sql = "SELECT";
876
        $sql .= " t.rowid,";
877
        $sql .= " t.ref,";
878
        $sql .= " t.ref_supplier,";
879
        $sql .= " t.ref_ext,";
880
        $sql .= " t.entity,";
881
        $sql .= " t.type,";
882
        $sql .= " t.subtype,";
883
        $sql .= " t.fk_soc,";
884
        $sql .= " t.datec,";
885
        $sql .= " t.datef,";
886
        $sql .= " t.tms,";
887
        $sql .= " t.libelle as label,";
888
        $sql .= " t.paye,";
889
        $sql .= " t.close_code,";
890
        $sql .= " t.close_note,";
891
        $sql .= " t.tva,";
892
        $sql .= " t.localtax1,";
893
        $sql .= " t.localtax2,";
894
        $sql .= " t.total_ht,";
895
        $sql .= " t.total_tva,";
896
        $sql .= " t.total_ttc,";
897
        $sql .= " t.fk_statut as status,";
898
        $sql .= " t.fk_user_author,";
899
        $sql .= " t.fk_user_valid,";
900
        $sql .= " t.fk_facture_source,";
901
        $sql .= " t.vat_reverse_charge,";
902
        $sql .= " t.fk_fac_rec_source,";
903
        $sql .= " t.fk_projet as fk_project,";
904
        $sql .= " t.fk_cond_reglement,";
905
        $sql .= " t.fk_account,";
906
        $sql .= " t.fk_mode_reglement,";
907
        $sql .= " t.date_lim_reglement,";
908
        $sql .= " t.note_private,";
909
        $sql .= " t.note_public,";
910
        $sql .= " t.model_pdf,";
911
        $sql .= " t.last_main_doc,";
912
        $sql .= " t.import_key,";
913
        $sql .= " t.extraparams,";
914
        $sql .= " cr.code as cond_reglement_code, cr.libelle as cond_reglement_label, cr.libelle_facture as cond_reglement_doc,";
915
        $sql .= " p.code as mode_reglement_code, p.libelle as mode_reglement_label,";
916
        $sql .= ' s.nom as socnom, s.rowid as socid,';
917
        $sql .= ' t.fk_incoterms, t.location_incoterms,';
918
        $sql .= " i.libelle as label_incoterms,";
919
        $sql .= ' t.fk_transport_mode,';
920
        $sql .= ' t.fk_multicurrency, t.multicurrency_code, t.multicurrency_tx, t.multicurrency_total_ht, t.multicurrency_total_tva, t.multicurrency_total_ttc';
921
        $sql .= ' FROM ' . MAIN_DB_PREFIX . 'facture_fourn as t';
922
        $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "societe as s ON (t.fk_soc = s.rowid)";
923
        $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "c_payment_term as cr ON t.fk_cond_reglement = cr.rowid";
924
        $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "c_paiement as p ON t.fk_mode_reglement = p.id";
925
        $sql .= ' LEFT JOIN ' . MAIN_DB_PREFIX . 'c_incoterms as i ON t.fk_incoterms = i.rowid';
926
        if ($id) {
927
            $sql .= " WHERE t.rowid = " . ((int) $id);
928
        } else {
929
            $sql .= ' WHERE t.entity IN (' . getEntity('supplier_invoice') . ')'; // Don't use entity if you use rowid
930
            if ($ref) {
931
                $sql .= " AND t.ref = '" . $this->db->escape($ref) . "'";
932
            }
933
            if ($ref_ext) {
934
                $sql .= " AND t.ref_ext = '" . $this->db->escape($ref_ext) . "'";
935
            }
936
        }
937
938
        dol_syslog(get_class($this) . "::fetch", LOG_DEBUG);
939
        $resql = $this->db->query($sql);
940
        if ($resql) {
941
            if ($this->db->num_rows($resql)) {
942
                $obj = $this->db->fetch_object($resql);
943
944
                $this->id = $obj->rowid;
945
                $this->ref = $obj->ref ? $obj->ref : $obj->rowid; // We take rowid if ref is empty for backward compatibility
946
947
                $this->ref_supplier         = $obj->ref_supplier;
948
                $this->ref_ext              = $obj->ref_ext;
949
                $this->entity               = $obj->entity;
950
                $this->type                 = empty($obj->type) ? self::TYPE_STANDARD : $obj->type;
951
                $this->subtype              = (int) $obj->subtype;
952
                $this->socid                = $obj->fk_soc;
953
                $this->datec                = $this->db->jdate($obj->datec);
954
                $this->date                 = $this->db->jdate($obj->datef);
955
                //$this->datep              = $this->db->jdate($obj->datef);
956
                $this->tms                  = $this->db->jdate($obj->tms);
957
                $this->libelle              = $obj->label; // deprecated
0 ignored issues
show
Deprecated Code introduced by
The property Dolibarr\Code\Fourn\Clas...reFournisseur::$libelle has been deprecated: Use $label ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

957
                /** @scrutinizer ignore-deprecated */ $this->libelle              = $obj->label; // deprecated

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
958
                $this->label                = $obj->label;
959
                $this->paye                 = $obj->paye;
0 ignored issues
show
Deprecated Code introduced by
The property Dolibarr\Code\Fourn\Clas...ctureFournisseur::$paye has been deprecated: Use $paid ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

959
                /** @scrutinizer ignore-deprecated */ $this->paye                 = $obj->paye;

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
960
                $this->paid                 = $obj->paye;
961
                $this->close_code           = $obj->close_code;
962
                $this->close_note           = $obj->close_note;
963
                $this->total_localtax1      = $obj->localtax1;
964
                $this->total_localtax2      = $obj->localtax2;
965
                $this->total_ht             = $obj->total_ht;
966
                $this->total_tva            = $obj->total_tva;
967
                $this->total_ttc            = $obj->total_ttc;
968
                $this->status               = $obj->status;
969
                $this->statut               = $obj->status; // For backward compatibility
970
                $this->fk_statut            = $obj->status; // For backward compatibility
971
                $this->user_creation_id     = $obj->fk_user_author;
972
                $this->author               = $obj->fk_user_author; // deprecated
0 ignored issues
show
Deprecated Code introduced by
The property Dolibarr\Code\Fourn\Clas...ureFournisseur::$author has been deprecated: Use $user_creation_id ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

972
                /** @scrutinizer ignore-deprecated */ $this->author               = $obj->fk_user_author; // deprecated

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
973
                $this->user_validation_id   = $obj->fk_user_valid;
974
                $this->fk_facture_source    = $obj->fk_facture_source;
975
                $this->vat_reverse_charge   = empty($obj->vat_reverse_charge) ? 0 : 1;
976
                $this->fk_fac_rec_source    = $obj->fk_fac_rec_source;
977
                $this->fk_project           = $obj->fk_project;
978
                $this->cond_reglement_id    = $obj->fk_cond_reglement;
979
                $this->cond_reglement_code  = $obj->cond_reglement_code;
980
                $this->cond_reglement       = $obj->cond_reglement_label; // deprecated
981
                $this->cond_reglement_label = $obj->cond_reglement_label;
982
                $this->cond_reglement_doc   = $obj->cond_reglement_doc;
983
                $this->fk_account           = $obj->fk_account;
984
                $this->mode_reglement_id    = $obj->fk_mode_reglement;
985
                $this->mode_reglement_code  = $obj->mode_reglement_code;
986
                $this->mode_reglement       = $obj->mode_reglement_label;
987
                $this->date_echeance        = $this->db->jdate($obj->date_lim_reglement);
988
                $this->note                 = $obj->note_private; // deprecated
989
                $this->note_private         = $obj->note_private;
990
                $this->note_public          = $obj->note_public;
991
                $this->model_pdf            = $obj->model_pdf;
992
                $this->last_main_doc = $obj->last_main_doc;
993
                $this->import_key           = $obj->import_key;
994
995
                //Incoterms
996
                $this->fk_incoterms = $obj->fk_incoterms;
997
                $this->location_incoterms = $obj->location_incoterms;
998
                $this->label_incoterms = $obj->label_incoterms;
999
                $this->transport_mode_id = $obj->fk_transport_mode;
1000
1001
                // Multicurrency
1002
                $this->fk_multicurrency = $obj->fk_multicurrency;
1003
                $this->multicurrency_code = $obj->multicurrency_code;
1004
                $this->multicurrency_tx = $obj->multicurrency_tx;
1005
                $this->multicurrency_total_ht = $obj->multicurrency_total_ht;
1006
                $this->multicurrency_total_tva = $obj->multicurrency_total_tva;
1007
                $this->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
1008
1009
                $this->extraparams = isset($obj->extraparams) ? (array) json_decode($obj->extraparams, true) : array();
1010
1011
                $this->socid  = $obj->socid;
1012
1013
                // Retrieve all extrafield
1014
                // fetch optionals attributes and labels
1015
                $this->fetch_optionals();
1016
1017
                $result = $this->fetch_lines();
1018
                if ($result < 0) {
1019
                    $this->error = $this->db->lasterror();
1020
                    return -3;
1021
                }
1022
            } else {
1023
                $this->error = 'Bill with id ' . $id . ' not found';
1024
                dol_syslog(get_class($this) . '::fetch ' . $this->error);
1025
                return 0;
1026
            }
1027
1028
            $this->db->free($resql);
1029
            return 1;
1030
        } else {
1031
            $this->error = "Error " . $this->db->lasterror();
1032
            return -1;
1033
        }
1034
    }
1035
1036
1037
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1038
    /**
1039
     *  Load this->lines
1040
     *
1041
     *  @return     int         1 si ok, < 0 si erreur
1042
     */
1043
    public function fetch_lines()
1044
    {
1045
		// phpcs:enable
1046
        $this->lines = array();
1047
1048
        $sql = 'SELECT f.rowid, f.ref as ref_supplier, f.description as line_desc, f.date_start, f.date_end, f.pu_ht, f.pu_ttc, f.qty, f.remise_percent, f.vat_src_code, f.tva_tx';
1049
        $sql .= ', f.localtax1_tx, f.localtax2_tx, f.localtax1_type, f.localtax2_type, f.total_localtax1, f.total_localtax2, f.fk_facture_fourn, f.fk_remise_except';
1050
        $sql .= ', f.total_ht, f.tva as total_tva, f.total_ttc, f.fk_product, f.product_type, f.info_bits, f.rang, f.special_code, f.fk_parent_line, f.fk_unit';
1051
        $sql .= ', p.rowid as product_id, p.ref as product_ref, p.label as label, p.barcode as product_barcode, p.description as product_desc';
1052
        $sql .= ', f.fk_code_ventilation, f.fk_multicurrency, f.multicurrency_code, f.multicurrency_subprice, f.multicurrency_total_ht, f.multicurrency_total_tva, f.multicurrency_total_ttc';
1053
        $sql .= ' FROM ' . MAIN_DB_PREFIX . 'facture_fourn_det as f';
1054
        $sql .= ' LEFT JOIN ' . MAIN_DB_PREFIX . 'product as p ON f.fk_product = p.rowid';
1055
        $sql .= ' WHERE fk_facture_fourn=' . ((int) $this->id);
1056
        $sql .= ' ORDER BY f.rang, f.rowid';
1057
1058
        dol_syslog(get_class($this) . "::fetch_lines", LOG_DEBUG);
1059
1060
        $resql_rows = $this->db->query($sql);
1061
        if ($resql_rows) {
1062
            $num_rows = $this->db->num_rows($resql_rows);
1063
            if ($num_rows) {
1064
                $i = 0;
1065
                while ($i < $num_rows) {
1066
                    $obj = $this->db->fetch_object($resql_rows);
1067
1068
                    $line = new SupplierInvoiceLine($this->db);
1069
1070
                    $line->id               = $obj->rowid;
1071
                    $line->rowid            = $obj->rowid;
0 ignored issues
show
Deprecated Code introduced by
The property Dolibarr\Core\Base\CommonObjectLine::$rowid has been deprecated: Try to use id property as possible (even if field into database is still rowid) ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

1071
                    /** @scrutinizer ignore-deprecated */ $line->rowid            = $obj->rowid;

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
1072
                    $line->description      = $obj->line_desc;
0 ignored issues
show
Deprecated Code introduced by
The property Dolibarr\Code\Fourn\Clas...voiceLine::$description has been deprecated: Use $desc ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

1072
                    /** @scrutinizer ignore-deprecated */ $line->description      = $obj->line_desc;

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
1073
                    $line->desc             = $obj->line_desc;
1074
                    $line->date_start       = $obj->date_start;
1075
                    $line->date_end         = $obj->date_end;
1076
                    $line->product_ref      = $obj->product_ref;
1077
                    $line->ref              = $obj->product_ref;
1078
                    $line->ref_supplier     = $obj->ref_supplier;
1079
                    $line->libelle          = $obj->label;
0 ignored issues
show
Bug Best Practice introduced by
The property libelle does not exist on Dolibarr\Code\Fourn\Classes\SupplierInvoiceLine. Since you implemented __set, consider adding a @property annotation.
Loading history...
1080
                    $line->label            = $obj->label;
0 ignored issues
show
Deprecated Code introduced by
The property Dolibarr\Code\Fourn\Clas...lierInvoiceLine::$label has been deprecated: Use $product_label ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

1080
                    /** @scrutinizer ignore-deprecated */ $line->label            = $obj->label;

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
1081
                    $line->product_barcode  = $obj->product_barcode;
1082
                    $line->product_desc     = $obj->product_desc;
1083
                    $line->subprice         = $obj->pu_ht;
1084
                    $line->pu_ht            = $obj->pu_ht;
0 ignored issues
show
Deprecated Code introduced by
The property Dolibarr\Code\Fourn\Clas...lierInvoiceLine::$pu_ht has been deprecated: Use $subprice ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

1084
                    /** @scrutinizer ignore-deprecated */ $line->pu_ht            = $obj->pu_ht;

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
1085
                    $line->pu_ttc           = $obj->pu_ttc;
1086
                    $line->vat_src_code     = $obj->vat_src_code;
1087
                    $line->tva_tx           = $obj->tva_tx;
1088
                    $line->localtax1_tx     = $obj->localtax1_tx;
1089
                    $line->localtax2_tx     = $obj->localtax2_tx;
1090
                    $line->localtax1_type   = $obj->localtax1_type;
1091
                    $line->localtax2_type   = $obj->localtax2_type;
1092
                    $line->qty              = $obj->qty;
1093
                    $line->remise_percent   = $obj->remise_percent;
1094
                    $line->fk_remise_except = $obj->fk_remise_except;
1095
                    //$line->tva            = $obj->total_tva; // deprecated
1096
                    $line->total_ht         = $obj->total_ht;
1097
                    $line->total_ttc        = $obj->total_ttc;
1098
                    $line->total_tva        = $obj->total_tva;
1099
                    $line->total_localtax1  = $obj->total_localtax1;
1100
                    $line->total_localtax2  = $obj->total_localtax2;
1101
                    $line->fk_facture_fourn = $obj->fk_facture_fourn;
1102
                    $line->fk_product       = $obj->fk_product;
1103
                    $line->product_type     = $obj->product_type;
1104
                    $line->product_label    = $obj->label;
1105
                    $line->info_bits        = $obj->info_bits;
1106
                    $line->fk_parent_line   = $obj->fk_parent_line;
1107
                    $line->special_code     = $obj->special_code;
1108
                    $line->rang             = $obj->rang;
1109
                    $line->fk_unit          = $obj->fk_unit;
1110
1111
                    // Accountancy
1112
                    $line->fk_accounting_account = $obj->fk_code_ventilation;
0 ignored issues
show
Bug Best Practice introduced by
The property fk_accounting_account does not exist on Dolibarr\Code\Fourn\Classes\SupplierInvoiceLine. Since you implemented __set, consider adding a @property annotation.
Loading history...
1113
1114
                    // Multicurrency
1115
                    $line->fk_multicurrency = $obj->fk_multicurrency;
1116
                    $line->multicurrency_code = $obj->multicurrency_code;
1117
                    $line->multicurrency_subprice = $obj->multicurrency_subprice;
1118
                    $line->multicurrency_total_ht = $obj->multicurrency_total_ht;
1119
                    $line->multicurrency_total_tva = $obj->multicurrency_total_tva;
1120
                    $line->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
1121
1122
                    // Extra fields
1123
                    $line->fetch_optionals();
1124
1125
                    $this->lines[$i] = $line;
1126
1127
                    $i++;
1128
                }
1129
            }
1130
            $this->db->free($resql_rows);
1131
            return 1;
1132
        } else {
1133
            $this->error = $this->db->error();
1134
            dol_syslog(get_class($this) . "::fetch_lines - No lines:{$this->error} Error:{$this->error}", LOG_DEBUG);
1135
            return -3;
1136
        }
1137
    }
1138
1139
1140
    /**
1141
     *  Update database
1142
     *
1143
     *  @param  User    $user            User that modify
1144
     *  @param  int     $notrigger       0=launch triggers after, 1=disable triggers
1145
     *  @return int                      Return integer <0 if KO, >0 if OK
1146
     */
1147
    public function update($user = null, $notrigger = 0)
1148
    {
1149
        global $langs;
1150
        $error = 0;
1151
1152
        // Clean parameters
1153
        if (empty($this->type)) {
1154
            $this->type = self::TYPE_STANDARD;
1155
        }
1156
        if (isset($this->ref)) {
1157
            $this->ref = trim($this->ref);
1158
        }
1159
        if (isset($this->ref_supplier)) {
1160
            $this->ref_supplier = trim($this->ref_supplier);
1161
        }
1162
        if (isset($this->ref_ext)) {
1163
            $this->ref_ext = trim($this->ref_ext);
1164
        }
1165
        if (isset($this->entity)) {
1166
            $this->entity = (int) $this->entity;
1167
        }
1168
        if (isset($this->type)) {
1169
            $this->type = (int) $this->type;
1170
        }
1171
        if (isset($this->subtype)) {
1172
            $this->subtype = (int) $this->subtype;
1173
        }
1174
        if (isset($this->socid)) {
1175
            $this->socid = (int) $this->socid;
1176
        }
1177
        if (isset($this->label)) {
1178
            $this->label = trim($this->label);
1179
        }
1180
        if (isset($this->paid)) {
1181
            $this->paid = (int) (bool) $this->paye;
1182
            $this->paye = $this->paid;
1183
        } elseif (isset($this->paye)) {
1184
            $this->paid = (int) (bool) $this->paye;
1185
            $this->paye = $this->paid;
1186
        }
1187
        if (isset($this->close_code)) {
1188
            $this->close_code = trim($this->close_code);
1189
        }
1190
        if (isset($this->close_note)) {
1191
            $this->close_note = trim($this->close_note);
1192
        }
1193
        if (empty($this->total_ht)) {
1194
            $this->total_ht = 0;
1195
        }
1196
        if (empty($this->total_tva)) {
1197
            $this->total_tva = 0;
1198
        }
1199
        if (isset($this->total_ttc)) {
1200
            $this->total_ttc = (float) $this->total_ttc;
1201
        }
1202
        if (isset($this->status)) {
1203
            $this->status = (int) $this->status;
1204
            $this->statut = $this->status;
1205
        } elseif (isset($this->statut)) {
1206
            $this->status = (int) $this->statut;
1207
            $this->statut = $this->status;
1208
        }
1209
        if (isset($this->author)) {  // TODO: user_creation_id?
1210
            $this->author = (int) $this->author;
1211
        }
1212
        if (isset($this->fk_user_valid)) {
1213
            $this->fk_user_valid = trim($this->fk_user_valid);
1214
        }
1215
        if (isset($this->fk_facture_source)) {
1216
            $this->fk_facture_source = (int) $this->fk_facture_source;
1217
        }
1218
        if (isset($this->fk_project)) {
1219
            if (empty($this->fk_project)) {
1220
                $this->fk_project = 0;
1221
            } else {
1222
                $this->fk_project = (int) $this->fk_project;
1223
            }
1224
        }
1225
        if (isset($this->cond_reglement_id)) {
1226
            $this->cond_reglement_id = (int) $this->cond_reglement_id;
1227
        }
1228
        if (isset($this->note_private)) {
1229
            $this->note_private = trim($this->note_private);
1230
            $this->note = $this->note_private;
1231
        }
1232
        if (isset($this->note_public)) {
1233
            $this->note_public = trim($this->note_public);
1234
        }
1235
        if (isset($this->model_pdf)) {
1236
            $this->model_pdf = trim($this->model_pdf);
1237
        }
1238
        if (isset($this->import_key)) {
1239
            $this->import_key = trim($this->import_key);
1240
        }
1241
1242
1243
        // Check parameters
1244
        // Put here code to add control on parameters values
1245
1246
        // Update request
1247
        $sql = "UPDATE " . MAIN_DB_PREFIX . "facture_fourn SET";
1248
        $sql .= " ref=" . (isset($this->ref) ? "'" . $this->db->escape($this->ref) . "'" : "null") . ",";
1249
        $sql .= " ref_supplier=" . (isset($this->ref_supplier) ? "'" . $this->db->escape($this->ref_supplier) . "'" : "null") . ",";
1250
        $sql .= " ref_ext=" . (isset($this->ref_ext) ? "'" . $this->db->escape($this->ref_ext) . "'" : "null") . ",";
1251
        $sql .= " entity=" . (isset($this->entity) ? ((int) $this->entity) : "null") . ",";
1252
        $sql .= " type=" . (isset($this->type) ? ((int) $this->type) : "null") . ",";
1253
        $sql .= " subtype=" . ((int) $this->subtype) . ",";
1254
        $sql .= " fk_soc=" . (isset($this->socid) ? ((int) $this->socid) : "null") . ",";
1255
        $sql .= " datec=" . (dol_strlen($this->datec) != 0 ? "'" . $this->db->idate($this->datec) . "'" : 'null') . ",";
1256
        $sql .= " datef=" . (dol_strlen($this->date) != 0 ? "'" . $this->db->idate($this->date) . "'" : 'null') . ",";
1257
        if (dol_strlen($this->tms) != 0) {
1258
            $sql .= " tms=" . (dol_strlen($this->tms) != 0 ? "'" . $this->db->idate($this->tms) . "'" : 'null') . ",";
1259
        }
1260
        $sql .= " libelle=" . (isset($this->label) ? "'" . $this->db->escape($this->label) . "'" : "null") . ",";
1261
        $sql .= " paye=" . (isset($this->paid) ? ((int) $this->paid) : "0") . ",";
1262
        $sql .= " close_code=" . (isset($this->close_code) ? "'" . $this->db->escape($this->close_code) . "'" : "null") . ",";
1263
        $sql .= " close_note=" . (isset($this->close_note) ? "'" . $this->db->escape($this->close_note) . "'" : "null") . ",";
1264
        $sql .= " localtax1=" . (isset($this->total_localtax1) ? ((float) $this->total_localtax1) : "null") . ",";
1265
        $sql .= " localtax2=" . (isset($this->total_localtax2) ? ((float) $this->total_localtax2) : "null") . ",";
1266
        $sql .= " total_ht=" . (isset($this->total_ht) ? ((float) $this->total_ht) : "null") . ",";
1267
        $sql .= " total_tva=" . (isset($this->total_tva) ? ((float) $this->total_tva) : "null") . ",";
1268
        $sql .= " total_ttc=" . (isset($this->total_ttc) ? ((float) $this->total_ttc) : "null") . ",";
1269
        $sql .= " fk_statut=" . (isset($this->status) ? ((int) $this->status) : (isset($this->statut) ? ((int) $this->statut) : "null")) . ",";
1270
        $sql .= " fk_user_author=" . (isset($this->author) ? ((int) $this->author) : "null") . ",";
1271
        $sql .= " fk_user_valid=" . (isset($this->fk_user_valid) ? ((int) $this->fk_user_valid) : "null") . ",";
1272
        $sql .= " fk_facture_source=" . ($this->fk_facture_source ? ((int) $this->fk_facture_source) : "null") . ",";
1273
        $sql .= " vat_reverse_charge = " . ($this->vat_reverse_charge != '' ? ((int) $this->db->escape($this->vat_reverse_charge)) : 0) . ",";
1274
        $sql .= " fk_projet=" . (!empty($this->fk_project) ? ((int) $this->fk_project) : "null") . ",";
1275
        $sql .= " fk_cond_reglement=" . (isset($this->cond_reglement_id) ? ((int) $this->cond_reglement_id) : "null") . ",";
1276
        $sql .= " date_lim_reglement=" . (dol_strlen($this->date_echeance) != 0 ? "'" . $this->db->idate($this->date_echeance) . "'" : 'null') . ",";
1277
        $sql .= " note_private=" . (isset($this->note_private) ? "'" . $this->db->escape($this->note_private) . "'" : "null") . ",";
1278
        $sql .= " note_public=" . (isset($this->note_public) ? "'" . $this->db->escape($this->note_public) . "'" : "null") . ",";
1279
        $sql .= " model_pdf=" . (isset($this->model_pdf) ? "'" . $this->db->escape($this->model_pdf) . "'" : "null") . ",";
1280
        $sql .= " import_key=" . (isset($this->import_key) ? "'" . $this->db->escape($this->import_key) . "'" : "null");
1281
        $sql .= " WHERE rowid=" . ((int) $this->id);
1282
1283
        $this->db->begin();
1284
1285
        dol_syslog(get_class($this) . "::update", LOG_DEBUG);
1286
        $resql = $this->db->query($sql);
1287
1288
        if (!$resql) {
1289
            $error++;
1290
1291
            if ($this->db->errno() == 'DB_ERROR_RECORD_ALREADY_EXISTS') {
1292
                $this->errors[] = $langs->trans('ErrorRefAlreadyExists');
1293
            } else {
1294
                $this->errors[] = "Error " . $this->db->lasterror();
1295
            }
1296
        }
1297
1298
        if (!$error) {
1299
            $result = $this->insertExtraFields();
1300
            if ($result < 0) {
1301
                $error++;
1302
            }
1303
        }
1304
1305
        if (!$error) {
1306
            if (!$notrigger) {
1307
                // Call trigger
1308
                $result = $this->call_trigger('BILL_SUPPLIER_MODIFY', $user);
1309
                if ($result < 0) {
1310
                    $error++;
1311
                }
1312
                // End call triggers
1313
            }
1314
        }
1315
1316
        // Commit or rollback
1317
        if ($error) {
1318
            foreach ($this->errors as $errmsg) {
1319
                dol_syslog(get_class($this) . "::update " . $errmsg, LOG_ERR);
1320
                $this->error .= ($this->error ? ', ' . $errmsg : $errmsg);
1321
            }
1322
            $this->db->rollback();
1323
            return -1 * $error;
1324
        } else {
1325
            $this->db->commit();
1326
            return 1;
1327
        }
1328
    }
1329
1330
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1331
    /**
1332
     *    Add a discount line into an invoice (as an invoice line) using an existing absolute discount (Consume the discount)
1333
     *
1334
     *    @param     int    $idremise   Id of absolute discount
1335
     *    @return    int                >0 if OK, <0 if KO
1336
     */
1337
    public function insert_discount($idremise)
1338
    {
1339
		// phpcs:enable
1340
        global $conf, $langs;
1341
1342
        include_once DOL_DOCUMENT_ROOT . '/core/lib/price.lib.php';
1343
        include_once DOL_DOCUMENT_ROOT . '/core/class/discount.class.php';
1344
1345
        $this->db->begin();
1346
1347
        $remise = new DiscountAbsolute($this->db);
1348
        $result = $remise->fetch($idremise);
1349
1350
        if ($result > 0) {
1351
            if ($remise->fk_invoice_supplier) { // Protection against multiple submission
1352
                $this->error = $langs->trans("ErrorDiscountAlreadyUsed");
1353
                $this->db->rollback();
1354
                return -5;
1355
            }
1356
1357
            $facligne = new SupplierInvoiceLine($this->db);
1358
            $facligne->fk_facture_fourn = $this->id;
1359
            $facligne->fk_remise_except = $remise->id;
1360
            $facligne->desc = $remise->description; // Description ligne
1361
            $facligne->vat_src_code = $remise->vat_src_code;
1362
            $facligne->tva_tx = $remise->tva_tx;
1363
            $facligne->subprice = -$remise->amount_ht;
1364
            $facligne->fk_product = 0; // Id produit predefini
1365
            $facligne->product_type = 0;
1366
            $facligne->qty = 1;
1367
            $facligne->remise_percent = 0;
1368
            $facligne->rang = -1;
1369
            $facligne->info_bits = 2;
1370
1371
            if (getDolGlobalString('MAIN_ADD_LINE_AT_POSITION')) {
1372
                $facligne->rang = 1;
1373
                $linecount = count($this->lines);
1374
                for ($ii = 1; $ii <= $linecount; $ii++) {
1375
                    $this->updateRangOfLine($this->lines[$ii - 1]->id, $ii + 1);
1376
                }
1377
            }
1378
1379
            // Get buy/cost price of invoice that is source of discount
1380
            if ($remise->fk_invoice_supplier_source > 0) {
1381
                $srcinvoice = new FactureFournisseur($this->db);
1382
                $srcinvoice->fetch($remise->fk_invoice_supplier_source);
1383
                $totalcostpriceofinvoice = 0;
1384
                include_once DOL_DOCUMENT_ROOT . '/core/class/html.formmargin.class.php'; // TODO Move this into commonobject
1385
                $formmargin = new FormMargin($this->db);
1386
                $arraytmp = $formmargin->getMarginInfosArray($srcinvoice, false);
1387
                $facligne->pa_ht = $arraytmp['pa_total'];
1388
            }
1389
1390
            $facligne->total_ht  = -$remise->amount_ht;
1391
            $facligne->total_tva = -$remise->amount_tva;
1392
            $facligne->total_ttc = -$remise->amount_ttc;
1393
1394
            $facligne->multicurrency_subprice = -$remise->multicurrency_subprice;
1395
            $facligne->multicurrency_total_ht = -$remise->multicurrency_total_ht;
1396
            $facligne->multicurrency_total_tva = -$remise->multicurrency_total_tva;
1397
            $facligne->multicurrency_total_ttc = -$remise->multicurrency_total_ttc;
1398
1399
            $lineid = $facligne->insert();
1400
            if ($lineid > 0) {
1401
                $result = $this->update_price(1);
1402
                if ($result > 0) {
1403
                    // Create link between discount and invoice line
1404
                    $result = $remise->link_to_invoice($lineid, 0);
1405
                    if ($result < 0) {
1406
                        $this->error = $remise->error;
1407
                        $this->db->rollback();
1408
                        return -4;
1409
                    }
1410
1411
                    $this->db->commit();
1412
                    return 1;
1413
                } else {
1414
                    $this->error = $facligne->error;
1415
                    $this->db->rollback();
1416
                    return -1;
1417
                }
1418
            } else {
1419
                $this->error = $facligne->error;
1420
                $this->db->rollback();
1421
                return -2;
1422
            }
1423
        } else {
1424
            $this->db->rollback();
1425
            return -3;
1426
        }
1427
    }
1428
1429
1430
    /**
1431
     *  Delete invoice from database
1432
     *
1433
     *  @param      User    $user           User object
1434
     *  @param      int     $notrigger      1=Does not execute triggers, 0= execute triggers
1435
     *  @return     int                     Return integer <0 if KO, >0 if OK
1436
     */
1437
    public function delete(User $user, $notrigger = 0)
1438
    {
1439
        global $conf;
1440
1441
        $rowid = $this->id;
1442
1443
        dol_syslog("FactureFournisseur::delete rowid=" . $rowid, LOG_DEBUG);
1444
1445
        // TODO Test if there is at least on payment. If yes, refuse to delete.
1446
1447
        $error = 0;
1448
        $this->db->begin();
1449
1450
        if (!$error && !$notrigger) {
1451
            // Call trigger
1452
            $result = $this->call_trigger('BILL_SUPPLIER_DELETE', $user);
1453
            if ($result < 0) {
1454
                $this->db->rollback();
1455
                return -1;
1456
            }
1457
            // Fin appel triggers
1458
        }
1459
1460
        if (!$error) {
1461
            // If invoice was converted into a discount not yet consumed, we remove discount
1462
            $sql = 'DELETE FROM ' . MAIN_DB_PREFIX . 'societe_remise_except';
1463
            $sql .= ' WHERE fk_invoice_supplier_source = ' . ((int) $rowid);
1464
            $sql .= ' AND fk_invoice_supplier_line IS NULL';
1465
            $resql = $this->db->query($sql);
1466
1467
            // If invoice has consumned discounts
1468
            $this->fetch_lines();
1469
            $list_rowid_det = array();
1470
            foreach ($this->lines as $key => $invoiceline) {
1471
                $list_rowid_det[] = $invoiceline->id;
1472
            }
1473
1474
            // Consumned discounts are freed
1475
            if (count($list_rowid_det)) {
1476
                $sql = 'UPDATE ' . MAIN_DB_PREFIX . 'societe_remise_except';
1477
                $sql .= ' SET fk_invoice_supplier = NULL, fk_invoice_supplier_line = NULL';
1478
                $sql .= ' WHERE fk_invoice_supplier_line IN (' . $this->db->sanitize(implode(',', $list_rowid_det)) . ')';
1479
1480
                dol_syslog(get_class($this) . "::delete", LOG_DEBUG);
1481
                if (!$this->db->query($sql)) {
1482
                    $error++;
1483
                }
1484
            }
1485
        }
1486
1487
        if (!$error) {
1488
            $main = MAIN_DB_PREFIX . 'facture_fourn_det';
1489
            $ef = $main . "_extrafields";
1490
            $sqlef = "DELETE FROM $ef WHERE fk_object IN (SELECT rowid FROM " . $main . " WHERE fk_facture_fourn = " . ((int) $rowid) . ")";
1491
            $resqlef = $this->db->query($sqlef);
1492
            $sql = 'DELETE FROM ' . MAIN_DB_PREFIX . 'facture_fourn_det WHERE fk_facture_fourn = ' . ((int) $rowid);
1493
            dol_syslog(get_class($this) . "::delete", LOG_DEBUG);
1494
            $resql = $this->db->query($sql);
1495
            if ($resqlef && $resql) {
1496
                $sql = 'DELETE FROM ' . MAIN_DB_PREFIX . 'facture_fourn WHERE rowid = ' . ((int) $rowid);
1497
                dol_syslog(get_class($this) . "::delete", LOG_DEBUG);
1498
                $resql2 = $this->db->query($sql);
1499
                if (!$resql2) {
1500
                    $error++;
1501
                }
1502
            } else {
1503
                $error++;
1504
            }
1505
        }
1506
1507
        if (!$error) {
1508
            // Delete linked object
1509
            $res = $this->deleteObjectLinked();
1510
            if ($res < 0) {
1511
                $error++;
1512
            }
1513
        }
1514
1515
        if (!$error) {
1516
            // Delete record into ECM index (Note that delete is also done when deleting files with the dol_delete_dir_recursive
1517
            $this->deleteEcmFiles(0); // Deleting files physically is done later with the dol_delete_dir_recursive
1518
            $this->deleteEcmFiles(1); // Deleting files physically is done later with the dol_delete_dir_recursive
1519
1520
            // We remove directory
1521
            if ($conf->fournisseur->facture->dir_output) {
1522
                include_once DOL_DOCUMENT_ROOT . '/core/lib/files.lib.php';
1523
1524
                $ref = dol_sanitizeFileName($this->ref);
1525
                $dir = $conf->fournisseur->facture->dir_output . '/' . get_exdir($this->id, 2, 0, 0, $this, 'invoice_supplier') . $ref;
1526
                $file = $dir . "/" . $ref . ".pdf";
1527
                if (file_exists($file)) {
1528
                    if (!dol_delete_file($file, 0, 0, 0, $this)) { // For triggers
1529
                        $this->error = 'ErrorFailToDeleteFile';
1530
                        $error++;
1531
                    }
1532
                }
1533
                if (file_exists($dir)) {
1534
                    $res = @dol_delete_dir_recursive($dir);
1535
1536
                    if (!$res) {
1537
                        $this->error = 'ErrorFailToDeleteDir';
1538
                        $error++;
1539
                    }
1540
                }
1541
            }
1542
        }
1543
1544
        // Remove extrafields
1545
        if (!$error) {
1546
            $result = $this->deleteExtraFields();
1547
            if ($result < 0) {
1548
                $error++;
1549
                dol_syslog(get_class($this) . "::delete error -4 " . $this->error, LOG_ERR);
1550
            }
1551
        }
1552
1553
        if (!$error) {
1554
            dol_syslog(get_class($this) . "::delete $this->id by $user->id", LOG_DEBUG);
1555
            $this->db->commit();
1556
            return 1;
1557
        } else {
1558
            $this->error = $this->db->lasterror();
1559
            $this->db->rollback();
1560
            return -$error;
1561
        }
1562
    }
1563
1564
1565
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1566
    /**
1567
     *  Tag invoice as a paid invoice
1568
     *
1569
     *  @deprecated
1570
     *  @see setPaid()
1571
     *  @param  User    $user       Object user
1572
     *  @param  string  $close_code Code indicates whether the class has paid in full while payment is incomplete. Not implemented yet.
1573
     *  @param  string  $close_note Comment informs if the class has been paid while payment is incomplete. Not implemented yet.
1574
     *  @return int                 Return integer <0 si ko, >0 si ok
1575
     */
1576
    public function set_paid($user, $close_code = '', $close_note = '')
1577
    {
1578
		// phpcs:enable
1579
        dol_syslog(get_class($this) . "::set_paid is deprecated, use setPaid instead", LOG_NOTICE);
1580
        return $this->setPaid($user, $close_code, $close_note);
1581
    }
1582
1583
    /**
1584
     *  Tag invoice as a paid invoice
1585
     *
1586
     *  @param  User    $user       Object user
1587
     *  @param  string  $close_code Code indicates whether the class has paid in full while payment is incomplete. Not implemented yet.
1588
     *  @param  string  $close_note Comment informs if the class has been paid while payment is incomplete. Not implemented yet.
1589
     *  @return int<-1,1>           Return integer <0 si ko, >0 si ok
1590
     */
1591
    public function setPaid($user, $close_code = '', $close_note = '')
1592
    {
1593
        $error = 0;
1594
1595
        if ($this->paid != 1) {
1596
            $this->db->begin();
1597
1598
            $now = dol_now();
1599
1600
            dol_syslog("FactureFournisseur::setPaid", LOG_DEBUG);
1601
1602
            $sql = 'UPDATE ' . MAIN_DB_PREFIX . 'facture_fourn SET';
1603
            $sql .= ' fk_statut = ' . self::STATUS_CLOSED;
1604
            if (!$close_code) {
1605
                $sql .= ', paye=1';
1606
            }
1607
            if ($close_code) {
1608
                $sql .= ", close_code='" . $this->db->escape($close_code) . "'";
1609
            }
1610
            if ($close_note) {
1611
                $sql .= ", close_note='" . $this->db->escape($close_note) . "'";
1612
            }
1613
            $sql .= ', fk_user_closing = ' . ((int) $user->id);
1614
            $sql .= ", date_closing = '" . $this->db->idate($now) . "'";
1615
            $sql .= ' WHERE rowid = ' . ((int) $this->id);
1616
1617
            $resql = $this->db->query($sql);
1618
            if ($resql) {
1619
                // Call trigger
1620
                $result = $this->call_trigger('BILL_SUPPLIER_PAYED', $user);
1621
                if ($result < 0) {
1622
                    $error++;
1623
                }
1624
                // End call triggers
1625
            } else {
1626
                $error++;
1627
                $this->error = $this->db->error();
1628
                dol_print_error($this->db);
1629
            }
1630
1631
            if (!$error) {
1632
                $this->db->commit();
1633
                return 1;
1634
            } else {
1635
                $this->db->rollback();
1636
                return -1;
1637
            }
1638
        } else {
1639
            return 0;
1640
        }
1641
    }
1642
1643
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1644
    /**
1645
     *  Tag the invoice as not fully paid + trigger call BILL_UNPAYED
1646
     *  Function used when a direct debit payment is refused,
1647
     *  or when the invoice was canceled and reopened.
1648
     *
1649
     *  @deprecated
1650
     *  @see setUnpaid()
1651
     *  @param      User    $user       Object user that change status
1652
     *  @return     int                 Return integer <0 si ok, >0 si ok
1653
     */
1654
    public function set_unpaid($user)
1655
    {
1656
		// phpcs:enable
1657
        dol_syslog(get_class($this) . "::set_unpaid is deprecated, use setUnpaid instead", LOG_NOTICE);
1658
        return $this->setUnpaid($user);
1659
    }
1660
1661
    /**
1662
     *  Tag the invoice as not fully paid + trigger call BILL_UNPAYED
1663
     *  Function used when a direct debit payment is refused,
1664
     *  or when the invoice was canceled and reopened.
1665
     *
1666
     *  @param      User    $user       Object user that change status
1667
     *  @return     int                 Return integer <0 si ok, >0 si ok
1668
     */
1669
    public function setUnpaid($user)
1670
    {
1671
        $error = 0;
1672
1673
        $this->db->begin();
1674
1675
        $sql = 'UPDATE ' . MAIN_DB_PREFIX . 'facture_fourn';
1676
        $sql .= ' SET paye=0, fk_statut=' . self::STATUS_VALIDATED . ', close_code=null, close_note=null,';
1677
        $sql .= ' date_closing=null,';
1678
        $sql .= ' fk_user_closing=null';
1679
        $sql .= ' WHERE rowid = ' . ((int) $this->id);
1680
1681
        dol_syslog(get_class($this) . "::set_unpaid", LOG_DEBUG);
1682
        $resql = $this->db->query($sql);
1683
        if ($resql) {
1684
            // Call trigger
1685
            $result = $this->call_trigger('BILL_SUPPLIER_UNPAYED', $user);
1686
            if ($result < 0) {
1687
                $error++;
1688
            }
1689
            // End call triggers
1690
        } else {
1691
            $error++;
1692
            $this->error = $this->db->error();
1693
            dol_print_error($this->db);
1694
        }
1695
1696
        if (!$error) {
1697
            $this->db->commit();
1698
            return 1;
1699
        } else {
1700
            $this->db->rollback();
1701
            return -1;
1702
        }
1703
    }
1704
1705
    /**
1706
     *  Tag invoice as canceled, with no payment on it (example for replacement invoice or payment never received) + call trigger BILL_CANCEL
1707
     *  Warning, if option to decrease stock on invoice was set, this function does not change stock (it might be a cancel because
1708
     *  of no payment even if merchandises were sent).
1709
     *
1710
     *  @param  User    $user           Object user making change
1711
     *  @param  string  $close_code     Code of closing invoice (CLOSECODE_REPLACED, CLOSECODE_...)
1712
     *  @param  string  $close_note     Comment
1713
     *  @return int                     Return integer <0 if KO, >0 if OK
1714
     */
1715
    public function setCanceled($user, $close_code = '', $close_note = '')
1716
    {
1717
        dol_syslog(get_class($this) . "::setCanceled rowid=" . ((int) $this->id), LOG_DEBUG);
1718
1719
        $this->db->begin();
1720
1721
        $sql = 'UPDATE ' . MAIN_DB_PREFIX . 'facture_fourn SET';
1722
        $sql .= ' fk_statut=' . self::STATUS_ABANDONED;
1723
        if ($close_code) {
1724
            $sql .= ", close_code='" . $this->db->escape($close_code) . "'";
1725
        }
1726
        if ($close_note) {
1727
            $sql .= ", close_note='" . $this->db->escape($close_note) . "'";
1728
        }
1729
        $sql .= " WHERE rowid = " . ((int) $this->id);
1730
1731
        $resql = $this->db->query($sql);
1732
        if ($resql) {
1733
            // Bound discounts are deducted from the invoice
1734
            // as they have not been used since the invoice is abandoned.
1735
            $sql = 'UPDATE ' . MAIN_DB_PREFIX . 'societe_remise_except';
1736
            $sql .= ' SET fk_invoice_supplier = NULL';
1737
            $sql .= ' WHERE fk_invoice_supplier = ' . ((int) $this->id);
1738
1739
            $resql = $this->db->query($sql);
1740
            if ($resql) {
1741
                // Call trigger
1742
                $result = $this->call_trigger('BILL_SUPPLIER_CANCEL', $user);
1743
                if ($result < 0) {
1744
                    $this->db->rollback();
1745
                    return -1;
1746
                }
1747
                // End call triggers
1748
1749
                $this->db->commit();
1750
                return 1;
1751
            } else {
1752
                $this->error = $this->db->error() . " sql=" . $sql;
1753
                $this->db->rollback();
1754
                return -1;
1755
            }
1756
        } else {
1757
            $this->error = $this->db->error() . " sql=" . $sql;
1758
            $this->db->rollback();
1759
            return -2;
1760
        }
1761
    }
1762
1763
    /**
1764
     *  Tag invoice as validated + call trigger BILL_VALIDATE
1765
     *
1766
     *  @param  User    $user           Object user that validate
1767
     *  @param  string  $force_number   Reference to force on invoice
1768
     *  @param  int     $idwarehouse    Id of warehouse for stock change
1769
     *  @param  int     $notrigger      1=Does not execute triggers, 0= execute triggers
1770
     *  @return int                     Return integer <0 if KO, =0 if nothing to do, >0 if OK
1771
     */
1772
    public function validate($user, $force_number = '', $idwarehouse = 0, $notrigger = 0)
1773
    {
1774
        global $mysoc, $conf, $langs;
1775
1776
        require_once constant('DOL_DOCUMENT_ROOT') . '/core/lib/files.lib.php';
1777
1778
        $now = dol_now();
1779
1780
        $error = 0;
1781
        dol_syslog(get_class($this) . '::validate user=' . $user->id . ', force_number=' . $force_number . ', idwarehouse=' . $idwarehouse);
1782
1783
        // Force to have object complete for checks
1784
        $this->fetch_thirdparty();
1785
        $this->fetch_lines();
1786
1787
        // Check parameters
1788
        if ($this->status > self::STATUS_DRAFT) {   // This is to avoid to validate twice (avoid errors on logs and stock management)
1789
            dol_syslog(get_class($this) . "::validate no draft status", LOG_WARNING);
1790
            return 0;
1791
        }
1792
        if (preg_match('/^' . preg_quote($langs->trans("CopyOf") . ' ') . '/', $this->ref_supplier)) {
1793
            $langs->load("errors");
1794
            $this->error = $langs->trans("ErrorFieldFormat", $langs->transnoentities("RefSupplier")) . '. ' . $langs->trans('RemoveString', $langs->transnoentitiesnoconv("CopyOf"));
1795
            return -1;
1796
        }
1797
        if (count($this->lines) <= 0) {
1798
            $langs->load("errors");
1799
            $this->error = $langs->trans("ErrorObjectMustHaveLinesToBeValidated", $this->ref);
1800
            return -1;
1801
        }
1802
1803
        // Check for mandatory fields in thirdparty (defined into setup)
1804
        if (!empty($this->thirdparty) && is_object($this->thirdparty)) {
1805
            $array_to_check = array('IDPROF1', 'IDPROF2', 'IDPROF3', 'IDPROF4', 'IDPROF5', 'IDPROF6', 'EMAIL', 'ACCOUNTANCY_CODE_SUPPLIER');
1806
            foreach ($array_to_check as $key) {
1807
                $keymin = strtolower($key);
1808
                if ($keymin == 'accountancy_code_supplier') {
1809
                    $keymin = 'code_compta_fournisseur';
1810
                }
1811
                if (!property_exists($this->thirdparty, $keymin)) {
1812
                    continue;
1813
                }
1814
                $vallabel = $this->thirdparty->$keymin;
1815
1816
                $i = (int) preg_replace('/[^0-9]/', '', $key);
1817
                if ($i > 0) {
1818
                    if ($this->thirdparty->isACompany()) {
1819
                        // Check for mandatory prof id (but only if country is other than ours)
1820
                        if ($mysoc->country_id > 0 && $this->thirdparty->country_id == $mysoc->country_id) {
1821
                            $idprof_mandatory = 'SOCIETE_' . $key . '_INVOICE_MANDATORY';
1822
                            if (!$vallabel && getDolGlobalString($idprof_mandatory)) {
1823
                                $langs->load("errors");
1824
                                $this->error = $langs->trans('ErrorProdIdIsMandatory', $langs->transcountry('ProfId' . $i, $this->thirdparty->country_code)) . ' (' . $langs->trans("ForbiddenBySetupRules") . ') [' . $langs->trans('Company') . ' : ' . $this->thirdparty->name . ']';
1825
                                dol_syslog(__METHOD__ . ' ' . $this->error, LOG_ERR);
1826
                                return -1;
1827
                            }
1828
                        }
1829
                    }
1830
                } else {
1831
                    if ($key == 'EMAIL') {
1832
                        // Check for mandatory
1833
                        if (getDolGlobalString('SOCIETE_EMAIL_INVOICE_MANDATORY') && !isValidEmail($this->thirdparty->email)) {
1834
                            $langs->load("errors");
1835
                            $this->error = $langs->trans("ErrorBadEMail", $this->thirdparty->email) . ' (' . $langs->trans("ForbiddenBySetupRules") . ') [' . $langs->trans('Company') . ' : ' . $this->thirdparty->name . ']';
1836
                            dol_syslog(__METHOD__ . ' ' . $this->error, LOG_ERR);
1837
                            return -1;
1838
                        }
1839
                    } elseif ($key == 'ACCOUNTANCY_CODE_SUPPLIER') {
1840
                        // Check for mandatory
1841
                        if (getDolGlobalString('SOCIETE_ACCOUNTANCY_CODE_SUPPLIER_INVOICE_MANDATORY') && empty($this->thirdparty->code_compta_fournisseur)) {
1842
                            $langs->load("errors");
1843
                            $this->error = $langs->trans("ErrorAccountancyCodeSupplierIsMandatory", $this->thirdparty->name) . ' (' . $langs->trans("ForbiddenBySetupRules") . ')';
1844
                            dol_syslog(__METHOD__ . ' ' . $this->error, LOG_ERR);
1845
                            return -1;
1846
                        }
1847
                    }
1848
                }
1849
            }
1850
        }
1851
1852
        $this->db->begin();
1853
1854
        // Define new ref
1855
        if ($force_number) {
1856
            $num = $force_number;
1857
        } elseif (preg_match('/^[\(]?PROV/i', $this->ref) || empty($this->ref)) { // empty should not happened, but when it occurs, the test save life
1858
            $num = $this->getNextNumRef($this->thirdparty);
1859
        } else {
1860
            $num = $this->ref;
1861
        }
1862
        $this->newref = dol_sanitizeFileName($num);
1863
1864
        $sql = "UPDATE " . MAIN_DB_PREFIX . "facture_fourn";
1865
        $sql .= " SET ref='" . $this->db->escape($num) . "', fk_statut = 1, fk_user_valid = " . ((int) $user->id) . ", date_valid = '" . $this->db->idate($now) . "'";
1866
        $sql .= " WHERE rowid = " . ((int) $this->id);
1867
1868
        dol_syslog(get_class($this) . "::validate", LOG_DEBUG);
1869
        $resql = $this->db->query($sql);
1870
        if ($resql) {
1871
            // Si on incrémente le produit principal et ses composants à la validation de facture fournisseur
1872
            if (!$error && isModEnabled('stock') && getDolGlobalString('STOCK_CALCULATE_ON_SUPPLIER_BILL')) {
1873
                require_once constant('DOL_DOCUMENT_ROOT') . '/product/stock/class/mouvementstock.class.php';
1874
                $langs->load("agenda");
1875
1876
                $cpt = count($this->lines);
1877
                for ($i = 0; $i < $cpt; $i++) {
1878
                    if ($this->lines[$i]->fk_product > 0) {
1879
                        $mouvP = new MouvementStock($this->db);
0 ignored issues
show
Bug introduced by
The type Dolibarr\Code\Fourn\Classes\MouvementStock was not found. Did you mean MouvementStock? If so, make sure to prefix the type with \.
Loading history...
1880
                        $mouvP->origin = &$this;
1881
                        $mouvP->setOrigin($this->element, $this->id);
1882
                        // We increase stock for product
1883
                        $up_ht_disc = $this->lines[$i]->subprice;
1884
                        if (!empty($this->lines[$i]->remise_percent) && !getDolGlobalString('STOCK_EXCLUDE_DISCOUNT_FOR_PMP')) {
1885
                            $up_ht_disc = price2num($up_ht_disc * (100 - $this->lines[$i]->remise_percent) / 100, 'MU');
1886
                        }
1887
                        if ($this->type == FactureFournisseur::TYPE_CREDIT_NOTE) {
1888
                            $result = $mouvP->livraison($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, $up_ht_disc, $langs->trans("InvoiceValidatedInDolibarr", $num));
1889
                        } else {
1890
                            $result = $mouvP->reception($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, $up_ht_disc, $langs->trans("InvoiceValidatedInDolibarr", $num));
1891
                        }
1892
                        if ($result < 0) {
1893
                            $this->error = $mouvP->error;
1894
                            if (count($mouvP->errors)) {
1895
                                $this->errors = $mouvP->errors;
1896
                            }
1897
                            return -2;
1898
                        }
1899
                    }
1900
                }
1901
            }
1902
1903
            // Triggers call
1904
            if (!$error && empty($notrigger)) {
1905
                // Call trigger
1906
                $result = $this->call_trigger('BILL_SUPPLIER_VALIDATE', $user);
1907
                if ($result < 0) {
1908
                    $error++;
1909
                }
1910
                // End call triggers
1911
            }
1912
1913
            if (!$error) {
1914
                $this->oldref = $this->ref;
1915
1916
                // Rename directory if dir was a temporary ref
1917
                if (preg_match('/^[\(]?PROV/i', $this->ref)) {
1918
                    // Now we rename also files into index
1919
                    $sql = 'UPDATE ' . MAIN_DB_PREFIX . "ecm_files set filename = CONCAT('" . $this->db->escape($this->newref) . "', SUBSTR(filename, " . (strlen($this->ref) + 1) . ")), filepath = 'fournisseur/facture/" . get_exdir($this->id, 2, 0, 0, $this, 'invoice_supplier') . $this->db->escape($this->newref) . "'";
1920
                    $sql .= " WHERE filename LIKE '" . $this->db->escape($this->ref) . "%' AND filepath = 'fournisseur/facture/" . get_exdir($this->id, 2, 0, 0, $this, 'invoice_supplier') . $this->db->escape($this->ref) . "' and entity = " . $conf->entity;
1921
                    $resql = $this->db->query($sql);
1922
                    if (!$resql) {
1923
                        $error++;
1924
                        $this->error = $this->db->lasterror();
1925
                    }
1926
                    $sql = 'UPDATE ' . MAIN_DB_PREFIX . "ecm_files set filepath = 'fournisseur/facture/" . get_exdir($this->id, 2, 0, 0, $this, 'invoice_supplier') . $this->db->escape($this->newref) . "'";
1927
                    $sql .= " WHERE filepath = 'fournisseur/facture/" . get_exdir($this->id, 2, 0, 0, $this, 'invoice_supplier') . $this->db->escape($this->ref) . "' and entity = " . $conf->entity;
1928
                    $resql = $this->db->query($sql);
1929
                    if (!$resql) {
1930
                        $error++;
1931
                        $this->error = $this->db->lasterror();
1932
                    }
1933
1934
                    // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
1935
                    $oldref = dol_sanitizeFileName($this->ref);
1936
                    $dirsource = $conf->fournisseur->facture->dir_output . '/' . get_exdir($this->id, 2, 0, 0, $this, 'invoice_supplier') . $oldref;
1937
                    $dirdest = $conf->fournisseur->facture->dir_output . '/' . get_exdir($this->id, 2, 0, 0, $this, 'invoice_supplier') . $this->newref;
1938
                    if (!$error && file_exists($dirsource)) {
1939
                        dol_syslog(get_class($this) . "::validate rename dir " . $dirsource . " into " . $dirdest);
1940
1941
                        if (@rename($dirsource, $dirdest)) {
1942
                            dol_syslog("Rename ok");
1943
                            // Rename docs starting with $oldref with $this->newref
1944
                            $listoffiles = dol_dir_list($conf->fournisseur->facture->dir_output . '/' . get_exdir($this->id, 2, 0, 0, $this, 'invoice_supplier') . $this->newref, 'files', 1, '^' . preg_quote($oldref, '/'));
1945
                            foreach ($listoffiles as $fileentry) {
1946
                                $dirsource = $fileentry['name'];
1947
                                $dirdest = preg_replace('/^' . preg_quote($oldref, '/') . '/', $this->newref, $dirsource);
1948
                                $dirsource = $fileentry['path'] . '/' . $dirsource;
1949
                                $dirdest = $fileentry['path'] . '/' . $dirdest;
1950
                                @rename($dirsource, $dirdest);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for rename(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

1950
                                /** @scrutinizer ignore-unhandled */ @rename($dirsource, $dirdest);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
1951
                            }
1952
                        }
1953
                    }
1954
                }
1955
            }
1956
1957
            // Set new ref and define current statut
1958
            if (!$error) {
1959
                $this->ref = $this->newref;
1960
                $this->statut = self::STATUS_VALIDATED;
1961
                $this->status = self::STATUS_VALIDATED;
1962
                //$this->date_validation=$now; this is stored into log table
1963
            }
1964
1965
            if (!$error) {
1966
                $this->db->commit();
1967
                return 1;
1968
            } else {
1969
                $this->db->rollback();
1970
                return -1;
1971
            }
1972
        } else {
1973
            $this->error = $this->db->error();
1974
            $this->db->rollback();
1975
            return -1;
1976
        }
1977
    }
1978
1979
    /**
1980
     *  Set draft status
1981
     *
1982
     *  @param  User    $user           Object user that modify
1983
     *  @param  int     $idwarehouse    Id warehouse to use for stock change.
1984
     *  @param  int     $notrigger      1=Does not execute triggers, 0= execute triggers
1985
     *  @return int                     Return integer <0 if KO, >0 if OK
1986
     */
1987
    public function setDraft($user, $idwarehouse = -1, $notrigger = 0)
1988
    {
1989
        // phpcs:enable
1990
        global $conf, $langs;
1991
1992
        $error = 0;
1993
1994
        if ($this->status == self::STATUS_DRAFT) {
1995
            dol_syslog(__METHOD__ . " already draft status", LOG_WARNING);
1996
            return 0;
1997
        }
1998
1999
        dol_syslog(__METHOD__, LOG_DEBUG);
2000
2001
        $this->db->begin();
2002
2003
        $sql = "UPDATE " . MAIN_DB_PREFIX . "facture_fourn";
2004
        $sql .= " SET fk_statut = " . self::STATUS_DRAFT;
2005
        $sql .= " WHERE rowid = " . ((int) $this->id);
2006
2007
        $result = $this->db->query($sql);
2008
        if ($result) {
2009
            if (!$error) {
2010
                $this->oldcopy = clone $this;
2011
            }
2012
2013
            // Si on incremente le produit principal et ses composants a la validation de facture fournisseur, on decremente
2014
            if ($result >= 0 && isModEnabled('stock') && getDolGlobalString('STOCK_CALCULATE_ON_SUPPLIER_BILL')) {
2015
                require_once constant('DOL_DOCUMENT_ROOT') . '/product/stock/class/mouvementstock.class.php';
2016
                $langs->load("agenda");
2017
2018
                $cpt = count($this->lines);
2019
                for ($i = 0; $i < $cpt; $i++) {
2020
                    if ($this->lines[$i]->fk_product > 0) {
2021
                        $mouvP = new MouvementStock($this->db);
2022
                        $mouvP->origin = &$this;
2023
                        $mouvP->setOrigin($this->element, $this->id);
2024
                        // We increase stock for product
2025
                        if ($this->type == FactureFournisseur::TYPE_CREDIT_NOTE) {
2026
                            $result = $mouvP->reception($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, $this->lines[$i]->subprice, $langs->trans("InvoiceBackToDraftInDolibarr", $this->ref));
2027
                        } else {
2028
                            $result = $mouvP->livraison($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, $this->lines[$i]->subprice, $langs->trans("InvoiceBackToDraftInDolibarr", $this->ref));
2029
                        }
2030
                    }
2031
                }
2032
            }
2033
            // Triggers call
2034
            if (!$error && empty($notrigger)) {
2035
                // Call trigger
2036
                $result = $this->call_trigger('BILL_SUPPLIER_UNVALIDATE', $user);
2037
                if ($result < 0) {
2038
                    $error++;
2039
                }
2040
                // End call triggers
2041
            }
2042
            if ($error == 0) {
2043
                $this->db->commit();
2044
                return 1;
2045
            } else {
2046
                $this->db->rollback();
2047
                return -1;
2048
            }
2049
        } else {
2050
            $this->error = $this->db->error();
2051
            $this->db->rollback();
2052
            return -1;
2053
        }
2054
    }
2055
2056
2057
    /**
2058
     *  Adds an invoice line (associated with no predefined product/service)
2059
     *  The parameters are already supposed to be correct and with final values when calling
2060
     *  this method. Also, for the VAT rate, it must already have been defined by the caller by
2061
     *  by the get_default_tva method(vendor_company, buying company, idprod) and the desc must
2062
     *  already have the right value (the caller has to manage the multilanguage).
2063
     *
2064
     *  @param      string      $desc                   Description of the line
2065
     *  @param      double      $pu                     Unit price (HT or TTC according to price_base_type, > 0 even for credit note)
2066
     *  @param      double      $txtva                  Force Vat rate to use, -1 for auto.
2067
     *  @param      double      $txlocaltax1            LocalTax1 Rate
2068
     *  @param      double      $txlocaltax2            LocalTax2 Rate
2069
     *  @param      double      $qty                    Quantity
2070
     *  @param      int         $fk_product             Product/Service ID predefined
2071
     *  @param      double      $remise_percent         Percentage discount of the line
2072
     *  @param      int         $date_start             Service start date
2073
     *  @param      int         $date_end               Service expiry date
2074
     *  @param      int         $fk_code_ventilation    Accounting breakdown code
2075
     *  @param      int         $info_bits              Line type bits
2076
     *  @param      string      $price_base_type        HT or TTC
2077
     *  @param      int         $type                   Type of line (0=product, 1=service)
2078
     *  @param      int         $rang                   Position of line
2079
     *  @param      int         $notrigger              Disable triggers
2080
     *  @param      array       $array_options          extrafields array
2081
     *  @param      int|null    $fk_unit                Code of the unit to use. Null to use the default one
2082
     *  @param      int         $origin_id              id origin document
2083
     *  @param      double      $pu_devise              Amount in currency
2084
     *  @param      string      $ref_supplier           Supplier ref
2085
     *  @param      int         $special_code           Special code
2086
     *  @param      int         $fk_parent_line         Parent line id
2087
     *  @param      int         $fk_remise_except       Id discount used
2088
     *  @return     int                                 >0 if OK, <0 if KO
2089
     */
2090
    public function addline($desc, $pu, $txtva, $txlocaltax1, $txlocaltax2, $qty, $fk_product = 0, $remise_percent = 0, $date_start = 0, $date_end = 0, $fk_code_ventilation = 0, $info_bits = 0, $price_base_type = 'HT', $type = 0, $rang = -1, $notrigger = 0, $array_options = [], $fk_unit = null, $origin_id = 0, $pu_devise = 0, $ref_supplier = '', $special_code = 0, $fk_parent_line = 0, $fk_remise_except = 0)
2091
    {
2092
        global $langs, $mysoc;
2093
2094
        dol_syslog(get_class($this) . "::addline $desc,$pu,$qty,$txtva,$fk_product,$remise_percent,$date_start,$date_end,$fk_code_ventilation,$info_bits,$price_base_type,$type,$fk_unit,fk_remise_except=$fk_remise_except", LOG_DEBUG);
2095
        include_once DOL_DOCUMENT_ROOT . '/core/lib/price.lib.php';
2096
2097
        if ($this->status == self::STATUS_DRAFT) {
2098
            // Clean parameters
2099
            if (empty($remise_percent)) {
2100
                $remise_percent = 0;
2101
            }
2102
            if (empty($qty)) {
2103
                $qty = 0;
2104
            }
2105
            if (empty($info_bits)) {
2106
                $info_bits = 0;
2107
            }
2108
            if (empty($rang)) {
2109
                $rang = 0;
2110
            }
2111
            if (empty($fk_code_ventilation)) {
2112
                $fk_code_ventilation = 0;
2113
            }
2114
            if (empty($txtva)) {
2115
                $txtva = 0;
2116
            }
2117
            if (empty($txlocaltax1)) {
2118
                $txlocaltax1 = 0;
2119
            }
2120
            if (empty($txlocaltax2)) {
2121
                $txlocaltax2 = 0;
2122
            }
2123
2124
            $remise_percent = price2num($remise_percent);
2125
            $qty = price2num($qty);
2126
            $pu = price2num($pu);
2127
            if (!preg_match('/\((.*)\)/', (string) $txtva)) {
2128
                $txtva = price2num($txtva); // $txtva can have format '5,1' or '5.1' or '5.1(XXX)', we must clean only if '5,1'
2129
            }
2130
            $txlocaltax1 = price2num($txlocaltax1);
2131
            $txlocaltax2 = price2num($txlocaltax2);
2132
2133
            if ($date_start && $date_end && $date_start > $date_end) {
2134
                $langs->load("errors");
2135
                $this->error = $langs->trans('ErrorStartDateGreaterEnd');
2136
                return -1;
2137
            }
2138
2139
            $this->db->begin();
2140
2141
            if ($fk_product > 0) {
2142
                if (getDolGlobalString('SUPPLIER_INVOICE_WITH_PREDEFINED_PRICES_ONLY')) {
2143
                    // Check quantity is enough
2144
                    dol_syslog(get_class($this) . "::addline we check supplier prices fk_product=" . $fk_product . " qty=" . $qty . " ref_supplier=" . $ref_supplier);
2145
                    $prod = new ProductFournisseur($this->db);
2146
                    if ($prod->fetch($fk_product) > 0) {
2147
                        $product_type = $prod->type;
2148
                        $label = $prod->label;
2149
                        $fk_prod_fourn_price = 0;
2150
2151
                        // We use 'none' instead of $ref_supplier, because $ref_supplier may not exists anymore. So we will take the first supplier price ok.
2152
                        // If we want a dedicated supplier price, we must provide $fk_prod_fourn_price.
2153
                        $result = $prod->get_buyprice($fk_prod_fourn_price, $qty, $fk_product, 'none', ($this->fk_soc ? $this->fk_soc : $this->socid)); // Search on couple $fk_prod_fourn_price/$qty first, then on triplet $qty/$fk_product/$ref_supplier/$this->fk_soc
2154
                        if ($result > 0) {
2155
                            if (empty($pu)) {
2156
                                $pu = $prod->fourn_pu; // Unit price supplier price set by get_buyprice
2157
                            }
2158
                            $ref_supplier = $prod->ref_supplier; // Ref supplier price set by get_buyprice
2159
                            // is remise percent not keyed but present for the product we add it
2160
                            if ($remise_percent == 0 && $prod->remise_percent != 0) {
2161
                                $remise_percent = $prod->remise_percent;
2162
                            }
2163
                        }
2164
                        if ($result == 0) {                   // If result == 0, we failed to found the supplier reference price
2165
                            $langs->load("errors");
2166
                            $this->error = "Ref " . $prod->ref . " " . $langs->trans("ErrorQtyTooLowForThisSupplier");
2167
                            $this->db->rollback();
2168
                            dol_syslog(get_class($this) . "::addline we did not found supplier price, so we can't guess unit price");
2169
                            //$pu    = $prod->fourn_pu;     // We do not overwrite unit price
2170
                            //$ref   = $prod->ref_fourn;    // We do not overwrite ref supplier price
2171
                            return -1;
2172
                        }
2173
                        if ($result == -1) {
2174
                            $langs->load("errors");
2175
                            $this->error = "Ref " . $prod->ref . " " . $langs->trans("ErrorQtyTooLowForThisSupplier");
2176
                            $this->db->rollback();
2177
                            dol_syslog(get_class($this) . "::addline result=" . $result . " - " . $this->error, LOG_DEBUG);
2178
                            return -1;
2179
                        }
2180
                        if ($result < -1) {
2181
                            $this->error = $prod->error;
2182
                            $this->db->rollback();
2183
                            dol_syslog(get_class($this) . "::addline result=" . $result . " - " . $this->error, LOG_ERR);
2184
                            return -1;
2185
                        }
2186
                    } else {
2187
                        $this->error = $prod->error;
2188
                        $this->db->rollback();
2189
                        return -1;
2190
                    }
2191
                }
2192
            } else {
2193
                $product_type = $type;
2194
            }
2195
2196
            if (isModEnabled("multicurrency") && $pu_devise > 0) {
2197
                $pu = 0;
2198
            }
2199
2200
            $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $mysoc, $this->thirdparty);
2201
2202
            // Clean vat code
2203
            $reg = array();
2204
            $vat_src_code = '';
2205
            if (preg_match('/\((.*)\)/', $txtva, $reg)) {
2206
                $vat_src_code = $reg[1];
2207
                $txtva = preg_replace('/\s*\(.*\)/', '', $txtva); // Remove code into vatrate.
2208
            }
2209
2210
            // Calcul du total TTC et de la TVA pour la ligne a partir de
2211
            // qty, pu, remise_percent et txtva
2212
            // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
2213
            // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
2214
2215
            $tabprice = calcul_price_total($qty, $pu, $remise_percent, $txtva, $txlocaltax1, $txlocaltax2, 0, $price_base_type, $info_bits, $type, $this->thirdparty, $localtaxes_type, 100, $this->multicurrency_tx, $pu_devise);
2216
            $total_ht  = $tabprice[0];
2217
            $total_tva = $tabprice[1];
2218
            $total_ttc = $tabprice[2];
2219
            $total_localtax1 = $tabprice[9];
2220
            $total_localtax2 = $tabprice[10];
2221
            $pu_ht = $tabprice[3];
2222
2223
            // MultiCurrency
2224
            $multicurrency_total_ht  = $tabprice[16];
2225
            $multicurrency_total_tva = $tabprice[17];
2226
            $multicurrency_total_ttc = $tabprice[18];
2227
            $pu_ht_devise = $tabprice[19];
2228
2229
            // Check parameters
2230
            if ($type < 0) {
2231
                return -1;
2232
            }
2233
2234
            if ($rang < 0) {
2235
                $rangmax = $this->line_max();
2236
                $rang = $rangmax + 1;
2237
            }
2238
2239
            // Insert line
2240
            $supplierinvoiceline = new SupplierInvoiceLine($this->db);
2241
2242
            $supplierinvoiceline->context = $this->context;
2243
2244
            $supplierinvoiceline->fk_facture_fourn = $this->id;
2245
            //$supplierinvoiceline->label=$label;   // deprecated
2246
            $supplierinvoiceline->desc = $desc;
2247
            $supplierinvoiceline->ref_supplier = $ref_supplier;
2248
2249
            $supplierinvoiceline->qty = ($this->type == self::TYPE_CREDIT_NOTE ? abs((float) $qty) : $qty); // For credit note, quantity is always positive and unit price negative
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->type == self::TYP...bs((double)$qty) : $qty can also be of type string. However, the property $qty is declared as type double. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
2250
            $supplierinvoiceline->subprice = ($this->type == self::TYPE_CREDIT_NOTE ? -abs($pu_ht) : $pu_ht); // For credit note, unit price always negative, always positive otherwise
2251
2252
            $supplierinvoiceline->vat_src_code = $vat_src_code;
2253
            $supplierinvoiceline->tva_tx = $txtva;
0 ignored issues
show
Documentation Bug introduced by
It seems like $txtva can also be of type string. However, the property $tva_tx is declared as type double. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
2254
            $supplierinvoiceline->localtax1_tx = ($total_localtax1 ? $localtaxes_type[1] : 0);
2255
            $supplierinvoiceline->localtax2_tx = ($total_localtax2 ? $localtaxes_type[3] : 0);
2256
            $supplierinvoiceline->localtax1_type = empty($localtaxes_type[0]) ? 0 : $localtaxes_type[0];
2257
            $supplierinvoiceline->localtax2_type = empty($localtaxes_type[2]) ? 0 : $localtaxes_type[2];
2258
2259
            $supplierinvoiceline->total_ht = (($this->type == self::TYPE_CREDIT_NOTE || $qty < 0) ? -abs($total_ht) : $total_ht); // For credit note and if qty is negative, total is negative
2260
            $supplierinvoiceline->total_tva = (($this->type == self::TYPE_CREDIT_NOTE || $qty < 0) ? -abs($total_tva) : $total_tva); // For credit note and if qty is negative, total is negative
2261
            $supplierinvoiceline->total_localtax1 = (($this->type == self::TYPE_CREDIT_NOTE || $qty < 0) ? -abs($total_localtax1) : $total_localtax1); // For credit note and if qty is negative, total is negative
2262
            $supplierinvoiceline->total_localtax2 = (($this->type == self::TYPE_CREDIT_NOTE || $qty < 0) ? -abs($total_localtax2) : $total_localtax2); // For credit note and if qty is negative, total is negative
2263
            $supplierinvoiceline->total_ttc = (($this->type == self::TYPE_CREDIT_NOTE || $qty < 0) ? -abs($total_ttc) : $total_ttc); // For credit note and if qty is negative, total is negative
2264
2265
            $supplierinvoiceline->fk_product = $fk_product;
2266
            $supplierinvoiceline->product_type = $type;
2267
            $supplierinvoiceline->remise_percent = $remise_percent;
2268
            $supplierinvoiceline->date_start = $date_start;
2269
            $supplierinvoiceline->date_end = $date_end;
2270
            $supplierinvoiceline->fk_code_ventilation = $fk_code_ventilation;
2271
            $supplierinvoiceline->rang = $rang;
2272
            $supplierinvoiceline->info_bits = $info_bits;
2273
            $supplierinvoiceline->fk_remise_except = $fk_remise_except;
2274
2275
2276
            $supplierinvoiceline->special_code = (int) $special_code;
2277
            $supplierinvoiceline->fk_parent_line = $fk_parent_line;
2278
            $supplierinvoiceline->origin = $this->origin;
0 ignored issues
show
Deprecated Code introduced by
The property Dolibarr\Core\Base\CommonObject::$origin has been deprecated: Use $origin_type and $origin_id instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

2278
            /** @scrutinizer ignore-deprecated */ $supplierinvoiceline->origin = $this->origin;

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
2279
            $supplierinvoiceline->origin_id = $origin_id;
2280
            $supplierinvoiceline->fk_unit = $fk_unit;
2281
2282
            // Multicurrency
2283
            $supplierinvoiceline->fk_multicurrency = $this->fk_multicurrency;
2284
            $supplierinvoiceline->multicurrency_code = $this->multicurrency_code;
2285
            $supplierinvoiceline->multicurrency_subprice    = ($this->type == self::TYPE_CREDIT_NOTE ? -abs($pu_ht_devise) : $pu_ht_devise); // For credit note, unit price always negative, always positive otherwise
2286
2287
            $supplierinvoiceline->multicurrency_total_ht = (($this->type == self::TYPE_CREDIT_NOTE || $qty < 0) ? -abs($multicurrency_total_ht) : $multicurrency_total_ht); // For credit note and if qty is negative, total is negative
2288
            $supplierinvoiceline->multicurrency_total_tva = (($this->type == self::TYPE_CREDIT_NOTE || $qty < 0) ? -abs($multicurrency_total_tva) : $multicurrency_total_tva); // For credit note and if qty is negative, total is negative
2289
            $supplierinvoiceline->multicurrency_total_ttc = (($this->type == self::TYPE_CREDIT_NOTE || $qty < 0) ? -abs($multicurrency_total_ttc) : $multicurrency_total_ttc); // For credit note and if qty is negative, total is negative
2290
2291
            if (is_array($array_options) && count($array_options) > 0) {
2292
                $supplierinvoiceline->array_options = $array_options;
2293
            }
2294
2295
            $result = $supplierinvoiceline->insert($notrigger);
2296
            if ($result > 0) {
2297
                // Reorder if child line
2298
                if (!empty($fk_parent_line)) {
2299
                    $this->line_order(true, 'DESC');
2300
                } elseif ($rang > 0 && $rang <= count($this->lines)) { // Update all rank of all other lines
2301
                    $linecount = count($this->lines);
2302
                    for ($ii = $rang; $ii <= $linecount; $ii++) {
2303
                        $this->updateRangOfLine($this->lines[$ii - 1]->id, $ii + 1);
2304
                    }
2305
                }
2306
2307
                // Mise a jour information denormalisees au niveau de la facture meme
2308
                $result = $this->update_price(1, 'auto', 0, $this->thirdparty); // The addline method is designed to add line from user input so total calculation with update_price must be done using 'auto' mode.
2309
                if ($result > 0) {
2310
                    $this->db->commit();
2311
                    return $supplierinvoiceline->id;
2312
                } else {
2313
                    $this->error = $this->db->error();
2314
                    $this->db->rollback();
2315
                    return -1;
2316
                }
2317
            } else {
2318
                $this->error = $supplierinvoiceline->error;
2319
                $this->errors = $supplierinvoiceline->errors;
2320
                $this->db->rollback();
2321
                return -2;
2322
            }
2323
        } else {
2324
            return 0;
2325
        }
2326
    }
2327
2328
    /**
2329
     * Update a line detail into database
2330
     *
2331
     * @param       int         $id                 Id of line invoice
2332
     * @param       string      $desc               Description of line
2333
     * @param       double      $pu                 Prix unitaire (HT ou TTC selon price_base_type)
2334
     * @param       double      $vatrate            VAT Rate (Can be '8.5', '8.5 (ABC)')
2335
     * @param       double      $txlocaltax1        LocalTax1 Rate
2336
     * @param       double      $txlocaltax2        LocalTax2 Rate
2337
     * @param       double      $qty                Quantity
2338
     * @param       int         $idproduct          Id produit
2339
     * @param       string      $price_base_type    HT or TTC
2340
     * @param       int         $info_bits          Miscellaneous information of line
2341
     * @param       int         $type               Type of line (0=product, 1=service)
2342
     * @param       double      $remise_percent     Percentage discount of the line
2343
     * @param       int         $notrigger          Disable triggers
2344
     * @param       int|string  $date_start         Date start of service
2345
     * @param       int|string  $date_end           Date end of service
2346
     * @param       array       $array_options      extrafields array
2347
     * @param       int|null    $fk_unit            Code of the unit to use. Null to use the default one
2348
     * @param       double      $pu_devise          Amount in currency
2349
     * @param       string      $ref_supplier       Supplier ref
2350
     * @param       int         $rang               Line rank
2351
     * @return      int<-1,1>                       Return integer <0 if KO, >0 if OK
2352
     */
2353
    public function updateline($id, $desc, $pu, $vatrate, $txlocaltax1 = 0, $txlocaltax2 = 0, $qty = 1, $idproduct = 0, $price_base_type = 'HT', $info_bits = 0, $type = 0, $remise_percent = 0, $notrigger = 0, $date_start = '', $date_end = '', $array_options = [], $fk_unit = null, $pu_devise = 0, $ref_supplier = '', $rang = 0)
2354
    {
2355
        global $mysoc, $langs;
2356
2357
        dol_syslog(get_class($this) . "::updateline $id,$desc,$pu,$vatrate,$qty,$idproduct,$price_base_type,$info_bits,$type,$remise_percent,$notrigger,$date_start,$date_end,$fk_unit,$pu_devise,$ref_supplier", LOG_DEBUG);
2358
        include_once DOL_DOCUMENT_ROOT . '/core/lib/price.lib.php';
2359
2360
        $pu = price2num($pu);
2361
        $qty = price2num($qty);
2362
        $remise_percent = (float) price2num($remise_percent);
2363
        $pu_devise = price2num($pu_devise);
2364
2365
        // Check parameters
2366
        //if (! is_numeric($pu) || ! is_numeric($qty)) return -1;
2367
        if ($type < 0) {
2368
            return -1;
2369
        }
2370
2371
        if ($date_start && $date_end && $date_start > $date_end) {
2372
            $langs->load("errors");
2373
            $this->error = $langs->trans('ErrorStartDateGreaterEnd');
2374
            return -1;
2375
        }
2376
2377
        // Clean parameters
2378
        if (empty($vatrate)) {
2379
            $vatrate = 0;
2380
        }
2381
        if (empty($txlocaltax1)) {
2382
            $txlocaltax1 = 0;
2383
        }
2384
        if (empty($txlocaltax2)) {
2385
            $txlocaltax2 = 0;
2386
        }
2387
2388
        $txlocaltax1 = (float) price2num($txlocaltax1);
2389
        $txlocaltax2 = (float) price2num($txlocaltax2);
2390
2391
        // Calcul du total TTC et de la TVA pour la ligne a partir de
2392
        // qty, pu, remise_percent et txtva
2393
        // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
2394
        // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
2395
2396
        $localtaxes_type = getLocalTaxesFromRate($vatrate, 0, $mysoc, $this->thirdparty);
2397
2398
        $reg = array();
2399
2400
        // Clean vat code
2401
        $vat_src_code = '';
2402
        if (preg_match('/\((.*)\)/', (string) $vatrate, $reg)) {
2403
            $vat_src_code = $reg[1];
2404
            $vatrate = preg_replace('/\s*\(.*\)/', '', (string) $vatrate); // Remove code into vatrate.
2405
        }
2406
2407
        $tabprice = calcul_price_total($qty, $pu, $remise_percent, $vatrate, $txlocaltax1, $txlocaltax2, 0, $price_base_type, $info_bits, $type, $this->thirdparty, $localtaxes_type, 100, $this->multicurrency_tx, $pu_devise);
2408
        $total_ht  = $tabprice[0];
2409
        $total_tva = $tabprice[1];
2410
        $total_ttc = $tabprice[2];
2411
        $pu_ht  = $tabprice[3];
2412
        $pu_tva = $tabprice[4];
2413
        $pu_ttc = $tabprice[5];
2414
        $total_localtax1 = $tabprice[9];
2415
        $total_localtax2 = $tabprice[10];
2416
2417
        // MultiCurrency
2418
        $multicurrency_total_ht = $tabprice[16];
2419
        $multicurrency_total_tva = $tabprice[17];
2420
        $multicurrency_total_ttc = $tabprice[18];
2421
        $pu_ht_devise = $tabprice[19];
2422
2423
        if (empty($info_bits)) {
2424
            $info_bits = 0;
2425
        }
2426
2427
        //Fetch current line from the database and then clone the object and set it in $oldline property
2428
        $line = new SupplierInvoiceLine($this->db);
2429
        $line->fetch($id);
2430
        $line->fetch_optionals();
2431
2432
        $staticline = clone $line;
2433
2434
        if ($idproduct) {
2435
            $product = new Product($this->db);
2436
            $result = $product->fetch($idproduct);
2437
            $product_type = $product->type;
2438
        } else {
2439
            $idproduct = $staticline->fk_product;
2440
            $product_type = $type;
2441
        }
2442
2443
        $line->oldline = $staticline;
2444
        $line->context = $this->context;
2445
2446
        $line->description = $desc;
0 ignored issues
show
Deprecated Code introduced by
The property Dolibarr\Code\Fourn\Clas...voiceLine::$description has been deprecated: Use $desc ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

2446
        /** @scrutinizer ignore-deprecated */ $line->description = $desc;

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
2447
        $line->desc = $desc;
2448
2449
        $line->qty = ($this->type == self::TYPE_CREDIT_NOTE ? abs((float) $qty) : $qty); // For credit note, quantity is always positive and unit price negative
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->type == self::TYP...bs((double)$qty) : $qty can also be of type string. However, the property $qty is declared as type double. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
2450
        $line->subprice = ($this->type == self::TYPE_CREDIT_NOTE ? -abs($pu_ht) : $pu_ht); // For credit note, unit price always negative, always positive otherwise
2451
        $line->pu_ht = $line->subprice;  // deprecated
0 ignored issues
show
Deprecated Code introduced by
The property Dolibarr\Code\Fourn\Clas...lierInvoiceLine::$pu_ht has been deprecated: Use $subprice ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

2451
        /** @scrutinizer ignore-deprecated */ $line->pu_ht = $line->subprice;  // deprecated

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
2452
        $line->pu_ttc = ($this->type == self::TYPE_CREDIT_NOTE ? -abs($pu_ttc) : $pu_ttc); // For credit note, unit price always negative, always positive otherwise
2453
2454
        $line->remise_percent = $remise_percent;
2455
        $line->ref_supplier = $ref_supplier;
2456
2457
        $line->date_start = $date_start;
2458
        $line->date_end = $date_end;
2459
2460
        $line->vat_src_code = $vat_src_code;
2461
        $line->tva_tx = $vatrate;
0 ignored issues
show
Documentation Bug introduced by
It seems like $vatrate can also be of type string. However, the property $tva_tx is declared as type double. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
2462
        $line->localtax1_tx = $txlocaltax1;
2463
        $line->localtax2_tx = $txlocaltax2;
2464
        $line->localtax1_type = empty($localtaxes_type[0]) ? 0 : $localtaxes_type[0];
2465
        $line->localtax2_type = empty($localtaxes_type[2]) ? 0 : $localtaxes_type[2];
2466
2467
        $line->total_ht = (($this->type == self::TYPE_CREDIT_NOTE || $qty < 0) ? -abs($total_ht) : $total_ht);
2468
        $line->total_tva = (($this->type == self::TYPE_CREDIT_NOTE || $qty < 0) ? -abs($total_tva) : $total_tva);
2469
        $line->total_localtax1 = $total_localtax1;
2470
        $line->total_localtax2 = $total_localtax2;
2471
        $line->total_ttc = (($this->type == self::TYPE_CREDIT_NOTE || $qty < 0) ? -abs($total_ttc) : $total_ttc);
2472
2473
        $line->fk_product = $idproduct;
2474
        $line->product_type = $product_type;
2475
        $line->info_bits = $info_bits;
2476
        $line->fk_unit = $fk_unit;
2477
        $line->rang = $rang;
2478
2479
        if (is_array($array_options) && count($array_options) > 0) {
2480
            // We replace values in this->line->array_options only for entries defined into $array_options
2481
            foreach ($array_options as $key => $value) {
2482
                $line->array_options[$key] = $array_options[$key];
2483
            }
2484
        }
2485
2486
        // Multicurrency
2487
        $line->multicurrency_subprice = $pu_ht_devise;
2488
        $line->multicurrency_total_ht = $multicurrency_total_ht;
2489
        $line->multicurrency_total_tva  = $multicurrency_total_tva;
2490
        $line->multicurrency_total_ttc  = $multicurrency_total_ttc;
2491
2492
        $res = $line->update($notrigger);
2493
2494
        if ($res < 1) {
2495
            $this->errors[] = $line->error;
2496
        } else {
2497
            // Update total price into invoice record
2498
            $res = $this->update_price('1', 'auto', 0, $this->thirdparty);
2499
        }
2500
2501
        return $res;
2502
    }
2503
2504
    /**
2505
     *  Delete a detail line from database
2506
     *
2507
     *  @param  int     $rowid          Id of line to delete
2508
     *  @param  int     $notrigger      1=Does not execute triggers, 0= execute triggers
2509
     *  @return int                     Return integer <0 if KO, >0 if OK
2510
     */
2511
    public function deleteLine($rowid, $notrigger = 0)
2512
    {
2513
        if (!$rowid) {
2514
            $rowid = $this->id;
2515
        }
2516
2517
        $this->db->begin();
2518
2519
        // Free the discount linked to a line of invoice
2520
        $sql = 'UPDATE ' . MAIN_DB_PREFIX . 'societe_remise_except';
2521
        $sql .= ' SET fk_invoice_supplier_line = NULL';
2522
        $sql .= ' WHERE fk_invoice_supplier_line = ' . ((int) $rowid);
2523
2524
        dol_syslog(get_class($this) . "::deleteline", LOG_DEBUG);
2525
        $result = $this->db->query($sql);
2526
        if (!$result) {
2527
            $this->error = $this->db->error();
2528
            $this->db->rollback();
2529
            return -2;
2530
        }
2531
2532
        $line = new SupplierInvoiceLine($this->db);
2533
2534
        if ($line->fetch($rowid) < 1) {
2535
            return -1;
2536
        }
2537
2538
        $res = $line->delete($notrigger);
2539
2540
        if ($res < 1) {
2541
            $this->errors[] = $line->error;
2542
            $this->db->rollback();
2543
            return -3;
2544
        } else {
2545
            $res = $this->update_price(1);
2546
2547
            if ($res > 0) {
2548
                $this->db->commit();
2549
                return 1;
2550
            } else {
2551
                $this->db->rollback();
2552
                $this->error = $this->db->lasterror();
2553
                return -4;
2554
            }
2555
        }
2556
    }
2557
2558
2559
    /**
2560
     *  Loads the info order information into the invoice object
2561
     *
2562
     *  @param  int     $id         Id of the invoice to load
2563
     *  @return void
2564
     */
2565
    public function info($id)
2566
    {
2567
        $sql = 'SELECT c.rowid, datec, tms as datem, ';
2568
        $sql .= ' fk_user_author, fk_user_modif, fk_user_valid';
2569
        $sql .= ' FROM ' . MAIN_DB_PREFIX . 'facture_fourn as c';
2570
        $sql .= ' WHERE c.rowid = ' . ((int) $id);
2571
2572
        $result = $this->db->query($sql);
2573
        if ($result) {
2574
            if ($this->db->num_rows($result)) {
2575
                $obj = $this->db->fetch_object($result);
2576
2577
                $this->id = $obj->rowid;
2578
2579
                $this->user_creation_id = $obj->fk_user_author;
2580
                $this->user_validation_id = $obj->fk_user_valid;
2581
                $this->user_modification_id = $obj->fk_user_modif;
2582
                $this->date_creation     = $this->db->jdate($obj->datec);
2583
                $this->date_modification = $this->db->jdate($obj->datem);
2584
                //$this->date_validation   = $obj->datev; // This field is not available. Should be store into log table and using this function should be replaced with showing content of log (like for supplier orders)
2585
            }
2586
            $this->db->free($result);
2587
        } else {
2588
            dol_print_error($this->db);
2589
        }
2590
    }
2591
2592
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2593
    /**
2594
     *  Return list of replaceable invoices
2595
     *  Status valid or abandoned for other reason + not paid + no payment + not already replaced
2596
     *
2597
     *  @param      int     $socid      Thirdparty id
2598
     *  @return     array|int           Table of invoices ('id'=>id, 'ref'=>ref, 'status'=>status, 'paymentornot'=>0/1)
2599
     *                                  <0 if error
2600
     */
2601
    public function list_replacable_supplier_invoices($socid = 0)
2602
    {
2603
		// phpcs:enable
2604
        global $conf;
2605
2606
        $return = array();
2607
2608
        $sql = "SELECT f.rowid as rowid, f.ref, f.fk_statut,";
2609
        $sql .= " ff.rowid as rowidnext";
2610
        $sql .= " FROM " . MAIN_DB_PREFIX . "facture_fourn as f";
2611
        $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "paiementfourn_facturefourn as pf ON f.rowid = pf.fk_facturefourn";
2612
        $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "facture_fourn as ff ON f.rowid = ff.fk_facture_source";
2613
        $sql .= " WHERE (f.fk_statut = " . self::STATUS_VALIDATED . " OR (f.fk_statut = " . self::STATUS_ABANDONED . " AND f.close_code = '" . self::CLOSECODE_ABANDONED . "'))";
2614
        $sql .= " AND f.entity = " . $conf->entity;
2615
        $sql .= " AND f.paye = 0"; // Pas classee payee completement
2616
        $sql .= " AND pf.fk_paiementfourn IS NULL"; // Aucun paiement deja fait
2617
        $sql .= " AND ff.fk_statut IS NULL"; // Renvoi vrai si pas facture de replacement
2618
        if ($socid > 0) {
2619
            $sql .= " AND f.fk_soc = " . ((int) $socid);
2620
        }
2621
        $sql .= " ORDER BY f.ref";
2622
2623
        dol_syslog(get_class($this) . "::list_replacable_supplier_invoices", LOG_DEBUG);
2624
        $resql = $this->db->query($sql);
2625
        if ($resql) {
2626
            while ($obj = $this->db->fetch_object($resql)) {
2627
                $return[$obj->rowid] = array(
2628
                    'id' => $obj->rowid,
2629
                    'ref' => $obj->ref,
2630
                    'status' => $obj->fk_statut
2631
                );
2632
            }
2633
            //print_r($return);
2634
            return $return;
2635
        } else {
2636
            $this->error = $this->db->error();
2637
            return -1;
2638
        }
2639
    }
2640
2641
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2642
    /**
2643
     *  Return list of qualifying invoices for correction by credit note
2644
     *  Invoices that respect the following rules are returned:
2645
     *  (validated + payment in progress) or classified (paid in full or paid in part) + not already replaced + not already having
2646
     *
2647
     *  @param      int     $socid      Thirdparty id
2648
     *  @return     array|int           Table of invoices ($id => array('ref'=>,'paymentornot'=>,'status'=>,'paye'=>)
2649
     *                                  <0 if error
2650
     */
2651
    public function list_qualified_avoir_supplier_invoices($socid = 0)
2652
    {
2653
		// phpcs:enable
2654
        global $conf;
2655
2656
        $return = array();
2657
2658
        $sql = "SELECT f.rowid as rowid, f.ref, f.fk_statut, f.type, f.subtype, f.paye, pf.fk_paiementfourn";
2659
        $sql .= " FROM " . MAIN_DB_PREFIX . "facture_fourn as f";
2660
        $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "paiementfourn_facturefourn as pf ON f.rowid = pf.fk_facturefourn";
2661
        $sql .= " WHERE f.entity = " . $conf->entity;
2662
        $sql .= " AND f.fk_statut in (" . self::STATUS_VALIDATED . "," . self::STATUS_CLOSED . ")";
2663
        $sql .= " AND NOT EXISTS (SELECT rowid from " . MAIN_DB_PREFIX . "facture_fourn as ff WHERE f.rowid = ff.fk_facture_source";
2664
        $sql .= " AND ff.type=" . self::TYPE_REPLACEMENT . ")";
2665
        $sql .= " AND f.type != " . self::TYPE_CREDIT_NOTE; // Type non 2 si facture non avoir
2666
        if ($socid > 0) {
2667
            $sql .= " AND f.fk_soc = " . ((int) $socid);
2668
        }
2669
        $sql .= " ORDER BY f.ref";
2670
2671
        dol_syslog(get_class($this) . "::list_qualified_avoir_supplier_invoices", LOG_DEBUG);
2672
        $resql = $this->db->query($sql);
2673
        if ($resql) {
2674
            while ($obj = $this->db->fetch_object($resql)) {
2675
                $qualified = 0;
2676
                if ($obj->fk_statut == self::STATUS_VALIDATED) {
2677
                    $qualified = 1;
2678
                }
2679
                if ($obj->fk_statut == self::STATUS_CLOSED) {
2680
                    $qualified = 1;
2681
                }
2682
                if ($qualified) {
2683
                    $paymentornot = ($obj->fk_paiementfourn ? 1 : 0);
2684
                    $return[$obj->rowid] = array('ref' => $obj->ref, 'status' => $obj->fk_statut, 'type' => $obj->type, 'paye' => $obj->paye, 'paymentornot' => $paymentornot);
2685
                }
2686
            }
2687
2688
            return $return;
2689
        } else {
2690
            $this->error = $this->db->error();
2691
            return -1;
2692
        }
2693
    }
2694
2695
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2696
    /**
2697
     *  Load indicators for dashboard (this->nbtodo and this->nbtodolate)
2698
     *
2699
     *  @param      User    $user       Object user
2700
     *  @return WorkboardResponse|int Return integer <0 if KO, WorkboardResponse if OK
2701
     */
2702
    public function load_board($user)
2703
    {
2704
		// phpcs:enable
2705
        global $conf, $langs;
2706
2707
        $sql = 'SELECT ff.rowid, ff.date_lim_reglement as datefin, ff.fk_statut as status, ff.total_ht, ff.total_ttc';
2708
        $sql .= ' FROM ' . MAIN_DB_PREFIX . 'facture_fourn as ff';
2709
        if (!$user->hasRight("societe", "client", "voir") && !$user->socid) {
2710
            $sql .= " JOIN " . MAIN_DB_PREFIX . "societe_commerciaux as sc ON ff.fk_soc = sc.fk_soc AND sc.fk_user = " . ((int) $user->id);
2711
        }
2712
        $sql .= ' WHERE ff.paye = 0';
2713
        $sql .= " AND ff.fk_statut IN (" . self::STATUS_VALIDATED . ")";
2714
        $sql .= " AND ff.entity = " . $conf->entity;
2715
        if ($user->socid) {
2716
            $sql .= ' AND ff.fk_soc = ' . ((int) $user->socid);
2717
        }
2718
2719
        $resql = $this->db->query($sql);
2720
        if ($resql) {
2721
            $langs->load("bills");
2722
            $now = dol_now();
2723
2724
            $response = new WorkboardResponse();
2725
            $response->warning_delay = $conf->facture->fournisseur->warning_delay / 60 / 60 / 24;
2726
            $response->label = $langs->trans("SupplierBillsToPay");
2727
            $response->labelShort = $langs->trans("StatusToPay");
2728
2729
            $response->url = constant('BASE_URL') . '/fourn/facture/list.php?search_status=1&mainmenu=billing&leftmenu=suppliers_bills';
2730
            $response->img = img_object($langs->trans("Bills"), "bill");
2731
2732
            $facturestatic = new FactureFournisseur($this->db);
2733
2734
            while ($obj = $this->db->fetch_object($resql)) {
2735
                $facturestatic->date_echeance = $this->db->jdate($obj->datefin);
2736
                $facturestatic->statut = $obj->status;  // For backward compatibility
2737
                $facturestatic->status = $obj->status;
2738
2739
                $response->nbtodo++;
2740
                $response->total += $obj->total_ht;
2741
2742
                if ($facturestatic->hasDelay()) {
2743
                    $response->nbtodolate++;
2744
                    $response->url_late = constant('BASE_URL') . '/fourn/facture/list.php?search_option=late&mainmenu=billing&leftmenu=suppliers_bills';
2745
                }
2746
            }
2747
2748
            $this->db->free($resql);
2749
            return $response;
2750
        } else {
2751
            dol_print_error($this->db);
2752
            $this->error = $this->db->error();
2753
            return -1;
2754
        }
2755
    }
2756
2757
    /**
2758
     * getTooltipContentArray
2759
     *
2760
     * @param array{moretitle?:string} $params ex option, infologin
2761
     * @since v18
2762
     * @return array{picto:string,ref?:string,refsupplier?:string,label?:string,date?:string,date_echeance?:string,amountht?:string,total_ht?:string,totaltva?:string,amountlt1?:string,amountlt2?:string,amountrevenustamp?:string,totalttc?:string}
2763
     */
2764
    public function getTooltipContentArray($params)
2765
    {
2766
        global $conf, $langs, $mysoc;
2767
2768
        $langs->load('bills');
2769
2770
        $datas = [];
2771
        $moretitle = $params['moretitle'] ?? '';
2772
2773
        $picto = $this->picto;
2774
        if ($this->type == self::TYPE_REPLACEMENT) {
2775
            $picto .= 'r'; // Replacement invoice
2776
        }
2777
        if ($this->type == self::TYPE_CREDIT_NOTE) {
2778
            $picto .= 'a'; // Credit note
2779
        }
2780
        if ($this->type == self::TYPE_DEPOSIT) {
2781
            $picto .= 'd'; // Deposit invoice
2782
        }
2783
2784
        $datas['picto'] = img_picto('', $picto) . ' <u class="paddingrightonly">' . $langs->trans("SupplierInvoice") . '</u>';
2785
        if ($this->type == self::TYPE_REPLACEMENT) {
2786
            $datas['picto'] .= '<u class="paddingrightonly">' . $langs->transnoentitiesnoconv("InvoiceReplace") . '</u>';
2787
        } elseif ($this->type == self::TYPE_CREDIT_NOTE) {
2788
            $datas['picto'] .= '<u class="paddingrightonly">' . $langs->transnoentitiesnoconv("CreditNote") . '</u>';
2789
        } elseif ($this->type == self::TYPE_DEPOSIT) {
2790
            $datas['picto'] .= '<u class="paddingrightonly">' . $langs->transnoentitiesnoconv("Deposit") . '</u>';
2791
        }
2792
        if (isset($this->status)) {
2793
            $alreadypaid = -1;
2794
            if (isset($this->totalpaid)) {
2795
                $alreadypaid = $this->totalpaid;
2796
            }
2797
2798
            $datas['picto'] .= ' ' . $this->getLibStatut(5, $alreadypaid);
2799
        }
2800
        if ($moretitle) {
2801
            $datas['picto'] .= ' - ' . $moretitle;
2802
        }
2803
        if (!empty($this->ref)) {
2804
            $datas['ref'] = '<br><b>' . $langs->trans('Ref') . ':</b> ' . $this->ref;
2805
        }
2806
        if (!empty($this->ref_supplier)) {
2807
            $datas['refsupplier'] = '<br><b>' . $langs->trans('RefSupplier') . ':</b> ' . $this->ref_supplier;
2808
        }
2809
        if (!empty($this->label)) {
2810
            $datas['label'] = '<br><b>' . $langs->trans('Label') . ':</b> ' . $this->label;
2811
        }
2812
        if (!empty($this->date)) {
2813
            $datas['date'] = '<br><b>' . $langs->trans('Date') . ':</b> ' . dol_print_date($this->date, 'day');
2814
        }
2815
        if (!empty($this->date_echeance)) {
2816
            $datas['date_echeance'] = '<br><b>' . $langs->trans('DateDue') . ':</b> ' . dol_print_date($this->date_echeance, 'day');
2817
        }
2818
        if (!empty($this->total_ht)) {
2819
            $datas['amountht'] = '<br><b>' . $langs->trans('AmountHT') . ':</b> ' . price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency);
2820
        }
2821
        if (!empty($this->total_tva)) {
2822
            $datas['totaltva'] = '<br><b>' . $langs->trans('AmountVAT') . ':</b> ' . price($this->total_tva, 0, $langs, 0, -1, -1, $conf->currency);
2823
        }
2824
        if (!empty($this->total_localtax1) && $this->total_localtax1 != 0) {
2825
            // We keep test != 0 because $this->total_localtax1 can be '0.00000000'
2826
            $datas['amountlt1'] = '<br><b>' . $langs->transcountry('AmountLT1', $mysoc->country_code) . ':</b> ' . price($this->total_localtax1, 0, $langs, 0, -1, -1, $conf->currency);
2827
        }
2828
        if (!empty($this->total_localtax2) && $this->total_localtax2 != 0) {
2829
            $datas['amountlt2'] = '<br><b>' . $langs->transcountry('AmountLT2', $mysoc->country_code) . ':</b> ' . price($this->total_localtax2, 0, $langs, 0, -1, -1, $conf->currency);
2830
        }
2831
        if (!empty($this->revenuestamp)) {
2832
            $datas['amountrevenustamp'] = '<br><b>' . $langs->trans('RevenueStamp') . ':</b> ' . price($this->revenuestamp, 0, $langs, 0, -1, -1, $conf->currency);
2833
        }
2834
        if (!empty($this->total_ttc)) {
2835
            $datas['totalttc'] = '<br><b>' . $langs->trans('AmountTTC') . ':</b> ' . price($this->total_ttc, 0, $langs, 0, -1, -1, $conf->currency);
2836
        }
2837
        return $datas;
2838
    }
2839
2840
    /**
2841
     *  Return clicable name (with picto eventually)
2842
     *
2843
     *  @param      int     $withpicto                  0=No picto, 1=Include picto into link, 2=Only picto
2844
     *  @param      string  $option                     Where point the link
2845
     *  @param      int     $max                        Max length of shown ref
2846
     *  @param      int     $short                      1=Return just URL
2847
     *  @param      string  $moretitle                  Add more text to title tooltip
2848
     *  @param      int     $notooltip                  1=Disable tooltip
2849
     *  @param      int     $save_lastsearch_value      -1=Auto, 0=No save of lastsearch_values when clicking, 1=Save lastsearch_values whenclicking
2850
     *  @param      int     $addlinktonotes             Add link to show notes
2851
     *  @return     string                              String with URL
2852
     */
2853
    public function getNomUrl($withpicto = 0, $option = '', $max = 0, $short = 0, $moretitle = '', $notooltip = 0, $save_lastsearch_value = -1, $addlinktonotes = 0)
2854
    {
2855
        global $langs, $conf, $user, $hookmanager;
2856
2857
        $result = '';
2858
2859
        if ($option == 'withdraw') {
2860
            $url = constant('BASE_URL') . '/compta/facture/prelevement.php?facid=' . $this->id . '&type=bank-transfer';
2861
        } elseif ($option == 'document') {
2862
            $url = constant('BASE_URL') . '/fourn/facture/document.php?facid=' . $this->id;
2863
        } else {
2864
            $url = constant('BASE_URL') . '/fourn/facture/card.php?facid=' . $this->id;
2865
        }
2866
2867
        if ($short) {
2868
            return $url;
2869
        }
2870
2871
        if ($option !== 'nolink') {
2872
            // Add param to save lastsearch_values or not
2873
            $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
2874
            if ($save_lastsearch_value == -1 && isset($_SERVER["PHP_SELF"]) && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
2875
                $add_save_lastsearch_values = 1;
2876
            }
2877
            if ($add_save_lastsearch_values) {
2878
                $url .= '&save_lastsearch_values=1';
2879
            }
2880
        }
2881
2882
        $picto = $this->picto;
2883
        if ($this->type == self::TYPE_REPLACEMENT) {
2884
            $picto .= 'r'; // Replacement invoice
2885
        }
2886
        if ($this->type == self::TYPE_CREDIT_NOTE) {
2887
            $picto .= 'a'; // Credit note
2888
        }
2889
        if ($this->type == self::TYPE_DEPOSIT) {
2890
            $picto .= 'd'; // Deposit invoice
2891
        }
2892
2893
        $params = [
2894
            'id' => $this->id,
2895
            'objecttype' => $this->element,
2896
            'option' => $option,
2897
            'moretitle' => $moretitle,
2898
        ];
2899
        $classfortooltip = 'classfortooltip';
2900
        $dataparams = '';
2901
        if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
2902
            $classfortooltip = 'classforajaxtooltip';
2903
            $dataparams = ' data-params="' . dol_escape_htmltag(json_encode($params)) . '"';
2904
            $label = '';
2905
        } else {
2906
            $label = implode($this->getTooltipContentArray($params));
2907
        }
2908
2909
        $ref = $this->ref;
2910
        if (empty($ref)) {
2911
            $ref = $this->id;
2912
        }
2913
2914
        $linkclose = '';
2915
        if (empty($notooltip)) {
2916
            if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
2917
                $label = $langs->trans("ShowSupplierInvoice");
2918
                $linkclose .= ' alt="' . dol_escape_htmltag($label, 1) . '"';
2919
            }
2920
            $linkclose .= ($label ? ' title="' . dol_escape_htmltag($label, 1) . '"' : ' title="tocomplete"');
2921
            $linkclose .= $dataparams . ' class="' . $classfortooltip . '"';
2922
        }
2923
2924
        $linkstart = '<a href="' . $url . '"';
2925
        $linkstart .= $linkclose . '>';
2926
        $linkend = '</a>';
2927
2928
        $result .= $linkstart;
2929
        if ($withpicto) {
2930
            $result .= img_object(($notooltip ? '' : $label), ($picto ? $picto : 'generic'), ($notooltip ? (($withpicto != 2) ? 'class="paddingright"' : '') : 'class="' . (($withpicto != 2) ? 'paddingright ' : '') . '"'), 0, 0, $notooltip ? 0 : 1);
2931
        }
2932
        if ($withpicto != 2) {
2933
            $result .= ($max ? dol_trunc($ref, $max) : $ref);
2934
        }
2935
        $result .= $linkend;
2936
2937
        if ($addlinktonotes) {
2938
            $txttoshow = ($user->socid > 0 ? $this->note_public : $this->note_private);
2939
            if ($txttoshow) {
2940
                $notetoshow = $langs->trans("ViewPrivateNote") . ':<br>' . dol_string_nohtmltag($txttoshow, 1);
2941
                $result .= ' <span class="note inline-block">';
2942
                $result .= '<a href="' . constant('BASE_URL') . '/fourn/facture/note.php?id=' . $this->id . '" class="classfortooltip" title="' . dol_escape_htmltag($notetoshow) . '">';
2943
                $result .= img_picto('', 'note');
2944
                $result .= '</a>';
2945
                $result .= '</span>';
2946
            }
2947
        }
2948
        global $action;
2949
        $hookmanager->initHooks(array($this->element . 'dao'));
2950
        $parameters = array('id' => $this->id, 'getnomurl' => &$result);
2951
        $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
2952
        if ($reshook > 0) {
2953
            $result = $hookmanager->resPrint;
2954
        } else {
2955
            $result .= $hookmanager->resPrint;
2956
        }
2957
        return $result;
2958
    }
2959
2960
    /**
2961
     *      Return next reference of supplier invoice not already used (or last reference)
2962
     *      according to numbering module defined into constant INVOICE_SUPPLIER_ADDON_NUMBER
2963
     *
2964
     *      @param     Societe      $soc        Thirdparty object
0 ignored issues
show
Bug introduced by
The type Dolibarr\Code\Fourn\Classes\Societe was not found. Did you mean Societe? If so, make sure to prefix the type with \.
Loading history...
2965
     *      @param    string        $mode       'next' for next value or 'last' for last value
2966
     *      @return   string|-1                 Returns free reference or last reference, or '' or -1 if error
0 ignored issues
show
Documentation Bug introduced by
The doc comment string|-1 at position 2 could not be parsed: Unknown type name '-1' at position 2 in string|-1.
Loading history...
2967
     */
2968
    public function getNextNumRef($soc, $mode = 'next')
2969
    {
2970
        global $db, $langs, $conf;
2971
        $langs->load("orders");
2972
2973
        // Clean parameters (if not defined or using deprecated value)
2974
        if (!getDolGlobalString('INVOICE_SUPPLIER_ADDON_NUMBER')) {
2975
            $conf->global->INVOICE_SUPPLIER_ADDON_NUMBER = 'mod_facture_fournisseur_cactus';
2976
        }
2977
2978
        $mybool = false;
2979
2980
        $file = getDolGlobalString('INVOICE_SUPPLIER_ADDON_NUMBER') . ".php";
2981
        $classname = getDolGlobalString('INVOICE_SUPPLIER_ADDON_NUMBER');
2982
2983
        // Include file with class
2984
        $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
2985
2986
        foreach ($dirmodels as $reldir) {
2987
            $dir = dol_buildpath($reldir . "core/modules/supplier_invoice/");
2988
2989
            // Load file with numbering class (if found)
2990
            $mybool = ((bool) @include_once $dir . $file) || $mybool;
2991
        }
2992
2993
        if (!$mybool) {
2994
            dol_print_error(null, "Failed to include file " . $file);
2995
            return '';
2996
        }
2997
2998
        $obj = new $classname();
2999
        '@phan-var-force ModeleNumRefSuppliersInvoices $obj';
3000
        $numref = "";
3001
        $numref = $obj->getNextValue($soc, $this, $mode);
3002
3003
        if ($numref != "") {
3004
            return $numref;
3005
        } else {
3006
            $this->error = $obj->error;
3007
            return -1;
3008
        }
3009
    }
3010
3011
3012
    /**
3013
     *  Initialise an instance with random values.
3014
     *  Used to build previews or test instances.
3015
     *  id must be 0 if object instance is a specimen.
3016
     *
3017
     *  @param  string      $option     ''=Create a specimen invoice with lines, 'nolines'=No lines
3018
     *  @return int
3019
     */
3020
    public function initAsSpecimen($option = '')
3021
    {
3022
        global $langs, $conf;
3023
        include_once DOL_DOCUMENT_ROOT . '/compta/facture/class/facture.class.php';
3024
3025
        $now = dol_now();
3026
3027
        // Load array of products prodids
3028
        $num_prods = 0;
3029
        $prodids = array();
3030
3031
        $sql = "SELECT rowid";
3032
        $sql .= " FROM " . MAIN_DB_PREFIX . "product";
3033
        $sql .= " WHERE entity IN (" . getEntity('product') . ")";
3034
        $sql .= $this->db->plimit(100);
3035
3036
        $resql = $this->db->query($sql);
3037
        if ($resql) {
3038
            $num_prods = $this->db->num_rows($resql);
3039
            $i = 0;
3040
            while ($i < $num_prods) {
3041
                $i++;
3042
                $row = $this->db->fetch_row($resql);
3043
                $prodids[$i] = $row[0];
3044
            }
3045
        }
3046
3047
        // Initialise parameters
3048
        $this->id = 0;
3049
        $this->ref = 'SPECIMEN';
3050
        $this->ref_supplier = 'SUPPLIER_REF_SPECIMEN';
3051
        $this->specimen = 1;
3052
        $this->socid = 1;
3053
        $this->date = $now;
3054
        $this->date_lim_reglement = $this->date + 3600 * 24 * 30;
3055
        $this->cond_reglement_code = 'RECEP';
3056
        $this->mode_reglement_code = 'CHQ';
3057
3058
        $this->note_public = 'This is a comment (public)';
3059
        $this->note_private = 'This is a comment (private)';
3060
3061
        $this->multicurrency_tx = 1;
3062
        $this->multicurrency_code = $conf->currency;
3063
3064
        $xnbp = 0;
3065
        if (empty($option) || $option != 'nolines') {
3066
            // Lines
3067
            $nbp = 5;
3068
            while ($xnbp < $nbp) {
3069
                $line = new SupplierInvoiceLine($this->db);
3070
                $line->desc = $langs->trans("Description") . " " . $xnbp;
3071
                $line->qty = 1;
3072
                $line->subprice = 100;
3073
                $line->pu_ht = $line->subprice; // the canelle template use pu_ht and not subprice
0 ignored issues
show
Deprecated Code introduced by
The property Dolibarr\Code\Fourn\Clas...lierInvoiceLine::$pu_ht has been deprecated: Use $subprice ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

3073
                /** @scrutinizer ignore-deprecated */ $line->pu_ht = $line->subprice; // the canelle template use pu_ht and not subprice

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
3074
                $line->price = 100;
0 ignored issues
show
Bug Best Practice introduced by
The property price does not exist on Dolibarr\Code\Fourn\Classes\SupplierInvoiceLine. Since you implemented __set, consider adding a @property annotation.
Loading history...
3075
                $line->tva_tx = 19.6;
3076
                $line->localtax1_tx = 0;
3077
                $line->localtax2_tx = 0;
3078
                if ($xnbp == 2) {
3079
                    $line->total_ht = 50;
3080
                    $line->total_ttc = 59.8;
3081
                    $line->total_tva = 9.8;
3082
                    $line->remise_percent = 50;
3083
                } else {
3084
                    $line->total_ht = 100;
3085
                    $line->total_ttc = 119.6;
3086
                    $line->total_tva = 19.6;
3087
                    $line->remise_percent = 0;
3088
                }
3089
3090
                if ($num_prods > 0) {
3091
                    $prodid = mt_rand(1, $num_prods);
3092
                    $line->fk_product = $prodids[$prodid];
3093
                }
3094
                $line->product_type = 0;
3095
3096
                $this->lines[$xnbp] = $line;
3097
3098
                $this->total_ht       += $line->total_ht;
3099
                $this->total_tva      += $line->total_tva;
3100
                $this->total_ttc      += $line->total_ttc;
3101
3102
                $xnbp++;
3103
            }
3104
        }
3105
3106
        $this->total_ht = $xnbp * 100;
3107
        $this->total_tva = $xnbp * 19.6;
3108
        $this->total_ttc = $xnbp * 119.6;
3109
3110
        return 1;
3111
    }
3112
3113
    /**
3114
     *      Load indicators for dashboard (this->nbtodo and this->nbtodolate)
3115
     *
3116
     *      @return         int     Return integer <0 if KO, >0 if OK
3117
     */
3118
    public function loadStateBoard()
3119
    {
3120
        global $conf, $user;
3121
3122
        $this->nb = array();
3123
3124
        $clause = "WHERE";
3125
3126
        $sql = "SELECT count(f.rowid) as nb";
3127
        $sql .= " FROM " . MAIN_DB_PREFIX . "facture_fourn as f";
3128
        $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "societe as s ON f.fk_soc = s.rowid";
3129
        if (!$user->hasRight("societe", "client", "voir") && !$user->socid) {
3130
            $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "societe_commerciaux as sc ON s.rowid = sc.fk_soc";
3131
            $sql .= " WHERE sc.fk_user = " . ((int) $user->id);
3132
            $clause = "AND";
3133
        }
3134
        $sql .= " " . $clause . " f.entity = " . $conf->entity;
3135
3136
        $resql = $this->db->query($sql);
3137
        if ($resql) {
3138
            while ($obj = $this->db->fetch_object($resql)) {
3139
                $this->nb["supplier_invoices"] = $obj->nb;
3140
            }
3141
            $this->db->free($resql);
3142
            return 1;
3143
        } else {
3144
            dol_print_error($this->db);
3145
            $this->error = $this->db->error();
3146
            return -1;
3147
        }
3148
    }
3149
3150
    /**
3151
     *  Load an object from its id and create a new one in database
3152
     *
3153
     *  @param      User    $user           User that clone
3154
     *  @param      int     $fromid         Id of object to clone
3155
     *  @param      int     $invertdetail   Reverse sign of amounts for lines
3156
     *  @return     int                     New id of clone
3157
     */
3158
    public function createFromClone(User $user, $fromid, $invertdetail = 0)
3159
    {
3160
        global $conf, $langs;
3161
3162
        $error = 0;
3163
3164
        $object = new FactureFournisseur($this->db);
3165
3166
        $this->db->begin();
3167
3168
        // Load source object
3169
        $object->fetch($fromid);
3170
        $object->id = 0;
3171
        $object->statut = self::STATUS_DRAFT;   // For backward compatibility
3172
        $object->status = self::STATUS_DRAFT;
3173
3174
        $object->fetch_thirdparty(); // We need it to recalculate VAT localtaxes according to main sale taxes and vendor
3175
3176
        // Clear fields
3177
        $object->ref_supplier       = (empty($this->ref_supplier) ? $langs->trans("CopyOf") . ' ' . $object->ref_supplier : $this->ref_supplier);
3178
        $object->author             = $user->id;  // FIXME? user_validation_id is replacement for author
0 ignored issues
show
Deprecated Code introduced by
The property Dolibarr\Code\Fourn\Clas...ureFournisseur::$author has been deprecated: Use $user_creation_id ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

3178
        /** @scrutinizer ignore-deprecated */ $object->author             = $user->id;  // FIXME? user_validation_id is replacement for author

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
3179
        $object->user_validation_id = 0;  // FIXME?  user_validation_id is replacement for author
3180
        $object->fk_facture_source  = 0;
3181
        $object->date_creation      = '';
3182
        $object->date_validation    = '';
3183
        $object->date               = (empty($this->date) ? dol_now() : $this->date);
3184
        $object->ref_client         = '';
3185
        $object->close_code         = '';
3186
        $object->close_note         = '';
3187
        if (getDolGlobalInt('MAIN_DONT_KEEP_NOTE_ON_CLONING') == 1) {
3188
            $object->note_private = '';
3189
            $object->note_public = '';
3190
        }
3191
3192
        $object->date_echeance = $object->calculate_date_lim_reglement();
3193
3194
        // Loop on each line of new invoice
3195
        foreach ($object->lines as $i => $line) {
3196
            if (isset($object->lines[$i]->info_bits) && ($object->lines[$i]->info_bits & 0x02) == 0x02) {   // We do not clone line of discounts
3197
                unset($object->lines[$i]);
3198
            }
3199
        }
3200
3201
        // Create clone
3202
        $object->context['createfromclone'] = 'createfromclone';
3203
        $result = $object->create($user);
3204
3205
        // Other options
3206
        if ($result < 0) {
3207
            $this->error = $object->error;
3208
            $this->errors = $object->errors;
3209
            $error++;
3210
        }
3211
3212
        if (!$error) {
3213
        }
3214
3215
        unset($object->context['createfromclone']);
3216
3217
        // End
3218
        if (!$error) {
3219
            $this->db->commit();
3220
            return $object->id;
3221
        } else {
3222
            $this->db->rollback();
3223
            return -1;
3224
        }
3225
    }
3226
3227
    /**
3228
     *  Create a document onto disk according to template model.
3229
     *
3230
     *  @param      string      $modele         Force template to use ('' to not force)
3231
     *  @param      Translate   $outputlangs    Object lang a utiliser pour traduction
3232
     *  @param      int         $hidedetails    Hide details of lines
3233
     *  @param      int         $hidedesc       Hide description
3234
     *  @param      int         $hideref        Hide ref
3235
     *  @param      ?array<string,mixed>    $moreparams     Array to provide more information
3236
     *  @return     int<-1,1>                   Return integer <0 if KO, 0 if nothing done, >0 if OK
3237
     */
3238
    public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
3239
    {
3240
        global $langs;
3241
3242
        $langs->load("suppliers");
3243
        $outputlangs->load("products");
3244
3245
        // Set the model on the model name to use
3246
        if (empty($modele)) {
3247
            if (getDolGlobalString('INVOICE_SUPPLIER_ADDON_PDF')) {
3248
                $modele = getDolGlobalString('INVOICE_SUPPLIER_ADDON_PDF');
3249
            } else {
3250
                $modele = ''; // No default value. For supplier invoice, we allow to disable all PDF generation
3251
            }
3252
        }
3253
3254
        if (empty($modele)) {
3255
            return 0;
3256
        } else {
3257
            $modelpath = "core/modules/supplier_invoice/doc/";
3258
3259
            return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
3260
        }
3261
    }
3262
3263
    /**
3264
     * Returns the rights used for this class
3265
     * @return int
3266
     */
3267
    public function getRights()
3268
    {
3269
        global $user;
3270
3271
        return $user->hasRight("fournisseur", "facture");
3272
    }
3273
3274
    /**
3275
     * Function used to replace a thirdparty id with another one.
3276
     *
3277
     * @param   DoliDB  $dbs        Database handler, because function is static we name it $dbs not $db to avoid breaking coding test
3278
     * @param   int     $origin_id  Old thirdparty id
3279
     * @param   int     $dest_id    New thirdparty id
3280
     * @return  bool
3281
     */
3282
    public static function replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
3283
    {
3284
        $tables = array(
3285
            'facture_fourn'
3286
        );
3287
3288
        return CommonObject::commonReplaceThirdparty($dbs, $origin_id, $dest_id, $tables);
3289
    }
3290
3291
    /**
3292
     * Function used to replace a product id with another one.
3293
     *
3294
     * @param DoliDB $db Database handler
3295
     * @param int $origin_id Old product id
3296
     * @param int $dest_id New product id
3297
     * @return bool
3298
     */
3299
    public static function replaceProduct(DoliDB $db, $origin_id, $dest_id)
3300
    {
3301
        $tables = array(
3302
            'facture_fourn_det'
3303
        );
3304
3305
        return CommonObject::commonReplaceProduct($db, $origin_id, $dest_id, $tables);
3306
    }
3307
3308
    /**
3309
     * Is the payment of the supplier invoice having a delay?
3310
     *
3311
     * @return bool
3312
     */
3313
    public function hasDelay()
3314
    {
3315
        global $conf;
3316
3317
        $now = dol_now();
3318
3319
        if (!$this->date_echeance) {
3320
            return false;
3321
        }
3322
3323
        $status = isset($this->status) ? $this->status : $this->statut;
3324
3325
        return ($status == self::STATUS_VALIDATED) && ($this->date_echeance < ($now - $conf->facture->fournisseur->warning_delay));
3326
    }
3327
3328
    /**
3329
     * Is credit note used
3330
     *
3331
     * @return bool
3332
     */
3333
    public function isCreditNoteUsed()
3334
    {
3335
        $isUsed = false;
3336
3337
        $sql = "SELECT fk_invoice_supplier FROM " . MAIN_DB_PREFIX . "societe_remise_except WHERE fk_invoice_supplier_source = " . ((int) $this->id);
3338
        $resql = $this->db->query($sql);
3339
        if (!empty($resql)) {
3340
            $obj = $this->db->fetch_object($resql);
3341
            if (!empty($obj->fk_invoice_supplier)) {
3342
                $isUsed = true;
3343
            }
3344
        }
3345
3346
        return $isUsed;
3347
    }
3348
    /**
3349
     *  Return clicable link of object (with eventually picto)
3350
     *
3351
     *  @param      string      $option                 Where point the link (0=> main card, 1,2 => shipment, 'nolink'=>No link)
3352
     *  @param      ?array{selected?:int<0,1>}  $arraydata  Array of data
0 ignored issues
show
Documentation Bug introduced by
The doc comment ?array{selected?:int<0,1>} at position 4 could not be parsed: Expected '}' at position 4, but found 'int'.
Loading history...
3353
     *  @return     string                              HTML Code for Kanban thumb.
3354
     */
3355
    public function getKanbanView($option = '', $arraydata = null)
3356
    {
3357
        global $langs;
3358
3359
        $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
3360
3361
        $picto = $this->picto;
3362
        if ($this->type == self::TYPE_REPLACEMENT) {
3363
            $picto .= 'r'; // Replacement invoice
3364
        }
3365
        if ($this->type == self::TYPE_CREDIT_NOTE) {
3366
            $picto .= 'a'; // Credit note
3367
        }
3368
        if ($this->type == self::TYPE_DEPOSIT) {
3369
            $picto .= 'd'; // Deposit invoice
3370
        }
3371
3372
        $return = '<div class="box-flex-item box-flex-grow-zero">';
3373
        $return .= '<div class="info-box info-box-sm">';
3374
        $return .= '<span class="info-box-icon bg-infobox-action">';
3375
        $return .= img_picto('', $picto);
3376
        $return .= '</span>';
3377
        $return .= '<div class="info-box-content">';
3378
        $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">' . (method_exists($this, 'getNomUrl') ? $this->getNomUrl(1) : $this->ref) . '</span>';
3379
        if ($selected >= 0) {
3380
            $return .= '<input id="cb' . $this->id . '" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="' . $this->id . '"' . ($selected ? ' checked="checked"' : '') . '>';
3381
        }
3382
        if (!empty($arraydata['thirdparty'])) {
3383
            $return .= '<br><span class="info-box-label">' . $arraydata['thirdparty'] . '</span>';
3384
        }
3385
        if (property_exists($this, 'date')) {
3386
            $return .= '<br><span class="info-box-label">' . dol_print_date($this->date, 'day') . '</span>';
3387
        }
3388
        if (property_exists($this, 'total_ht')) {
3389
            $return .= ' &nbsp; <span class="info-box-label amount" title="' . dol_escape_htmltag($langs->trans("AmountHT")) . '">' . price($this->total_ht);
3390
            $return .= ' ' . $langs->trans("HT");
3391
            $return .= '</span>';
3392
        }
3393
        if (method_exists($this, 'getLibStatut')) {
3394
            $alreadypaid = (empty($arraydata['alreadypaid']) ? 0 : $arraydata['alreadypaid']);
3395
            $return .= '<br><div class="info-box-status">' . $this->getLibStatut(3, $alreadypaid) . '</div>';
3396
        }
3397
        $return .= '</div>';
3398
        $return .= '</div>';
3399
        $return .= '</div>';
3400
        return $return;
3401
    }
3402
3403
    /**
3404
     *  Change the option VAT reverse charge
3405
     *
3406
     *  @param      int     $vatreversecharge   0 = Off, 1 = On
3407
     *  @return     int                         1 if OK, 0 if KO
3408
     */
3409
    public function setVATReverseCharge($vatreversecharge)
3410
    {
3411
        if (!$this->table_element) {
3412
            dol_syslog(get_class($this) . "::setVATReverseCharge was called on object with property table_element not defined", LOG_ERR);
3413
            return -1;
3414
        }
3415
3416
        dol_syslog(get_class($this) . '::setVATReverseCharge(' . $vatreversecharge . ')');
3417
3418
        $sql = "UPDATE " . MAIN_DB_PREFIX . $this->table_element;
3419
        $sql .= " SET vat_reverse_charge = " . ((int) $vatreversecharge);
3420
        $sql .= " WHERE rowid=" . ((int) $this->id);
3421
3422
        if ($this->db->query($sql)) {
3423
            $this->vat_reverse_charge = ($vatreversecharge == 0) ? 0 : 1;
3424
            return 1;
3425
        } else {
3426
            dol_syslog(get_class($this) . '::setVATReverseCharge Error ', LOG_DEBUG);
3427
            $this->error = $this->db->error();
3428
            return 0;
3429
        }
3430
    }
3431
3432
    /**
3433
     *  Send reminders by emails for supplier invoices validated that are due.
3434
     *  CAN BE A CRON TASK
3435
     *
3436
     *  @param  int         $nbdays             Delay before due date (or after if delay is negative)
3437
     *  @param  string      $paymentmode        '' or 'all' by default (no filter), or 'LIQ', 'CHQ', CB', ...
3438
     *  @param  int|string  $template           Name (or id) of email template (Must be a template of type 'invoice_supplier_send')
3439
     *  @param  string      $datetouse          'duedate' (default) or 'invoicedate'
3440
     *  @param  string      $forcerecipient     Force email of recipient (for example to send the email to an accountant supervisor instead of the customer)
3441
     *  @return int                             0 if OK, <>0 if KO (this function is used also by cron so only 0 is OK)
3442
     */
3443
    public function sendEmailsRemindersOnSupplierInvoiceDueDate($nbdays = 0, $paymentmode = 'all', $template = '', $datetouse = 'duedate', $forcerecipient = '')
3444
    {
3445
        global $conf, $langs, $user;
3446
3447
        $this->output = '';
3448
        $this->error = '';
3449
        $nbMailSend = 0;
3450
3451
        $error = 0;
3452
        $errorsMsg = array();
3453
3454
        $langs->load('bills');
3455
3456
        if (!isModEnabled(empty($conf->global->MAIN_USE_NEW_SUPPLIERMOD) ? 'fournisseur' : 'supplier_invoice')) {   // Should not happen. If module disabled, cron job should not be visible.
3457
            $this->output .= $langs->trans('ModuleNotEnabled', $langs->transnoentitiesnoconv('Suppliers'));
3458
            return 0;
3459
        }
3460
        if (!in_array($datetouse, array('duedate', 'invoicedate'))) {
3461
            $this->output .= 'Bad value for parameter datetouse. Must be "duedate" or "invoicedate"';
3462
            return 0;
3463
        }
3464
3465
        require_once constant('DOL_DOCUMENT_ROOT') . '/core/lib/date.lib.php';
3466
        require_once constant('DOL_DOCUMENT_ROOT') . '/core/class/html.formmail.class.php';
3467
        require_once constant('DOL_DOCUMENT_ROOT') . '/core/class/CMailFile.class.php';
3468
        $formmail = new FormMail($this->db);
3469
3470
        $now = dol_now();
3471
        $tmpidate = dol_get_first_hour(dol_time_plus_duree($now, $nbdays, 'd'), 'gmt');
3472
3473
        $tmpinvoice = new FactureFournisseur($this->db);
3474
3475
        dol_syslog(__METHOD__ . " start", LOG_INFO);
3476
3477
        // Select all action comm reminder
3478
        $sql = "SELECT rowid as id FROM " . MAIN_DB_PREFIX . "facture_fourn as f";
3479
        if (!empty($paymentmode) && $paymentmode != 'all') {
3480
            $sql .= ", " . MAIN_DB_PREFIX . "c_paiement as cp";
3481
        }
3482
        $sql .= " WHERE f.paye = 0";    // Only unpaid
3483
        $sql .= " AND f.fk_statut = " . self::STATUS_VALIDATED;   // Only validated status
3484
        if ($datetouse == 'invoicedate') {
3485
            $sql .= " AND f.datef = '" . $this->db->idate($tmpidate, 'gmt') . "'";
3486
        } else {
3487
            $sql .= " AND f.date_lim_reglement = '" . $this->db->idate($tmpidate, 'gmt') . "'";
3488
        }
3489
        $sql .= " AND f.entity IN (" . getEntity('supplier_invoice', 0) . ")";  // One batch process only one company (no sharing)
3490
        if (!empty($paymentmode) && $paymentmode != 'all') {
3491
            $sql .= " AND f.fk_mode_reglement = cp.id AND cp.code = '" . $this->db->escape($paymentmode) . "'";
3492
        }
3493
        // TODO Add a filter to check there is no payment started yet
3494
        if ($datetouse == 'invoicedate') {
3495
            $sql .= $this->db->order("datef", "ASC");
3496
        } else {
3497
            $sql .= $this->db->order("date_lim_reglement", "ASC");
3498
        }
3499
3500
        $resql = $this->db->query($sql);
3501
3502
        $stmpidate = dol_print_date($tmpidate, 'day', 'gmt');
3503
        if ($datetouse == 'invoicedate') {
3504
            $this->output .= $langs->transnoentitiesnoconv("SearchValidatedSupplierInvoicesWithDate", $stmpidate);
3505
        } else {
3506
            $this->output .= $langs->transnoentitiesnoconv("SearchUnpaidSupplierInvoicesWithDueDate", $stmpidate);
3507
        }
3508
        if (!empty($paymentmode) && $paymentmode != 'all') {
3509
            $this->output .= ' (' . $langs->transnoentitiesnoconv("PaymentMode") . ' ' . $paymentmode . ')';
3510
        }
3511
        $this->output .= '<br>';
3512
3513
        if ($resql) {
3514
            while ($obj = $this->db->fetch_object($resql)) {
3515
                if (!$error) {
3516
                    // Load event
3517
                    $res = $tmpinvoice->fetch($obj->id);
3518
                    if ($res > 0) {
3519
                        $tmpinvoice->fetch_thirdparty();
3520
3521
                        $outputlangs = new Translate('', $conf);
3522
                        if ($tmpinvoice->thirdparty->default_lang) {
3523
                            $outputlangs->setDefaultLang($tmpinvoice->thirdparty->default_lang);
3524
                            $outputlangs->loadLangs(array("main", "suppliers"));
3525
                        } else {
3526
                            $outputlangs = $langs;
3527
                        }
3528
3529
                        // Select email template according to language of recipient
3530
                        $templateId = 0;
3531
                        $templateLabel = '';
3532
                        if (empty($template) || $template == 'EmailTemplateCode') {
3533
                            $templateLabel = '(SendingReminderEmailOnUnpaidSupplierInvoice)';
3534
                        } else {
3535
                            if (is_numeric($template)) {
3536
                                $templateId = $template;
3537
                            } else {
3538
                                $templateLabel = $template;
3539
                            }
3540
                        }
3541
3542
                        $arraymessage = $formmail->getEMailTemplate($this->db, 'invoice_supplier_send', $user, $outputlangs, $templateId, 1, $templateLabel);
3543
                        if (is_numeric($arraymessage) && $arraymessage <= 0) {
3544
                            $langs->load("errors");
3545
                            $this->output .= $langs->trans('ErrorFailedToFindEmailTemplate', $template);
3546
                            return 0;
3547
                        }
3548
3549
                        // PREPARE EMAIL
3550
                        $errormesg = '';
3551
3552
                        // Make substitution in email content
3553
                        $substitutionarray = getCommonSubstitutionArray($outputlangs, 0, '', $tmpinvoice);
3554
3555
                        complete_substitutions_array($substitutionarray, $outputlangs, $tmpinvoice);
3556
3557
                        // Topic
3558
                        $sendTopic = make_substitutions(empty($arraymessage->topic) ? $outputlangs->transnoentitiesnoconv('InformationMessage') : $arraymessage->topic, $substitutionarray, $outputlangs, 1);
3559
3560
                        // Content
3561
                        $content = $outputlangs->transnoentitiesnoconv($arraymessage->content);
3562
3563
                        $sendContent = make_substitutions($content, $substitutionarray, $outputlangs, 1);
3564
3565
                        // Recipient
3566
                        $to = array();
3567
                        if ($forcerecipient) {  // If a recipient was forced
3568
                            $to = array($forcerecipient);
3569
                        } else {
3570
                            $res = $tmpinvoice->fetch_thirdparty();
3571
                            $recipient = $tmpinvoice->thirdparty;
3572
                            if ($res > 0) {
3573
                                $tmparraycontact = $tmpinvoice->liste_contact(-1, 'internal', 0, 'SALESREPFOLL');
3574
                                if (is_array($tmparraycontact) && count($tmparraycontact) > 0) {
3575
                                    foreach ($tmparraycontact as $data_email) {
3576
                                        if (!empty($data_email['email'])) {
3577
                                            $to[] = $data_email['email'];
3578
                                        }
3579
                                    }
3580
                                }
3581
                                if (empty($to) && !empty($recipient->email)) {
3582
                                    $to[] = $recipient->email;
3583
                                }
3584
                                if (empty($to)) {
3585
                                    $errormesg = "Failed to send remind to thirdparty id=" . $tmpinvoice->socid . ". No email defined for supplier invoice or customer.";
3586
                                    $error++;
3587
                                }
3588
                            } else {
3589
                                $errormesg = "Failed to load recipient with thirdparty id=" . $tmpinvoice->socid;
3590
                                $error++;
3591
                            }
3592
                        }
3593
3594
                        // Sender
3595
                        $from = getDolGlobalString('MAIN_MAIL_EMAIL_FROM');
3596
                        if (!empty($arraymessage->email_from)) {    // If a sender is defined into template, we use it in priority
3597
                            $from = $arraymessage->email_from;
3598
                        }
3599
                        if (empty($from)) {
3600
                            $errormesg = "Failed to get sender into global setup MAIN_MAIL_EMAIL_FROM";
3601
                            $error++;
3602
                        }
3603
3604
                        if (!$error && !empty($to)) {
3605
                            $this->db->begin();
3606
3607
                            $to = implode(',', $to);
3608
                            if (!empty($arraymessage->email_to)) {  // If a recipient is defined into template, we add it
3609
                                $to = $to . ',' . $arraymessage->email_to;
3610
                            }
3611
3612
                            // Errors Recipient
3613
                            $errors_to = $conf->global->MAIN_MAIL_ERRORS_TO;
3614
3615
                            $trackid = 'inv' . $tmpinvoice->id;
3616
                            $sendcontext = 'standard';
3617
3618
                            $email_tocc = '';
3619
                            if (!empty($arraymessage->email_tocc)) {    // If a CC is defined into template, we use it
3620
                                $email_tocc = $arraymessage->email_tocc;
3621
                            }
3622
3623
                            $email_tobcc = '';
3624
                            if (!empty($arraymessage->email_tobcc)) {   // If a BCC is defined into template, we use it
3625
                                $email_tobcc = $arraymessage->email_tobcc;
3626
                            }
3627
3628
                            // Mail Creation
3629
                            $cMailFile = new CMailFile($sendTopic, $to, $from, $sendContent, array(), array(), array(), $email_tocc, $email_tobcc, 0, 1, $errors_to, '', $trackid, '', $sendcontext, '');
3630
3631
                            // Sending Mail
3632
                            if ($cMailFile->sendfile()) {
3633
                                $nbMailSend++;
3634
3635
                                // Add a line into event table
3636
                                require_once constant('DOL_DOCUMENT_ROOT') . '/comm/action/class/actioncomm.class.php';
3637
3638
                                // Insert record of emails sent
3639
                                $actioncomm = new ActionComm($this->db);
3640
3641
                                $actioncomm->type_code = 'AC_OTH_AUTO'; // Event insert into agenda automatically
3642
                                $actioncomm->socid = $tmpinvoice->thirdparty->id; // To link to a company
3643
                                $actioncomm->contact_id = 0;
3644
3645
                                $actioncomm->code = 'AC_EMAIL';
3646
                                $actioncomm->label = 'sendEmailsRemindersOnInvoiceDueDateOK (nbdays=' . $nbdays . ' paymentmode=' . $paymentmode . ' template=' . $template . ' datetouse=' . $datetouse . ' forcerecipient=' . $forcerecipient . ')';
3647
                                $actioncomm->note_private = $sendContent;
3648
                                $actioncomm->fk_project = $tmpinvoice->fk_project;
3649
                                $actioncomm->datep = dol_now();
3650
                                $actioncomm->datef = $actioncomm->datep;
3651
                                $actioncomm->percentage = -1; // Not applicable
3652
                                $actioncomm->authorid = $user->id; // User saving action
3653
                                $actioncomm->userownerid = $user->id; // Owner of action
3654
                                // Fields when action is an email (content should be added into note)
3655
                                $actioncomm->email_msgid = $cMailFile->msgid;
3656
                                $actioncomm->email_subject = $sendTopic;
3657
                                $actioncomm->email_from = $from;
3658
                                $actioncomm->email_sender = '';
3659
                                $actioncomm->email_to = $to;
3660
                                //$actioncomm->email_tocc = $sendtocc;
3661
                                //$actioncomm->email_tobcc = $sendtobcc;
3662
                                //$actioncomm->email_subject = $subject;
3663
                                $actioncomm->errors_to = $errors_to;
3664
3665
                                $actioncomm->elementtype = 'invoice_supplier';
3666
                                $actioncomm->fk_element = $tmpinvoice->id;
3667
3668
                                //$actioncomm->extraparams = $extraparams;
3669
3670
                                $actioncomm->create($user);
3671
                            } else {
3672
                                $errormesg = $cMailFile->error . ' : ' . $to;
3673
                                $error++;
3674
3675
                                // Add a line into event table
3676
                                require_once constant('DOL_DOCUMENT_ROOT') . '/comm/action/class/actioncomm.class.php';
3677
3678
                                // Insert record of emails sent
3679
                                $actioncomm = new ActionComm($this->db);
3680
3681
                                $actioncomm->type_code = 'AC_OTH_AUTO'; // Event insert into agenda automatically
3682
                                $actioncomm->socid = $tmpinvoice->thirdparty->id; // To link to a company
3683
                                $actioncomm->contact_id = 0;
3684
3685
                                $actioncomm->code = 'AC_EMAIL';
3686
                                $actioncomm->label = 'sendEmailsRemindersOnInvoiceDueDateKO';
3687
                                $actioncomm->note_private = $errormesg;
3688
                                $actioncomm->fk_project = $tmpinvoice->fk_project;
3689
                                $actioncomm->datep = dol_now();
3690
                                $actioncomm->datef = $actioncomm->datep;
3691
                                $actioncomm->percentage = -1; // Not applicable
3692
                                $actioncomm->authorid = $user->id; // User saving action
3693
                                $actioncomm->userownerid = $user->id; // Owner of action
3694
                                // Fields when action is an email (content should be added into note)
3695
                                $actioncomm->email_msgid = $cMailFile->msgid;
3696
                                $actioncomm->email_from = $from;
3697
                                $actioncomm->email_sender = '';
3698
                                $actioncomm->email_to = $to;
3699
                                //$actioncomm->email_tocc = $sendtocc;
3700
                                //$actioncomm->email_tobcc = $sendtobcc;
3701
                                //$actioncomm->email_subject = $subject;
3702
                                $actioncomm->errors_to = $errors_to;
3703
3704
                                //$actioncomm->extraparams = $extraparams;
3705
3706
                                $actioncomm->create($user);
3707
                            }
3708
3709
                            $this->db->commit();    // We always commit
3710
                        }
3711
3712
                        if ($errormesg) {
3713
                            $errorsMsg[] = $errormesg;
3714
                        }
3715
                    } else {
3716
                        $errorsMsg[] = 'Failed to fetch record invoice with ID = ' . $obj->id;
3717
                        $error++;
3718
                    }
3719
                }
3720
            }
3721
        } else {
3722
            $error++;
3723
        }
3724
3725
        if (!$error) {
3726
            $this->output .= 'Nb of emails sent : ' . $nbMailSend;
3727
3728
            dol_syslog(__METHOD__ . " end - " . $this->output, LOG_INFO);
3729
3730
            return 0;
3731
        } else {
3732
            $this->error = 'Nb of emails sent : ' . $nbMailSend . ', ' . (empty($errorsMsg) ? $error : implode(', ', $errorsMsg));
3733
3734
            dol_syslog(__METHOD__ . " end - " . $this->error, LOG_INFO);
3735
3736
            return $error;
3737
        }
3738
    }
3739
}
3740