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

ContratLigne::insert()   F

Complexity

Conditions 14
Paths 2048

Size

Total Lines 83
Code Lines 60

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 14
eloc 60
nc 2048
nop 1
dl 0
loc 83
rs 2.1
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) 2003       Rodolphe Quiedeville    <[email protected]>
4
 * Copyright (C) 2004-2012	Destailleur Laurent		<[email protected]>
5
 * Copyright (C) 2005-2014	Regis Houssin			<[email protected]>
6
 * Copyright (C) 2006		Andre Cianfarani		<[email protected]>
7
 * Copyright (C) 2008		Raphael Bertrand		<[email protected]>
8
 * Copyright (C) 2010-2016	Juanjo Menent			<[email protected]>
9
 * Copyright (C) 2013		Christophe Battarel		<[email protected]>
10
 * Copyright (C) 2013		Florian Henry			<[email protected]>
11
 * Copyright (C) 2014-2015	Marcos García			<[email protected]>
12
 * Copyright (C) 2018   	Nicolas ZABOURI			<[email protected]>
13
 * Copyright (C) 2018-2024  Frédéric France         <[email protected]>
14
 * Copyright (C) 2015-2018	Ferran Marcet			<[email protected]>
15
 * Copyright (C) 2024		William Mead			<[email protected]>
16
 * Copyright (C) 2024		MDW						<[email protected]>
17
 * Copyright (C) 2024       Rafael San José         <[email protected]>
18
 *
19
 * This program is free software; you can redistribute it and/or modify
20
 * it under the terms of the GNU General Public License as published by
21
 * the Free Software Foundation; either version 3 of the License, or
22
 * (at your option) any later version.
23
 *
24
 * This program is distributed in the hope that it will be useful,
25
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
26
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
27
 * GNU General Public License for more details.
28
 *
29
 * You should have received a copy of the GNU General Public License
30
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
31
 */
32
33
/**
34
 *  \file       htdocs/contrat/class/contrat.class.php
35
 *  \ingroup    contrat
36
 *  \brief      File of class to manage contracts
37
 */
38
39
use DoliCore\Base\GenericDocumentLine;
40
41
require_once DOL_DOCUMENT_ROOT . '/core/lib/price.lib.php';
42
require_once DOL_DOCUMENT_ROOT . '/margin/lib/margins.lib.php';
43
44
/**
45
 *  Class to manage lines of contracts
46
 */
47
class ContratLigne extends GenericDocumentLine
0 ignored issues
show
Deprecated Code introduced by
The class DoliCore\Base\GenericDocumentLine has been deprecated: This class is only needed for compatibility with Dolibarr. ( Ignorable by Annotation )

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

