Test Failed
Branch main (fda838)
by Rafael
54:38 queued 11s
created

InvoiceLine::insert()   F

Complexity

Conditions 54
Paths > 20000

Size

Total Lines 227
Code Lines 164

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 54
eloc 164
nc 20971520
nop 2
dl 0
loc 227
rs 0
c 1
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-2003  Rodolphe Quiedeville    <[email protected]>
4
 * Copyright (C) 2002-2003	Jean-Louis Bergamo		<[email protected]>
5
 * Copyright (C) 2004-2012	Laurent Destailleur		<[email protected]>
6
 * Copyright (C) 2004		Sebastien Di Cintio		<[email protected]>
7
 * Copyright (C) 2004		Benoit Mortier			<[email protected]>
8
 * Copyright (C) 2009-2017	Regis Houssin			<[email protected]>
9
 * Copyright (C) 2014-2018	Alexandre Spangaro		<[email protected]>
10
 * Copyright (C) 2015		Marcos García			<[email protected]>
11
 * Copyright (C) 2015-2024	Frédéric France			<[email protected]>
12
 * Copyright (C) 2015		Raphaël Doursenaud		<[email protected]>
13
 * Copyright (C) 2016		Juanjo Menent			<[email protected]>
14
 * Copyright (C) 2018-2019	Thibault FOUCART		<[email protected]>
15
 * Copyright (C) 2019		Nicolas ZABOURI 		<[email protected]>
16
 * Copyright (C) 2020		Josep Lluís Amador 		<[email protected]>
17
 * Copyright (C) 2021		Waël Almoman            <[email protected]>
18
 * Copyright (C) 2021		Philippe Grand          <[email protected]>
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
/**
37
 *  \file       htdocs/compta/facture/class/facture.class.php
38
 *  \ingroup    facture
39
 *  \brief      File of class to manage invoices
40
 */
41
42
namespace DoliModules\Billing\Model;
43
44
use DoliDB;
45
46
/**
47
 *  Class to manage invoice lines.
48
 *  Saved into database table llx_facturedet
49
 */