47
class ContratLigne extends /** @scrutinizer ignore-deprecated */ GenericDocumentLine
Loading history...
48
{
49
    /**
50
     * @var string ID to identify managed object
51
     */
52
    public $element = 'contratdet';
53
54
    /**
55
     * @var string Name of table without prefix where object is stored
56
     */
57
    public $table_element = 'contratdet';
58
59
    /**
60
     * @var string  Name to use for 'features' parameter to check module permissions user->rights->feature with restrictedArea().
61
     *              Undefined means same value than $element. Can be use to force a check on another element for example for class of line, we mention here the parent element.
62
     */
63
    public $element_for_permission = 'contrat';
64
65
    /**
66
     * @var int ID
67
     */
68
    public $id;
69
70
    /**
71
     * @var string Ref
72
     */
73
    public $ref;
74
75
    /**
76
     * @var int ID
77
     */
78
    public $fk_contrat;
79
80
    /**
81
     * @var int ID
82
     */
83
    public $fk_product;
84
85
    public $statut; // 0 inactive, 4 active, 5 closed
86
    public $type; // 0 for product, 1 for service
87
88
    /**
89
     * @var string
90
     * @deprecated
91
     */
92
    public $label;
93
94
    /**
95
     * @var string
96
     * @deprecated
97
     */
98
    public $libelle;
99
100
    /**
101
     * @var string description
102
     */
103
    public $description;
104
105
    public $product_type; // 0 for product, 1 for service
106
    public $product_ref;
107
    public $product_label;
108
109
    public $date_commande;
110
111
    public $date_start; // date start planned
112
    public $date_start_real; // date start real
113
    public $date_end; // date end planned
114
    public $date_end_real; // date end real
115
116
    public $tva_tx;
117
    public $vat_src_code;
118
    public $localtax1_tx;
119
    public $localtax2_tx;
120
    public $localtax1_type; // Local tax 1 type
121
    public $localtax2_type; // Local tax 2 type
122
    public $qty;
123
    public $remise_percent;
124
    public $remise;
125
126
    /**
127
     * @var int ID
128
     */
129
    public $fk_remise_except;
130
131
    public $subprice; // Unit price HT
132
133
    /**
134
     * @var float
135
     * @deprecated Use $price_ht instead
136
     * @see $price_ht
137
     */
138
    public $price;
139
140
    public $price_ht;
141
142
    public $total_ht;
143
    public $total_tva;
144
    public $total_localtax1;
145
    public $total_localtax2;
146
    public $total_ttc;
147
148
    /**
149
     * @var int     ID
150
     */
151
    public $fk_fournprice;
152
153
    public $pa_ht;
154
155
    /**
156
     * @var int     Info bits
157
     */
158
    public $info_bits;
159
160
    /**
161
     * @var int     ID of user that insert the service
162
     */
163
    public $fk_user_author;
164
165
    /**
166
     * @var int     ID of user opening the service
167
     */
168
    public $fk_user_ouverture;
169
170
    /**
171
     * @var int     ID of user closing the service
172
     */
173
    public $fk_user_cloture;
174
175
    /**
176
     * @var string  Comment
177
     */
178
    public $commentaire;
179
180
181
    /**
182
     * @var int line rank
183
     */
184
    public $rang = 0;
185
186
187
    const STATUS_INITIAL = 0;
188
    const STATUS_OPEN = 4;
189
    const STATUS_CLOSED = 5;
190
191
192
    // BEGIN MODULEBUILDER PROPERTIES
193
    /**
194
     * @var array<string,array{type:string,label:string,enabled:int<0,2>|string,position:int,notnull:int,visible:int,noteditable?:int,default?:string,index?:int,foreignkey?:string,searchall?:int,isameasure?:int,css?:string,csslist?:string,help?:string,showoncombobox?:int,disabled?:int,arrayofkeyval?:array<int,string>,comment?:string}>  Array with all fields and their property. Do not use it as a static var. It may be modified by constructor.
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<string,array{type:...ring>,comment?:string}> at position 16 could not be parsed: Expected '}' at position 16, but found 'int'.
Loading history...
195
     */
196
    public $fields = array(
197
        'rowid' => array('type' => 'integer', 'label' => 'TechnicalID', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 10),
198
        'entity' => array('type' => 'integer', 'label' => 'Entity', 'default' => '1', 'enabled' => 1, 'visible' => -2, 'notnull' => 1, 'position' => 30, 'index' => 1),
199
        'tms' => array('type' => 'timestamp', 'label' => 'DateModification', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 35),
200
        'qty' => array('type' => 'integer', 'label' => 'Quantity', 'enabled' => 1, 'visible' => 1, 'notnull' => 1, 'position' => 35, 'isameasure' => 1),
201
        'total_ht' => array('type' => 'integer', 'label' => 'AmountHT', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 36, 'isameasure' => 1),
202
        'total_tva' => array('type' => 'integer', 'label' => 'AmountVAT', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 37, 'isameasure' => 1),
203
        'total_ttc' => array('type' => 'integer', 'label' => 'AmountTTC', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 38, 'isameasure' => 1),
204
        //'datec' =>array('type'=>'datetime', 'label'=>'DateCreation', 'enabled'=>1, 'visible'=>-1, 'position'=>40),
205
        //'fk_soc' =>array('type'=>'integer:Societe:societe/class/societe.class.php', 'label'=>'ThirdParty', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>70),
206
        'fk_contrat' => array('type' => 'integer:Contrat:contrat/class/contrat.class.php', 'label' => 'Contract', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 70),
207
        'fk_product' => array('type' => 'integer:Product:product/class/product.class.php:1', 'label' => 'Product', 'enabled' => 1, 'visible' => -1, 'position' => 75),
208
        //'fk_user_author' =>array('type'=>'integer:User:user/class/user.class.php', 'label'=>'Fk user author', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>90),
209
        'note_private' => array('type' => 'html', 'label' => 'NotePublic', 'enabled' => 1, 'visible' => 0, 'position' => 105),
210
        'note_public' => array('type' => 'html', 'label' => 'NotePrivate', 'enabled' => 1, 'visible' => 0, 'position' => 110),
211
        //'model_pdf' =>array('type'=>'varchar(255)', 'label'=>'Model pdf', 'enabled'=>1, 'visible'=>0, 'position'=>115),
212
        //'import_key' =>array('type'=>'varchar(14)', 'label'=>'ImportId', 'enabled'=>1, 'visible'=>-2, 'position'=>120),
213
        //'extraparams' =>array('type'=>'varchar(255)', 'label'=>'Extraparams', 'enabled'=>1, 'visible'=>-1, 'position'=>125),
214
        'fk_user_ouverture' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserStartingService', 'enabled' => 1, 'visible' => -2, 'notnull' => -1, 'position' => 135),
215
        'fk_user_cloture' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserClosingService', 'enabled' => 1, 'visible' => -2, 'notnull' => -1, 'position' => 135),
216
        'statut' => array('type' => 'smallint(6)', 'label' => 'Statut', 'enabled' => 1, 'visible' => -1, 'position' => 500, 'arrayofkeyval' => array(0 => 'Draft', 4 => 'Open', 5 => 'Closed')),
217
        'rang' => array('type' => 'integer', 'label' => 'Rank', 'enabled' => 1, 'visible' => 0, 'position' => 500, 'default' => '0')
218
    );
219
    // END MODULEBUILDER PROPERTIES
220
221
222
    /**
223
     *  Constructor
224
     *
225
     *  @param      DoliDB      $db      Database handler
226
     */
227
    public function __construct($db)
228
    {
229
        $this->db = $db;
230
    }
231
232
233
    /**
234
     *  Return label of this contract line status
235
     *
236
     *  @param  int     $mode       0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short label + Picto, 6=Long label + Picto
237
     *  @return string              Label of status
238
     */
239
    public function getLibStatut($mode)
240
    {
241
        return $this->LibStatut($this->statut, $mode, ((!empty($this->date_end)) ? ($this->date_end < dol_now() ? 1 : 0) : -1));
242
    }
243
244
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
245
    /**
246
     *  Return label of a contract line status
247
     *
248
     *  @param  int     $status     Id status
249
     *  @param  int     $mode       0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short label + Picto, 6=Long label + Picto
250
     *  @param  int     $expired    0=Not expired, 1=Expired, -1=Both or unknown
251
     *  @param  string  $moreatt    More attribute
252
     *  @return string              Label of status
253
     */
254
    public static function LibStatut($status, $mode, $expired = -1, $moreatt = '')
255
    {
256
		// phpcs:enable
257
        global $langs;
258
        $langs->load("contracts");
259
260
        if ($status == self::STATUS_INITIAL) {
261
            $labelStatus = $langs->transnoentities("ServiceStatusInitial");
262
            $labelStatusShort = $langs->transnoentities("ServiceStatusInitial");
263
        } elseif ($status == self::STATUS_OPEN && $expired == -1) {
264
            $labelStatus = $langs->transnoentities("ServiceStatusRunning");
265
            $labelStatusShort = $langs->transnoentities("ServiceStatusRunning");
266
        } elseif ($status == self::STATUS_OPEN && $expired == 0) {
267
            $labelStatus = $langs->transnoentities("ServiceStatusNotLate");
268
            $labelStatusShort = $langs->transnoentities("ServiceStatusNotLateShort");
269
        } elseif ($status == self::STATUS_OPEN && $expired == 1) {
270
            $labelStatus = $langs->transnoentities("ServiceStatusLate");
271
            $labelStatusShort = $langs->transnoentities("ServiceStatusLateShort");
272
        } elseif ($status == self::STATUS_CLOSED) {
273
            $labelStatus = $langs->transnoentities("ServiceStatusClosed");
274
            $labelStatusShort = $langs->transnoentities("ServiceStatusClosed");
275
        }
276
277
        $statusType = 'status' . $status;
278
        if ($status == self::STATUS_OPEN && $expired == 1) {
279
            $statusType = 'status1';
280
        }
281
        if ($status == self::STATUS_CLOSED) {
282
            $statusType = 'status6';
283
        }
284
285
        $params = array();
286
        $reg = array();
287
        if (preg_match('/class="(.*)"/', $moreatt, $reg)) {
288
            $params = array('badgeParams' => array('css' => $reg[1]));
289
        }
290
        return dolGetStatus($labelStatus, $labelStatusShort, '', $statusType, $mode, '', $params);
291
    }
292
293
    /**
294
     * getTooltipContentArray
295
     * @param array $params params to construct tooltip data
296
     * @since v18
297
     * @return array
298
     */
299
    public function getTooltipContentArray($params)
300
    {
301
        global $conf, $langs, $user;
302
303
        $datas = [];
304
        $datas['label'] = $langs->trans("ShowContractOfService") . ': ' . $this->label;
305
        if (empty($datas['label'])) {
306
            $datas['label'] = $this->description;
307
        }
308
309
        return $datas;
310
    }
311
312
    /**
313
     *  Return clicable name (with picto eventually) for ContratLigne
314
     *
315
     *  @param  int     $withpicto      0=No picto, 1=Include picto into link, 2=Only picto
316
     *  @param  int     $maxlength      Max length
317
     *  @return string                  Chaine avec URL
318
     */
319
    public function getNomUrl($withpicto = 0, $maxlength = 0)
320
    {
321
        global $langs;
322
323
        $result = '';
324
        $label = $langs->trans("ShowContractOfService") . ': ' . $this->label;
325
        if (empty($label)) {
326
            $label = $this->description;
327
        }
328
        $classfortooltip = 'classfortooltip';
329
        $dataparams = '';
330
        if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
331
            $params = [
332
                'id' => $this->fk_contrat,
333
                'objecttype' => $this->element,
334
            ];
335
            $classfortooltip = 'classforajaxtooltip';
336
            $dataparams = ' data-params="' . dol_escape_htmltag(json_encode($params)) . '"';
337
            $label = '';
338
        }
339
340
        $link = '<a href="' . DOL_URL_ROOT . '/contrat/card.php?id=' . $this->fk_contrat . '"';
341
        $link .= ($label ? ' title="' . dol_escape_htmltag($label, 1) . '"' : ' title="tocomplete"');
342
        $link .= $dataparams . ' class="' . $classfortooltip . '">';
343
        $linkend = '</a>';
344
345
        $picto = 'service';
346
        if ($this->type == 0) {
347
            $picto = 'product';
348
        }
349
350
        if ($withpicto) {
351
            $result .= ($link . img_object($label, $picto, $dataparams . ' class="' . $classfortooltip . '"') . $linkend);
352
        }
353
        if ($withpicto && $withpicto != 2) {
354
            $result .= ' ';
355
        }
356
        if ($withpicto != 2) {
357
            $result .= $link . ($this->product_ref ? $this->product_ref . ' ' : '') . ($this->label ? $this->label : $this->description) . $linkend;
358
        }
359
        return $result;
360
    }