50
class InvoiceLine extends CommonInvoiceLine
51
{
52
    /**
53
     * @var string ID to identify managed object
54
     */
55
    public $element = 'facturedet';
56
57
    /**
58
     * @var string Name of table without prefix where object is stored
59
     */
60
    public $table_element = 'facturedet';
61
62
    /**
63
     * @var FactureLigne
64
     */
65
    public $oldline;
66
67
    //! From llx_facturedet
68
    //! Id facture
69
    public $fk_facture;
70
    //! Id parent line
71
    public $fk_parent_line;
72
73
    //! Description ligne
74
    public $desc;
75
    public $ref_ext; // External reference of the line
76
77
    public $localtax1_type; // Local tax 1 type
78
    public $localtax2_type; // Local tax 2 type
79
    public $fk_remise_except; // Link to line into llx_remise_except
80
    public $rang = 0;
81
82
    public $fk_fournprice;
83
    public $pa_ht;
84
    public $marge_tx;
85
    public $marque_tx;
86
87
    /**
88
     * @var int
89
     */
90
    public $tva_npr;
91
92
    public $remise_percent;
93
94
    /**
95
     * List of special options to define line:
96
     * 1: shipment cost lines
97
     * 2: ecotaxe
98
     * 3: ??
99
     * idofmodule: a meaning for the module
100
     */
101
    public $special_code;
102
103
    /**
104
     * @var string      To store the batch to consume in stock when using a POS module
105
     */
106
    public $batch;
107
    /**
108
     * @var string      To store the warehouse where to consume stock when using a POS module
109
     */
110
    public $fk_warehouse;
111
112
113
    public $origin;
114
    public $origin_id;
115
116
    /**
117
     * @var integer     Id in table llx_accounting_bookeeping to know accounting account for product line
118
     */
119
    public $fk_code_ventilation = 0;
120
121
122
    public $date_start;
123
    public $date_end;
124
125
    public $skip_update_total; // Skip update price total for special lines
126
127
    /**
128
     * @var int Situation advance percentage
129
     */
130
    public $situation_percent;
131
132
    /**
133
     * @var int Previous situation line id reference
134
     */
135
    public $fk_prev_id;
136
137
    /**
138
     *      Constructor
139
     *
140
     *      @param     DoliDB   $db      handler d'acces base de donnee
141
     */
142
    public function __construct($db)
143
    {
144
        $this->db = $db;
145
    }
146
147
    /**
148
     *  Load invoice line from database
149
     *
150
     *  @param  int     $rowid      id of invoice line to get
151
     *  @return int                 Return integer <0 if KO, >0 if OK
152
     */
153
    public function fetch($rowid)
154
    {
155
        $sql = 'SELECT fd.rowid, fd.fk_facture, fd.fk_parent_line, fd.fk_product, fd.product_type, fd.label as custom_label, fd.description, fd.price, fd.qty, fd.vat_src_code, fd.tva_tx,';
156
        $sql .= ' fd.localtax1_tx, fd. localtax2_tx, fd.remise, fd.remise_percent, fd.fk_remise_except, fd.subprice, fd.ref_ext,';
157
        $sql .= ' fd.date_start as date_start, fd.date_end as date_end, fd.fk_product_fournisseur_price as fk_fournprice, fd.buy_price_ht as pa_ht,';
158
        $sql .= ' fd.info_bits, fd.special_code, fd.total_ht, fd.total_tva, fd.total_ttc, fd.total_localtax1, fd.total_localtax2, fd.rang,';
159
        $sql .= ' fd.fk_code_ventilation,';
160
        $sql .= ' fd.fk_unit, fd.fk_user_author, fd.fk_user_modif,';
161
        $sql .= ' fd.situation_percent, fd.fk_prev_id,';
162
        $sql .= ' fd.multicurrency_subprice,';
163
        $sql .= ' fd.multicurrency_total_ht,';
164
        $sql .= ' fd.multicurrency_total_tva,';
165
        $sql .= ' fd.multicurrency_total_ttc,';
166
        $sql .= ' p.ref as product_ref, p.label as product_label, p.description as product_desc';
167
        $sql .= ' FROM ' . MAIN_DB_PREFIX . 'facturedet as fd';
168
        $sql .= ' LEFT JOIN ' . MAIN_DB_PREFIX . 'product as p ON fd.fk_product = p.rowid';
169
        $sql .= ' WHERE fd.rowid = ' . ((int) $rowid);
170
171
        $result = $this->db->query($sql);
172
        if ($result) {
173
            $objp = $this->db->fetch_object($result);
174
175
            if (!$objp) {
176
                $this->error = 'InvoiceLine with id ' . $rowid . ' not found sql=' . $sql;
177
                return 0;
178
            }
179
180
            $this->rowid = $objp->rowid;
0 ignored issues
show
Deprecated Code introduced by
The property DoliCore\Base\GenericDocumentLine::$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

180
            /** @scrutinizer ignore-deprecated */ $this->rowid = $objp->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...
181
            $this->id = $objp->rowid;
182
            $this->fk_facture = $objp->fk_facture;
183
            $this->fk_parent_line = $objp->fk_parent_line;
184
            $this->label                = $objp->custom_label;
185
            $this->desc                 = $objp->description;
186
            $this->qty = $objp->qty;
187
            $this->subprice = $objp->subprice;
188
            $this->ref_ext = $objp->ref_ext;
189
            $this->vat_src_code = $objp->vat_src_code;
190
            $this->tva_tx = $objp->tva_tx;
191
            $this->localtax1_tx         = $objp->localtax1_tx;
192
            $this->localtax2_tx         = $objp->localtax2_tx;
193
            $this->remise_percent = $objp->remise_percent;
194
            $this->fk_remise_except = $objp->fk_remise_except;
195
            $this->fk_product           = $objp->fk_product;
196
            $this->product_type = $objp->product_type;
197
            $this->date_start           = $this->db->jdate($objp->date_start);
198
            $this->date_end             = $this->db->jdate($objp->date_end);
199
            $this->info_bits            = $objp->info_bits;
200
            $this->tva_npr = (($objp->info_bits & 1) == 1) ? 1 : 0;
201
            $this->special_code = $objp->special_code;
202
            $this->total_ht             = $objp->total_ht;
203
            $this->total_tva            = $objp->total_tva;
204
            $this->total_localtax1      = $objp->total_localtax1;
205
            $this->total_localtax2      = $objp->total_localtax2;
206
            $this->total_ttc            = $objp->total_ttc;
207
            $this->fk_code_ventilation  = $objp->fk_code_ventilation;
208
            $this->rang                 = $objp->rang;
209
            $this->fk_fournprice = $objp->fk_fournprice;
210
            $marginInfos                = getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $this->fk_fournprice, $objp->pa_ht);
211
            $this->pa_ht                = $marginInfos[0];
212
            $this->marge_tx             = $marginInfos[1];
213
            $this->marque_tx            = $marginInfos[2];
214
215
            $this->ref = $objp->product_ref; // deprecated
216
217
            $this->product_ref = $objp->product_ref;
218
            $this->product_label        = $objp->product_label;
219
            $this->product_desc         = $objp->product_desc;
220
221
            $this->fk_unit = $objp->fk_unit;
222
            $this->fk_user_modif        = $objp->fk_user_modif;
0 ignored issues
show
Deprecated Code introduced by
The property DoliModules\Billing\Mode...iceLine::$fk_user_modif has been deprecated: Use user_modification_id ( Ignorable by Annotation )

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

222
            /** @scrutinizer ignore-deprecated */ $this->fk_user_modif        = $objp->fk_user_modif;

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...
223
            $this->fk_user_author = $objp->fk_user_author;
0 ignored issues
show
Deprecated Code introduced by
The property DoliModules\Billing\Mode...ceLine::$fk_user_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

223
            /** @scrutinizer ignore-deprecated */ $this->fk_user_author = $objp->fk_user_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...
224
225
            $this->situation_percent    = $objp->situation_percent;
226
            $this->fk_prev_id           = $objp->fk_prev_id;
227
228
            $this->multicurrency_subprice = $objp->multicurrency_subprice;
229
            $this->multicurrency_total_ht = $objp->multicurrency_total_ht;
230
            $this->multicurrency_total_tva = $objp->multicurrency_total_tva;
231
            $this->multicurrency_total_ttc = $objp->multicurrency_total_ttc;
232
233
            $this->fetch_optionals();
234
235
            $this->db->free($result);
236
237
            return 1;
238
        } else {
239
            $this->error = $this->db->lasterror();
240
            return -1;
241
        }
242
    }