361
362
    /**
363
     *  Load object in memory from database
364
     *
365
     *  @param  int     $id         Id object
366
     *  @param  string  $ref        Ref of contract line
367
     *  @return int                 Return integer <0 if KO, >0 if OK
368
     */
369
    public function fetch($id, $ref = '')
370
    {
371
        // Check parameters
372
        if (empty($id) && empty($ref)) {
373
            return -1;
374
        }
375
376
        $sql = "SELECT";
377
        $sql .= " t.rowid,";
378
        $sql .= " t.tms,";
379
        $sql .= " t.fk_contrat,";
380
        $sql .= " t.fk_product,";
381
        $sql .= " t.statut,";
382
        $sql .= " t.label,"; // This field is not used. Only label of product
383
        $sql .= " p.ref as product_ref,";
384
        $sql .= " p.label as product_label,";
385
        $sql .= " p.description as product_desc,";
386
        $sql .= " p.fk_product_type as product_type,";
387
        $sql .= " t.description,";
388
        $sql .= " t.date_commande,";
389
        $sql .= " t.date_ouverture_prevue as date_start,";
390
        $sql .= " t.date_ouverture as date_start_real,";
391
        $sql .= " t.date_fin_validite as date_end,";
392
        $sql .= " t.date_cloture as date_end_real,";
393
        $sql .= " t.tva_tx,";
394
        $sql .= " t.vat_src_code,";
395
        $sql .= " t.localtax1_tx,";
396
        $sql .= " t.localtax2_tx,";
397
        $sql .= " t.localtax1_type,";
398
        $sql .= " t.localtax2_type,";
399
        $sql .= " t.qty,";
400
        $sql .= " t.remise_percent,";
401
        $sql .= " t.remise,";
402
        $sql .= " t.fk_remise_except,";
403
        $sql .= " t.subprice,";
404
        $sql .= " t.price_ht,";
405
        $sql .= " t.total_ht,";
406
        $sql .= " t.total_tva,";
407
        $sql .= " t.total_localtax1,";
408
        $sql .= " t.total_localtax2,";
409
        $sql .= " t.total_ttc,";
410
        $sql .= " t.fk_product_fournisseur_price as fk_fournprice,";
411
        $sql .= " t.buy_price_ht as pa_ht,";
412
        $sql .= " t.info_bits,";
413
        $sql .= " t.fk_user_author,";
414
        $sql .= " t.fk_user_ouverture,";
415
        $sql .= " t.fk_user_cloture,";
416
        $sql .= " t.commentaire,";
417
        $sql .= " t.fk_unit,";
418
        $sql .= " t.rang";
419
        $sql .= " FROM " . MAIN_DB_PREFIX . "contratdet as t LEFT JOIN " . MAIN_DB_PREFIX . "product as p ON p.rowid = t.fk_product";
420
        if ($id) {
421
            $sql .= " WHERE t.rowid = " . ((int) $id);
422
        }
423
        if ($ref) {
424
            $sql .= " WHERE t.rowid = '" . $this->db->escape($ref) . "'";
425
        }
426
427
        dol_syslog(get_class($this) . "::fetch", LOG_DEBUG);
428
        $resql = $this->db->query($sql);
429
        if ($resql) {
430
            if ($this->db->num_rows($resql)) {
431
                $obj = $this->db->fetch_object($resql);
432
433
                $this->id    = $obj->rowid;
434
                $this->ref   = $obj->rowid;
435
436
                $this->tms = $this->db->jdate($obj->tms);
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->db->jdate($obj->tms) can also be of type string. However, the property $tms is declared as type integer. 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...
Deprecated Code introduced by
The property DoliCore\Base\GenericDocument::$tms has been deprecated: Use $date_modification ( Ignorable by Annotation )

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

436
                /** @scrutinizer ignore-deprecated */ $this->tms = $this->db->jdate($obj->tms);

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...
437
                $this->fk_contrat = $obj->fk_contrat;
438
                $this->fk_product = $obj->fk_product;
439
                $this->statut = $obj->statut;
440
                $this->product_ref = $obj->product_ref;
441
                $this->product_label = $obj->product_label;
442
                $this->product_type = $obj->product_type;
443
                $this->label = $obj->label; // deprecated. We do not use this field. Only ref and label of product, and description of contract line
444
                $this->description = $obj->description;
445
                $this->date_commande = $this->db->jdate($obj->date_commande);
446
447
                $this->date_start = $this->db->jdate($obj->date_start);
448
                $this->date_start_real = $this->db->jdate($obj->date_start_real);
449
                $this->date_end = $this->db->jdate($obj->date_end);
450
                $this->date_end_real = $this->db->jdate($obj->date_end_real);
451
                // For backward compatibility
452
                //$this->date_ouverture_prevue = $this->db->jdate($obj->date_ouverture_prevue);
453
                //$this->date_ouverture = $this->db->jdate($obj->date_ouverture);
454
                //$this->date_fin_validite = $this->db->jdate($obj->date_fin_validite);
455
                //$this->date_cloture = $this->db->jdate($obj->date_cloture);
456
457
                $this->tva_tx = $obj->tva_tx;
458
                $this->vat_src_code = $obj->vat_src_code;
459
                $this->localtax1_tx = $obj->localtax1_tx;
460
                $this->localtax2_tx = $obj->localtax2_tx;
461
                $this->localtax1_type = $obj->localtax1_type;
462
                $this->localtax2_type = $obj->localtax2_type;
463
                $this->qty = $obj->qty;
464
                $this->remise_percent = $obj->remise_percent;
465
                $this->fk_remise_except = $obj->fk_remise_except;
466
                $this->subprice = $obj->subprice;
467
                $this->price_ht = $obj->price_ht;
468
                $this->total_ht = $obj->total_ht;
469
                $this->total_tva = $obj->total_tva;
470
                $this->total_localtax1 = $obj->total_localtax1;
471
                $this->total_localtax2 = $obj->total_localtax2;
472
                $this->total_ttc = $obj->total_ttc;
473
                $this->info_bits = $obj->info_bits;
474
                $this->fk_user_author = $obj->fk_user_author;
475
                $this->fk_user_ouverture = $obj->fk_user_ouverture;
476
                $this->fk_user_cloture = $obj->fk_user_cloture;
477
                $this->commentaire = $obj->commentaire;
478
                $this->fk_fournprice = $obj->fk_fournprice;
479
480
                $marginInfos = getMarginInfos($obj->subprice, $obj->remise_percent, $obj->tva_tx, $obj->localtax1_tx, $obj->localtax2_tx, $this->fk_fournprice, $obj->pa_ht);
481
                $this->pa_ht = $marginInfos[0];
482
                $this->fk_unit = $obj->fk_unit;
483
484
                $this->rang = $obj->rang;
485
486
                $this->fetch_optionals();
487
            }
488
489
            $this->db->free($resql);
490
491
            return 1;
492
        } else {
493
            $this->error = "Error " . $this->db->lasterror();
494
            return -1;
495
        }
496
    }