243
244
    /**
245
     *  Insert line into database
246
     *
247
     *  @param      int     $notrigger                       1 no triggers
248
     *  @param      int     $noerrorifdiscountalreadylinked  1=Do not make error if lines is linked to a discount and discount already linked to another
249
     *  @return     int                                      Return integer <0 if KO, >0 if OK
250
     */
251
    public function insert($notrigger = 0, $noerrorifdiscountalreadylinked = 0)
252
    {
253
        global $langs, $user;
254
255
        $error = 0;
256
257
        $pa_ht_isemptystring = (empty($this->pa_ht) && $this->pa_ht == ''); // If true, we can use a default value. If this->pa_ht = '0', we must use '0'.
258
259
        dol_syslog(get_class($this) . "::insert rang=" . $this->rang, LOG_DEBUG);
260
261
        // Clean parameters
262
        $this->desc = trim($this->desc);
263
        if (empty($this->tva_tx)) {
264
            $this->tva_tx = 0;
265
        }
266
        if (empty($this->localtax1_tx)) {
267
            $this->localtax1_tx = 0;
268
        }
269
        if (empty($this->localtax2_tx)) {
270
            $this->localtax2_tx = 0;
271
        }
272
        if (empty($this->localtax1_type)) {
273
            $this->localtax1_type = 0;
274
        }
275
        if (empty($this->localtax2_type)) {
276
            $this->localtax2_type = 0;
277
        }
278
        if (empty($this->total_localtax1)) {
279
            $this->total_localtax1 = 0;
280
        }
281
        if (empty($this->total_localtax2)) {
282
            $this->total_localtax2 = 0;
283
        }
284
        if (empty($this->rang)) {
285
            $this->rang = 0;
286
        }
287
        if (empty($this->remise_percent)) {
288
            $this->remise_percent = 0;
289
        }
290
        if (empty($this->info_bits)) {
291
            $this->info_bits = 0;
292
        }
293
        if (empty($this->subprice)) {
294
            $this->subprice = 0;
295
        }
296
        if (empty($this->ref_ext)) {
297
            $this->ref_ext = '';
298
        }
299
        if (empty($this->special_code)) {
300
            $this->special_code = 0;
301
        }
302
        if (empty($this->fk_parent_line)) {
303
            $this->fk_parent_line = 0;
304
        }
305
        if (empty($this->fk_prev_id)) {
306
            $this->fk_prev_id = 0;
307
        }
308
        if (!isset($this->situation_percent) || $this->situation_percent > 100 || (string) $this->situation_percent == '') {
309
            $this->situation_percent = 100;
310
        }
311
312
        if (empty($this->pa_ht)) {
313
            $this->pa_ht = 0;
314
        }
315
        if (empty($this->multicurrency_subprice)) {
316
            $this->multicurrency_subprice = 0;
317
        }
318
        if (empty($this->multicurrency_total_ht)) {
319
            $this->multicurrency_total_ht = 0;
320
        }
321
        if (empty($this->multicurrency_total_tva)) {
322
            $this->multicurrency_total_tva = 0;
323
        }
324
        if (empty($this->multicurrency_total_ttc)) {
325
            $this->multicurrency_total_ttc = 0;
326
        }
327
328
        // if buy price not defined, define buyprice as configured in margin admin
329
        if ($this->pa_ht == 0 && $pa_ht_isemptystring) {
330
            $result = $this->defineBuyPrice($this->subprice, $this->remise_percent, $this->fk_product);
331
            if ($result < 0) {
332
                return $result;
333
            } else {
334
                $this->pa_ht = $result;
335
            }
336
        }
337
338
        // Check parameters
339
        if ($this->product_type < 0) {
340
            $this->error = 'ErrorProductTypeMustBe0orMore';
341
            return -1;
342
        }
343
        if (!empty($this->fk_product) && $this->fk_product > 0) {
344
            // Check product exists
345
            $result = Product::isExistingObject('product', $this->fk_product);
0 ignored issues
show
Bug introduced by
The type DoliModules\Billing\Model\Product was not found. Did you mean Product? If so, make sure to prefix the type with \.
Loading history...
346
            if ($result <= 0) {
347
                $this->error = 'ErrorProductIdDoesNotExists';
348
                dol_syslog(get_class($this) . "::insert Error " . $this->error, LOG_ERR);
349
                return -1;
350
            }
351
        }
352
353
        $this->db->begin();
354
355
        // Update line in database
356
        $sql = 'INSERT INTO ' . MAIN_DB_PREFIX . 'facturedet';
357
        $sql .= ' (fk_facture, fk_parent_line, label, description, qty,';
358
        $sql .= ' vat_src_code, tva_tx, localtax1_tx, localtax2_tx, localtax1_type, localtax2_type,';
359
        $sql .= ' fk_product, product_type, remise_percent, subprice, ref_ext, fk_remise_except,';
360
        $sql .= ' date_start, date_end, fk_code_ventilation,';
361
        $sql .= ' rang, special_code, fk_product_fournisseur_price, buy_price_ht,';
362
        $sql .= ' info_bits, total_ht, total_tva, total_ttc, total_localtax1, total_localtax2,';
363
        $sql .= ' situation_percent, fk_prev_id,';
364
        $sql .= ' fk_unit, fk_user_author, fk_user_modif,';
365
        $sql .= ' fk_multicurrency, multicurrency_code, multicurrency_subprice, multicurrency_total_ht, multicurrency_total_tva, multicurrency_total_ttc';
366
        $sql .= ')';
367
        $sql .= " VALUES (" . $this->fk_facture . ",";
368
        $sql .= " " . ($this->fk_parent_line > 0 ? $this->fk_parent_line : "null") . ",";
369
        $sql .= " " . (!empty($this->label) ? "'" . $this->db->escape($this->label) . "'" : "null") . ",";
370
        $sql .= " '" . $this->db->escape($this->desc) . "',";
371
        $sql .= " " . price2num($this->qty) . ",";
372
        $sql .= " " . (empty($this->vat_src_code) ? "''" : "'" . $this->db->escape($this->vat_src_code) . "'") . ",";
373
        $sql .= " " . price2num($this->tva_tx) . ",";
374
        $sql .= " " . price2num($this->localtax1_tx) . ",";
375
        $sql .= " " . price2num($this->localtax2_tx) . ",";
376
        $sql .= " '" . $this->db->escape($this->localtax1_type) . "',";
377
        $sql .= " '" . $this->db->escape($this->localtax2_type) . "',";
378
        $sql .= ' ' . ((!empty($this->fk_product) && $this->fk_product > 0) ? $this->fk_product : "null") . ',';
379
        $sql .= " " . ((int) $this->product_type) . ",";
380
        $sql .= " " . price2num($this->remise_percent) . ",";
381
        $sql .= " " . price2num($this->subprice) . ",";
382
        $sql .= " '" . $this->db->escape($this->ref_ext) . "',";
383
        $sql .= ' ' . (!empty($this->fk_remise_except) ? $this->fk_remise_except : "null") . ',';
384
        $sql .= " " . (!empty($this->date_start) ? "'" . $this->db->idate($this->date_start) . "'" : "null") . ",";
385
        $sql .= " " . (!empty($this->date_end) ? "'" . $this->db->idate($this->date_end) . "'" : "null") . ",";
386
        $sql .= ' ' . ((int) $this->fk_code_ventilation) . ',';
387
        $sql .= ' ' . ((int) $this->rang) . ',';
388
        $sql .= ' ' . ((int) $this->special_code) . ',';
389
        $sql .= ' ' . (!empty($this->fk_fournprice) ? $this->fk_fournprice : "null") . ',';
390
        $sql .= ' ' . price2num($this->pa_ht) . ',';
391
        $sql .= " '" . $this->db->escape($this->info_bits) . "',";
392
        $sql .= " " . price2num($this->total_ht) . ",";
393
        $sql .= " " . price2num($this->total_tva) . ",";
394
        $sql .= " " . price2num($this->total_ttc) . ",";
395
        $sql .= " " . price2num($this->total_localtax1) . ",";
396
        $sql .= " " . price2num($this->total_localtax2);
397
        $sql .= ", " . ((float) $this->situation_percent);
398
        $sql .= ", " . (!empty($this->fk_prev_id) ? $this->fk_prev_id : "null");
399
        $sql .= ", " . (!$this->fk_unit ? 'NULL' : $this->fk_unit);
400
        $sql .= ", " . ((int) $user->id);
401
        $sql .= ", " . ((int) $user->id);
402
        $sql .= ", " . (int) $this->fk_multicurrency;
403
        $sql .= ", '" . $this->db->escape($this->multicurrency_code) . "'";
404
        $sql .= ", " . price2num($this->multicurrency_subprice);
405
        $sql .= ", " . price2num($this->multicurrency_total_ht);
406
        $sql .= ", " . price2num($this->multicurrency_total_tva);
407
        $sql .= ", " . price2num($this->multicurrency_total_ttc);
408
        $sql .= ')';
409
410
        dol_syslog(get_class($this) . "::insert", LOG_DEBUG);
411
        $resql = $this->db->query($sql);
412
        if ($resql) {
413
            $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX . 'facturedet');
414
            $this->rowid = $this->id; // For backward compatibility
415
416
            if (!$error) {
417
                $result = $this->insertExtraFields();
418
                if ($result < 0) {
419
                    $error++;
420
                }
421
            }
422
423
            // If fk_remise_except is defined, the discount is linked to the invoice
424
            // which flags it as "consumed".
425
            if ($this->fk_remise_except) {
426
                $discount = new DiscountAbsolute($this->db);
0 ignored issues
show
Bug introduced by
The type DoliModules\Billing\Model\DiscountAbsolute was not found. Did you mean DiscountAbsolute? If so, make sure to prefix the type with \.
Loading history...
427
                $result = $discount->fetch($this->fk_remise_except);
428
                if ($result >= 0) {
429
                    // Check if discount was found
430
                    if ($result > 0) {
431
                        // Check if discount not already affected to another invoice
432
                        if ($discount->fk_facture_line > 0) {
433
                            if (empty($noerrorifdiscountalreadylinked)) {
434
                                $this->error = $langs->trans("ErrorDiscountAlreadyUsed", $discount->id);
435
                                dol_syslog(get_class($this) . "::insert Error " . $this->error, LOG_ERR);
436
                                $this->db->rollback();
437
                                return -3;
438
                            }
439
                        } else {
440
                            $result = $discount->link_to_invoice($this->rowid, 0);
441
                            if ($result < 0) {
442
                                $this->error = $discount->error;
443
                                dol_syslog(get_class($this) . "::insert Error " . $this->error, LOG_ERR);
444
                                $this->db->rollback();
445
                                return -3;
446
                            }
447
                        }
448
                    } else {
449
                        $this->error = $langs->trans("ErrorADiscountThatHasBeenRemovedIsIncluded");
450
                        dol_syslog(get_class($this) . "::insert Error " . $this->error, LOG_ERR);
451
                        $this->db->rollback();
452
                        return -3;
453
                    }
454
                } else {
455
                    $this->error = $discount->error;
456
                    dol_syslog(get_class($this) . "::insert Error " . $this->error, LOG_ERR);
457
                    $this->db->rollback();
458
                    return -3;
459
                }
460
            }
461
462
            if (!$notrigger) {
463
                // Call trigger
464
                $result = $this->call_trigger('LINEBILL_INSERT', $user);
465
                if ($result < 0) {
466
                    $this->db->rollback();
467
                    return -2;
468
                }
469
                // End call triggers
470
            }
471
472
            $this->db->commit();
473
            return $this->id;
474
        } else {
475
            $this->error = $this->db->lasterror();
476
            $this->db->rollback();
477
            return -2;
478
        }