497
498
499
    /**
500
     *      Update database for contract line
501
     *
502
     *      @param  User    $user           User that modify
503
     *      @param  int     $notrigger      0=no, 1=yes (no update trigger)
504
     *      @return int                     Return integer <0 if KO, >0 if OK
505
     */
506
    public function update($user, $notrigger = 0)
507
    {
508
        global $mysoc;
509
510
        $error = 0;
511
512
        // Clean parameters
513
        $this->fk_contrat = (int) $this->fk_contrat;
514
        $this->fk_product = (int) $this->fk_product;
515
        $this->statut = (int) $this->statut;
516
        $this->label = trim($this->label);
517
        $this->description = trim($this->description);
518
        $this->vat_src_code = trim($this->vat_src_code);
519
        $this->tva_tx = trim($this->tva_tx);
520
        $this->localtax1_tx = trim($this->localtax1_tx);
521
        $this->localtax2_tx = trim($this->localtax2_tx);
522
        $this->qty = trim($this->qty);
523
        $this->remise_percent = trim($this->remise_percent);
524
        $this->fk_remise_except = (int) $this->fk_remise_except;
525
        $this->subprice = price2num($this->subprice);
526
        $this->price_ht = price2num($this->price_ht);
527
        $this->info_bits = (int) $this->info_bits;
528
        $this->fk_user_author = (int) $this->fk_user_author;
529
        $this->fk_user_ouverture = (int) $this->fk_user_ouverture;
530
        $this->fk_user_cloture = (int) $this->fk_user_cloture;
531
        $this->commentaire = trim($this->commentaire);
532
        $this->rang = (int) $this->rang;
533
        //if (empty($this->subprice)) $this->subprice = 0;
534
        if (empty($this->price_ht)) {
535
            $this->price_ht = 0;
536
        }
537
        if (empty($this->total_ht)) {
538
            $this->total_ht = 0;
539
        }
540
        if (empty($this->total_tva)) {
541
            $this->total_tva = 0;
542
        }
543
        if (empty($this->total_ttc)) {
544
            $this->total_ttc = 0;
545
        }
546
        if (empty($this->localtax1_tx)) {
547
            $this->localtax1_tx = 0;
548
        }
549
        if (empty($this->localtax2_tx)) {
550
            $this->localtax2_tx = 0;
551
        }
552
        if (empty($this->remise_percent)) {
553
            $this->remise_percent = 0;
554
        }
555
556
        // Calcul du total TTC et de la TVA pour la ligne a partir de
557
        // qty, pu, remise_percent et txtva
558
        // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
559
        // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
560
        $localtaxes_type = getLocalTaxesFromRate($this->tva_tx, 0, $this->thirdparty, $mysoc);
561
562
        $tabprice = calcul_price_total($this->qty, $this->price_ht, $this->remise_percent, $this->tva_tx, $this->localtax1_tx, $this->localtax2_tx, 0, 'HT', 0, 1, $mysoc, $localtaxes_type);
563
        $this->total_ht  = $tabprice[0];
564
        $this->total_tva = $tabprice[1];
565
        $this->total_ttc = $tabprice[2];
566
        $this->total_localtax1 = $tabprice[9];
567
        $this->total_localtax2 = $tabprice[10];
568
569
        if (empty($this->pa_ht)) {
570
            $this->pa_ht = 0;
571
        }
572
573
        // if buy price not defined, define buyprice as configured in margin admin
574
        if ($this->pa_ht == 0) {
575
            $result = $this->defineBuyPrice($this->subprice, $this->remise_percent, $this->fk_product);
576
            if ($result < 0) {
577
                return -1;
578
            } else {
579
                $this->pa_ht = $result;
580
            }
581
        }
582
583
        // $this->oldcopy should have been set by the caller of update (here properties were already modified)
584
        if (empty($this->oldcopy)) {
585
            $this->oldcopy = dol_clone($this);
586
        }
587
588
        $this->db->begin();
589
590
        // Update request
591
        $sql = "UPDATE " . MAIN_DB_PREFIX . "contratdet SET";
592
        $sql .= " fk_contrat = " . ((int) $this->fk_contrat) . ",";
593
        $sql .= " fk_product = " . ($this->fk_product ? ((int) $this->fk_product) : 'null') . ",";
594
        $sql .= " statut = " . ((int) $this->statut) . ",";
595
        $sql .= " label = '" . $this->db->escape($this->label) . "',";
596
        $sql .= " description = '" . $this->db->escape($this->description) . "',";
597
        $sql .= " date_commande = " . ($this->date_commande != '' ? "'" . $this->db->idate($this->date_commande) . "'" : "null") . ",";
598
        $sql .= " date_ouverture_prevue = " . ($this->date_start != '' ? "'" . $this->db->idate($this->date_start) . "'" : "null") . ",";
599
        $sql .= " date_ouverture = " . ($this->date_start_real != '' ? "'" . $this->db->idate($this->date_start_real) . "'" : "null") . ",";
600
        $sql .= " date_fin_validite = " . ($this->date_end != '' ? "'" . $this->db->idate($this->date_end) . "'" : "null") . ",";
601
        $sql .= " date_cloture = " . ($this->date_end_real != '' ? "'" . $this->db->idate($this->date_end_real) . "'" : "null") . ",";
602
        $sql .= " vat_src_code = '" . $this->db->escape($this->vat_src_code) . "',";
603
        $sql .= " tva_tx = " . price2num($this->tva_tx) . ",";
604
        $sql .= " localtax1_tx = " . price2num($this->localtax1_tx) . ",";
605
        $sql .= " localtax2_tx = " . price2num($this->localtax2_tx) . ",";
606
        $sql .= " qty = " . price2num($this->qty) . ",";
607
        $sql .= " remise_percent = " . price2num($this->remise_percent) . ",";
608
        $sql .= " remise = " . ($this->remise ? price2num($this->remise) : "null") . ",";
609
        $sql .= " fk_remise_except = " . ($this->fk_remise_except > 0 ? $this->fk_remise_except : "null") . ",";
610
        $sql .= " subprice = " . ($this->subprice != '' ? $this->subprice : "null") . ",";
611
        $sql .= " price_ht = " . ($this->price_ht != '' ? $this->price_ht : "null") . ",";
612
        $sql .= " total_ht = " . $this->total_ht . ",";
613
        $sql .= " total_tva = " . $this->total_tva . ",";
614
        $sql .= " total_localtax1 = " . $this->total_localtax1 . ",";
615
        $sql .= " total_localtax2 = " . $this->total_localtax2 . ",";
616
        $sql .= " total_ttc = " . $this->total_ttc . ",";
617
        $sql .= " fk_product_fournisseur_price = " . (!empty($this->fk_fournprice) ? $this->fk_fournprice : "NULL") . ",";
618
        $sql .= " buy_price_ht = '" . price2num($this->pa_ht) . "',";
619
        $sql .= " info_bits = '" . $this->db->escape($this->info_bits) . "',";
620
        $sql .= " fk_user_author = " . ($this->fk_user_author >= 0 ? $this->fk_user_author : "NULL") . ",";
621
        $sql .= " fk_user_ouverture = " . ($this->fk_user_ouverture > 0 ? $this->fk_user_ouverture : "NULL") . ",";
622
        $sql .= " fk_user_cloture = " . ($this->fk_user_cloture > 0 ? $this->fk_user_cloture : "NULL") . ",";
623
        $sql .= " commentaire = '" . $this->db->escape($this->commentaire) . "',";
624
        $sql .= " fk_unit = " . (!$this->fk_unit ? 'NULL' : $this->fk_unit) . ",";
625
        $sql .= " rang = " . (empty($this->rang) ? '0' : (int) $this->rang);
626
        $sql .= " WHERE rowid = " . ((int) $this->id);
627
628
        dol_syslog(get_class($this) . "::update", LOG_DEBUG);
629
        $resql = $this->db->query($sql);
630
        if (!$resql) {
631
            $this->error = "Error " . $this->db->lasterror();
632
            $error++;
633
        }
634
635
        if (!$error) { // For avoid conflicts if trigger used
636
            $result = $this->insertExtraFields();
637
            if ($result < 0) {
638
                $error++;
639
            }
640
        }
641
642
        // If we change a planned date (start or end) of one contract line, sync dates for all other services too
643
        if (!$error && getDolGlobalString('CONTRACT_SYNC_PLANNED_DATE_OF_SERVICES')) {
644
            dol_syslog(get_class($this) . "::update CONTRACT_SYNC_PLANNED_DATE_OF_SERVICES is on so we update date for all lines", LOG_DEBUG);
645
646
            if ($this->date_start != $this->oldcopy->date_start) {
647
                $sql = 'UPDATE ' . MAIN_DB_PREFIX . 'contratdet SET';
648
                $sql .= " date_ouverture_prevue = " . ($this->date_start != '' ? "'" . $this->db->idate($this->date_start) . "'" : "null");
649
                $sql .= " WHERE fk_contrat = " . ((int) $this->fk_contrat);
650
651
                $resql = $this->db->query($sql);
652
                if (!$resql) {
653
                    $error++;
654
                    $this->error = "Error " . $this->db->lasterror();
655
                }
656
            }
657
            if ($this->date_end != $this->oldcopy->date_end) {
658
                $sql = 'UPDATE ' . MAIN_DB_PREFIX . 'contratdet SET';
659
                $sql .= " date_fin_validite = " . ($this->date_end != '' ? "'" . $this->db->idate($this->date_end) . "'" : "null");
660
                $sql .= " WHERE fk_contrat = " . ((int) $this->fk_contrat);
661
662
                $resql = $this->db->query($sql);
663
                if (!$resql) {
664
                    $error++;
665
                    $this->error = "Error " . $this->db->lasterror();
666
                }
667
            }
668
        }
669
670
        if (!$error && !$notrigger) {
671
            // Call trigger
672
            $result = $this->call_trigger('LINECONTRACT_MODIFY', $user);
673
            if ($result < 0) {
674
                $error++;
675
                $this->db->rollback();
676
            }
677
            // End call triggers
678
        }
679
680
        if (!$error) {
681
            $this->db->commit();
682
            return 1;
683
        } else {
684
            $this->db->rollback();
685
            $this->errors[] = $this->error;
686
            return -1;
687
        }
688
    }
689
690
691
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
692
    /**
693
     *  Update in database the fields total_xxx of lines
694
     *  Used by migration process
695
     *
696
     *  @return     int     Return integer <0 if KO, >0 if OK
697
     */
698
    public function update_total()
699
    {
700
		// phpcs:enable
701
        $this->db->begin();
702
703
        // Mise a jour ligne en base
704
        $sql = "UPDATE " . MAIN_DB_PREFIX . "contratdet SET";
705
        $sql .= " total_ht=" . price2num($this->total_ht, 'MT');
706
        $sql .= ",total_tva=" . price2num($this->total_tva, 'MT');
707
        $sql .= ",total_localtax1=" . price2num($this->total_localtax1, 'MT');
708
        $sql .= ",total_localtax2=" . price2num($this->total_localtax2, 'MT');
709
        $sql .= ",total_ttc=" . price2num($this->total_ttc, 'MT');
710
        $sql .= " WHERE rowid = " . ((int) $this->id);
711
712
        dol_syslog(get_class($this) . "::update_total", LOG_DEBUG);
713
714
        $resql = $this->db->query($sql);
715
        if ($resql) {
716
            $this->db->commit();
717
            return 1;
718
        } else {
719
            $this->error = $this->db->error();
720
            $this->db->rollback();
721
            return -2;
722
        }
723
    }