479
    }
480
481
    /**
482
     *  Update line into database
483
     *
484
     *  @param      User    $user       User object
0 ignored issues
show
Bug introduced by
The type DoliModules\Billing\Model\User was not found. Did you mean User? If so, make sure to prefix the type with \.
Loading history...
485
     *  @param      int     $notrigger  Disable triggers
486
     *  @return     int                 Return integer <0 if KO, >0 if OK
487
     */
488
    public function update($user = null, $notrigger = 0)
489
    {
490
        global $user, $conf;
491
492
        $error = 0;
493
494
        $pa_ht_isemptystring = (empty($this->pa_ht) && $this->pa_ht == ''); // If true, we can use a default value. If this->pa_ht = '0', we must use '0'.
495
496
        // Clean parameters
497
        $this->desc = trim($this->desc);
498
        if (empty($this->ref_ext)) {
499
            $this->ref_ext = '';
500
        }
501
        if (empty($this->tva_tx)) {
502
            $this->tva_tx = 0;
503
        }
504
        if (empty($this->localtax1_tx)) {
505
            $this->localtax1_tx = 0;
506
        }
507
        if (empty($this->localtax2_tx)) {
508
            $this->localtax2_tx = 0;
509
        }
510
        if (empty($this->localtax1_type)) {
511
            $this->localtax1_type = 0;
512
        }
513
        if (empty($this->localtax2_type)) {
514
            $this->localtax2_type = 0;
515
        }
516
        if (empty($this->total_localtax1)) {
517
            $this->total_localtax1 = 0;
518
        }
519
        if (empty($this->total_localtax2)) {
520
            $this->total_localtax2 = 0;
521
        }
522
        if (empty($this->remise_percent)) {
523
            $this->remise_percent = 0;
524
        }
525
        if (empty($this->info_bits)) {
526
            $this->info_bits = 0;
527
        }
528
        if (empty($this->special_code)) {
529
            $this->special_code = 0;
530
        }
531
        if (empty($this->product_type)) {
532
            $this->product_type = 0;
533
        }
534
        if (empty($this->fk_parent_line)) {
535
            $this->fk_parent_line = 0;
536
        }
537
        if (!isset($this->situation_percent) || $this->situation_percent > 100 || (string) $this->situation_percent == '') {
538
            $this->situation_percent = 100;
539
        }
540
        if (empty($this->pa_ht)) {
541
            $this->pa_ht = 0;
542
        }
543
544
        if (empty($this->multicurrency_subprice)) {
545
            $this->multicurrency_subprice = 0;
546
        }
547
        if (empty($this->multicurrency_total_ht)) {
548
            $this->multicurrency_total_ht = 0;
549
        }
550
        if (empty($this->multicurrency_total_tva)) {
551
            $this->multicurrency_total_tva = 0;
552
        }
553
        if (empty($this->multicurrency_total_ttc)) {
554
            $this->multicurrency_total_ttc = 0;
555
        }
556
557
        // Check parameters
558
        if ($this->product_type < 0) {
559
            return -1;
560
        }
561
562
        // if buy price not provided, define buyprice as configured in margin admin
563
        if ($this->pa_ht == 0 && $pa_ht_isemptystring) {
564
            // We call defineBuyPrice only if data was not provided (if input was '0', we will not go here and value will remaine '0')
565
            $result = $this->defineBuyPrice($this->subprice, $this->remise_percent, $this->fk_product);
566
            if ($result < 0) {
567
                return $result;
568
            } else {
569
                $this->pa_ht = $result;
570
            }
571
        }
572
573
        $this->db->begin();
574
575
        // Update line in database
576
        $sql = "UPDATE " . MAIN_DB_PREFIX . "facturedet SET";
577
        $sql .= " description='" . $this->db->escape($this->desc) . "'";
578
        $sql .= ", ref_ext='" . $this->db->escape($this->ref_ext) . "'";
579
        $sql .= ", label=" . (!empty($this->label) ? "'" . $this->db->escape($this->label) . "'" : "null");
580
        $sql .= ", subprice=" . price2num($this->subprice);
581
        $sql .= ", remise_percent=" . price2num($this->remise_percent);
582
        if ($this->fk_remise_except) {
583
            $sql .= ", fk_remise_except=" . $this->fk_remise_except;
584
        } else {
585
            $sql .= ", fk_remise_except=null";
586
        }
587
        $sql .= ", vat_src_code = '" . (empty($this->vat_src_code) ? '' : $this->db->escape($this->vat_src_code)) . "'";
588
        $sql .= ", tva_tx=" . price2num($this->tva_tx);
589
        $sql .= ", localtax1_tx=" . price2num($this->localtax1_tx);
590
        $sql .= ", localtax2_tx=" . price2num($this->localtax2_tx);
591
        $sql .= ", localtax1_type='" . $this->db->escape($this->localtax1_type) . "'";
592
        $sql .= ", localtax2_type='" . $this->db->escape($this->localtax2_type) . "'";
593
        $sql .= ", qty=" . price2num($this->qty);
594
        $sql .= ", date_start=" . (!empty($this->date_start) ? "'" . $this->db->idate($this->date_start) . "'" : "null");
595
        $sql .= ", date_end=" . (!empty($this->date_end) ? "'" . $this->db->idate($this->date_end) . "'" : "null");
596
        $sql .= ", product_type=" . $this->product_type;
597
        $sql .= ", info_bits='" . $this->db->escape($this->info_bits) . "'";
598
        $sql .= ", special_code='" . $this->db->escape($this->special_code) . "'";
599
        if (empty($this->skip_update_total)) {
600
            $sql .= ", total_ht=" . price2num($this->total_ht);
601
            $sql .= ", total_tva=" . price2num($this->total_tva);
602
            $sql .= ", total_ttc=" . price2num($this->total_ttc);
603
            $sql .= ", total_localtax1=" . price2num($this->total_localtax1);
604
            $sql .= ", total_localtax2=" . price2num($this->total_localtax2);
605
        }
606
        $sql .= ", fk_product_fournisseur_price=" . (!empty($this->fk_fournprice) ? "'" . $this->db->escape($this->fk_fournprice) . "'" : "null");
607
        $sql .= ", buy_price_ht=" . (($this->pa_ht || (string) $this->pa_ht === '0') ? price2num($this->pa_ht) : "null"); // $this->pa_ht should always be defined (set to 0 or to sell price depending on option)
608
        $sql .= ", fk_parent_line=" . ($this->fk_parent_line > 0 ? $this->fk_parent_line : "null");
609
        if (!empty($this->rang)) {
610
            $sql .= ", rang=" . ((int) $this->rang);
611
        }
612
        $sql .= ", situation_percent = " . ((float) $this->situation_percent);
613
        $sql .= ", fk_unit = " . (!$this->fk_unit ? 'NULL' : $this->fk_unit);
614
        $sql .= ", fk_user_modif = " . ((int) $user->id);
615
616
        // Multicurrency
617
        $sql .= ", multicurrency_subprice=" . price2num($this->multicurrency_subprice);
618
        $sql .= ", multicurrency_total_ht=" . price2num($this->multicurrency_total_ht);
619
        $sql .= ", multicurrency_total_tva=" . price2num($this->multicurrency_total_tva);
620
        $sql .= ", multicurrency_total_ttc=" . price2num($this->multicurrency_total_ttc);
621
622
        $sql .= " WHERE rowid = " . ((int) $this->rowid);
623
624
        dol_syslog(get_class($this) . "::update", LOG_DEBUG);
625
        $resql = $this->db->query($sql);
626
        if ($resql) {
627
            if (!$error) {
628
                $this->id = $this->rowid;
629
                $result = $this->insertExtraFields();
630
                if ($result < 0) {
631
                    $error++;
632
                }
633
            }
634
635
            if (!$error && !$notrigger) {
636
                // Call trigger
637
                $result = $this->call_trigger('LINEBILL_MODIFY', $user);
638
                if ($result < 0) {
639
                    $this->db->rollback();
640
                    return -2;
641
                }
642
                // End call triggers
643
            }
644
            $this->db->commit();
645
            return 1;
646
        } else {
647
            $this->error = $this->db->error();
648
            $this->db->rollback();
649
            return -2;
650
        }
651
    }