724
725
726
    /**
727
     * Inserts a contrat line into database
728
     *
729
     * @param int $notrigger Set to 1 if you don't want triggers to be fired
730
     * @return int Return integer <0 if KO, >0 if OK
731
     */
732
    public function insert($notrigger = 0)
733
    {
734
        global $user;
735
736
        $error = 0;
737
738
        // Insertion dans la base
739
        $sql = "INSERT INTO " . MAIN_DB_PREFIX . "contratdet";
740
        $sql .= " (fk_contrat, label, description, fk_product, qty, vat_src_code, tva_tx,";
741
        $sql .= " localtax1_tx, localtax2_tx, localtax1_type, localtax2_type, remise_percent, subprice,";
742
        $sql .= " total_ht, total_tva, total_localtax1, total_localtax2, total_ttc,";
743
        $sql .= " info_bits,";
744
        $sql .= " rang,";
745
        $sql .= " price_ht, remise, fk_product_fournisseur_price, buy_price_ht";
746
        if ($this->date_start > 0) {
747
            $sql .= ",date_ouverture_prevue";
748
        }
749
        if ($this->date_end > 0) {
750
            $sql .= ",date_fin_validite";
751
        }
752
        $sql .= ") VALUES ($this->fk_contrat, '', '" . $this->db->escape($this->description) . "',";
753
        $sql .= ($this->fk_product > 0 ? $this->fk_product : "null") . ",";
754
        $sql .= " '" . $this->db->escape($this->qty) . "',";
755
        $sql .= " '" . $this->db->escape($this->vat_src_code) . "',";
756
        $sql .= " '" . $this->db->escape($this->tva_tx) . "',";
757
        $sql .= " '" . $this->db->escape($this->localtax1_tx) . "',";
758
        $sql .= " '" . $this->db->escape($this->localtax2_tx) . "',";
759
        $sql .= " '" . $this->db->escape($this->localtax1_type) . "',";
760
        $sql .= " '" . $this->db->escape($this->localtax2_type) . "',";
761
        $sql .= " " . price2num($this->remise_percent) . "," . price2num($this->subprice) . ",";
762
        $sql .= " " . price2num($this->total_ht) . "," . price2num($this->total_tva) . "," . price2num($this->total_localtax1) . "," . price2num($this->total_localtax2) . "," . price2num($this->total_ttc) . ",";
763
        $sql .= " '" . $this->db->escape($this->info_bits) . "',";
764
        $sql .= " " . (empty($this->rang) ? '0' : (int) $this->rang) . ",";
765
        $sql .= " " . price2num($this->price_ht) . "," . price2num($this->remise) . ",";
766
        if ($this->fk_fournprice > 0) {
767
            $sql .= ' ' . ((int) $this->fk_fournprice) . ',';
768
        } else {
769
            $sql .= ' null,';
770
        }
771
        if ($this->pa_ht > 0) {
772
            $sql .= ' ' . ((float) price2num($this->pa_ht));
773
        } else {
774
            $sql .= ' null';
775
        }
776
        if ($this->date_start > 0) {
777
            $sql .= ",'" . $this->db->idate($this->date_start) . "'";
778
        }
779
        if ($this->date_end > 0) {
780
            $sql .= ",'" . $this->db->idate($this->date_end) . "'";
781
        }
782
        $sql .= ")";
783
784
        dol_syslog(get_class($this) . "::insert", LOG_DEBUG);
785
786
        $resql = $this->db->query($sql);
787
        if ($resql) {
788
            $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX . 'contratdet');
789
790
            // Insert of extrafields
791
            if (!$error) {
792
                $result = $this->insertExtraFields();
793
                if ($result < 0) {
794
                    $this->db->rollback();
795
                    return -1;
796
                }
797
            }
798
799
            if (!$notrigger) {
800
                // Call trigger
801
                $result = $this->call_trigger('LINECONTRACT_INSERT', $user);
802
                if ($result < 0) {
803
                    $this->db->rollback();
804
                    return -1;
805
                }
806
                // End call triggers
807
            }
808
809
            $this->db->commit();
810
            return 1;
811
        } else {
812
            $this->db->rollback();
813
            $this->error = $this->db->error() . " sql=" . $sql;
814
            return -1;
815
        }
816
    }
817
818
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
819
    /**
820
     *  Activate a contract line
821
     *
822
     * @param   User        $user       Object User who activate contract
823
     * @param   int         $date       Date real activation
824
     * @param   int|string  $date_end   Date planned end. Use '-1' to keep it unchanged.
825
     * @param   string      $comment    A comment typed by user
826
     * @return  int                     Return integer <0 if KO, >0 if OK
827
     */
828
    public function active_line($user, $date, $date_end = '', $comment = '')
829
    {
830
		// phpcs:enable
831
        $error = 0;
832
833
        $this->db->begin();
834
835
        $this->statut = ContratLigne::STATUS_OPEN;
836
        $this->date_start_real = $date;
837
        $this->date_end = $date_end;
838
        $this->fk_user_ouverture = $user->id;
839
        $this->date_end_real = null;
840
        $this->commentaire = $comment;
841
842
        $sql = "UPDATE " . MAIN_DB_PREFIX . "contratdet SET statut = " . ((int) $this->statut) . ",";
843
        $sql .= " date_ouverture = " . (dol_strlen($this->date_start_real) != 0 ? "'" . $this->db->idate($this->date_start_real) . "'" : "null") . ",";
844
        if ($date_end >= 0) {
845
            $sql .= " date_fin_validite = " . (dol_strlen($this->date_end) != 0 ? "'" . $this->db->idate($this->date_end) . "'" : "null") . ",";
846
        }
847
        $sql .= " fk_user_ouverture = " . ((int) $this->fk_user_ouverture) . ",";
848
        $sql .= " date_cloture = null,";
849
        $sql .= " commentaire = '" . $this->db->escape($comment) . "'";
850
        $sql .= " WHERE rowid = " . ((int) $this->id) . " AND (statut = " . ContratLigne::STATUS_INITIAL . " OR statut = " . ContratLigne::STATUS_CLOSED . ")";
851
852
        dol_syslog(get_class($this) . "::active_line", LOG_DEBUG);
853
        $resql = $this->db->query($sql);
854
        if ($resql) {
855
            // Call trigger
856
            $result = $this->call_trigger('LINECONTRACT_ACTIVATE', $user);
857
            if ($result < 0) {
858
                $error++;
859
            }
860
            // End call triggers
861
862
            if (!$error) {
863
                $this->db->commit();
864
                return 1;
865
            } else {
866
                $this->db->rollback();
867
                return -1;
868
            }
869
        } else {
870
            $this->error = $this->db->lasterror();
871
            $this->db->rollback();
872
            return -1;
873
        }
874
    }