652
653
    /**
654
     * Delete line in database
655
     *
656
     * @param   User    $tmpuser    User that deletes
657
     * @param   int     $notrigger  0=launch triggers after, 1=disable triggers
658
     * @return  int                 Return integer <0 if KO, >0 if OK
659
     */
660
    public function delete($tmpuser = null, $notrigger = 0)
661
    {
662
        global $user;
663
664
        $this->db->begin();
665
666
        // Call trigger
667
        if (empty($notrigger)) {
668
            $result = $this->call_trigger('LINEBILL_DELETE', $user);
669
            if ($result < 0) {
670
                $this->db->rollback();
671
                return -1;
672
            }
673
        }
674
        // End call triggers
675
676
        // extrafields
677
        $result = $this->deleteExtraFields();
678
        if ($result < 0) {
679
            $this->db->rollback();
680
            return -1;
681
        }
682
683
        // Free discount linked to invoice line
684
        $sql = 'UPDATE ' . MAIN_DB_PREFIX . 'societe_remise_except';
685
        $sql .= ' SET fk_facture_line = NULL';
686
        $sql .= ' WHERE fk_facture_line = ' . ((int) $this->id);
687
688
        dol_syslog(get_class($this) . "::deleteline", LOG_DEBUG);
689
        $result = $this->db->query($sql);
690
        if (!$result) {
691
            $this->error = $this->db->error();
692
            $this->errors[] = $this->error;
693
            $this->db->rollback();
694
            return -1;
695
        }
696
697
        $sql = 'UPDATE ' . MAIN_DB_PREFIX . 'element_time';
698
        $sql .= ' SET invoice_id = NULL, invoice_line_id = NULL';
699
        $sql .= ' WHERE invoice_line_id = ' . ((int) $this->id);
700
        if (!$this->db->query($sql)) {
701
            $this->error = $this->db->error() . " sql=" . $sql;
702
            $this->errors[] = $this->error;
703
            $this->db->rollback();
704
            return -1;
705
        }
706
707
        $sql = "DELETE FROM " . MAIN_DB_PREFIX . "facturedet WHERE rowid = " . ((int) $this->id);
708
709
        if ($this->db->query($sql)) {
710
            $this->db->commit();
711
            return 1;
712
        } else {
713
            $this->error = $this->db->error() . " sql=" . $sql;
714
            $this->errors[] = $this->error;
715
            $this->db->rollback();
716
            return -1;
717
        }
718
    }