875
876
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
877
    /**
878
     *  Close a contract line
879
     *
880
     * @param    User   $user           Object User who close contract
881
     * @param    int    $date_end_real  Date end
882
     * @param    string $comment        A comment typed by user
883
     * @param    int    $notrigger      1=Does not execute triggers, 0=Execute triggers
884
     * @return int                      Return integer <0 if KO, >0 if OK
885
     */
886
    public function close_line($user, $date_end_real, $comment = '', $notrigger = 0)
887
    {
888
		// phpcs:enable
889
        $this->date_cloture = $date_end_real;
890
        $this->date_end_real = $date_end_real;
891
        $this->user_closing_id = $user->id;
892
        $this->commentaire = $comment;
893
894
        $error = 0;
895
896
        // statut actif : 4
897
898
        $this->db->begin();
899
900
        $sql = "UPDATE " . MAIN_DB_PREFIX . "contratdet SET statut = " . ((int) ContratLigne::STATUS_CLOSED) . ",";
901
        $sql .= " date_cloture = '" . $this->db->idate($date_end_real) . "',";
902
        $sql .= " fk_user_cloture = " . ((int) $user->id) . ",";
903
        $sql .= " commentaire = '" . $this->db->escape($comment) . "'";
904
        $sql .= " WHERE rowid = " . ((int) $this->id) . " AND statut = " . ((int) ContratLigne::STATUS_OPEN);
905
906
        $resql = $this->db->query($sql);
907
        if ($resql) {
908
            if (!$notrigger) {
909
                // Call trigger
910
                $result = $this->call_trigger('LINECONTRACT_CLOSE', $user);
911
                if ($result < 0) {
912
                    $error++;
913
                    $this->db->rollback();
914
                    return -1;
915
                }
916
                // End call triggers
917
            }
918
919
            $this->db->commit();
920
            return 1;
921
        } else {
922
            $this->error = $this->db->lasterror();
923
            $this->db->rollback();
924
            return -1;
925
        }
926
    }
927
}
928