719
720
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
721
    /**
722
     *  Update DB line fields total_xxx
723
     *  Used by migration
724
     *
725
     *  @return     int     Return integer <0 if KO, >0 if OK
726
     */
727
    public function update_total()
728
    {
729
        // phpcs:enable
730
        $this->db->begin();
731
        dol_syslog(get_class($this) . "::update_total", LOG_DEBUG);
732
733
        // Clean parameters
734
        if (empty($this->total_localtax1)) {
735
            $this->total_localtax1 = 0;
736
        }
737
        if (empty($this->total_localtax2)) {
738
            $this->total_localtax2 = 0;
739
        }
740
741
        // Update line in database
742
        $sql = "UPDATE " . MAIN_DB_PREFIX . "facturedet SET";
743
        $sql .= " total_ht=" . price2num($this->total_ht);
744
        $sql .= ",total_tva=" . price2num($this->total_tva);
745
        $sql .= ",total_localtax1=" . price2num($this->total_localtax1);
746
        $sql .= ",total_localtax2=" . price2num($this->total_localtax2);
747
        $sql .= ",total_ttc=" . price2num($this->total_ttc);
748
        $sql .= " WHERE rowid = " . ((int) $this->rowid);
0 ignored issues
show
Deprecated Code introduced by
The property DoliCore\Base\GenericDocumentLine::$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

748
        $sql .= " WHERE rowid = " . ((int) /** @scrutinizer ignore-deprecated */ $this->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...
749
750
        dol_syslog(get_class($this) . "::update_total", LOG_DEBUG);
751
752
        $resql = $this->db->query($sql);
753
        if ($resql) {
754
            $this->db->commit();
755
            return 1;
756
        } else {
757
            $this->error = $this->db->error();
758
            $this->db->rollback();
759
            return -2;
760
        }
761
    }
762
763
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
764
    /**
765
     * Returns situation_percent of the previous line.
766
     * Warning: If invoice is a replacement invoice, this->fk_prev_id is id of the replaced line.
767
     *
768
     * @param  int     $invoiceid               Invoice id
769
     * @param  bool    $include_credit_note     Include credit note or not
770
     * @return float|int                        Reurrn previous situation percent, 0 or -1 if error
771
     */
772
    public function get_prev_progress($invoiceid, $include_credit_note = true)
773
    {
774
        // phpcs:enable
775
        global $invoicecache;
776
777
        if (is_null($this->fk_prev_id) || empty($this->fk_prev_id) || $this->fk_prev_id == "") {
778
            return 0;
779
        } else {
780
            // If invoice is not a situation invoice, this->fk_prev_id is used for something else
781
            if (!isset($invoicecache[$invoiceid])) {
782
                $invoicecache[$invoiceid] = new Facture($this->db);
783
                $invoicecache[$invoiceid]->fetch($invoiceid);
784
            }
785
            if ($invoicecache[$invoiceid]->type != Facture::TYPE_SITUATION) {
786
                return 0;
787
            }
788
789
            $sql = "SELECT situation_percent FROM " . MAIN_DB_PREFIX . "facturedet WHERE rowid = " . ((int) $this->fk_prev_id);
790
            $resql = $this->db->query($sql);
791
            if ($resql && $this->db->num_rows($resql) > 0) {
792
                $res = $this->db->fetch_array($resql);
793
794
                $returnPercent = (float) $res['situation_percent'];
795
796
                if ($include_credit_note) {
797
                    $sql = 'SELECT fd.situation_percent FROM ' . MAIN_DB_PREFIX . 'facturedet fd';
798
                    $sql .= ' JOIN ' . MAIN_DB_PREFIX . 'facture f ON (f.rowid = fd.fk_facture) ';
799
                    $sql .= " WHERE fd.fk_prev_id = " . ((int) $this->fk_prev_id);
800
                    $sql .= " AND f.situation_cycle_ref = " . ((int) $invoicecache[$invoiceid]->situation_cycle_ref); // Prevent cycle outed
801
                    $sql .= " AND f.type = " . Facture::TYPE_CREDIT_NOTE;
802
803
                    $res = $this->db->query($sql);
804
                    if ($res) {
805
                        while ($obj = $this->db->fetch_object($res)) {
806
                            $returnPercent = $returnPercent + (float) $obj->situation_percent;
807
                        }
808
                    } else {
809
                        dol_print_error($this->db);
810
                    }
811
                }
812
813
                return $returnPercent;
814
            } else {
815
                $this->error = $this->db->error();
816
                dol_syslog(get_class($this) . "::select Error " . $this->error, LOG_ERR);
817
                $this->db->rollback();
818
                return -1;
819
            }
820
        }
821
    }
822
}
823