Test Failed
Branch main (a69845)
by Rafael
63:42
created

Propal::updateline()   F

Complexity

Conditions 23
Paths 3104

Size

Total Lines 164
Code Lines 108

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 23
eloc 108
c 0
b 0
f 0
nc 3104
nop 24
dl 0
loc 164
rs 0

How to fix   Long Method    Complexity    Many Parameters   

Long Method

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

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

Commonly applied refactorings include:

Many Parameters

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

There are several approaches to avoid long parameter lists:

1
<?php
2
3
/* Copyright (C) 2002-2004  Rodolphe Quiedeville    <[email protected]>
4
 * Copyright (C) 2004       Eric Seigne             <[email protected]>
5
 * Copyright (C) 2004-2011  Laurent Destailleur     <[email protected]>
6
 * Copyright (C) 2005       Marc Barilley           <[email protected]>
7
 * Copyright (C) 2005-2013  Regis Houssin           <[email protected]>
8
 * Copyright (C) 2006       Andre Cianfarani        <[email protected]>
9
 * Copyright (C) 2008       Raphael Bertrand        <[email protected]>
10
 * Copyright (C) 2010-2020  Juanjo Menent           <[email protected]>
11
 * Copyright (C) 2010-2022  Philippe Grand          <[email protected]>
12
 * Copyright (C) 2012-2014  Christophe Battarel     <[email protected]>
13
 * Copyright (C) 2012       Cedric Salvador         <[email protected]>
14
 * Copyright (C) 2013       Florian Henry           <[email protected]>
15
 * Copyright (C) 2014-2015  Marcos García           <[email protected]>
16
 * Copyright (C) 2018       Nicolas ZABOURI         <[email protected]>
17
 * Copyright (C) 2018-2024  Frédéric France         <[email protected]>
18
 * Copyright (C) 2018       Ferran Marcet           <[email protected]>
19
 * Copyright (C) 2022       ATM Consulting          <[email protected]>
20
 * Copyright (C) 2022       OpenDSI                 <[email protected]>
21
 * Copyright (C) 2022      	Gauthier VERDOL     	<[email protected]>
22
 * Copyright (C) 2023		William Mead			<[email protected]>
23
 * Copyright (C) 2024		MDW							<[email protected]>
24
 * Copyright (C) 2024       Rafael San José             <[email protected]>
25
 *
26
 * This program is free software; you can redistribute it and/or modify
27
 * it under the terms of the GNU General Public License as published by
28
 * the Free Software Foundation; either version 3 of the License, or
29
 * (at your option) any later version.
30
 *
31
 * This program is distributed in the hope that it will be useful,
32
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
33
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
34
 * GNU General Public License for more details.
35
 *
36
 * You should have received a copy of the GNU General Public License
37
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
38
 */
39
40
/**
41
 *  \file       htdocs/comm/propal/class/propal.class.php
42
 *  \brief      File of class to manage proposals
43
 */
44
45
require_once constant('DOL_DOCUMENT_ROOT') . '/core/class/commonobject.class.php';
46
require_once DOL_DOCUMENT_ROOT . "/core/class/commonobjectline.class.php";
47
require_once constant('DOL_DOCUMENT_ROOT') . '/product/class/product.class.php';
48
require_once constant('DOL_DOCUMENT_ROOT') . '/contact/class/contact.class.php';
49
require_once constant('DOL_DOCUMENT_ROOT') . '/margin/lib/margins.lib.php';
50
require_once constant('DOL_DOCUMENT_ROOT') . '/multicurrency/class/multicurrency.class.php';
51
require_once constant('DOL_DOCUMENT_ROOT') . '/core/class/commonincoterm.class.php';
52
53
/**
54
 *  Class to manage proposals
55
 */
56
class Propal extends CommonObject
57
{
58
    use CommonIncoterm;
59
60
    /**
61
     * @var string code
62
     */
63
    public $code = "";
64
65
    /**
66
     * @var string ID to identify managed object
67
     */
68
    public $element = 'propal';
69
70
    /**
71
     * @var string Name of table without prefix where object is stored
72
     */
73
    public $table_element = 'propal';
74
75
    /**
76
     * @var string    Name of subtable line
77
     */
78
    public $table_element_line = 'propaldet';
79
80
    /**
81
     * @var string Fieldname with ID of parent key if this field has a parent
82
     */
83
    public $fk_element = 'fk_propal';
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 = 'propal';
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
     * ID of the client
103
     * @var int
104
     */
105
    public $socid;
106
107
    /**
108
     * ID of the contact
109
     * @var int
110
     */
111
    public $contactid;
112
    public $author;
113
114
    /**
115
     * Ref from thirdparty
116
     * @var string
117
     * @deprecated
118
     * @see $ref_customer
119
     */
120
    public $ref_client;
121
122
    /**
123
     * Ref from thirdparty
124
     * @var string
125
     */
126
    public $ref_customer;
127
128
    /**
129
     * @var static oldcopy with propal properties
130
     */
131
    public $oldcopy;
132
133
    /**
134
     * Status of the quote
135
     * @var int
136
     * @deprecated Try to use $status now
137
     * @see Propal::STATUS_DRAFT, Propal::STATUS_VALIDATED, Propal::STATUS_SIGNED, Propal::STATUS_NOTSIGNED, Propal::STATUS_BILLED, Propal::STATUS_CANCELED
138
     */
139
    public $statut;
140
141
    /**
142
     * Status of the quote
143
     * @var int
144
     * @see Propal::STATUS_DRAFT, Propal::STATUS_VALIDATED, Propal::STATUS_SIGNED, Propal::STATUS_NOTSIGNED, Propal::STATUS_BILLED, Propal::STATUS_CANCELED
145
     */
146
    public $status;
147
148
    /**
149
     * @deprecated
150
     * @see $date_creation
151
     */
152
    public $datec;
153
154
    /**
155
     * @var integer|string $date_creation;
156
     */
157
    public $date_creation;
158
159
    /**
160
     * @deprecated
161
     * @see $date_validation
162
     */
163
    public $datev;
164
165
    /**
166
     * @var integer|string $date_validation;
167
     */
168
    public $date_validation;
169
170
    /**
171
     * @var integer|string $date_signature;
172
     */
173
    public $date_signature;
174
175
    /**
176
     * @var User $user_signature
177
     */
178
    public $user_signature;
179
180
    /**
181
     * @var integer|string date of the quote;
182
     */
183
    public $date;
184
185
    /**
186
     * @deprecated
187
     * @see $date
188
     */
189
    public $datep;
190
191
    /**
192
     * @var integer|string  $delivery_date;
193
     */
194
    public $delivery_date; // Date expected of shipment (date starting shipment, not the reception that occurs some days after)
195
196
197
    public $fin_validite;
198
199
    public $user_author_id;
200
201
    /**
202
     * @deprecated
203
     * @see $total_ht
204
     */
205
    public $price;
206
    /**
207
     * @deprecated
208
     * @see $total_tva
209
     */
210
    public $tva;
211
    /**
212
     * @deprecated
213
     * @see $total_ttc
214
     */
215
    public $total;
216
217
    public $cond_reglement_code;    // code
218
    public $cond_reglement;         // label
219
    public $cond_reglement_doc;     // label doc
220
221
    public $mode_reglement_code;    // code
222
    public $mode_reglement;         // label
223
224
    public $deposit_percent;
225
226
    /**
227
     * @var int ID
228
     * @deprecated
229
     */
230
    public $fk_address;
231
232
    public $address_type;
233
    public $address;
234
235
    /**
236
     * @var int availability ID
237
     */
238
    public $availability_id;
239
240
    /**
241
     * @var int availability ID
242
     * @deprecated
243
     * @see $availability_id
244
     */
245
    public $fk_availability;
246
247
    /**
248
     * @var string availability code
249
     */
250
    public $availability_code;
251
252
    /**
253
     * @var string availability label
254
     */
255
    public $availability;
256
257
    public $duree_validite;
258
259
    public $demand_reason_id;       // id
260
    public $demand_reason_code;     // code
261
    public $demand_reason;          // label
262
263
    public $warehouse_id;
264
265
    public $extraparams = array();
266
267
    /**
268
     * @var PropaleLigne[]
269
     */
270
    public $lines = array();
271
272
    /**
273
     * @var PropaleLigne
274
     */
275
    public $line;
276
277
    public $labelStatus = array();
278
    public $labelStatusShort = array();
279
280
281
    /**
282
     *  'type' if the field format ('integer', 'integer:ObjectClass:PathToClass[:AddCreateButtonOrNot[:Filter]]', 'varchar(x)', 'double(24,8)', 'real', 'price', 'text', 'html', 'date', 'datetime', 'timestamp', 'duration', 'mail', 'phone', 'url', 'password')
283
     *         Note: Filter can be a string like "(t.ref:like:'SO-%') or (t.date_creation:<:'20160101') or (t.nature:is:NULL)"
284
     *  'label' the translation key.
285
     *  'enabled' is a condition when the field must be managed.
286
     *  'position' is the sort order of field.
287
     *  'notnull' is set to 1 if not null in database. Set to -1 if we must set data to null if empty ('' or 0).
288
     *  'visible' says if field is visible in list (Examples: 0=Not visible, 1=Visible on list and create/update/view forms, 2=Visible on list only, 3=Visible on create/update/view form only (not list), 4=Visible on list and update/view form only (not create). 5=Visible on list and view only (not create/not update). Using a negative value means field is not shown by default on list but can be selected for viewing)
289
     *  'noteditable' says if field is not editable (1 or 0)
290
     *  'default' is a default value for creation (can still be overwrote by the Setup of Default Values if field is editable in creation form). Note: If default is set to '(PROV)' and field is 'ref', the default value will be set to '(PROVid)' where id is rowid when a new record is created.
291
     *  'index' if we want an index in database.
292
     *  'foreignkey'=>'tablename.field' if the field is a foreign key (it is recommended to name the field fk_...).
293
     *  'searchall' is 1 if we want to search in this field when making a search from the quick search button.
294
     *  'isameasure' must be set to 1 if you want to have a total on list for this field. Field type must be summable like integer or double(24,8).
295
     *  'css' is the CSS style to use on field. For example: 'maxwidth200'
296
     *  'help' is a string visible as a tooltip on field
297
     *  'showoncombobox' if value of the field must be visible into the label of the combobox that list record
298
     *  'disabled' is 1 if we want to have the field locked by a 'disabled' attribute. In most cases, this is never set into the definition of $fields into class, but is set dynamically by some part of code.
299
     *  'arrayofkeyval' to set list of value if type is a list of predefined values. For example: array("0"=>"Draft","1"=>"Active","-1"=>"Cancel")
300
     *  'comment' is not used. You can store here any text of your choice. It is not used by application.
301
     *
302
     *  Note: To have value dynamic, you can set value to 0 in definition and edit the value on the fly into the constructor.
303
     */
304
305
    // BEGIN MODULEBUILDER PROPERTIES
306
    /**
307
     * @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...
308
     */
309
    public $fields = array(
310
        'rowid' => array('type' => 'integer', 'label' => 'TechnicalID', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 10),
311
        'entity' => array('type' => 'integer', 'label' => 'Entity', 'default' => '1', 'enabled' => 1, 'visible' => -2, 'notnull' => 1, 'position' => 15, 'index' => 1),
312
        'ref' => array('type' => 'varchar(30)', 'label' => 'Ref', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'showoncombobox' => 1, 'position' => 20),
313
        'ref_client' => array('type' => 'varchar(255)', 'label' => 'RefCustomer', 'enabled' => 1, 'visible' => -1, 'position' => 22),
314
        'ref_ext' => array('type' => 'varchar(255)', 'label' => 'RefExt', 'enabled' => 1, 'visible' => 0, 'position' => 40),
315
        'fk_soc' => array('type' => 'integer:Societe:societe/class/societe.class.php', 'label' => 'ThirdParty', 'enabled' => 'isModEnabled("societe")', 'visible' => -1, 'position' => 23),
316
        'fk_projet' => array('type' => 'integer:Project:projet/class/project.class.php:1:(fk_statut:=:1)', 'label' => 'Fk projet', 'enabled' => "isModEnabled('project')", 'visible' => -1, 'position' => 24),
317
        'tms' => array('type' => 'timestamp', 'label' => 'DateModification', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 25),
318
        'datec' => array('type' => 'datetime', 'label' => 'DateCreation', 'enabled' => 1, 'visible' => -1, 'position' => 55),
319
        'datep' => array('type' => 'date', 'label' => 'Date', 'enabled' => 1, 'visible' => -1, 'position' => 60),
320
        'fin_validite' => array('type' => 'datetime', 'label' => 'DateEnd', 'enabled' => 1, 'visible' => -1, 'position' => 65),
321
        'date_valid' => array('type' => 'datetime', 'label' => 'DateValidation', 'enabled' => 1, 'visible' => -1, 'position' => 70),
322
        'date_cloture' => array('type' => 'datetime', 'label' => 'DateClosing', 'enabled' => 1, 'visible' => -1, 'position' => 75),
323
        'fk_user_author' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'Fk user author', 'enabled' => 1, 'visible' => -1, 'position' => 80),
324
        'fk_user_modif' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserModif', 'enabled' => 1, 'visible' => -2, 'notnull' => -1, 'position' => 85),
325
        'fk_user_valid' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserValidation', 'enabled' => 1, 'visible' => -1, 'position' => 90),
326
        'fk_user_cloture' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'Fk user cloture', 'enabled' => 1, 'visible' => -1, 'position' => 95),
327
        'price' => array('type' => 'double', 'label' => 'Price', 'enabled' => 1, 'visible' => -1, 'position' => 105),
328
        'total_ht' => array('type' => 'double(24,8)', 'label' => 'TotalHT', 'enabled' => 1, 'visible' => -1, 'position' => 125, 'isameasure' => 1),
329
        'total_tva' => array('type' => 'double(24,8)', 'label' => 'VAT', 'enabled' => 1, 'visible' => -1, 'position' => 130, 'isameasure' => 1),
330
        'localtax1' => array('type' => 'double(24,8)', 'label' => 'LocalTax1', 'enabled' => 1, 'visible' => -1, 'position' => 135, 'isameasure' => 1),
331
        'localtax2' => array('type' => 'double(24,8)', 'label' => 'LocalTax2', 'enabled' => 1, 'visible' => -1, 'position' => 140, 'isameasure' => 1),
332
        'total_ttc' => array('type' => 'double(24,8)', 'label' => 'TotalTTC', 'enabled' => 1, 'visible' => -1, 'position' => 145, 'isameasure' => 1),
333
        'fk_account' => array('type' => 'integer', 'label' => 'BankAccount', 'enabled' => 'isModEnabled("bank")', 'visible' => -1, 'position' => 150),
334
        'fk_currency' => array('type' => 'varchar(3)', 'label' => 'Currency', 'enabled' => 1, 'visible' => -1, 'position' => 155),
335
        'fk_cond_reglement' => array('type' => 'integer', 'label' => 'PaymentTerm', 'enabled' => 1, 'visible' => -1, 'position' => 160),
336
        'deposit_percent' => array('type' => 'varchar(63)', 'label' => 'DepositPercent', 'enabled' => 1, 'visible' => -1, 'position' => 161),
337
        'fk_mode_reglement' => array('type' => 'integer', 'label' => 'PaymentMode', 'enabled' => 1, 'visible' => -1, 'position' => 165),
338
        'note_private' => array('type' => 'html', 'label' => 'NotePrivate', 'enabled' => 1, 'visible' => 0, 'position' => 170),
339
        'note_public' => array('type' => 'html', 'label' => 'NotePublic', 'enabled' => 1, 'visible' => 0, 'position' => 175),
340
        'model_pdf' => array('type' => 'varchar(255)', 'label' => 'PDFTemplate', 'enabled' => 1, 'visible' => 0, 'position' => 180),
341
        'date_livraison' => array('type' => 'date', 'label' => 'DateDeliveryPlanned', 'enabled' => 1, 'visible' => -1, 'position' => 185),
342
        'fk_shipping_method' => array('type' => 'integer', 'label' => 'ShippingMethod', 'enabled' => 1, 'visible' => -1, 'position' => 190),
343
        'fk_warehouse' => array('type' => 'integer:Entrepot:product/stock/class/entrepot.class.php', 'label' => 'Fk warehouse', 'enabled' => 'isModEnabled("stock")', 'visible' => -1, 'position' => 191),
344
        'fk_availability' => array('type' => 'integer', 'label' => 'Availability', 'enabled' => 1, 'visible' => -1, 'position' => 195),
345
        'fk_delivery_address' => array('type' => 'integer', 'label' => 'DeliveryAddress', 'enabled' => 1, 'visible' => 0, 'position' => 200), // deprecated
346
        'fk_input_reason' => array('type' => 'integer', 'label' => 'InputReason', 'enabled' => 1, 'visible' => -1, 'position' => 205),
347
        'extraparams' => array('type' => 'varchar(255)', 'label' => 'Extraparams', 'enabled' => 1, 'visible' => -1, 'position' => 215),
348
        'fk_incoterms' => array('type' => 'integer', 'label' => 'IncotermCode', 'enabled' => '$conf->incoterm->enabled', 'visible' => -1, 'position' => 220),
349
        'location_incoterms' => array('type' => 'varchar(255)', 'label' => 'IncotermLabel', 'enabled' => '$conf->incoterm->enabled', 'visible' => -1, 'position' => 225),
350
        'fk_multicurrency' => array('type' => 'integer', 'label' => 'MulticurrencyID', 'enabled' => 1, 'visible' => -1, 'position' => 230),
351
        'multicurrency_code' => array('type' => 'varchar(255)', 'label' => 'MulticurrencyCurrency', 'enabled' => 'isModEnabled("multicurrency")', 'visible' => -1, 'position' => 235),
352
        'multicurrency_tx' => array('type' => 'double(24,8)', 'label' => 'MulticurrencyRate', 'enabled' => 'isModEnabled("multicurrency")', 'visible' => -1, 'position' => 240, 'isameasure' => 1),
353
        'multicurrency_total_ht' => array('type' => 'double(24,8)', 'label' => 'MulticurrencyAmountHT', 'enabled' => 'isModEnabled("multicurrency")', 'visible' => -1, 'position' => 245, 'isameasure' => 1),
354
        'multicurrency_total_tva' => array('type' => 'double(24,8)', 'label' => 'MulticurrencyAmountVAT', 'enabled' => 'isModEnabled("multicurrency")', 'visible' => -1, 'position' => 250, 'isameasure' => 1),
355
        'multicurrency_total_ttc' => array('type' => 'double(24,8)', 'label' => 'MulticurrencyAmountTTC', 'enabled' => 'isModEnabled("multicurrency")', 'visible' => -1, 'position' => 255, 'isameasure' => 1),
356
        'last_main_doc' => array('type' => 'varchar(255)', 'label' => 'LastMainDoc', 'enabled' => 1, 'visible' => -1, 'position' => 260),
357
        'fk_statut' => array('type' => 'smallint(6)', 'label' => 'Status', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 500),
358
        'import_key' => array('type' => 'varchar(14)', 'label' => 'ImportId', 'enabled' => 1, 'visible' => -2, 'position' => 900),
359
    );
360
    // END MODULEBUILDER PROPERTIES
361
362
    /**
363
     * Canceled status
364
     */
365
    const STATUS_CANCELED = -1;
366
    /**
367
     * Draft status
368
     */
369
    const STATUS_DRAFT = 0;
370
    /**
371
     * Validated status
372
     */
373
    const STATUS_VALIDATED = 1;
374
    /**
375
     * Signed quote
376
     */
377
    const STATUS_SIGNED = 2;
378
    /**
379
     * Not signed quote
380
     */
381
    const STATUS_NOTSIGNED = 3;
382
    /**
383
     * Billed or processed quote
384
     */
385
    const STATUS_BILLED = 4; // Todo rename into STATUS_CLOSE ?
386
387
388
    /**
389
     *  Constructor
390
     *
391
     *  @param      DoliDB  $db         Database handler
392
     *  @param      int     $socid      Id third party
393
     *  @param      int     $propalid   Id proposal
394
     */
395
    public function __construct($db, $socid = 0, $propalid = 0)
396
    {
397
        $this->db = $db;
398
399
        $this->ismultientitymanaged = 1;
400
        $this->socid = $socid;
401
        $this->id = $propalid;
402
403
        $this->duree_validite = getDolGlobalInt('PROPALE_VALIDITY_DURATION', 0);
404
    }
405
406
407
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
408
    /**
409
     *  Add line into array ->lines
410
     *  $this->thirdparty should be loaded
411
     *
412
     *  @param  int     $idproduct          Product Id to add
413
     *  @param  float   $qty                Quantity
414
     *  @param  float   $remise_percent     Discount effected on Product
415
     *  @return int                         Return integer <0 if KO, >0 if OK
416
     *
417
     *  TODO    Replace calls to this function by generation object Ligne
418
     */
419
    public function add_product($idproduct, $qty, $remise_percent = 0)
420
    {
421
		// phpcs:enable
422
        global $conf, $mysoc;
423
424
        if (!$qty) {
425
            $qty = 1;
426
        }
427
428
        dol_syslog(get_class($this) . "::add_product $idproduct, $qty, $remise_percent");
429
        if ($idproduct > 0) {
430
            $prod = new Product($this->db);
431
            $prod->fetch($idproduct);
432
433
            $productdesc = $prod->description;
434
435
            $tva_tx = get_default_tva($mysoc, $this->thirdparty, $prod->id);
436
            $tva_npr = get_default_npr($mysoc, $this->thirdparty, $prod->id);
437
            if (empty($tva_tx)) {
438
                $tva_npr = 0;
439
            }
440
            $vat_src_code = ''; // May be defined into tva_tx
441
442
            $localtax1_tx = get_localtax($tva_tx, 1, $mysoc, $this->thirdparty, $tva_npr);
443
            $localtax2_tx = get_localtax($tva_tx, 2, $mysoc, $this->thirdparty, $tva_npr);
444
445
            // multiprices
446
            if ($conf->global->PRODUIT_MULTIPRICES && $this->thirdparty->price_level) {
447
                $price = $prod->multiprices[$this->thirdparty->price_level];
448
            } else {
449
                $price = $prod->price;
450
            }
451
452
            $line = new PropaleLigne($this->db);
453
454
            $line->fk_product = $idproduct;
455
            $line->desc = $productdesc;
456
            $line->qty = $qty;
457
            $line->subprice = $price;
458
            $line->remise_percent = $remise_percent;
459
            $line->vat_src_code = $vat_src_code;
460
            $line->tva_tx = $tva_tx;
461
            $line->fk_unit = $prod->fk_unit;
462
            if ($tva_npr) {
463
                $line->info_bits = 1;
464
            }
465
466
            $this->lines[] = $line;
467
        }
468
469
        return 1;
470
    }
471
472
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
473
    /**
474
     *  Adding line of fixed discount in the proposal in DB
475
     *
476
     *  @param     int      $idremise           Id of fixed discount
477
     *  @return    int                          >0 if OK, <0 if KO
478
     */
479
    public function insert_discount($idremise)
480
    {
481
		// phpcs:enable
482
        global $langs;
483
484
        include_once DOL_DOCUMENT_ROOT . '/core/lib/price.lib.php';
485
        include_once DOL_DOCUMENT_ROOT . '/core/class/discount.class.php';
486
487
        $this->db->begin();
488
489
        $remise = new DiscountAbsolute($this->db);
490
        $result = $remise->fetch($idremise);
491
492
        if ($result > 0) {
493
            if ($remise->fk_facture) {  // Protection against multiple submission
494
                $this->error = $langs->trans("ErrorDiscountAlreadyUsed");
495
                $this->db->rollback();
496
                return -5;
497
            }
498
499
            $line = new PropaleLigne($this->db);
500
501
            $line->context = $this->context;
502
503
            $line->fk_propal = $this->id;
504
            $line->fk_remise_except = $remise->id;
505
            $line->desc = $remise->description; // Description ligne
506
            $line->vat_src_code = $remise->vat_src_code;
507
            $line->tva_tx = $remise->tva_tx;
508
            $line->subprice = -$remise->amount_ht;
509
            $line->fk_product = 0; // Id produit predefined
510
            $line->qty = 1;
511
            $line->remise_percent = 0;
512
            $line->rang = -1;
513
            $line->info_bits = 2;
514
515
            // TODO deprecated
516
            $line->price = -$remise->amount_ht;
517
518
            $line->total_ht  = -$remise->amount_ht;
519
            $line->total_tva = -$remise->amount_tva;
520
            $line->total_ttc = -$remise->amount_ttc;
521
522
            $result = $line->insert();
523
            if ($result > 0) {
524
                $result = $this->update_price(1);
525
                if ($result > 0) {
526
                    $this->db->commit();
527
                    return 1;
528
                } else {
529
                    $this->db->rollback();
530
                    return -1;
531
                }
532
            } else {
533
                $this->error = $line->error;
534
                $this->errors = $line->errors;
535
                $this->db->rollback();
536
                return -2;
537
            }
538
        } else {
539
            $this->db->rollback();
540
            return -2;
541
        }
542
    }
543
544
    /**
545
     *      Add a proposal line into database (linked to product/service or not)
546
     *      The parameters are already supposed to be appropriate and with final values to the call
547
     *      of this method. Also, for the VAT rate, it must have already been defined
548
     *      by whose calling the method get_default_tva (societe_vendeuse, societe_acheteuse, '' product)
549
     *      and desc must already have the right value (it's up to the caller to manage multilanguage)
550
     *
551
     *      @param      string      $desc               Description of line
552
     *      @param      float       $pu_ht              Unit price
553
     *      @param      float       $qty                Quantity
554
     *      @param      float|string    $txtva              Force Vat rate, -1 for auto (Can contain the vat_src_code too with syntax '9.9 (CODE)')
555
     *      @param      float       $txlocaltax1        Local tax 1 rate (deprecated, use instead txtva with code inside)
556
     *      @param      float       $txlocaltax2        Local tax 2 rate (deprecated, use instead txtva with code inside)
557
     *      @param      int         $fk_product         Product/Service ID predefined
558
     *      @param      float       $remise_percent     Pourcentage de remise de la ligne
559
     *      @param      string      $price_base_type    HT or TTC
560
     *      @param      float       $pu_ttc             Prix unitaire TTC
561
     *      @param      int         $info_bits          Bits for type of lines
562
     *      @param      int         $type               Type of line (0=product, 1=service). Not used if fk_product is defined, the type of product is used.
563
     *      @param      int         $rang               Position of line
564
     *      @param      int         $special_code       Special code (also used by externals modules!)
565
     *      @param      int         $fk_parent_line     Id of parent line
566
     *      @param      int         $fk_fournprice      Id supplier price
567
     *      @param      int         $pa_ht              Buying price without tax
568
     *      @param      string      $label              ???
569
     *      @param      int|string  $date_start         Start date of the line
570
     *      @param      int|string  $date_end           End date of the line
571
     *      @param      array       $array_options      extrafields array
572
     *      @param      int|null    $fk_unit            Code of the unit to use. Null to use the default one
573
     *      @param      string      $origin             Depend on global conf MAIN_CREATEFROM_KEEP_LINE_ORIGIN_INFORMATION can be 'orderdet', 'propaldet'..., else 'order','propal,'....
574
     *      @param      int         $origin_id          Depend on global conf MAIN_CREATEFROM_KEEP_LINE_ORIGIN_INFORMATION can be Id of origin object (aka line id), else object id
575
     *      @param      double      $pu_ht_devise       Unit price in currency
576
     *      @param      int         $fk_remise_except   Id discount if line is from a discount
577
     *      @param      int         $noupdateafterinsertline    No update after insert of line
578
     *      @return     int                             >0 if OK, <0 if KO
579
     *      @see        add_product()
580
     */
581
    public function addline($desc, $pu_ht, $qty, $txtva, $txlocaltax1 = 0.0, $txlocaltax2 = 0.0, $fk_product = 0, $remise_percent = 0.0, $price_base_type = 'HT', $pu_ttc = 0.0, $info_bits = 0, $type = 0, $rang = -1, $special_code = 0, $fk_parent_line = 0, $fk_fournprice = 0, $pa_ht = 0, $label = '', $date_start = '', $date_end = '', $array_options = array(), $fk_unit = null, $origin = '', $origin_id = 0, $pu_ht_devise = 0, $fk_remise_except = 0, $noupdateafterinsertline = 0)
582
    {
583
        global $mysoc, $conf, $langs;
584
585
        dol_syslog(get_class($this) . "::addline propalid=$this->id, desc=$desc, pu_ht=$pu_ht, qty=$qty, txtva=$txtva, fk_product=$fk_product, remise_except=$remise_percent, price_base_type=$price_base_type, pu_ttc=$pu_ttc, info_bits=$info_bits, type=$type, fk_remise_except=" . $fk_remise_except);
586
587
        if ($this->statut == self::STATUS_DRAFT) {
0 ignored issues
show
Deprecated Code introduced by
The property Propal::$statut has been deprecated: Try to use $status now ( Ignorable by Annotation )

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

587
        if (/** @scrutinizer ignore-deprecated */ $this->statut == self::STATUS_DRAFT) {

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...
588
            include_once DOL_DOCUMENT_ROOT . '/core/lib/price.lib.php';
589
590
            // Clean parameters
591
            if (empty($remise_percent)) {
592
                $remise_percent = 0;
593
            }
594
            if (empty($qty)) {
595
                $qty = 0;
596
            }
597
            if (empty($info_bits)) {
598
                $info_bits = 0;
599
            }
600
            if (empty($rang)) {
601
                $rang = 0;
602
            }
603
            if (empty($fk_parent_line) || $fk_parent_line < 0) {
604
                $fk_parent_line = 0;
605
            }
606
607
            $remise_percent = price2num($remise_percent);
608
            $qty = (float) price2num($qty);
609
            $pu_ht = price2num($pu_ht);
610
            $pu_ht_devise = price2num($pu_ht_devise);
611
            $pu_ttc = price2num($pu_ttc);
612
            if (!preg_match('/\((.*)\)/', (string) $txtva)) {
613
                $txtva = price2num($txtva); // $txtva can have format '5,1' or '5.1' or '5.1(XXX)', we must clean only if '5,1'
614
            }
615
            $txlocaltax1 = price2num($txlocaltax1);
616
            $txlocaltax2 = price2num($txlocaltax2);
617
            $pa_ht = price2num($pa_ht);
618
            if ($price_base_type == 'HT') {
619
                $pu = $pu_ht;
620
            } else {
621
                $pu = $pu_ttc;
622
            }
623
624
            // Check parameters
625
            if ($type < 0) {
626
                return -1;
627
            }
628
629
            if ($date_start && $date_end && $date_start > $date_end) {
630
                $langs->load("errors");
631
                $this->error = $langs->trans('ErrorStartDateGreaterEnd');
632
                return -1;
633
            }
634
635
            $this->db->begin();
636
637
            $product_type = $type;
638
            if (!empty($fk_product) && $fk_product > 0) {
639
                $product = new Product($this->db);
640
                $result = $product->fetch($fk_product);
641
                $product_type = $product->type;
642
643
                if (getDolGlobalString('STOCK_MUST_BE_ENOUGH_FOR_PROPOSAL') && $product_type == 0 && $product->stock_reel < $qty) {
644
                    $langs->load("errors");
645
                    $this->error = $langs->trans('ErrorStockIsNotEnoughToAddProductOnProposal', $product->ref);
646
                    $this->db->rollback();
647
                    return -3;
648
                }
649
            }
650
651
            // Calcul du total TTC et de la TVA pour la ligne a partir de
652
            // qty, pu, remise_percent et txtva
653
            // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
654
            // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
655
656
            $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $this->thirdparty, $mysoc);
657
658
            // Clean vat code
659
            $reg = array();
660
            $vat_src_code = '';
661
            if (preg_match('/\((.*)\)/', $txtva, $reg)) {
662
                $vat_src_code = $reg[1];
663
                $txtva = preg_replace('/\s*\(.*\)/', '', $txtva); // Remove code into vatrate.
664
            }
665
666
            $tabprice = calcul_price_total($qty, $pu, $remise_percent, $txtva, $txlocaltax1, $txlocaltax2, 0, $price_base_type, $info_bits, $product_type, $mysoc, $localtaxes_type, 100, $this->multicurrency_tx, $pu_ht_devise);
667
668
            $total_ht  = $tabprice[0];
669
            $total_tva = $tabprice[1];
670
            $total_ttc = $tabprice[2];
671
            $total_localtax1 = $tabprice[9];
672
            $total_localtax2 = $tabprice[10];
673
            $pu_ht  = $tabprice[3];
674
            $pu_tva = $tabprice[4];
675
            $pu_ttc = $tabprice[5];
676
677
            // MultiCurrency
678
            $multicurrency_total_ht  = $tabprice[16];
679
            $multicurrency_total_tva = $tabprice[17];
680
            $multicurrency_total_ttc = $tabprice[18];
681
            $pu_ht_devise = $tabprice[19];
682
683
            // Rang to use
684
            $ranktouse = $rang;
685
            if ($ranktouse == -1) {
686
                $rangmax = $this->line_max($fk_parent_line);
687
                $ranktouse = $rangmax + 1;
688
            }
689
690
            // TODO A virer
691
            // Anciens indicateurs: $price, $remise (a ne plus utiliser)
692
            $price = $pu;
693
            $remise = 0;
694
            if ((float) $remise_percent > 0) {
695
                $remise = round(((float) $pu * (float) $remise_percent / 100), 2);
696
                $price = (float) $pu - $remise;
697
            }
698
699
            // Insert line
700
            $this->line = new PropaleLigne($this->db);
701
702
            $this->line->context = $this->context;
703
704
            $this->line->fk_propal = $this->id;
705
            $this->line->label = $label;
706
            $this->line->desc = $desc;
707
            $this->line->qty = $qty;
708
709
            $this->line->vat_src_code = $vat_src_code;
710
            $this->line->tva_tx = $txtva;
711
            $this->line->localtax1_tx = ($total_localtax1 ? $localtaxes_type[1] : 0);
712
            $this->line->localtax2_tx = ($total_localtax2 ? $localtaxes_type[3] : 0);
713
            $this->line->localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
714
            $this->line->localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
715
            $this->line->fk_product = $fk_product;
716
            $this->line->product_type = $type;
717
            $this->line->fk_remise_except = $fk_remise_except;
718
            $this->line->remise_percent = $remise_percent;
719
            $this->line->subprice = $pu_ht;
720
            $this->line->rang = $ranktouse;
721
            $this->line->info_bits = $info_bits;
722
            $this->line->total_ht = $total_ht;
723
            $this->line->total_tva = $total_tva;
724
            $this->line->total_localtax1 = $total_localtax1;
725
            $this->line->total_localtax2 = $total_localtax2;
726
            $this->line->total_ttc = $total_ttc;
727
            $this->line->special_code = $special_code;
728
            $this->line->fk_parent_line = $fk_parent_line;
729
            $this->line->fk_unit = $fk_unit;
730
731
            $this->line->date_start = $date_start;
732
            $this->line->date_end = $date_end;
733
734
            $this->line->fk_fournprice = $fk_fournprice;
735
            $this->line->pa_ht = $pa_ht;
736
737
            $this->line->origin_id = $origin_id;
738
            $this->line->origin = $origin;
0 ignored issues
show
Deprecated Code introduced by
The property 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

738
            /** @scrutinizer ignore-deprecated */ $this->line->origin = $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...
739
740
            // Multicurrency
741
            $this->line->fk_multicurrency = $this->fk_multicurrency;
742
            $this->line->multicurrency_code = $this->multicurrency_code;
743
            $this->line->multicurrency_subprice     = $pu_ht_devise;
744
            $this->line->multicurrency_total_ht     = $multicurrency_total_ht;
745
            $this->line->multicurrency_total_tva    = $multicurrency_total_tva;
746
            $this->line->multicurrency_total_ttc    = $multicurrency_total_ttc;
747
748
            // Mise en option de la ligne
749
            if (empty($qty) && empty($special_code)) {
750
                $this->line->special_code = 3;
751
            }
752
753
            // TODO deprecated
754
            $this->line->price = $price;
755
756
            if (is_array($array_options) && count($array_options) > 0) {
757
                $this->line->array_options = $array_options;
758
            }
759
760
            $result = $this->line->insert();
761
            if ($result > 0) {
762
                // Reorder if child line
763
                if (!empty($fk_parent_line)) {
764
                    $this->line_order(true, 'DESC');
765
                } elseif ($ranktouse > 0 && $ranktouse <= count($this->lines)) { // Update all rank of all other lines
766
                    $linecount = count($this->lines);
767
                    for ($ii = $ranktouse; $ii <= $linecount; $ii++) {
768
                        $this->updateRangOfLine($this->lines[$ii - 1]->id, $ii + 1);
769
                    }
770
                }
771
772
                // Mise a jour information denormalisees au niveau de la propale meme
773
                if (empty($noupdateafterinsertline)) {
774
                    $result = $this->update_price(1, 'auto', 0, $mysoc); // This method is designed to add line from user input so total calculation must be done using 'auto' mode.
775
                }
776
777
                if ($result > 0) {
778
                    $this->db->commit();
779
                    return $this->line->id;
780
                } else {
781
                    $this->error = $this->db->error();
782
                    $this->db->rollback();
783
                    return -1;
784
                }
785
            } else {
786
                $this->error = $this->line->error;
787
                $this->errors = $this->line->errors;
788
                $this->db->rollback();
789
                return -2;
790
            }
791
        } else {
792
            dol_syslog(get_class($this) . "::addline status of proposal must be Draft to allow use of ->addline()", LOG_ERR);
793
            return -3;
794
        }
795
    }
796
797
798
    /**
799
     *  Update a proposal line
800
     *
801
     *  @param      int         $rowid              Id of line
802
     *  @param      float       $pu                 Unit price (HT or TTC depending on price_base_type)
803
     *  @param      float       $qty                Quantity
804
     *  @param      float       $remise_percent     Discount on line
805
     *  @param      float|string    $txtva              VAT Rate (Can be '1.23' or '1.23 (ABC)')
806
     *  @param      float       $txlocaltax1        Local tax 1 rate
807
     *  @param      float       $txlocaltax2        Local tax 2 rate
808
     *  @param      string      $desc               Description
809
     *  @param      string      $price_base_type    HT or TTC
810
     *  @param      int         $info_bits          Miscellaneous information
811
     *  @param      int         $special_code       Special code (also used by externals modules!)
812
     *  @param      int         $fk_parent_line     Id of parent line (0 in most cases, used by modules adding sublevels into lines).
813
     *  @param      int         $skip_update_total  Keep fields total_xxx to 0 (used for special lines by some modules)
814
     *  @param      int         $fk_fournprice      Id of origin supplier price
815
     *  @param      int         $pa_ht              Price (without tax) of product when it was bought
816
     *  @param      string      $label              ???
817
     *  @param      int         $type               0/1=Product/service
818
     *  @param      int|string  $date_start         Start date of the line
819
     *  @param      int|string  $date_end           End date of the line
820
     *  @param      array       $array_options      extrafields array
821
     *  @param      int|null    $fk_unit            Code of the unit to use. Null to use the default one
822
     *  @param      double      $pu_ht_devise       Unit price in currency
823
     *  @param      int         $notrigger          disable line update trigger
824
     * @param       integer $rang   line rank
825
     *  @return     int                             0 if OK, <0 if KO
826
     */
827
    public function updateline($rowid, $pu, $qty, $remise_percent, $txtva, $txlocaltax1 = 0.0, $txlocaltax2 = 0.0, $desc = '', $price_base_type = 'HT', $info_bits = 0, $special_code = 0, $fk_parent_line = 0, $skip_update_total = 0, $fk_fournprice = 0, $pa_ht = 0, $label = '', $type = 0, $date_start = '', $date_end = '', $array_options = array(), $fk_unit = null, $pu_ht_devise = 0, $notrigger = 0, $rang = 0)
828
    {
829
        global $mysoc, $langs;
830
831
        dol_syslog(get_class($this) . "::updateLine rowid=$rowid, pu=$pu, qty=$qty, remise_percent=$remise_percent,
832
        txtva=$txtva, desc=$desc, price_base_type=$price_base_type, info_bits=$info_bits, special_code=$special_code, fk_parent_line=$fk_parent_line, pa_ht=$pa_ht, type=$type, date_start=$date_start, date_end=$date_end");
833
        include_once DOL_DOCUMENT_ROOT . '/core/lib/price.lib.php';
834
835
        // Clean parameters
836
        $remise_percent = price2num($remise_percent);
837
        $qty = (float) price2num($qty);
838
        $pu = price2num($pu);
839
        $pu_ht_devise = price2num($pu_ht_devise);
840
        if (!preg_match('/\((.*)\)/', (string) $txtva)) {
841
            $txtva = price2num($txtva); // $txtva can have format '5.0(XXX)' or '5'
842
        }
843
        $txlocaltax1 = price2num($txlocaltax1);
844
        $txlocaltax2 = price2num($txlocaltax2);
845
        $pa_ht = price2num($pa_ht);
846
        if (empty($qty) && empty($special_code)) {
847
            $special_code = 3; // Set option tag
848
        }
849
        if (!empty($qty) && $special_code == 3) {
850
            $special_code = 0; // Remove option tag
851
        }
852
        if (empty($type)) {
853
            $type = 0;
854
        }
855
856
        if ($date_start && $date_end && $date_start > $date_end) {
857
            $langs->load("errors");
858
            $this->error = $langs->trans('ErrorStartDateGreaterEnd');
859
            return -1;
860
        }
861
862
        if ($this->status == self::STATUS_DRAFT) {
863
            $this->db->begin();
864
865
            // Calcul du total TTC et de la TVA pour la ligne a partir de
866
            // qty, pu, remise_percent et txtva
867
            // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
868
            // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
869
870
            $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $this->thirdparty, $mysoc);
871
872
            // Clean vat code
873
            $reg = array();
874
            $vat_src_code = '';
875
            if (preg_match('/\((.*)\)/', $txtva, $reg)) {
876
                $vat_src_code = $reg[1];
877
                $txtva = preg_replace('/\s*\(.*\)/', '', $txtva); // Remove code into vatrate.
878
            }
879
880
            // TODO Implement  if (getDolGlobalInt('MAIN_UNIT_PRICE_WITH_TAX_IS_FOR_ALL_TAXES')) ?
881
882
            $tabprice = calcul_price_total($qty, $pu, $remise_percent, $txtva, $txlocaltax1, $txlocaltax2, 0, $price_base_type, $info_bits, $type, $mysoc, $localtaxes_type, 100, $this->multicurrency_tx, $pu_ht_devise);
883
            $total_ht  = $tabprice[0];
884
            $total_tva = $tabprice[1];
885
            $total_ttc = $tabprice[2];
886
            $total_localtax1 = $tabprice[9];
887
            $total_localtax2 = $tabprice[10];
888
            $pu_ht  = $tabprice[3];
889
            $pu_tva = $tabprice[4];
890
            $pu_ttc = $tabprice[5];
891
892
            // MultiCurrency
893
            $multicurrency_total_ht  = $tabprice[16];
894
            $multicurrency_total_tva = $tabprice[17];
895
            $multicurrency_total_ttc = $tabprice[18];
896
            $pu_ht_devise = $tabprice[19];
897
898
            // Anciens indicateurs: $price, $remise (a ne plus utiliser)
899
            $price = $pu;
900
            $remise = 0;
901
            if ((float) $remise_percent > 0) {
902
                $remise = round(((float) $pu * (float) $remise_percent / 100), 2);
903
                $price = (float) $pu - $remise;
904
            }
905
906
            //Fetch current line from the database and then clone the object and set it in $oldline property
907
            $line = new PropaleLigne($this->db);
908
            $line->fetch($rowid);
909
910
            $staticline = clone $line;
911
912
            $line->oldline = $staticline;
913
            $this->line = $line;
914
            $this->line->context = $this->context;
915
            $this->line->rang = $rang;
916
917
            // Reorder if fk_parent_line change
918
            if (!empty($fk_parent_line) && !empty($staticline->fk_parent_line) && $fk_parent_line != $staticline->fk_parent_line) {
919
                $rangmax = $this->line_max($fk_parent_line);
920
                $this->line->rang = $rangmax + 1;
921
            }
922
923
            $this->line->id = $rowid;
924
            $this->line->label = $label;
925
            $this->line->desc = $desc;
926
            $this->line->qty = $qty;
927
            $this->line->product_type       = $type;
928
            $this->line->vat_src_code       = $vat_src_code;
929
            $this->line->tva_tx = $txtva;
930
            $this->line->localtax1_tx       = $txlocaltax1;
931
            $this->line->localtax2_tx       = $txlocaltax2;
932
            $this->line->localtax1_type     = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
933
            $this->line->localtax2_type     = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
934
            $this->line->remise_percent     = $remise_percent;
935
            $this->line->subprice           = $pu_ht;
936
            $this->line->info_bits          = $info_bits;
937
938
            $this->line->total_ht           = $total_ht;
939
            $this->line->total_tva          = $total_tva;
940
            $this->line->total_localtax1    = $total_localtax1;
941
            $this->line->total_localtax2    = $total_localtax2;
942
            $this->line->total_ttc          = $total_ttc;
943
            $this->line->special_code = $special_code;
944
            $this->line->fk_parent_line     = $fk_parent_line;
945
            $this->line->skip_update_total = $skip_update_total;
946
            $this->line->fk_unit = $fk_unit;
947
948
            $this->line->fk_fournprice = $fk_fournprice;
949
            $this->line->pa_ht = $pa_ht;
950
951
            $this->line->date_start = $date_start;
952
            $this->line->date_end = $date_end;
953
954
            if (is_array($array_options) && count($array_options) > 0) {
955
                // We replace values in this->line->array_options only for entries defined into $array_options
956
                foreach ($array_options as $key => $value) {
957
                    $this->line->array_options[$key] = $array_options[$key];
958
                }
959
            }
960
961
            // Multicurrency
962
            $this->line->multicurrency_subprice     = $pu_ht_devise;
963
            $this->line->multicurrency_total_ht     = $multicurrency_total_ht;
964
            $this->line->multicurrency_total_tva    = $multicurrency_total_tva;
965
            $this->line->multicurrency_total_ttc    = $multicurrency_total_ttc;
966
967
            $result = $this->line->update($notrigger);
968
            if ($result > 0) {
969
                // Reorder if child line
970
                if (!empty($fk_parent_line)) {
971
                    $this->line_order(true, 'DESC');
972
                }
973
974
                $this->update_price(1, 'auto');
975
976
                // $this is Propal
977
                // $this->fk_propal = $this->id;
978
                // $this->rowid = $rowid;
979
980
                $this->db->commit();
981
                return $result;
982
            } else {
983
                $this->error = $this->line->error;
984
                $this->errors = $this->line->errors;
985
                $this->db->rollback();
986
                return -1;
987
            }
988
        } else {
989
            dol_syslog(get_class($this) . "::updateline Erreur -2 Propal en mode incompatible pour cette action");
990
            return -2;
991
        }
992
    }
993
994
995
    /**
996
     *  Delete detail line
997
     *
998
     *  @param      int     $lineid         Id of line to delete
999
     *  @param      int     $id             Id of object (for a check)
1000
     *  @return     int                     >0 if OK, <0 if KO
1001
     */
1002
    public function deleteLine($lineid, $id = 0)
1003
    {
1004
        global $user;
1005
1006
        if ($this->statut == self::STATUS_DRAFT) {
0 ignored issues
show
Deprecated Code introduced by
The property Propal::$statut has been deprecated: Try to use $status now ( Ignorable by Annotation )

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

1006
        if (/** @scrutinizer ignore-deprecated */ $this->statut == self::STATUS_DRAFT) {

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...
1007
            $this->db->begin();
1008
1009
            $line = new PropaleLigne($this->db);
1010
1011
            $line->context = $this->context;
1012
1013
            // Load data
1014
            $line->fetch($lineid);
1015
1016
            if ($id > 0 && $line->fk_propal != $id) {
1017
                $this->error = 'ErrorLineIDDoesNotMatchWithObjectID';
1018
                return -1;
1019
            }
1020
1021
            // Memorize previous line for triggers
1022
            $staticline = clone $line;
1023
            $line->oldline = $staticline;
1024
1025
            if ($line->delete($user) > 0) {
1026
                $this->update_price(1);
1027
1028
                $this->db->commit();
1029
                return 1;
1030
            } else {
1031
                $this->error = $line->error;
1032
                $this->errors = $line->errors;
1033
                $this->db->rollback();
1034
                return -1;
1035
            }
1036
        } else {
1037
            $this->error = 'ErrorDeleteLineNotAllowedByObjectStatus';
1038
            return -2;
1039
        }
1040
    }
1041
1042
1043
    /**
1044
     *  Create commercial proposal into database
1045
     *  this->ref can be set or empty. If empty, we will use "(PROVid)"
1046
     *
1047
     *  @param      User    $user       User that create
1048
     *  @param      int     $notrigger  1=Does not execute triggers, 0= execute triggers
1049
     *  @return     int                 Return integer <0 if KO, >=0 if OK
1050
     */
1051
    public function create($user, $notrigger = 0)
1052
    {
1053
        global $conf, $hookmanager, $mysoc;
1054
        $error = 0;
1055
1056
        $now = dol_now();
1057
1058
        // Clean parameters
1059
        if (empty($this->date)) {
1060
            $this->date = $this->datep;
1061
        }
1062
        $this->fin_validite = $this->date + ($this->duree_validite * 24 * 3600);
1063
        if (empty($this->availability_id)) {
1064
            $this->availability_id = 0;
1065
        }
1066
        if (empty($this->demand_reason_id)) {
1067
            $this->demand_reason_id = 0;
1068
        }
1069
1070
        // Multicurrency (test on $this->multicurrency_tx because we should take the default rate only if not using origin rate)
1071
        if (!empty($this->multicurrency_code) && empty($this->multicurrency_tx)) {
1072
            list($this->fk_multicurrency, $this->multicurrency_tx) = MultiCurrency::getIdAndTxFromCode($this->db, $this->multicurrency_code, $this->date);
1073
        } else {
1074
            $this->fk_multicurrency = MultiCurrency::getIdFromCode($this->db, $this->multicurrency_code);
1075
        }
1076
        if (empty($this->fk_multicurrency)) {
1077
            $this->multicurrency_code = $conf->currency;
1078
            $this->fk_multicurrency = 0;
1079
            $this->multicurrency_tx = 1;
1080
        }
1081
1082
        // Set tmp vars
1083
        $delivery_date = $this->delivery_date;
1084
1085
        dol_syslog(get_class($this) . "::create");
1086
1087
        // Check parameters
1088
        $result = $this->fetch_thirdparty();
1089
        if ($result < 0) {
1090
            $this->error = "Failed to fetch company";
1091
            dol_syslog(get_class($this) . "::create " . $this->error, LOG_ERR);
1092
            return -3;
1093
        }
1094
1095
        // Check parameters
1096
        if (!empty($this->ref)) {   // We check that ref is not already used
1097
            $result = self::isExistingObject($this->element, 0, $this->ref); // Check ref is not yet used
1098
            if ($result > 0) {
1099
                $this->error = 'ErrorRefAlreadyExists';
1100
                dol_syslog(get_class($this) . "::create " . $this->error, LOG_WARNING);
1101
                $this->db->rollback();
1102
                return -1;
1103
            }
1104
        }
1105
1106
        if (empty($this->date)) {
1107
            $this->error = "Date of proposal is required";
1108
            dol_syslog(get_class($this) . "::create " . $this->error, LOG_ERR);
1109
            return -4;
1110
        }
1111
1112
1113
        $this->db->begin();
1114
1115
        // Insert into database
1116
        $sql = "INSERT INTO " . MAIN_DB_PREFIX . "propal (";
1117
        $sql .= "fk_soc";
1118
        $sql .= ", price";
1119
        $sql .= ", total_tva";
1120
        $sql .= ", total_ttc";
1121
        $sql .= ", datep";
1122
        $sql .= ", datec";
1123
        $sql .= ", ref";
1124
        $sql .= ", fk_user_author";
1125
        $sql .= ", note_private";
1126
        $sql .= ", note_public";
1127
        $sql .= ", model_pdf";
1128
        $sql .= ", fin_validite";
1129
        $sql .= ", fk_cond_reglement";
1130
        $sql .= ", deposit_percent";
1131
        $sql .= ", fk_mode_reglement";
1132
        $sql .= ", fk_account";
1133
        $sql .= ", ref_client";
1134
        $sql .= ", ref_ext";
1135
        $sql .= ", date_livraison";
1136
        $sql .= ", fk_shipping_method";
1137
        $sql .= ", fk_warehouse";
1138
        $sql .= ", fk_availability";
1139
        $sql .= ", fk_input_reason";
1140
        $sql .= ", fk_projet";
1141
        $sql .= ", fk_incoterms";
1142
        $sql .= ", location_incoterms";
1143
        $sql .= ", entity";
1144
        $sql .= ", fk_multicurrency";
1145
        $sql .= ", multicurrency_code";
1146
        $sql .= ", multicurrency_tx";
1147
        $sql .= ") ";
1148
        $sql .= " VALUES (";
1149
        $sql .= $this->socid;
1150
        $sql .= ", 0";
1151
        $sql .= ", 0";
1152
        $sql .= ", 0";
1153
        $sql .= ", '" . $this->db->idate($this->date) . "'";
1154
        $sql .= ", '" . $this->db->idate($now) . "'";
1155
        $sql .= ", '(PROV)'";
1156
        $sql .= ", " . ($user->id > 0 ? ((int) $user->id) : "NULL");
1157
        $sql .= ", '" . $this->db->escape($this->note_private) . "'";
1158
        $sql .= ", '" . $this->db->escape($this->note_public) . "'";
1159
        $sql .= ", '" . $this->db->escape($this->model_pdf) . "'";
1160
        $sql .= ", " . ($this->fin_validite != '' ? "'" . $this->db->idate($this->fin_validite) . "'" : "NULL");
1161
        $sql .= ", " . ($this->cond_reglement_id > 0 ? ((int) $this->cond_reglement_id) : 'NULL');
1162
        $sql .= ", " . (!empty($this->deposit_percent) ? "'" . $this->db->escape($this->deposit_percent) . "'" : 'NULL');
1163
        $sql .= ", " . ($this->mode_reglement_id > 0 ? ((int) $this->mode_reglement_id) : 'NULL');
1164
        $sql .= ", " . ($this->fk_account > 0 ? ((int) $this->fk_account) : 'NULL');
1165
        $sql .= ", '" . $this->db->escape($this->ref_client) . "'";
1166
        $sql .= ", '" . $this->db->escape($this->ref_ext) . "'";
1167
        $sql .= ", " . (empty($delivery_date) ? "NULL" : "'" . $this->db->idate($delivery_date) . "'");
1168
        $sql .= ", " . ($this->shipping_method_id > 0 ? $this->shipping_method_id : 'NULL');
1169
        $sql .= ", " . ($this->warehouse_id > 0 ? $this->warehouse_id : 'NULL');
1170
        $sql .= ", " . $this->availability_id;
1171
        $sql .= ", " . $this->demand_reason_id;
1172
        $sql .= ", " . ($this->fk_project ? $this->fk_project : "null");
1173
        $sql .= ", " . (int) $this->fk_incoterms;
1174
        $sql .= ", '" . $this->db->escape($this->location_incoterms) . "'";
1175
        $sql .= ", " . setEntity($this);
1176
        $sql .= ", " . (int) $this->fk_multicurrency;
1177
        $sql .= ", '" . $this->db->escape($this->multicurrency_code) . "'";
1178
        $sql .= ", " . (float) $this->multicurrency_tx;
1179
        $sql .= ")";
1180
1181
        dol_syslog(get_class($this) . "::create", LOG_DEBUG);
1182
        $resql = $this->db->query($sql);
1183
        if ($resql) {
1184
            $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX . "propal");
1185
1186
            if ($this->id) {
1187
                $this->ref = '(PROV' . $this->id . ')';
1188
                $sql = 'UPDATE ' . MAIN_DB_PREFIX . "propal SET ref='" . $this->db->escape($this->ref) . "' WHERE rowid=" . ((int) $this->id);
1189
1190
                dol_syslog(get_class($this) . "::create", LOG_DEBUG);
1191
                $resql = $this->db->query($sql);
1192
                if (!$resql) {
1193
                    $error++;
1194
                }
1195
1196
                if (!empty($this->linkedObjectsIds) && empty($this->linked_objects)) {  // To use new linkedObjectsIds instead of old linked_objects
1197
                    $this->linked_objects = $this->linkedObjectsIds; // TODO Replace linked_objects with linkedObjectsIds
1198
                }
1199
1200
                // Add object linked
1201
                if (!$error && $this->id && !empty($this->linked_objects) && is_array($this->linked_objects)) {
1202
                    foreach ($this->linked_objects as $origin => $tmp_origin_id) {
1203
                        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, ...))
1204
                            foreach ($tmp_origin_id as $origin_id) {
1205
                                $ret = $this->add_object_linked($origin, $origin_id);
1206
                                if (!$ret) {
1207
                                    $this->error = $this->db->lasterror();
1208
                                    $error++;
1209
                                }
1210
                            }
1211
                        } else { // Old behaviour, if linked_object has only one link per type, so is something like array('contract'=>id1))
1212
                            $origin_id = $tmp_origin_id;
1213
                            $ret = $this->add_object_linked($origin, $origin_id);
1214
                            if (!$ret) {
1215
                                $this->error = $this->db->lasterror();
1216
                                $error++;
1217
                            }
1218
                        }
1219
                    }
1220
                }
1221
1222
                /*
1223
                 *  Insertion du detail des produits dans la base
1224
                 *  Insert products detail in database
1225
                 */
1226
                if (!$error) {
1227
                    $fk_parent_line = 0;
1228
                    $num = count($this->lines);
1229
1230
                    for ($i = 0; $i < $num; $i++) {
1231
                        if (!is_object($this->lines[$i])) { // If this->lines is not array of objects, coming from REST API
1232
                            // Convert into object this->lines[$i].
1233
                            $line = (object) $this->lines[$i];
1234
                        } else {
1235
                            $line = $this->lines[$i];
1236
                        }
1237
                        // Reset fk_parent_line for line that are not child lines or special product
1238
                        if (($line->product_type != 9 && empty($line->fk_parent_line)) || $line->product_type == 9) {
1239
                            $fk_parent_line = 0;
1240
                        }
1241
                        // Complete vat rate with code
1242
                        $vatrate = $line->tva_tx;
1243
                        if ($line->vat_src_code && !preg_match('/\(.*\)/', $vatrate)) {
1244
                            $vatrate .= ' (' . $line->vat_src_code . ')';
1245
                        }
1246
1247
                        if (getDolGlobalString('MAIN_CREATEFROM_KEEP_LINE_ORIGIN_INFORMATION')) {
1248
                            $originid = $line->origin_id;
1249
                            $origintype = $line->origin;
0 ignored issues
show
Deprecated Code introduced by
The property 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

1249
                            $origintype = /** @scrutinizer ignore-deprecated */ $line->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...
1250
                        } else {
1251
                            $originid = $line->id;
1252
                            $origintype = $this->element;
1253
                        }
1254
1255
                        $result = $this->addline(
1256
                            $line->desc,
1257
                            $line->subprice,
1258
                            $line->qty,
1259
                            $vatrate,
1260
                            $line->localtax1_tx,
1261
                            $line->localtax2_tx,
1262
                            $line->fk_product,
1263
                            $line->remise_percent,
1264
                            'HT',
1265
                            0,
1266
                            $line->info_bits,
1267
                            $line->product_type,
1268
                            $line->rang,
1269
                            $line->special_code,
1270
                            $fk_parent_line,
1271
                            $line->fk_fournprice,
1272
                            $line->pa_ht,
1273
                            $line->label,
1274
                            $line->date_start,
1275
                            $line->date_end,
1276
                            $line->array_options,
1277
                            $line->fk_unit,
1278
                            $origintype,
1279
                            $originid,
1280
                            0,
1281
                            0,
1282
                            1
1283
                        );
1284
1285
                        if ($result < 0) {
1286
                            $error++;
1287
                            $this->error = $this->db->error;
1288
                            dol_print_error($this->db);
1289
                            break;
1290
                        }
1291
1292
                        // Set the id on created row
1293
                        $line->id = $result;
1294
1295
                        // Defined the new fk_parent_line
1296
                        if ($result > 0 && $line->product_type == 9) {
1297
                            $fk_parent_line = $result;
1298
                        }
1299
                    }
1300
                }
1301
1302
                // Set delivery address
1303
                /*if (! $error && $this->fk_delivery_address)
1304
                {
1305
                    $sql = "UPDATE ".MAIN_DB_PREFIX."propal";
1306
                    $sql.= " SET fk_delivery_address = ".((int) $this->fk_delivery_address);
1307
                    $sql.= " WHERE ref = '".$this->db->escape($this->ref)."'";
1308
                    $sql.= " AND entity = ".setEntity($this);
1309
1310
                    $result=$this->db->query($sql);
1311
                }*/
1312
1313
                if (!$error) {
1314
                    // Mise a jour infos denormalisees
1315
                    $resql = $this->update_price(1, 'auto', 0, $mysoc);
1316
                    if ($resql) {
1317
                        $action = 'update';
1318
1319
                        // Actions on extra fields
1320
                        if (!$error) {
1321
                            $result = $this->insertExtraFields();
1322
                            if ($result < 0) {
1323
                                $error++;
1324
                            }
1325
                        }
1326
1327
                        if (!$error && !$notrigger) {
1328
                            // Call trigger
1329
                            $result = $this->call_trigger('PROPAL_CREATE', $user);
1330
                            if ($result < 0) {
1331
                                $error++;
1332
                            }
1333
                            // End call triggers
1334
                        }
1335
                    } else {
1336
                        $this->error = $this->db->lasterror();
1337
                        $error++;
1338
                    }
1339
                }
1340
            } else {
1341
                $this->error = $this->db->lasterror();
1342
                $error++;
1343
            }
1344
1345
            if (!$error) {
1346
                $this->db->commit();
1347
                dol_syslog(get_class($this) . "::create done id=" . $this->id);
1348
                return $this->id;
1349
            } else {
1350
                $this->db->rollback();
1351
                return -2;
1352
            }
1353
        } else {
1354
            $this->error = $this->db->lasterror();
1355
            $this->db->rollback();
1356
            return -1;
1357
        }
1358
    }
1359
1360
    /**
1361
     *      Load an object from its id and create a new one in database
1362
     *
1363
     *      @param      User    $user           User making the clone
1364
     *      @param      int     $socid          Id of thirdparty
1365
     *      @param      int     $forceentity    Entity id to force
1366
     *      @param      bool    $update_prices  [=false] Update prices if true
1367
     *      @param      bool    $update_desc    [=false] Update description if true
1368
     *      @return     int                     New id of clone
1369
     */
1370
    public function createFromClone(User $user, $socid = 0, $forceentity = null, $update_prices = false, $update_desc = false)
1371
    {
1372
        global $conf, $hookmanager, $mysoc;
1373
1374
        dol_include_once('/projet/class/project.class.php');
1375
1376
        $error = 0;
1377
        $now = dol_now();
1378
1379
        dol_syslog(__METHOD__, LOG_DEBUG);
1380
1381
        $object = new self($this->db);
1382
1383
        $this->db->begin();
1384
1385
        // Load source object
1386
        $object->fetch($this->id);
1387
1388
        $objsoc = new Societe($this->db);
1389
1390
        // Change socid if needed
1391
        if (!empty($socid) && $socid != $object->socid) {
1392
            if ($objsoc->fetch($socid) > 0) {
1393
                $object->socid = $objsoc->id;
1394
                $object->cond_reglement_id  = (!empty($objsoc->cond_reglement_id) ? $objsoc->cond_reglement_id : 0);
1395
                $object->deposit_percent = (!empty($objsoc->deposit_percent) ? $objsoc->deposit_percent : null);
1396
                $object->mode_reglement_id  = (!empty($objsoc->mode_reglement_id) ? $objsoc->mode_reglement_id : 0);
1397
                $object->fk_delivery_address = 0;
1398
1399
                /*if (isModEnabled('project'))
1400
                {
1401
                    $project = new Project($db);
1402
                    if ($this->fk_project > 0 && $project->fetch($this->fk_project)) {
1403
                        if ($project->socid <= 0) $clonedObj->fk_project = $this->fk_project;
1404
                        else $clonedObj->fk_project = '';
1405
                    } else {
1406
                        $clonedObj->fk_project = '';
1407
                    }
1408
                }*/
1409
                $object->fk_project = 0; // A cloned proposal is set by default to no project.
1410
            }
1411
1412
            // reset ref_client
1413
            $object->ref_client = '';
1414
1415
            // TODO Change product price if multi-prices
1416
        } else {
1417
            $objsoc->fetch($object->socid);
1418
        }
1419
1420
        // update prices
1421
        if ($update_prices === true || $update_desc === true) {
1422
            if ($objsoc->id > 0 && !empty($object->lines)) {
1423
                if ($update_prices === true && getDolGlobalString('PRODUIT_CUSTOMER_PRICES')) {
1424
                    // If price per customer
1425
                    require_once constant('DOL_DOCUMENT_ROOT') . '/product/class/productcustomerprice.class.php';
1426
                }
1427
1428
                foreach ($object->lines as $line) {
1429
                    $line->id = 0;
1430
1431
                    if ($line->fk_product > 0) {
1432
                        $prod = new Product($this->db);
1433
                        $res = $prod->fetch($line->fk_product);
1434
                        if ($res > 0) {
1435
                            if ($update_prices === true) {
1436
                                $pu_ht = $prod->price;
1437
                                $tva_tx = get_default_tva($mysoc, $objsoc, $prod->id);
1438
                                $remise_percent = $objsoc->remise_percent;
1439
1440
                                if (getDolGlobalString('PRODUIT_MULTIPRICES') && $objsoc->price_level > 0) {
1441
                                    $pu_ht = $prod->multiprices[$objsoc->price_level];
1442
                                    if (getDolGlobalString('PRODUIT_MULTIPRICES_USE_VAT_PER_LEVEL')) {  // using this option is a bug. kept for backward compatibility
1443
                                        if (isset($prod->multiprices_tva_tx[$objsoc->price_level])) {
1444
                                            $tva_tx = $prod->multiprices_tva_tx[$objsoc->price_level];
1445
                                        }
1446
                                    }
1447
                                } elseif (getDolGlobalString('PRODUIT_CUSTOMER_PRICES')) {
1448
                                    $prodcustprice = new ProductCustomerPrice($this->db);
1449
                                    $filter = array('t.fk_product' => $prod->id, 't.fk_soc' => $objsoc->id);
1450
                                    $result = $prodcustprice->fetchAll('', '', 0, 0, $filter);
1451
                                    if ($result) {
1452
                                        // If there is some prices specific to the customer
1453
                                        if (count($prodcustprice->lines) > 0) {
1454
                                            $pu_ht = price($prodcustprice->lines[0]->price);
1455
                                            $tva_tx = ($prodcustprice->lines[0]->default_vat_code ? $prodcustprice->lines[0]->tva_tx . ' (' . $prodcustprice->lines[0]->default_vat_code . ' )' : $prodcustprice->lines[0]->tva_tx);
1456
                                            if ($prodcustprice->lines[0]->default_vat_code && !preg_match('/\(.*\)/', $tva_tx)) {
1457
                                                $tva_tx .= ' (' . $prodcustprice->lines[0]->default_vat_code . ')';
1458
                                            }
1459
                                        }
1460
                                    }
1461
                                }
1462
1463
                                $line->subprice = $pu_ht;
1464
                                $line->tva_tx = $tva_tx;
1465
                                $line->remise_percent = $remise_percent;
1466
                            }
1467
                            if ($update_desc === true) {
1468
                                $line->desc = $prod->description;
1469
                            }
1470
                        }
1471
                    }
1472
                }
1473
            }
1474
        }
1475
1476
        $object->id = 0;
1477
        $object->ref = '';
1478
        $object->entity = (!empty($forceentity) ? $forceentity : $object->entity);
1479
        $object->statut = self::STATUS_DRAFT;
0 ignored issues
show
Deprecated Code introduced by
The property Propal::$statut has been deprecated: Try to use $status now ( Ignorable by Annotation )

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

1479
        /** @scrutinizer ignore-deprecated */ $object->statut = self::STATUS_DRAFT;

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...
1480
1481
        // Clear fields
1482
        $object->user_creation_id = $user->id;
1483
        $object->user_validation_id = 0;
1484
        $object->date = $now;
1485
        $object->datep = $now; // deprecated
1486
        $object->fin_validite = $object->date + ($object->duree_validite * 24 * 3600);
1487
        if (!getDolGlobalString('MAIN_KEEP_REF_CUSTOMER_ON_CLONING')) {
1488
            $object->ref_client = '';
1489
        }
1490
        if (getDolGlobalInt('MAIN_DONT_KEEP_NOTE_ON_CLONING') == 1) {
1491
            $object->note_private = '';
1492
            $object->note_public = '';
1493
        }
1494
        // Create clone
1495
        $object->context['createfromclone'] = 'createfromclone';
1496
        $result = $object->create($user);
1497
        if ($result < 0) {
1498
            $this->error = $object->error;
1499
            $this->errors = array_merge($this->errors, $object->errors);
1500
            $error++;
1501
        }
1502
1503
        if (!$error && !getDolGlobalInt('MAIN_IGNORE_CONTACTS_ON_CLONING')) {
1504
            // copy internal contacts
1505
            if ($object->copy_linked_contact($this, 'internal') < 0) {
1506
                $error++;
1507
            }
1508
        }
1509
1510
        if (!$error) {
1511
            // copy external contacts if same company
1512
            if ($this->socid == $object->socid) {
1513
                if ($object->copy_linked_contact($this, 'external') < 0) {
1514
                    $error++;
1515
                }
1516
            }
1517
        }
1518
1519
        if (!$error) {
1520
            // Hook of thirdparty module
1521
            if (is_object($hookmanager)) {
1522
                $parameters = array('objFrom' => $this, 'clonedObj' => $object);
1523
                $action = '';
1524
                $reshook = $hookmanager->executeHooks('createFrom', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
1525
                if ($reshook < 0) {
1526
                    $this->setErrorsFromObject($hookmanager);
1527
                    $error++;
1528
                }
1529
            }
1530
        }
1531
1532
        unset($object->context['createfromclone']);
1533
1534
        // End
1535
        if (!$error) {
1536
            $this->db->commit();
1537
            return $object->id;
1538
        } else {
1539
            $this->db->rollback();
1540
            return -1;
1541
        }
1542
    }
1543
1544
    /**
1545
     *  Load a proposal from database. Get also lines.
1546
     *
1547
     *  @param      int         $rowid          Id of object to load
1548
     *  @param      string      $ref            Ref of proposal
1549
     *  @param      string      $ref_ext        Ref ext of proposal
1550
     *  @param      int         $forceentity    Entity id to force when searching on ref or ref_ext
1551
     *  @return     int                         >0 if OK, <0 if KO
1552
     */
1553
    public function fetch($rowid, $ref = '', $ref_ext = '', $forceentity = 0)
1554
    {
1555
        $sql = "SELECT p.rowid, p.ref, p.entity, p.fk_soc";
1556
        $sql .= ", p.total_ttc, p.total_tva, p.localtax1, p.localtax2, p.total_ht";
1557
        $sql .= ", p.datec";
1558
        $sql .= ", p.date_signature as dates";
1559
        $sql .= ", p.date_valid as datev";
1560
        $sql .= ", p.datep as dp";
1561
        $sql .= ", p.fin_validite as dfv";
1562
        $sql .= ", p.date_livraison as delivery_date";
1563
        $sql .= ", p.model_pdf, p.last_main_doc, p.ref_client, ref_ext, p.extraparams";
1564
        $sql .= ", p.note_private, p.note_public";
1565
        $sql .= ", p.fk_projet as fk_project, p.fk_statut";
1566
        $sql .= ", p.fk_user_author, p.fk_user_valid, p.fk_user_cloture";
1567
        $sql .= ", p.fk_delivery_address";
1568
        $sql .= ", p.fk_availability";
1569
        $sql .= ", p.fk_input_reason";
1570
        $sql .= ", p.fk_cond_reglement";
1571
        $sql .= ", p.fk_mode_reglement";
1572
        $sql .= ', p.fk_account';
1573
        $sql .= ", p.fk_shipping_method";
1574
        $sql .= ", p.fk_warehouse";
1575
        $sql .= ", p.fk_incoterms, p.location_incoterms";
1576
        $sql .= ", p.fk_multicurrency, p.multicurrency_code, p.multicurrency_tx, p.multicurrency_total_ht, p.multicurrency_total_tva, p.multicurrency_total_ttc";
1577
        $sql .= ", p.tms as date_modification";
1578
        $sql .= ", i.libelle as label_incoterms";
1579
        $sql .= ", c.label as statut_label";
1580
        $sql .= ", ca.code as availability_code, ca.label as availability";
1581
        $sql .= ", dr.code as demand_reason_code, dr.label as demand_reason";
1582
        $sql .= ", cr.code as cond_reglement_code, cr.libelle as cond_reglement, cr.libelle_facture as cond_reglement_libelle_doc, p.deposit_percent";
1583
        $sql .= ", cp.code as mode_reglement_code, cp.libelle as mode_reglement";
1584
        $sql .= " FROM " . MAIN_DB_PREFIX . "propal as p";
1585
        $sql .= ' LEFT JOIN ' . MAIN_DB_PREFIX . 'c_propalst as c ON p.fk_statut = c.id';
1586
        $sql .= ' LEFT JOIN ' . MAIN_DB_PREFIX . 'c_paiement as cp ON p.fk_mode_reglement = cp.id AND cp.entity IN (' . getEntity('c_paiement') . ')';
1587
        $sql .= ' LEFT JOIN ' . MAIN_DB_PREFIX . 'c_payment_term as cr ON p.fk_cond_reglement = cr.rowid AND cr.entity IN (' . getEntity('c_payment_term') . ')';
1588
        $sql .= ' LEFT JOIN ' . MAIN_DB_PREFIX . 'c_availability as ca ON p.fk_availability = ca.rowid';
1589
        $sql .= ' LEFT JOIN ' . MAIN_DB_PREFIX . 'c_input_reason as dr ON p.fk_input_reason = dr.rowid';
1590
        $sql .= ' LEFT JOIN ' . MAIN_DB_PREFIX . 'c_incoterms as i ON p.fk_incoterms = i.rowid';
1591
1592
        if (!empty($ref)) {
1593
            if (!empty($forceentity)) {
1594
                $sql .= " WHERE p.entity = " . (int) $forceentity; // Check only the current entity because we may have the same reference in several entities
1595
            } else {
1596
                $sql .= " WHERE p.entity IN (" . getEntity('propal') . ")";
1597
            }
1598
            $sql .= " AND p.ref='" . $this->db->escape($ref) . "'";
1599
        } else {
1600
            // Don't use entity if you use rowid
1601
            $sql .= " WHERE p.rowid = " . ((int) $rowid);
1602
        }
1603
1604
        dol_syslog(get_class($this) . "::fetch", LOG_DEBUG);
1605
        $resql = $this->db->query($sql);
1606
        if ($resql) {
1607
            if ($this->db->num_rows($resql)) {
1608
                $obj = $this->db->fetch_object($resql);
1609
1610
                $this->id                   = $obj->rowid;
1611
                $this->entity               = $obj->entity;
1612
1613
                $this->ref                  = $obj->ref;
1614
                $this->ref_client           = $obj->ref_client;
1615
                $this->ref_customer         = $obj->ref_client;
1616
                $this->ref_ext              = $obj->ref_ext;
1617
1618
                $this->total                = $obj->total_ttc;          // TODO deprecated
1619
                $this->total_ttc            = $obj->total_ttc;
1620
                $this->total_ht             = $obj->total_ht;
1621
                $this->total_tva            = $obj->total_tva;
1622
                $this->total_localtax1      = $obj->localtax1;
1623
                $this->total_localtax2      = $obj->localtax2;
1624
1625
                $this->socid = $obj->fk_soc;
1626
                $this->thirdparty = null; // Clear if another value was already set by fetch_thirdparty
1627
1628
                $this->fk_project = $obj->fk_project;
1629
                $this->project = null; // Clear if another value was already set by fetch_projet
1630
1631
                $this->model_pdf            = $obj->model_pdf;
1632
                $this->last_main_doc = $obj->last_main_doc;
1633
                $this->note                 = $obj->note_private; // TODO deprecated
0 ignored issues
show
Deprecated Code introduced by
The property CommonObject::$note has been deprecated: Use $note_private instead. ( Ignorable by Annotation )

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

1633
                /** @scrutinizer ignore-deprecated */ $this->note                 = $obj->note_private; // TODO 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...
1634
                $this->note_private         = $obj->note_private;
1635
                $this->note_public          = $obj->note_public;
1636
1637
                $this->status               = (int) $obj->fk_statut;
1638
                $this->statut               = $this->status; // deprecated
0 ignored issues
show
Deprecated Code introduced by
The property Propal::$statut has been deprecated: Try to use $status now ( Ignorable by Annotation )

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

1638
                /** @scrutinizer ignore-deprecated */ $this->statut               = $this->status; // 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...
1639
1640
                $this->datec                = $this->db->jdate($obj->datec); // TODO deprecated
1641
                $this->datev                = $this->db->jdate($obj->datev); // TODO deprecated
1642
                $this->date_creation = $this->db->jdate($obj->datec); //Creation date
1643
                $this->date_validation = $this->db->jdate($obj->datev); //Validation date
1644
                $this->date_modification = $this->db->jdate($obj->date_modification); // tms
1645
                $this->date_signature = $this->db->jdate($obj->dates); // Signature date
1646
                $this->date                 = $this->db->jdate($obj->dp); // Proposal date
1647
                $this->datep                = $this->db->jdate($obj->dp); // deprecated
1648
                $this->fin_validite         = $this->db->jdate($obj->dfv);
1649
                $this->delivery_date        = $this->db->jdate($obj->delivery_date);
1650
                $this->shipping_method_id   = ($obj->fk_shipping_method > 0) ? $obj->fk_shipping_method : null;
1651
                $this->warehouse_id         = ($obj->fk_warehouse > 0) ? $obj->fk_warehouse : null;
1652
                $this->availability_id      = $obj->fk_availability;
1653
                $this->availability_code    = $obj->availability_code;
1654
                $this->availability         = $obj->availability;
1655
                $this->demand_reason_id     = $obj->fk_input_reason;
1656
                $this->demand_reason_code   = $obj->demand_reason_code;
1657
                $this->demand_reason        = $obj->demand_reason;
1658
                $this->fk_address = $obj->fk_delivery_address;
1659
1660
                $this->mode_reglement_id    = $obj->fk_mode_reglement;
1661
                $this->mode_reglement_code  = $obj->mode_reglement_code;
1662
                $this->mode_reglement       = $obj->mode_reglement;
1663
                $this->fk_account           = ($obj->fk_account > 0) ? $obj->fk_account : null;
1664
                $this->cond_reglement_id    = $obj->fk_cond_reglement;
1665
                $this->cond_reglement_code  = $obj->cond_reglement_code;
1666
                $this->cond_reglement       = $obj->cond_reglement;
1667
                $this->cond_reglement_doc   = $obj->cond_reglement_libelle_doc;
1668
                $this->deposit_percent      = $obj->deposit_percent;
1669
1670
                $this->extraparams = !empty($obj->extraparams) ? (array) json_decode($obj->extraparams, true) : array();
1671
1672
                $this->user_author_id = $obj->fk_user_author;
1673
                $this->user_validation_id = $obj->fk_user_valid;
1674
                $this->user_closing_id = $obj->fk_user_cloture;
1675
1676
                //Incoterms
1677
                $this->fk_incoterms = $obj->fk_incoterms;
1678
                $this->location_incoterms = $obj->location_incoterms;
1679
                $this->label_incoterms = $obj->label_incoterms;
1680
1681
                // Multicurrency
1682
                $this->fk_multicurrency         = $obj->fk_multicurrency;
1683
                $this->multicurrency_code = $obj->multicurrency_code;
1684
                $this->multicurrency_tx         = $obj->multicurrency_tx;
1685
                $this->multicurrency_total_ht = $obj->multicurrency_total_ht;
1686
                $this->multicurrency_total_tva = $obj->multicurrency_total_tva;
1687
                $this->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
1688
1689
                // Retrieve all extrafield
1690
                // fetch optionals attributes and labels
1691
                $this->fetch_optionals();
1692
1693
                $this->db->free($resql);
1694
1695
                $this->lines = array();
1696
1697
                // Lines
1698
                $result = $this->fetch_lines();
1699
                if ($result < 0) {
1700
                    return -3;
1701
                }
1702
1703
                return 1;
1704
            }
1705
1706
            $this->error = "Record Not Found";
1707
            return 0;
1708
        } else {
1709
            $this->error = $this->db->lasterror();
1710
            return -1;
1711
        }
1712
    }
1713
1714
    /**
1715
     *      Update database
1716
     *
1717
     *      @param      User    $user           User that modify
1718
     *      @param      int     $notrigger      0=launch triggers after, 1=disable triggers
1719
     *      @return     int                     Return integer <0 if KO, >0 if OK
1720
     */
1721
    public function update(User $user, $notrigger = 0)
1722
    {
1723
        global $conf;
1724
1725
        $error = 0;
1726
1727
        // Clean parameters
1728
        if (isset($this->ref)) {
1729
            $this->ref = trim($this->ref);
1730
        }
1731
        if (isset($this->ref_client)) {
1732
            $this->ref_client = trim($this->ref_client);
1733
        }
1734
        if (isset($this->note) || isset($this->note_private)) {
1735
            $this->note_private = (isset($this->note_private) ? trim($this->note_private) : trim($this->note));
1736
        }
1737
        if (isset($this->note_public)) {
1738
            $this->note_public = trim($this->note_public);
1739
        }
1740
        if (isset($this->model_pdf)) {
1741
            $this->model_pdf = trim($this->model_pdf);
1742
        }
1743
        if (isset($this->import_key)) {
1744
            $this->import_key = trim($this->import_key);
1745
        }
1746
        if (!empty($this->duree_validite) && is_numeric($this->duree_validite)) {
1747
            $this->fin_validite = $this->date + ($this->duree_validite * 24 * 3600);
1748
        }
1749
1750
        // Check parameters
1751
        // Put here code to add control on parameters values
1752
1753
        // Update request
1754
        $sql = "UPDATE " . MAIN_DB_PREFIX . "propal SET";
1755
        $sql .= " ref=" . (isset($this->ref) ? "'" . $this->db->escape($this->ref) . "'" : "null") . ",";
1756
        $sql .= " ref_client=" . (isset($this->ref_client) ? "'" . $this->db->escape($this->ref_client) . "'" : "null") . ",";
1757
        $sql .= " ref_ext=" . (isset($this->ref_ext) ? "'" . $this->db->escape($this->ref_ext) . "'" : "null") . ",";
1758
        $sql .= " fk_soc=" . (isset($this->socid) ? $this->socid : "null") . ",";
1759
        $sql .= " datep=" . (strval($this->date) != '' ? "'" . $this->db->idate($this->date) . "'" : 'null') . ",";
1760
        if (!empty($this->fin_validite)) {
1761
            $sql .= " fin_validite=" . (strval($this->fin_validite) != '' ? "'" . $this->db->idate($this->fin_validite) . "'" : 'null') . ",";
1762
        }
1763
        $sql .= " date_valid=" . (strval($this->date_validation) != '' ? "'" . $this->db->idate($this->date_validation) . "'" : 'null') . ",";
1764
        $sql .= " total_tva=" . (isset($this->total_tva) ? $this->total_tva : "null") . ",";
1765
        $sql .= " localtax1=" . (isset($this->total_localtax1) ? $this->total_localtax1 : "null") . ",";
1766
        $sql .= " localtax2=" . (isset($this->total_localtax2) ? $this->total_localtax2 : "null") . ",";
1767
        $sql .= " total_ht=" . (isset($this->total_ht) ? $this->total_ht : "null") . ",";
1768
        $sql .= " total_ttc=" . (isset($this->total_ttc) ? $this->total_ttc : "null") . ",";
1769
        $sql .= " fk_statut=" . (isset($this->statut) ? $this->statut : "null") . ",";
1770
        $sql .= " fk_user_author=" . (isset($this->user_author_id) ? $this->user_author_id : "null") . ",";
1771
        $sql .= " fk_user_valid=" . (isset($this->user_validation_id) ? $this->user_validation_id : "null") . ",";
1772
        $sql .= " fk_projet=" . (isset($this->fk_project) ? $this->fk_project : "null") . ",";
1773
        $sql .= " fk_cond_reglement=" . (isset($this->cond_reglement_id) ? $this->cond_reglement_id : "null") . ",";
1774
        $sql .= " deposit_percent=" . (!empty($this->deposit_percent) ? "'" . $this->db->escape($this->deposit_percent) . "'" : "null") . ",";
1775
        $sql .= " fk_mode_reglement=" . (isset($this->mode_reglement_id) ? $this->mode_reglement_id : "null") . ",";
1776
        $sql .= " fk_input_reason=" . (isset($this->demand_reason_id) ? $this->demand_reason_id : "null") . ",";
1777
        $sql .= " note_private=" . (isset($this->note_private) ? "'" . $this->db->escape($this->note_private) . "'" : "null") . ",";
1778
        $sql .= " note_public=" . (isset($this->note_public) ? "'" . $this->db->escape($this->note_public) . "'" : "null") . ",";
1779
        $sql .= " model_pdf=" . (isset($this->model_pdf) ? "'" . $this->db->escape($this->model_pdf) . "'" : "null") . ",";
1780
        $sql .= " import_key=" . (isset($this->import_key) ? "'" . $this->db->escape($this->import_key) . "'" : "null");
1781
        $sql .= " WHERE rowid=" . ((int) $this->id);
1782
1783
        $this->db->begin();
1784
1785
        dol_syslog(get_class($this) . "::update", LOG_DEBUG);
1786
        $resql = $this->db->query($sql);
1787
        if (!$resql) {
1788
            $error++;
1789
            $this->errors[] = "Error " . $this->db->lasterror();
1790
        }
1791
1792
        if (!$error) {
1793
            $result = $this->insertExtraFields();
1794
            if ($result < 0) {
1795
                $error++;
1796
            }
1797
        }
1798
1799
        if (!$error && !$notrigger) {
1800
            // Call trigger
1801
            $result = $this->call_trigger('PROPAL_MODIFY', $user);
1802
            if ($result < 0) {
1803
                $error++;
1804
            }
1805
            // End call triggers
1806
        }
1807
1808
        // Commit or rollback
1809
        if ($error) {
1810
            foreach ($this->errors as $errmsg) {
1811
                dol_syslog(get_class($this) . "::update " . $errmsg, LOG_ERR);
1812
                $this->error .= ($this->error ? ', ' . $errmsg : $errmsg);
1813
            }
1814
            $this->db->rollback();
1815
            return -1 * $error;
1816
        } else {
1817
            $this->db->commit();
1818
            return 1;
1819
        }
1820
    }
1821
1822
1823
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1824
    /**
1825
     * Load array lines
1826
     *
1827
     *  @param      int         $only_product           Return only physical products
1828
     *  @param      int         $loadalsotranslation    Return translation for products
1829
     *  @param      string      $sqlforgedfilters       Filter on other fields
1830
     *  @return     int                                 Return integer <0 if KO, >0 if OK
1831
     */
1832
    public function fetch_lines($only_product = 0, $loadalsotranslation = 0, $sqlforgedfilters = '')
1833
    {
1834
		// phpcs:enable
1835
        $this->lines = array();
1836
1837
        $sql = 'SELECT d.rowid, d.fk_propal, d.fk_parent_line, d.label as custom_label, d.description, d.price, d.vat_src_code, d.tva_tx, d.localtax1_tx, d.localtax2_tx, d.localtax1_type, d.localtax2_type, d.qty, d.fk_remise_except, d.remise_percent, d.subprice, d.fk_product,';
1838
        $sql .= ' d.info_bits, d.total_ht, d.total_tva, d.total_localtax1, d.total_localtax2, d.total_ttc, d.fk_product_fournisseur_price as fk_fournprice, d.buy_price_ht as pa_ht, d.special_code, d.rang, d.product_type,';
1839
        $sql .= ' d.fk_unit,';
1840
        $sql .= ' p.ref as product_ref, p.description as product_desc, p.fk_product_type, p.label as product_label, p.tobatch as product_tobatch, p.barcode as product_barcode,';
1841
        $sql .= ' p.weight, p.weight_units, p.volume, p.volume_units,';
1842
        $sql .= ' d.date_start, d.date_end,';
1843
        $sql .= ' d.fk_multicurrency, d.multicurrency_code, d.multicurrency_subprice, d.multicurrency_total_ht, d.multicurrency_total_tva, d.multicurrency_total_ttc';
1844
        $sql .= ' FROM ' . MAIN_DB_PREFIX . 'propaldet as d';
1845
        $sql .= ' LEFT JOIN ' . MAIN_DB_PREFIX . 'product as p ON (d.fk_product = p.rowid)';
1846
        $sql .= ' WHERE d.fk_propal = ' . ((int) $this->id);
1847
        if ($only_product) {
1848
            $sql .= ' AND p.fk_product_type = 0';
1849
        }
1850
        if ($sqlforgedfilters) {
1851
            $sql .= $sqlforgedfilters;
1852
        }
1853
        $sql .= ' ORDER by d.rang';
1854
1855
        dol_syslog(get_class($this) . "::fetch_lines", LOG_DEBUG);
1856
        $result = $this->db->query($sql);
1857
        if ($result) {
1858
            require_once constant('DOL_DOCUMENT_ROOT') . '/core/class/extrafields.class.php';
1859
1860
            $num = $this->db->num_rows($result);
1861
1862
            $i = 0;
1863
            while ($i < $num) {
1864
                $objp                   = $this->db->fetch_object($result);
1865
1866
                $line                   = new PropaleLigne($this->db);
1867
1868
                $line->rowid = $objp->rowid; //Deprecated
0 ignored issues
show
Deprecated Code introduced by
The property 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

1868
                /** @scrutinizer ignore-deprecated */ $line->rowid = $objp->rowid; //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...
1869
                $line->id = $objp->rowid;
1870
                $line->fk_propal = $objp->fk_propal;
1871
                $line->fk_parent_line = $objp->fk_parent_line;
1872
                $line->product_type     = $objp->product_type;
1873
                $line->label            = $objp->custom_label;
1874
                $line->desc             = $objp->description; // Description ligne
1875
                $line->description      = $objp->description; // Description ligne
1876
                $line->qty              = $objp->qty;
1877
                $line->vat_src_code     = $objp->vat_src_code;
1878
                $line->tva_tx           = $objp->tva_tx;
1879
                $line->localtax1_tx     = $objp->localtax1_tx;
1880
                $line->localtax2_tx     = $objp->localtax2_tx;
1881
                $line->localtax1_type   = $objp->localtax1_type;
1882
                $line->localtax2_type   = $objp->localtax2_type;
1883
                $line->subprice         = $objp->subprice;
1884
                $line->fk_remise_except = $objp->fk_remise_except;
1885
                $line->remise_percent   = $objp->remise_percent;
1886
                $line->price            = $objp->price; // TODO deprecated
1887
1888
                $line->info_bits        = $objp->info_bits;
1889
                $line->total_ht         = $objp->total_ht;
1890
                $line->total_tva        = $objp->total_tva;
1891
                $line->total_localtax1  = $objp->total_localtax1;
1892
                $line->total_localtax2  = $objp->total_localtax2;
1893
                $line->total_ttc        = $objp->total_ttc;
1894
                $line->fk_fournprice = $objp->fk_fournprice;
1895
                $marginInfos = getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $line->fk_fournprice, $objp->pa_ht);
1896
                $line->pa_ht = $marginInfos[0];
1897
                $line->marge_tx         = $marginInfos[1];
1898
                $line->marque_tx        = $marginInfos[2];
1899
                $line->special_code     = $objp->special_code;
1900
                $line->rang             = $objp->rang;
1901
1902
                $line->fk_product       = $objp->fk_product;
1903
1904
                $line->ref = $objp->product_ref; // deprecated
1905
                $line->libelle = $objp->product_label; // deprecated
1906
1907
                $line->product_ref = $objp->product_ref;
1908
                $line->product_label = $objp->product_label;
1909
                $line->product_desc     = $objp->product_desc; // Description produit
1910
                $line->product_tobatch  = $objp->product_tobatch;
1911
                $line->product_barcode  = $objp->product_barcode;
1912
1913
                $line->fk_product_type  = $objp->fk_product_type; // deprecated
1914
                $line->fk_unit          = $objp->fk_unit;
1915
                $line->weight = $objp->weight;
1916
                $line->weight_units = $objp->weight_units;
1917
                $line->volume = $objp->volume;
1918
                $line->volume_units = $objp->volume_units;
1919
1920
                $line->date_start = $this->db->jdate($objp->date_start);
1921
                $line->date_end = $this->db->jdate($objp->date_end);
1922
1923
                // Multicurrency
1924
                $line->fk_multicurrency = $objp->fk_multicurrency;
1925
                $line->multicurrency_code = $objp->multicurrency_code;
1926
                $line->multicurrency_subprice   = $objp->multicurrency_subprice;
1927
                $line->multicurrency_total_ht   = $objp->multicurrency_total_ht;
1928
                $line->multicurrency_total_tva  = $objp->multicurrency_total_tva;
1929
                $line->multicurrency_total_ttc  = $objp->multicurrency_total_ttc;
1930
1931
                $line->fetch_optionals();
1932
1933
                // multilangs
1934
                if (getDolGlobalInt('MAIN_MULTILANGS') && !empty($objp->fk_product) && !empty($loadalsotranslation)) {
1935
                    $tmpproduct = new Product($this->db);
1936
                    $tmpproduct->fetch($objp->fk_product);
1937
                    $tmpproduct->getMultiLangs();
1938
1939
                    $line->multilangs = $tmpproduct->multilangs;
1940
                }
1941
1942
                $this->lines[$i] = $line;
1943
1944
                $i++;
1945
            }
1946
1947
            $this->db->free($result);
1948
1949
            return $num;
1950
        } else {
1951
            $this->error = $this->db->lasterror();
1952
            return -3;
1953
        }
1954
    }
1955
1956
    /**
1957
     *  Set status to validated
1958
     *
1959
     *  @param  User    $user       Object user that validate
1960
     *  @param  int     $notrigger  1=Does not execute triggers, 0=execute triggers
1961
     *  @return int                 Return integer <0 if KO, 0=Nothing done, >=0 if OK
1962
     */
1963
    public function valid($user, $notrigger = 0)
1964
    {
1965
        global $conf;
1966
1967
        require_once constant('DOL_DOCUMENT_ROOT') . '/core/lib/files.lib.php';
1968
1969
        $error = 0;
1970
1971
        // Protection
1972
        if ($this->statut == self::STATUS_VALIDATED) {
0 ignored issues
show
Deprecated Code introduced by
The property Propal::$statut has been deprecated: Try to use $status now ( Ignorable by Annotation )

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

1972
        if (/** @scrutinizer ignore-deprecated */ $this->statut == self::STATUS_VALIDATED) {

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...
1973
            dol_syslog(get_class($this) . "::valid action abandoned: already validated", LOG_WARNING);
1974
            return 0;
1975
        }
1976
1977
        if (
1978
            !((!getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('propal', 'creer'))
1979
            || (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('propal', 'propal_advance', 'validate')))
1980
        ) {
1981
            $this->error = 'ErrorPermissionDenied';
1982
            dol_syslog(get_class($this) . "::valid " . $this->error, LOG_ERR);
1983
            return -1;
1984
        }
1985
1986
        $now = dol_now();
1987
1988
        $this->db->begin();
1989
1990
        // Numbering module definition
1991
        $soc = new Societe($this->db);
1992
        $soc->fetch($this->socid);
1993
1994
        // Define new ref
1995
        if (!$error && (preg_match('/^[\(]?PROV/i', $this->ref) || empty($this->ref))) { // empty should not happened, but when it occurs, the test save life
1996
            $num = $this->getNextNumRef($soc);
1997
        } else {
1998
            $num = $this->ref;
1999
        }
2000
        $this->newref = dol_sanitizeFileName($num);
2001
2002
        $sql = "UPDATE " . MAIN_DB_PREFIX . "propal";
2003
        $sql .= " SET ref = '" . $this->db->escape($num) . "',";
2004
        $sql .= " fk_statut = " . self::STATUS_VALIDATED . ", date_valid='" . $this->db->idate($now) . "', fk_user_valid=" . ((int) $user->id);
2005
        $sql .= " WHERE rowid = " . ((int) $this->id) . " AND fk_statut = " . self::STATUS_DRAFT;
2006
2007
        dol_syslog(get_class($this) . "::valid", LOG_DEBUG);
2008
        $resql = $this->db->query($sql);
2009
        if (!$resql) {
2010
            dol_print_error($this->db);
2011
            $error++;
2012
        }
2013
2014
        // Trigger calls
2015
        if (!$error && !$notrigger) {
2016
            // Call trigger
2017
            $result = $this->call_trigger('PROPAL_VALIDATE', $user);
2018
            if ($result < 0) {
2019
                $error++;
2020
            }
2021
            // End call triggers
2022
        }
2023
2024
        if (!$error) {
2025
            $this->oldref = $this->ref;
2026
2027
            // Rename directory if dir was a temporary ref
2028
            if (preg_match('/^[\(]?PROV/i', $this->ref)) {
2029
                // Now we rename also files into index
2030
                $sql = 'UPDATE ' . MAIN_DB_PREFIX . "ecm_files set filename = CONCAT('" . $this->db->escape($this->newref) . "', SUBSTR(filename, " . (strlen($this->ref) + 1) . ")), filepath = 'propale/" . $this->db->escape($this->newref) . "'";
2031
                $sql .= " WHERE filename LIKE '" . $this->db->escape($this->ref) . "%' AND filepath = 'propale/" . $this->db->escape($this->ref) . "' and entity = " . ((int) $conf->entity);
2032
                $resql = $this->db->query($sql);
2033
                if (!$resql) {
2034
                    $error++;
2035
                    $this->error = $this->db->lasterror();
2036
                }
2037
                $sql = 'UPDATE ' . MAIN_DB_PREFIX . "ecm_files set filepath = 'propale/" . $this->db->escape($this->newref) . "'";
2038
                $sql .= " WHERE filepath = 'propale/" . $this->db->escape($this->ref) . "' and entity = " . $conf->entity;
2039
                $resql = $this->db->query($sql);
2040
                if (!$resql) {
2041
                    $error++;
2042
                    $this->error = $this->db->lasterror();
2043
                }
2044
2045
                // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
2046
                $oldref = dol_sanitizeFileName($this->ref);
2047
                $newref = dol_sanitizeFileName($num);
2048
                $dirsource = $conf->propal->multidir_output[$this->entity] . '/' . $oldref;
2049
                $dirdest = $conf->propal->multidir_output[$this->entity] . '/' . $newref;
2050
                if (!$error && file_exists($dirsource)) {
2051
                    dol_syslog(get_class($this) . "::validate rename dir " . $dirsource . " into " . $dirdest);
2052
                    if (@rename($dirsource, $dirdest)) {
2053
                        dol_syslog("Rename ok");
2054
                        // Rename docs starting with $oldref with $newref
2055
                        $listoffiles = dol_dir_list($dirdest, 'files', 1, '^' . preg_quote($oldref, '/'));
2056
                        foreach ($listoffiles as $fileentry) {
2057
                            $dirsource = $fileentry['name'];
2058
                            $dirdest = preg_replace('/^' . preg_quote($oldref, '/') . '/', $newref, $dirsource);
2059
                            $dirsource = $fileentry['path'] . '/' . $dirsource;
2060
                            $dirdest = $fileentry['path'] . '/' . $dirdest;
2061
                            @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

2061
                            /** @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...
2062
                        }
2063
                    }
2064
                }
2065
            }
2066
2067
            $this->ref = $num;
2068
            $this->statut = self::STATUS_VALIDATED;
0 ignored issues
show
Deprecated Code introduced by
The property Propal::$statut has been deprecated: Try to use $status now ( Ignorable by Annotation )

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

2068
            /** @scrutinizer ignore-deprecated */ $this->statut = self::STATUS_VALIDATED;

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...
2069
            $this->status = self::STATUS_VALIDATED;
2070
            $this->user_validation_id = $user->id;
2071
            $this->datev = $now;
2072
            $this->date_validation = $now;
2073
2074
            $this->db->commit();
2075
            return 1;
2076
        } else {
2077
            $this->db->rollback();
2078
            return -1;
2079
        }
2080
    }
2081
2082
2083
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2084
    /**
2085
     *  Define proposal date
2086
     *
2087
     *  @param  User        $user       Object user that modify
2088
     *  @param  int         $date       Date
2089
     *  @param  int         $notrigger  1=Does not execute triggers, 0= execute triggers
2090
     *  @return int                     Return integer <0 if KO, >0 if OK
2091
     */
2092
    public function set_date($user, $date, $notrigger = 0)
2093
    {
2094
		// phpcs:enable
2095
        if (empty($date)) {
2096
            $this->error = 'ErrorBadParameter';
2097
            dol_syslog(get_class($this) . "::set_date " . $this->error, LOG_ERR);
2098
            return -1;
2099
        }
2100
2101
        if ($user->hasRight('propal', 'creer')) {
2102
            $error = 0;
2103
2104
            $this->db->begin();
2105
2106
            $sql = "UPDATE " . MAIN_DB_PREFIX . "propal SET datep = '" . $this->db->idate($date) . "'";
2107
            $sql .= " WHERE rowid = " . ((int) $this->id);
2108
2109
            dol_syslog(__METHOD__, LOG_DEBUG);
2110
            $resql = $this->db->query($sql);
2111
            if (!$resql) {
2112
                $this->errors[] = $this->db->error();
2113
                $error++;
2114
            }
2115
2116
            if (!$error) {
2117
                $this->oldcopy = clone $this;
2118
                $this->date = $date;
2119
                $this->datep = $date; // deprecated
2120
            }
2121
2122
            if (!$notrigger && empty($error)) {
2123
                // Call trigger
2124
                $result = $this->call_trigger('PROPAL_MODIFY', $user);
2125
                if ($result < 0) {
2126
                    $error++;
2127
                }
2128
                // End call triggers
2129
            }
2130
2131
            if (!$error) {
2132
                $this->db->commit();
2133
                return 1;
2134
            } else {
2135
                foreach ($this->errors as $errmsg) {
2136
                    dol_syslog(__METHOD__ . ' Error: ' . $errmsg, LOG_ERR);
2137
                    $this->error .= ($this->error ? ', ' . $errmsg : $errmsg);
2138
                }
2139
                $this->db->rollback();
2140
                return -1 * $error;
2141
            }
2142
        }
2143
2144
        return -1;
2145
    }
2146
2147
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2148
    /**
2149
     *  Define end validity date
2150
     *
2151
     *  @param      User    $user               Object user that modify
2152
     *  @param      int     $date_end_validity  End of validity date
2153
     *  @param      int     $notrigger          1=Does not execute triggers, 0= execute triggers
2154
     *  @return     int                         Return integer <0 if KO, >0 if OK
2155
     */
2156
    public function set_echeance($user, $date_end_validity, $notrigger = 0)
2157
    {
2158
		// phpcs:enable
2159
        if ($user->hasRight('propal', 'creer')) {
2160
            $error = 0;
2161
2162
            $this->db->begin();
2163
2164
            $sql = "UPDATE " . MAIN_DB_PREFIX . "propal SET fin_validite = " . ($date_end_validity != '' ? "'" . $this->db->idate($date_end_validity) . "'" : 'null');
2165
            $sql .= " WHERE rowid = " . ((int) $this->id);
2166
2167
            dol_syslog(__METHOD__, LOG_DEBUG);
2168
2169
            $resql = $this->db->query($sql);
2170
            if (!$resql) {
2171
                $this->errors[] = $this->db->error();
2172
                $error++;
2173
            }
2174
2175
2176
            if (!$error) {
2177
                $this->oldcopy = clone $this;
2178
                $this->fin_validite = $date_end_validity;
2179
            }
2180
2181
            if (!$notrigger && empty($error)) {
2182
                // Call trigger
2183
                $result = $this->call_trigger('PROPAL_MODIFY', $user);
2184
                if ($result < 0) {
2185
                    $error++;
2186
                }
2187
                // End call triggers
2188
            }
2189
2190
            if (!$error) {
2191
                $this->db->commit();
2192
                return 1;
2193
            } else {
2194
                foreach ($this->errors as $errmsg) {
2195
                    dol_syslog(__METHOD__ . ' Error: ' . $errmsg, LOG_ERR);
2196
                    $this->error .= ($this->error ? ', ' . $errmsg : $errmsg);
2197
                }
2198
                $this->db->rollback();
2199
                return -1 * $error;
2200
            }
2201
        }
2202
2203
        return -1;
2204
    }
2205
2206
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2207
    /**
2208
     *  Set delivery date
2209
     *
2210
     *  @param      User    $user               Object user that modify
2211
     *  @param      int     $delivery_date      Delivery date
2212
     *  @param      int     $notrigger          1=Does not execute triggers, 0= execute triggers
2213
     *  @return     int                         Return integer <0 if ko, >0 if ok
2214
     *  @deprecated Use  setDeliveryDate
2215
     */
2216
    public function set_date_livraison($user, $delivery_date, $notrigger = 0)
2217
    {
2218
		// phpcs:enable
2219
        return $this->setDeliveryDate($user, $delivery_date, $notrigger);
2220
    }
2221
2222
    /**
2223
     *  Set delivery date
2224
     *
2225
     *  @param      User    $user               Object user that modify
2226
     *  @param      int     $delivery_date     Delivery date
2227
     *  @param      int     $notrigger          1=Does not execute triggers, 0= execute triggers
2228
     *  @return     int                         Return integer <0 if ko, >0 if ok
2229
     */
2230
    public function setDeliveryDate($user, $delivery_date, $notrigger = 0)
2231
    {
2232
        if ($user->hasRight('propal', 'creer')) {
2233
            $error = 0;
2234
2235
            $this->db->begin();
2236
2237
            $sql = "UPDATE " . MAIN_DB_PREFIX . "propal ";
2238
            $sql .= " SET date_livraison = " . ($delivery_date != '' ? "'" . $this->db->idate($delivery_date) . "'" : 'null');
2239
            $sql .= " WHERE rowid = " . ((int) $this->id);
2240
2241
            dol_syslog(__METHOD__, LOG_DEBUG);
2242
            $resql = $this->db->query($sql);
2243
            if (!$resql) {
2244
                $this->errors[] = $this->db->error();
2245
                $error++;
2246
            }
2247
2248
            if (!$error) {
2249
                $this->oldcopy = clone $this;
2250
                $this->delivery_date = $delivery_date;
2251
            }
2252
2253
            if (!$notrigger && empty($error)) {
2254
                // Call trigger
2255
                $result = $this->call_trigger('PROPAL_MODIFY', $user);
2256
                if ($result < 0) {
2257
                    $error++;
2258
                }
2259
                // End call triggers
2260
            }
2261
2262
            if (!$error) {
2263
                $this->db->commit();
2264
                return 1;
2265
            } else {
2266
                foreach ($this->errors as $errmsg) {
2267
                    dol_syslog(__METHOD__ . ' Error: ' . $errmsg, LOG_ERR);
2268
                    $this->error .= ($this->error ? ', ' . $errmsg : $errmsg);
2269
                }
2270
                $this->db->rollback();
2271
                return -1 * $error;
2272
            }
2273
        }
2274
2275
        return -1;
2276
    }
2277
2278
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2279
    /**
2280
     *  Set delivery
2281
     *
2282
     *  @param      User    $user           Object user that modify
2283
     *  @param      int     $id             Availability id
2284
     *  @param      int     $notrigger      1=Does not execute triggers, 0= execute triggers
2285
     *  @return     int                     Return integer <0 if KO, >0 if OK
2286
     */
2287
    public function set_availability($user, $id, $notrigger = 0)
2288
    {
2289
		// phpcs:enable
2290
        if ($user->hasRight('propal', 'creer') && $this->statut >= self::STATUS_DRAFT) {
0 ignored issues
show
Deprecated Code introduced by
The property Propal::$statut has been deprecated: Try to use $status now ( Ignorable by Annotation )

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

2290
        if ($user->hasRight('propal', 'creer') && /** @scrutinizer ignore-deprecated */ $this->statut >= self::STATUS_DRAFT) {

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...
2291
            $error = 0;
2292
2293
            $this->db->begin();
2294
2295
            $sql = "UPDATE " . MAIN_DB_PREFIX . "propal";
2296
            $sql .= " SET fk_availability = " . ((int) $id);
2297
            $sql .= " WHERE rowid = " . ((int) $this->id);
2298
2299
            dol_syslog(__METHOD__ . ' availability(' . $id . ')', LOG_DEBUG);
2300
            $resql = $this->db->query($sql);
2301
            if (!$resql) {
2302
                $this->errors[] = $this->db->error();
2303
                $error++;
2304
            }
2305
2306
            if (!$error) {
2307
                $this->oldcopy = clone $this;
2308
                $this->fk_availability = $id;
2309
                $this->availability_id = $id;
2310
            }
2311
2312
            if (!$notrigger && empty($error)) {
2313
                // Call trigger
2314
                $result = $this->call_trigger('PROPAL_MODIFY', $user);
2315
                if ($result < 0) {
2316
                    $error++;
2317
                }
2318
                // End call triggers
2319
            }
2320
2321
            if (!$error) {
2322
                $this->db->commit();
2323
                return 1;
2324
            } else {
2325
                foreach ($this->errors as $errmsg) {
2326
                    dol_syslog(__METHOD__ . ' Error: ' . $errmsg, LOG_ERR);
2327
                    $this->error .= ($this->error ? ', ' . $errmsg : $errmsg);
2328
                }
2329
                $this->db->rollback();
2330
                return -1 * $error;
2331
            }
2332
        } else {
2333
            $error_str = 'Propal status do not meet requirement ' . $this->statut;
0 ignored issues
show
Deprecated Code introduced by
The property Propal::$statut has been deprecated: Try to use $status now ( Ignorable by Annotation )

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

2333
            $error_str = 'Propal status do not meet requirement ' . /** @scrutinizer ignore-deprecated */ $this->statut;

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...
2334
            dol_syslog(__METHOD__ . $error_str, LOG_ERR);
2335
            $this->error = $error_str;
2336
            $this->errors[] = $this->error;
2337
            return -2;
2338
        }
2339
    }
2340
2341
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2342
    /**
2343
     *  Set source of demand
2344
     *
2345
     *  @param      User    $user       Object user that modify
2346
     *  @param      int     $id         Input reason id
2347
     *  @param      int     $notrigger  1=Does not execute triggers, 0= execute triggers
2348
     *  @return     int                 Return integer <0 if KO, >0 if OK
2349
     */
2350
    public function set_demand_reason($user, $id, $notrigger = 0)
2351
    {
2352
		// phpcs:enable
2353
        if ($user->hasRight('propal', 'creer') && $this->statut >= self::STATUS_DRAFT) {
0 ignored issues
show
Deprecated Code introduced by
The property Propal::$statut has been deprecated: Try to use $status now ( Ignorable by Annotation )

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

2353
        if ($user->hasRight('propal', 'creer') && /** @scrutinizer ignore-deprecated */ $this->statut >= self::STATUS_DRAFT) {

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...
2354
            $error = 0;
2355
2356
            $this->db->begin();
2357
2358
            $sql = "UPDATE " . MAIN_DB_PREFIX . "propal ";
2359
            $sql .= " SET fk_input_reason = " . ((int) $id);
2360
            $sql .= " WHERE rowid = " . ((int) $this->id);
2361
2362
            dol_syslog(__METHOD__, LOG_DEBUG);
2363
            $resql = $this->db->query($sql);
2364
            if (!$resql) {
2365
                $this->errors[] = $this->db->error();
2366
                $error++;
2367
            }
2368
2369
2370
            if (!$error) {
2371
                $this->oldcopy = clone $this;
2372
2373
                $this->demand_reason_id = $id;
2374
            }
2375
2376
2377
            if (!$notrigger && empty($error)) {
2378
                // Call trigger
2379
                $result = $this->call_trigger('PROPAL_MODIFY', $user);
2380
                if ($result < 0) {
2381
                    $error++;
2382
                }
2383
                // End call triggers
2384
            }
2385
2386
            if (!$error) {
2387
                $this->db->commit();
2388
                return 1;
2389
            } else {
2390
                foreach ($this->errors as $errmsg) {
2391
                    dol_syslog(__METHOD__ . ' Error: ' . $errmsg, LOG_ERR);
2392
                    $this->error .= ($this->error ? ', ' . $errmsg : $errmsg);
2393
                }
2394
                $this->db->rollback();
2395
                return -1 * $error;
2396
            }
2397
        } else {
2398
            $error_str = 'Propal status do not meet requirement ' . $this->statut;
0 ignored issues
show
Deprecated Code introduced by
The property Propal::$statut has been deprecated: Try to use $status now ( Ignorable by Annotation )

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

2398
            $error_str = 'Propal status do not meet requirement ' . /** @scrutinizer ignore-deprecated */ $this->statut;

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...
2399
            dol_syslog(__METHOD__ . $error_str, LOG_ERR);
2400
            $this->error = $error_str;
2401
            $this->errors[] = $this->error;
2402
            return -2;
2403
        }
2404
    }
2405
2406
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2407
    /**
2408
     * Set customer reference number
2409
     *
2410
     *  @param      User    $user           Object user that modify
2411
     *  @param      string  $ref_client     Customer reference
2412
     *  @param      int     $notrigger      1=Does not execute triggers, 0= execute triggers
2413
     *  @return     int                     Return integer <0 if ko, >0 if ok
2414
     */
2415
    public function set_ref_client($user, $ref_client, $notrigger = 0)
2416
    {
2417
		// phpcs:enable
2418
        if ($user->hasRight('propal', 'creer')) {
2419
            $error = 0;
2420
2421
            $this->db->begin();
2422
2423
            $sql = "UPDATE " . MAIN_DB_PREFIX . "propal SET ref_client = " . (empty($ref_client) ? 'NULL' : "'" . $this->db->escape($ref_client) . "'");
2424
            $sql .= " WHERE rowid = " . ((int) $this->id);
2425
2426
            dol_syslog(__METHOD__ . ' $this->id=' . $this->id . ', ref_client=' . $ref_client, LOG_DEBUG);
2427
            $resql = $this->db->query($sql);
2428
            if (!$resql) {
2429
                $this->errors[] = $this->db->error();
2430
                $error++;
2431
            }
2432
2433
            if (!$error) {
2434
                $this->oldcopy = clone $this;
2435
                $this->ref_client = $ref_client;
2436
            }
2437
2438
            if (!$notrigger && empty($error)) {
2439
                // Call trigger
2440
                $result = $this->call_trigger('PROPAL_MODIFY', $user);
2441
                if ($result < 0) {
2442
                    $error++;
2443
                }
2444
                // End call triggers
2445
            }
2446
2447
            if (!$error) {
2448
                $this->db->commit();
2449
                return 1;
2450
            } else {
2451
                foreach ($this->errors as $errmsg) {
2452
                    dol_syslog(__METHOD__ . ' Error: ' . $errmsg, LOG_ERR);
2453
                    $this->error .= ($this->error ? ', ' . $errmsg : $errmsg);
2454
                }
2455
                $this->db->rollback();
2456
                return -1 * $error;
2457
            }
2458
        }
2459
2460
        return -1;
2461
    }
2462
2463
2464
    /**
2465
     *  Reopen the commercial proposal
2466
     *
2467
     *  @param      User    $user       Object user that close
2468
     *  @param      int     $status     Status
2469
     *  @param      string  $note       Comment
2470
     *  @param      int     $notrigger  1=Does not execute triggers, 0= execute triggers
2471
     *  @return     int                 Return integer <0 if KO, >0 if OK
2472
     */
2473
    public function reopen($user, $status, $note = '', $notrigger = 0)
2474
    {
2475
        $error = 0;
2476
2477
        $sql = "UPDATE " . MAIN_DB_PREFIX . "propal";
2478
        $sql .= " SET fk_statut = " . ((int) $status) . ",";
2479
        if (!empty($note)) {
2480
            $sql .= " note_private = '" . $this->db->escape($note) . "',";
2481
        }
2482
        $sql .= " date_cloture=NULL, fk_user_cloture=NULL";
2483
        $sql .= " WHERE rowid = " . ((int) $this->id);
2484
2485
        $this->db->begin();
2486
2487
        dol_syslog(get_class($this) . "::reopen", LOG_DEBUG);
2488
        $resql = $this->db->query($sql);
2489
        if (!$resql) {
2490
            $error++;
2491
            $this->errors[] = "Error " . $this->db->lasterror();
2492
        }
2493
        if (!$error) {
2494
            if (!$notrigger) {
2495
                // Call trigger
2496
                $result = $this->call_trigger('PROPAL_REOPEN', $user);
2497
                if ($result < 0) {
2498
                    $error++;
2499
                }
2500
                // End call triggers
2501
            }
2502
        }
2503
2504
        // Commit or rollback
2505
        if ($error) {
2506
            if (!empty($this->errors)) {
2507
                foreach ($this->errors as $errmsg) {
2508
                    dol_syslog(get_class($this) . "::update " . $errmsg, LOG_ERR);
2509
                    $this->error .= ($this->error ? ', ' . $errmsg : $errmsg);
2510
                }
2511
            }
2512
            $this->db->rollback();
2513
            return -1 * $error;
2514
        } else {
2515
            $this->statut = $status;
0 ignored issues
show
Deprecated Code introduced by
The property Propal::$statut has been deprecated: Try to use $status now ( Ignorable by Annotation )

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

2515
            /** @scrutinizer ignore-deprecated */ $this->statut = $status;

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...
2516
            $this->status = $status;
2517
2518
            $this->db->commit();
2519
            return 1;
2520
        }
2521
    }
2522
2523
    /**
2524
     *  Close/set the commercial proposal to status signed or refused (fill also date signature)
2525
     *
2526
     *  @param      User    $user       Object user that close
2527
     *  @param      int     $status     Status (self::STATUS_BILLED or self::STATUS_REFUSED)
2528
     *  @param      string  $note       Complete private note with this note
2529
     *  @param      int     $notrigger  1=Does not execute triggers, 0=Execute triggers
2530
     *  @return     int                 Return integer <0 if KO, >0 if OK
2531
     */
2532
    public function closeProposal($user, $status, $note = '', $notrigger = 0)
2533
    {
2534
        global $langs,$conf;
2535
2536
        $error = 0;
2537
        $now = dol_now();
2538
2539
        $this->db->begin();
2540
2541
        $newprivatenote = dol_concatdesc($this->note_private, $note);
2542
2543
        if (!getDolGlobalString('PROPALE_KEEP_OLD_SIGNATURE_INFO')) {
2544
            $date_signature = $now;
2545
            $fk_user_signature = $user->id;
2546
        } else {
2547
            $this->info($this->id);
2548
            if (!isset($this->date_signature) || $this->date_signature == '') {
2549
                $date_signature = $now;
2550
                $fk_user_signature = $user->id;
2551
            } else {
2552
                $date_signature = $this->date_signature;
2553
                $fk_user_signature = $this->user_signature->id;
2554
            }
2555
        }
2556
2557
        $sql  = "UPDATE " . MAIN_DB_PREFIX . "propal";
2558
        $sql .= " SET fk_statut = " . ((int) $status) . ", note_private = '" . $this->db->escape($newprivatenote) . "', date_signature='" . $this->db->idate($date_signature) . "', fk_user_signature=" . $fk_user_signature;
2559
        $sql .= " WHERE rowid = " . ((int) $this->id);
2560
2561
        $resql = $this->db->query($sql);
2562
        if ($resql) {
2563
            // Status self::STATUS_REFUSED by default
2564
            $modelpdf = getDolGlobalString('PROPALE_ADDON_PDF_ODT_CLOSED', $this->model_pdf);
2565
            $trigger_name = 'PROPAL_CLOSE_REFUSED';     // used later in call_trigger()
2566
2567
            if ($status == self::STATUS_SIGNED) {   // Status self::STATUS_SIGNED
2568
                $trigger_name = 'PROPAL_CLOSE_SIGNED';  // used later in call_trigger()
2569
                $modelpdf = getDolGlobalString('PROPALE_ADDON_PDF_ODT_TOBILL') ? $conf->global->PROPALE_ADDON_PDF_ODT_TOBILL : $this->model_pdf;
2570
2571
                // The connected company is classified as a client
2572
                $soc = new Societe($this->db);
2573
                $soc->id = $this->socid;
2574
                $result = $soc->setAsCustomer();
2575
2576
                if ($result < 0) {
2577
                    $this->error = $this->db->lasterror();
2578
                    $this->db->rollback();
2579
                    return -2;
2580
                }
2581
            }
2582
2583
            if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE') && !getDolGlobalInt('PROPAL_DISABLE_AUTOUPDATE_ON_CLOSE')) {
2584
                // Define output language
2585
                $outputlangs = $langs;
2586
                if (getDolGlobalInt('MAIN_MULTILANGS')) {
2587
                    $outputlangs = new Translate("", $conf);
2588
                    $newlang = (GETPOST('lang_id', 'aZ09') ? GETPOST('lang_id', 'aZ09') : $this->thirdparty->default_lang);
2589
                    $outputlangs->setDefaultLang($newlang);
2590
                }
2591
2592
                // PDF
2593
                $hidedetails = (getDolGlobalString('MAIN_GENERATE_DOCUMENTS_HIDE_DETAILS') ? 1 : 0);
2594
                $hidedesc = (getDolGlobalString('MAIN_GENERATE_DOCUMENTS_HIDE_DESC') ? 1 : 0);
2595
                $hideref = (getDolGlobalString('MAIN_GENERATE_DOCUMENTS_HIDE_REF') ? 1 : 0);
2596
2597
                //$ret=$object->fetch($id);    // Reload to get new records
2598
                $this->generateDocument($modelpdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
2599
            }
2600
2601
            if (!$error) {
2602
                $this->oldcopy = clone $this;
2603
                $this->statut = $status;
2604
                $this->status = $status;
2605
                $this->date_signature = $date_signature;
2606
                $this->note_private = $newprivatenote;
2607
            }
2608
2609
            if (!$notrigger && empty($error)) {
2610
                // Call trigger
2611
                $result = $this->call_trigger($trigger_name, $user);
2612
                if ($result < 0) {
2613
                    $error++;
2614
                }
2615
                // End call triggers
2616
            }
2617
2618
            if (!$error) {
2619
                $this->db->commit();
2620
                return 1;
2621
            } else {
2622
                $this->statut = $this->oldcopy->statut;
0 ignored issues
show
Deprecated Code introduced by
The property Propal::$statut has been deprecated: Try to use $status now ( Ignorable by Annotation )

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

2622
                $this->statut = /** @scrutinizer ignore-deprecated */ $this->oldcopy->statut;

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...
2623
                $this->status = $this->oldcopy->statut;
0 ignored issues
show
Deprecated Code introduced by
The property Propal::$statut has been deprecated: Try to use $status now ( Ignorable by Annotation )

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

2623
                $this->status = /** @scrutinizer ignore-deprecated */ $this->oldcopy->statut;

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...
2624
                $this->date_signature = $this->oldcopy->date_signature;
2625
                $this->note_private = $this->oldcopy->note_private;
2626
2627
                $this->db->rollback();
2628
                return -1;
2629
            }
2630
        } else {
2631
            $this->error = $this->db->lasterror();
2632
            $this->db->rollback();
2633
            return -1;
2634
        }
2635
    }
2636
2637
    /**
2638
     *  Classify the proposal to status Billed
2639
     *
2640
     *  @param      User    $user       Object user
2641
     *  @param      int     $notrigger  1=Does not execute triggers, 0= execute triggers
2642
     *  @param      string  $note       Complete private note with this note
2643
     *  @return     int                 Return integer <0 if KO, 0 = nothing done, >0 if OK
2644
     */
2645
    public function classifyBilled(User $user, $notrigger = 0, $note = '')
2646
    {
2647
        global $conf, $langs;
2648
2649
        $error = 0;
2650
2651
        $now = dol_now();
2652
        $num = 0;
2653
2654
        $triggerName = 'PROPAL_CLASSIFY_BILLED';
2655
2656
        $this->db->begin();
2657
2658
        $newprivatenote = dol_concatdesc($this->note_private, $note);
2659
2660
        $sql = 'UPDATE ' . MAIN_DB_PREFIX . 'propal SET fk_statut = ' . self::STATUS_BILLED . ", ";
2661
        $sql .= " note_private = '" . $this->db->escape($newprivatenote) . "', date_cloture='" . $this->db->idate($now) . "', fk_user_cloture=" . ((int) $user->id);
2662
        $sql .= ' WHERE rowid = ' . ((int) $this->id) . ' AND fk_statut = ' . ((int) self::STATUS_SIGNED);
2663
2664
        dol_syslog(__METHOD__, LOG_DEBUG);
2665
        $resql = $this->db->query($sql);
2666
        if (!$resql) {
2667
            $this->errors[] = $this->db->error();
2668
            $error++;
2669
        } else {
2670
            $num = $this->db->affected_rows($resql);
2671
        }
2672
2673
        if (!$error) {
2674
            $modelpdf = getDolGlobalString('PROPALE_ADDON_PDF_ODT_CLOSED', $this->model_pdf);
2675
2676
            if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
2677
                // Define output language
2678
                $outputlangs = $langs;
2679
                if (getDolGlobalInt('MAIN_MULTILANGS')) {
2680
                    $outputlangs = new Translate("", $conf);
2681
                    $newlang = (GETPOST('lang_id', 'aZ09') ? GETPOST('lang_id', 'aZ09') : $this->thirdparty->default_lang);
2682
                    $outputlangs->setDefaultLang($newlang);
2683
                }
2684
2685
                // PDF
2686
                $hidedetails = (getDolGlobalString('MAIN_GENERATE_DOCUMENTS_HIDE_DETAILS') ? 1 : 0);
2687
                $hidedesc = (getDolGlobalString('MAIN_GENERATE_DOCUMENTS_HIDE_DESC') ? 1 : 0);
2688
                $hideref = (getDolGlobalString('MAIN_GENERATE_DOCUMENTS_HIDE_REF') ? 1 : 0);
2689
2690
                //$ret=$object->fetch($id);    // Reload to get new records
2691
                $this->generateDocument($modelpdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
2692
            }
2693
2694
            $this->oldcopy = clone $this;
2695
            $this->statut = self::STATUS_BILLED;
0 ignored issues
show
Deprecated Code introduced by
The property Propal::$statut has been deprecated: Try to use $status now ( Ignorable by Annotation )

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

2695
            /** @scrutinizer ignore-deprecated */ $this->statut = self::STATUS_BILLED;

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...
2696
            $this->date_cloture = $now;
2697
            $this->note_private = $newprivatenote;
2698
        }
2699
2700
        if (!$notrigger && empty($error)) {
2701
            // Call trigger
2702
            $result = $this->call_trigger($triggerName, $user);
2703
            if ($result < 0) {
2704
                $error++;
2705
            }
2706
            // End call triggers
2707
        }
2708
2709
        if (!$error) {
2710
            $this->db->commit();
2711
            return $num;
2712
        } else {
2713
            foreach ($this->errors as $errmsg) {
2714
                dol_syslog(__METHOD__ . ' Error: ' . $errmsg, LOG_ERR);
2715
                $this->error .= ($this->error ? ', ' . $errmsg : $errmsg);
2716
            }
2717
            $this->db->rollback();
2718
            return -1 * $error;
2719
        }
2720
    }
2721
2722
    /**
2723
     *  Cancel the proposal
2724
     *
2725
     *  @param      User    $user   Object user
2726
     *  @return     int             Return integer if KO <0 , if OK >0
2727
     */
2728
    public function setCancel(User $user)
2729
    {
2730
        $error = 0;
2731
2732
        $this->db->begin();
2733
2734
        $sql = "UPDATE " . MAIN_DB_PREFIX . "propal";
2735
        $sql .= " SET fk_statut = " . self::STATUS_CANCELED . ",";
2736
        $sql .= " fk_user_modif = " . ((int) $user->id);
2737
        $sql .= " WHERE rowid = " . ((int) $this->id);
2738
2739
        dol_syslog(get_class($this) . "::cancel", LOG_DEBUG);
2740
        if ($this->db->query($sql)) {
2741
            if (!$error) {
2742
                // Call trigger
2743
                $result = $this->call_trigger('PROPAL_CANCEL', $user);
2744
                if ($result < 0) {
2745
                    $error++;
2746
                }
2747
                // End call triggers
2748
            }
2749
2750
            if (!$error) {
2751
                $this->statut = self::STATUS_CANCELED;
0 ignored issues
show
Deprecated Code introduced by
The property Propal::$statut has been deprecated: Try to use $status now ( Ignorable by Annotation )

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

2751
                /** @scrutinizer ignore-deprecated */ $this->statut = self::STATUS_CANCELED;

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...
2752
                $this->db->commit();
2753
                return 1;
2754
            } else {
2755
                foreach ($this->errors as $errmsg) {
2756
                    dol_syslog(get_class($this) . "::cancel " . $errmsg, LOG_ERR);
2757
                    $this->error .= ($this->error ? ', ' . $errmsg : $errmsg);
2758
                }
2759
                $this->db->rollback();
2760
                return -1;
2761
            }
2762
        } else {
2763
            $this->error = $this->db->error();
2764
            $this->db->rollback();
2765
            return -1;
2766
        }
2767
    }
2768
2769
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2770
    /**
2771
     *  Set draft status
2772
     *
2773
     *  @param      User    $user       Object user that modify
2774
     *  @param      int     $notrigger  1=Does not execute triggers, 0= execute triggers
2775
     *  @return     int                 Return integer <0 if KO, >0 if OK
2776
     */
2777
    public function setDraft($user, $notrigger = 0)
2778
    {
2779
		// phpcs:enable
2780
        $error = 0;
2781
2782
        // Protection
2783
        if ($this->statut <= self::STATUS_DRAFT) {
0 ignored issues
show
Deprecated Code introduced by
The property Propal::$statut has been deprecated: Try to use $status now ( Ignorable by Annotation )

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

2783
        if (/** @scrutinizer ignore-deprecated */ $this->statut <= self::STATUS_DRAFT) {

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...
2784
            return 0;
2785
        }
2786
2787
        dol_syslog(get_class($this) . "::setDraft", LOG_DEBUG);
2788
2789
        $this->db->begin();
2790
2791
        $sql = "UPDATE " . MAIN_DB_PREFIX . "propal";
2792
        $sql .= " SET fk_statut = " . self::STATUS_DRAFT;
2793
        $sql .= ",  online_sign_ip = NULL , online_sign_name = NULL";
2794
        $sql .= " WHERE rowid = " . ((int) $this->id);
2795
2796
        $resql = $this->db->query($sql);
2797
        if (!$resql) {
2798
            $this->errors[] = $this->db->error();
2799
            $error++;
2800
        }
2801
2802
        if (!$error) {
2803
            $this->oldcopy = clone $this;
2804
        }
2805
2806
        if (!$notrigger && empty($error)) {
2807
            // Call trigger
2808
            $result = $this->call_trigger('PROPAL_MODIFY', $user);
2809
            if ($result < 0) {
2810
                $error++;
2811
            }
2812
            // End call triggers
2813
        }
2814
2815
        if (!$error) {
2816
            $this->statut = self::STATUS_DRAFT;
0 ignored issues
show
Deprecated Code introduced by
The property Propal::$statut has been deprecated: Try to use $status now ( Ignorable by Annotation )

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

2816
            /** @scrutinizer ignore-deprecated */ $this->statut = self::STATUS_DRAFT;

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...
2817
            $this->status = self::STATUS_DRAFT;
2818
2819
            $this->db->commit();
2820
            return 1;
2821
        } else {
2822
            foreach ($this->errors as $errmsg) {
2823
                dol_syslog(__METHOD__ . ' Error: ' . $errmsg, LOG_ERR);
2824
                $this->error .= ($this->error ? ', ' . $errmsg : $errmsg);
2825
            }
2826
            $this->db->rollback();
2827
            return -1 * $error;
2828
        }
2829
    }
2830
2831
2832
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2833
    /**
2834
     *    Return list of proposal (eventually filtered on user) into an array
2835
     *
2836
     *    @param    int     $shortlist          0=Return array[id]=ref, 1=Return array[](id=>id,ref=>ref,name=>name)
2837
     *    @param    int     $draft              0=not draft, 1=draft
2838
     *    @param    int     $notcurrentuser     0=all user, 1=not current user
2839
     *    @param    int     $socid              Id third party
2840
     *    @param    int     $limit              For pagination
2841
     *    @param    int     $offset             For pagination
2842
     *    @param    string  $sortfield          Sort criteria
2843
     *    @param    string  $sortorder          Sort order
2844
     *    @return   array|int                   -1 if KO, array with result if OK
2845
     */
2846
    public function liste_array($shortlist = 0, $draft = 0, $notcurrentuser = 0, $socid = 0, $limit = 0, $offset = 0, $sortfield = 'p.datep', $sortorder = 'DESC')
2847
    {
2848
		// phpcs:enable
2849
        global $user;
2850
2851
        $ga = array();
2852
2853
        $sql = "SELECT s.rowid, s.nom as name, s.client,";
2854
        $sql .= " p.rowid as propalid, p.fk_statut, p.total_ht, p.ref, p.remise, ";
2855
        $sql .= " p.datep as dp, p.fin_validite as datelimite";
2856
        $sql .= " FROM " . MAIN_DB_PREFIX . "societe as s, " . MAIN_DB_PREFIX . "propal as p, " . MAIN_DB_PREFIX . "c_propalst as c";
2857
        $sql .= " WHERE p.entity IN (" . getEntity('propal') . ")";
2858
        $sql .= " AND p.fk_soc = s.rowid";
2859
        $sql .= " AND p.fk_statut = c.id";
2860
2861
        // If the internal user must only see his customers, force searching by him
2862
        $search_sale = 0;
2863
        if (!$user->hasRight('societe', 'client', 'voir')) {
2864
            $search_sale = $user->id;
2865
        }
2866
        // Search on sale representative
2867
        if ($search_sale && $search_sale != '-1') {
2868
            if ($search_sale == -2) {
2869
                $sql .= " AND NOT EXISTS (SELECT sc.fk_soc FROM " . MAIN_DB_PREFIX . "societe_commerciaux as sc WHERE sc.fk_soc = p.fk_soc)";
2870
            } elseif ($search_sale > 0) {
2871
                $sql .= " AND EXISTS (SELECT sc.fk_soc FROM " . MAIN_DB_PREFIX . "societe_commerciaux as sc WHERE sc.fk_soc = p.fk_soc AND sc.fk_user = " . ((int) $search_sale) . ")";
2872
            }
2873
        }
2874
        // Search on socid
2875
        if ($socid) {
2876
            $sql .= " AND p.fk_soc = " . ((int) $socid);
2877
        }
2878
        if ($draft) {
2879
            $sql .= " AND p.fk_statut = " . ((int) self::STATUS_DRAFT);
2880
        }
2881
        if ($notcurrentuser > 0) {
2882
            $sql .= " AND p.fk_user_author <> " . ((int) $user->id);
2883
        }
2884
        $sql .= $this->db->order($sortfield, $sortorder);
2885
        $sql .= $this->db->plimit($limit, $offset);
2886
2887
        $result = $this->db->query($sql);
2888
        if ($result) {
2889
            $num = $this->db->num_rows($result);
2890
            if ($num) {
2891
                $i = 0;
2892
                while ($i < $num) {
2893
                    $obj = $this->db->fetch_object($result);
2894
2895
                    if ($shortlist == 1) {
2896
                        $ga[$obj->propalid] = $obj->ref;
2897
                    } elseif ($shortlist == 2) {
2898
                        $ga[$obj->propalid] = $obj->ref . ' (' . $obj->name . ')';
2899
                    } else {
2900
                        $ga[$i]['id'] = $obj->propalid;
2901
                        $ga[$i]['ref']  = $obj->ref;
2902
                        $ga[$i]['name'] = $obj->name;
2903
                    }
2904
2905
                    $i++;
2906
                }
2907
            }
2908
            return $ga;
2909
        } else {
2910
            dol_print_error($this->db);
2911
            return -1;
2912
        }
2913
    }
2914
2915
    /**
2916
     *  Returns an array with the numbers of related invoices
2917
     *
2918
     *  @return array       Array of invoices
2919
     */
2920
    public function getInvoiceArrayList()
2921
    {
2922
        return $this->InvoiceArrayList($this->id);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->InvoiceArrayList($this->id) also could return the type integer which is incompatible with the documented return type array.
Loading history...
2923
    }
2924
2925
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2926
    /**
2927
     *  Returns an array with id and ref of related invoices
2928
     *
2929
     *  @param      int         $id         Id propal
2930
     *  @return     array|int               Array of invoices id
2931
     */
2932
    public function InvoiceArrayList($id)
2933
    {
2934
		// phpcs:enable
2935
        $ga = array();
2936
        $linkedInvoices = array();
2937
2938
        $this->fetchObjectLinked($id, $this->element);
2939
        foreach ($this->linkedObjectsIds as $objecttype => $objectid) {
2940
            // Nouveau système du common object renvoi des rowid et non un id linéaire de 1 à n
2941
            // On parcourt donc une liste d'objets en tant qu'objet unique
2942
            foreach ($objectid as $key => $object) {
2943
                // Cas des factures liees directement
2944
                if ($objecttype == 'facture') {
2945
                    $linkedInvoices[] = $object;
2946
                } else {
2947
                    // Cas des factures liees par un autre object (ex: commande)
2948
                    $this->fetchObjectLinked($object, $objecttype);
2949
                    foreach ($this->linkedObjectsIds as $subobjecttype => $subobjectid) {
2950
                        foreach ($subobjectid as $subkey => $subobject) {
2951
                            if ($subobjecttype == 'facture') {
2952
                                $linkedInvoices[] = $subobject;
2953
                            }
2954
                        }
2955
                    }
2956
                }
2957
            }
2958
        }
2959
2960
        if (count($linkedInvoices) > 0) {
2961
            $sql = "SELECT rowid as facid, ref, total_ht as total, datef as df, fk_user_author, fk_statut, paye";
2962
            $sql .= " FROM " . MAIN_DB_PREFIX . "facture";
2963
            $sql .= " WHERE rowid IN (" . $this->db->sanitize(implode(',', $linkedInvoices)) . ")";
2964
2965
            dol_syslog(get_class($this) . "::InvoiceArrayList", LOG_DEBUG);
2966
            $resql = $this->db->query($sql);
2967
2968
            if ($resql) {
2969
                $tab_sqlobj = array();
2970
                $nump = $this->db->num_rows($resql);
2971
                for ($i = 0; $i < $nump; $i++) {
2972
                    $sqlobj = $this->db->fetch_object($resql);
2973
                    $tab_sqlobj[] = $sqlobj;
2974
                }
2975
                $this->db->free($resql);
2976
2977
                $nump = count($tab_sqlobj);
2978
2979
                if ($nump) {
2980
                    $i = 0;
2981
                    while ($i < $nump) {
2982
                        $obj = array_shift($tab_sqlobj);
2983
2984
                        $ga[$i] = $obj;
2985
2986
                        $i++;
2987
                    }
2988
                }
2989
                return $ga;
2990
            } else {
2991
                return -1;
2992
            }
2993
        } else {
2994
            return $ga;
2995
        }
2996
    }
2997
2998
    /**
2999
     *  Delete proposal
3000
     *
3001
     *  @param  User    $user           Object user that delete
3002
     *  @param  int     $notrigger      1=Does not execute triggers, 0= execute triggers
3003
     *  @return int                     >0 if OK, <=0 if KO
3004
     */
3005
    public function delete($user, $notrigger = 0)
3006
    {
3007
        global $conf;
3008
        require_once constant('DOL_DOCUMENT_ROOT') . '/core/lib/files.lib.php';
3009
3010
        $error = 0;
3011
3012
        $this->db->begin();
3013
3014
        if (!$notrigger) {
3015
            // Call trigger
3016
            $result = $this->call_trigger('PROPAL_DELETE', $user);
3017
            if ($result < 0) {
3018
                $error++;
3019
            }
3020
            // End call triggers
3021
        }
3022
3023
        // Delete extrafields of lines and lines
3024
        if (!$error && !empty($this->table_element_line)) {
3025
            $tabletodelete = $this->table_element_line;
3026
            $sqlef = "DELETE FROM " . MAIN_DB_PREFIX . $tabletodelete . "_extrafields WHERE fk_object IN (SELECT rowid FROM " . MAIN_DB_PREFIX . $tabletodelete . " WHERE " . $this->fk_element . " = " . ((int) $this->id) . ")";
3027
            $sql = "DELETE FROM " . MAIN_DB_PREFIX . $tabletodelete . " WHERE " . $this->fk_element . " = " . ((int) $this->id);
3028
            if (!$this->db->query($sqlef) || !$this->db->query($sql)) {
3029
                $error++;
3030
                $this->error = $this->db->lasterror();
3031
                $this->errors[] = $this->error;
3032
                dol_syslog(get_class($this) . "::delete error " . $this->error, LOG_ERR);
3033
            }
3034
        }
3035
3036
        if (!$error) {
3037
            // Delete linked object
3038
            $res = $this->deleteObjectLinked();
3039
            if ($res < 0) {
3040
                $error++;
3041
            }
3042
        }
3043
3044
        if (!$error) {
3045
            // Delete linked contacts
3046
            $res = $this->delete_linked_contact();
3047
            if ($res < 0) {
3048
                $error++;
3049
            }
3050
        }
3051
3052
        // Removed extrafields of object
3053
        if (!$error) {
3054
            $result = $this->deleteExtraFields();
3055
            if ($result < 0) {
3056
                $error++;
3057
                dol_syslog(get_class($this) . "::delete error " . $this->error, LOG_ERR);
3058
            }
3059
        }
3060
3061
        // Delete main record
3062
        if (!$error) {
3063
            $sql = "DELETE FROM " . MAIN_DB_PREFIX . $this->table_element . " WHERE rowid = " . ((int) $this->id);
3064
            $res = $this->db->query($sql);
3065
            if (!$res) {
3066
                $error++;
3067
                $this->error = $this->db->lasterror();
3068
                $this->errors[] = $this->error;
3069
                dol_syslog(get_class($this) . "::delete error " . $this->error, LOG_ERR);
3070
            }
3071
        }
3072
3073
        // Delete record into ECM index and physically
3074
        if (!$error) {
3075
            $res = $this->deleteEcmFiles(0); // Deleting files physically is done later with the dol_delete_dir_recursive
3076
            $res = $this->deleteEcmFiles(1); // Deleting files physically is done later with the dol_delete_dir_recursive
3077
            if (!$res) {
3078
                $error++;
3079
            }
3080
        }
3081
3082
        if (!$error) {
3083
            // We remove directory
3084
            $ref = dol_sanitizeFileName($this->ref);
3085
            if ($conf->propal->multidir_output[$this->entity] && !empty($this->ref)) {
3086
                $dir = $conf->propal->multidir_output[$this->entity] . "/" . $ref;
3087
                $file = $dir . "/" . $ref . ".pdf";
3088
                if (file_exists($file)) {
3089
                    dol_delete_preview($this);
3090
3091
                    if (!dol_delete_file($file, 0, 0, 0, $this)) {
3092
                        $this->error = 'ErrorFailToDeleteFile';
3093
                        $this->errors[] = $this->error;
3094
                        $this->db->rollback();
3095
                        return 0;
3096
                    }
3097
                }
3098
                if (file_exists($dir)) {
3099
                    $res = @dol_delete_dir_recursive($dir);     // delete files physically + into ecm tables
3100
                    if (!$res) {
3101
                        $this->error = 'ErrorFailToDeleteDir';
3102
                        $this->errors[] = $this->error;
3103
                        $this->db->rollback();
3104
                        return 0;
3105
                    }
3106
                }
3107
            }
3108
        }
3109
3110
        if (!$error) {
3111
            dol_syslog(get_class($this) . "::delete " . $this->id . " by " . $user->id, LOG_DEBUG);
3112
            $this->db->commit();
3113
            return 1;
3114
        } else {
3115
            $this->db->rollback();
3116
            return -1;
3117
        }
3118
    }
3119
3120
    /**
3121
     *  Change the delivery time
3122
     *
3123
     *  @param  int $availability_id    Id of new delivery time
3124
     *  @param  int $notrigger          1=Does not execute triggers, 0= execute triggers
3125
     *  @return int                     >0 if OK, <0 if KO
3126
     *  @deprecated  use set_availability
3127
     */
3128
    public function availability($availability_id, $notrigger = 0)
3129
    {
3130
        global $user;
3131
3132
        if ($this->statut >= self::STATUS_DRAFT) {
0 ignored issues
show
Deprecated Code introduced by
The property Propal::$statut has been deprecated: Try to use $status now ( Ignorable by Annotation )

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

3132
        if (/** @scrutinizer ignore-deprecated */ $this->statut >= self::STATUS_DRAFT) {

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...
3133
            $error = 0;
3134
3135
            $this->db->begin();
3136
3137
            $sql = 'UPDATE ' . MAIN_DB_PREFIX . 'propal';
3138
            $sql .= ' SET fk_availability = ' . ((int) $availability_id);
3139
            $sql .= ' WHERE rowid=' . ((int) $this->id);
3140
3141
            dol_syslog(__METHOD__ . ' availability(' . $availability_id . ')', LOG_DEBUG);
3142
            $resql = $this->db->query($sql);
3143
            if (!$resql) {
3144
                $this->errors[] = $this->db->error();
3145
                $error++;
3146
            }
3147
3148
            if (!$error) {
3149
                $this->oldcopy = clone $this;
3150
                $this->availability_id = $availability_id;
3151
            }
3152
3153
            if (!$notrigger && empty($error)) {
3154
                // Call trigger
3155
                $result = $this->call_trigger('PROPAL_MODIFY', $user);
3156
                if ($result < 0) {
3157
                    $error++;
3158
                }
3159
                // End call triggers
3160
            }
3161
3162
            if (!$error) {
3163
                $this->db->commit();
3164
                return 1;
3165
            } else {
3166
                foreach ($this->errors as $errmsg) {
3167
                    dol_syslog(__METHOD__ . ' Error: ' . $errmsg, LOG_ERR);
3168
                    $this->error .= ($this->error ? ', ' . $errmsg : $errmsg);
3169
                }
3170
                $this->db->rollback();
3171
                return -1 * $error;
3172
            }
3173
        } else {
3174
            $error_str = 'Propal status do not meet requirement ' . $this->statut;
0 ignored issues
show
Deprecated Code introduced by
The property Propal::$statut has been deprecated: Try to use $status now ( Ignorable by Annotation )

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

3174
            $error_str = 'Propal status do not meet requirement ' . /** @scrutinizer ignore-deprecated */ $this->statut;

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...
3175
            dol_syslog(__METHOD__ . $error_str, LOG_ERR);
3176
            $this->error = $error_str;
3177
            $this->errors[] = $this->error;
3178
            return -2;
3179
        }
3180
    }
3181
3182
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3183
    /**
3184
     *  Change source demand
3185
     *
3186
     *  @param  int $demand_reason_id   Id of new source demand
3187
     *  @param  int $notrigger          1=Does not execute triggers, 0= execute triggers
3188
     *  @return int                     >0 si ok, <0 si ko
3189
     *  @deprecated use set_demand_reason
3190
     */
3191
    public function demand_reason($demand_reason_id, $notrigger = 0)
3192
    {
3193
		// phpcs:enable
3194
        global $user;
3195
3196
        if ($this->status >= self::STATUS_DRAFT) {
3197
            $error = 0;
3198
3199
            $this->db->begin();
3200
3201
            $sql = 'UPDATE ' . MAIN_DB_PREFIX . 'propal';
3202
            $sql .= ' SET fk_input_reason = ' . ((int) $demand_reason_id);
3203
            $sql .= ' WHERE rowid=' . ((int) $this->id);
3204
3205
            dol_syslog(__METHOD__ . ' demand_reason(' . $demand_reason_id . ')', LOG_DEBUG);
3206
            $resql = $this->db->query($sql);
3207
            if (!$resql) {
3208
                $this->errors[] = $this->db->error();
3209
                $error++;
3210
            }
3211
3212
            if (!$error) {
3213
                $this->oldcopy = clone $this;
3214
                $this->demand_reason_id = $demand_reason_id;
3215
            }
3216
3217
            if (!$notrigger && empty($error)) {
3218
                // Call trigger
3219
                $result = $this->call_trigger('PROPAL_MODIFY', $user);
3220
                if ($result < 0) {
3221
                    $error++;
3222
                }
3223
                // End call triggers
3224
            }
3225
3226
            if (!$error) {
3227
                $this->db->commit();
3228
                return 1;
3229
            } else {
3230
                foreach ($this->errors as $errmsg) {
3231
                    dol_syslog(__METHOD__ . ' Error: ' . $errmsg, LOG_ERR);
3232
                    $this->error .= ($this->error ? ', ' . $errmsg : $errmsg);
3233
                }
3234
                $this->db->rollback();
3235
                return -1 * $error;
3236
            }
3237
        } else {
3238
            $error_str = 'Propal status do not meet requirement ' . $this->statut;
0 ignored issues
show
Deprecated Code introduced by
The property Propal::$statut has been deprecated: Try to use $status now ( Ignorable by Annotation )

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

3238
            $error_str = 'Propal status do not meet requirement ' . /** @scrutinizer ignore-deprecated */ $this->statut;

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...
3239
            dol_syslog(__METHOD__ . $error_str, LOG_ERR);
3240
            $this->error = $error_str;
3241
            $this->errors[] = $this->error;
3242
            return -2;
3243
        }
3244
    }
3245
3246
3247
    /**
3248
     *  Object Proposal Information
3249
     *
3250
     *  @param  int     $id     Proposal id
3251
     *  @return void
3252
     */
3253
    public function info($id)
3254
    {
3255
        $sql = "SELECT c.rowid, ";
3256
        $sql .= " c.datec, c.date_valid as datev, c.date_signature, c.date_cloture,";
3257
        $sql .= " c.fk_user_author, c.fk_user_valid, c.fk_user_signature, c.fk_user_cloture";
3258
        $sql .= " FROM " . MAIN_DB_PREFIX . "propal as c";
3259
        $sql .= " WHERE c.rowid = " . ((int) $id);
3260
3261
        $result = $this->db->query($sql);
3262
3263
        if ($result) {
3264
            if ($this->db->num_rows($result)) {
3265
                $obj = $this->db->fetch_object($result);
3266
3267
                $this->id                = $obj->rowid;
3268
3269
                $this->date_creation     = $this->db->jdate($obj->datec);
3270
                $this->date_validation   = $this->db->jdate($obj->datev);
3271
                $this->date_signature    = $this->db->jdate($obj->date_signature);
3272
                $this->date_cloture      = $this->db->jdate($obj->date_cloture);
3273
3274
                $this->user_creation_id = $obj->fk_user_author;
3275
                $this->user_validation_id = $obj->fk_user_valid;
3276
3277
                if ($obj->fk_user_signature) {
3278
                    $user_signature = new User($this->db);
3279
                    $user_signature->fetch($obj->fk_user_signature);
3280
                    $this->user_signature = $user_signature;
3281
                }
3282
3283
                $this->user_closing_id = $obj->fk_user_cloture;
3284
            }
3285
            $this->db->free($result);
3286
        } else {
3287
            dol_print_error($this->db);
3288
        }
3289
    }
3290
3291
3292
    /**
3293
     *      Return label of status of proposal (draft, validated, ...)
3294
     *
3295
     *      @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
3296
     *      @return     string      Label
3297
     */
3298
    public function getLibStatut($mode = 0)
3299
    {
3300
        return $this->LibStatut($this->statut, $mode);
0 ignored issues
show
Deprecated Code introduced by
The property Propal::$statut has been deprecated: Try to use $status now ( Ignorable by Annotation )

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

3300
        return $this->LibStatut(/** @scrutinizer ignore-deprecated */ $this->statut, $mode);

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...
3301
    }
3302
3303
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3304
    /**
3305
     *      Return label of a status (draft, validated, ...)
3306
     *
3307
     *      @param      int         $status     Id status
3308
     *      @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
3309
     *      @return     string      Label
3310
     */
3311
    public function LibStatut($status, $mode = 1)
3312
    {
3313
		// phpcs:enable
3314
        global $hookmanager;
3315
3316
        // Init/load array of translation of status
3317
        if (empty($this->labelStatus) || empty($this->labelStatusShort)) {
3318
            global $langs;
3319
            $langs->load("propal");
3320
            $this->labelStatus[-1] = $langs->transnoentitiesnoconv("PropalStatusCanceled");
3321
            $this->labelStatus[0] = $langs->transnoentitiesnoconv("PropalStatusDraft");
3322
            $this->labelStatus[1] = $langs->transnoentitiesnoconv("PropalStatusValidated");
3323
            $this->labelStatus[2] = $langs->transnoentitiesnoconv("PropalStatusSigned");
3324
            $this->labelStatus[3] = $langs->transnoentitiesnoconv("PropalStatusNotSigned");
3325
            $this->labelStatus[4] = $langs->transnoentitiesnoconv("PropalStatusBilled");
3326
            $this->labelStatusShort[-1] = $langs->transnoentitiesnoconv("PropalStatusCanceledShort");
3327
            $this->labelStatusShort[0] = $langs->transnoentitiesnoconv("PropalStatusDraftShort");
3328
            $this->labelStatusShort[1] = $langs->transnoentitiesnoconv("PropalStatusValidatedShort");
3329
            $this->labelStatusShort[2] = $langs->transnoentitiesnoconv("PropalStatusSignedShort");
3330
            $this->labelStatusShort[3] = $langs->transnoentitiesnoconv("PropalStatusNotSignedShort");
3331
            $this->labelStatusShort[4] = $langs->transnoentitiesnoconv("PropalStatusBilledShort");
3332
        }
3333
3334
        $statusType = '';
3335
        if ($status == self::STATUS_CANCELED) {
3336
            $statusType = 'status9';
3337
        } elseif ($status == self::STATUS_DRAFT) {
3338
            $statusType = 'status0';
3339
        } elseif ($status == self::STATUS_VALIDATED) {
3340
            $statusType = 'status1';
3341
        } elseif ($status == self::STATUS_SIGNED) {
3342
            $statusType = 'status4';
3343
        } elseif ($status == self::STATUS_NOTSIGNED) {
3344
            $statusType = 'status9';
3345
        } elseif ($status == self::STATUS_BILLED) {
3346
            $statusType = 'status6';
3347
        }
3348
3349
        $parameters = array('status' => $status, 'mode' => $mode);
3350
        $reshook = $hookmanager->executeHooks('LibStatut', $parameters, $this); // Note that $action and $object may have been modified by hook
3351
3352
        if ($reshook > 0) {
3353
            return $hookmanager->resPrint;
3354
        }
3355
3356
        return dolGetStatus($this->labelStatus[$status], $this->labelStatusShort[$status], '', $statusType, $mode);
3357
    }
3358
3359
3360
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3361
    /**
3362
     *      Load indicators for dashboard (this->nbtodo and this->nbtodolate)
3363
     *
3364
     *      @param          User    $user   Object user
3365
     *      @param          string  $mode   "opened" for proposal to close, "signed" for proposal to invoice
3366
     *      @return WorkboardResponse|int Return integer <0 if KO, WorkboardResponse if OK
3367
     */
3368
    public function load_board($user, $mode)
3369
    {
3370
		// phpcs:enable
3371
        global $conf, $langs;
3372
3373
        $clause = " WHERE";
3374
3375
        $sql = "SELECT p.rowid, p.ref, p.datec as datec, p.fin_validite as datefin, p.total_ht";
3376
        $sql .= " FROM " . MAIN_DB_PREFIX . "propal as p";
3377
        $sql .= $clause . " p.entity IN (" . getEntity('propal') . ")";
3378
        if ($mode == 'opened') {
3379
            $sql .= " AND p.fk_statut = " . self::STATUS_VALIDATED;
3380
        }
3381
        if ($mode == 'signed') {
3382
            $sql .= " AND p.fk_statut = " . self::STATUS_SIGNED;
3383
        }
3384
        // If the internal user must only see his customers, force searching by him
3385
        $search_sale = 0;
3386
        if (!$user->hasRight('societe', 'client', 'voir')) {
3387
            $search_sale = $user->id;
3388
        }
3389
        // Search on sale representative
3390
        if ($search_sale && $search_sale != '-1') {
3391
            if ($search_sale == -2) {
3392
                $sql .= " AND NOT EXISTS (SELECT sc.fk_soc FROM " . MAIN_DB_PREFIX . "societe_commerciaux as sc WHERE sc.fk_soc = p.fk_soc)";
3393
            } elseif ($search_sale > 0) {
3394
                $sql .= " AND EXISTS (SELECT sc.fk_soc FROM " . MAIN_DB_PREFIX . "societe_commerciaux as sc WHERE sc.fk_soc = p.fk_soc AND sc.fk_user = " . ((int) $search_sale) . ")";
3395
            }
3396
        }
3397
3398
        $resql = $this->db->query($sql);
3399
        if ($resql) {
3400
            $langs->load("propal");
3401
            $now = dol_now();
3402
3403
            $delay_warning = 0;
3404
            $status = 0;
3405
            $label = $labelShort = '';
3406
            if ($mode == 'opened') {
3407
                $delay_warning = $conf->propal->cloture->warning_delay;
3408
                $status = self::STATUS_VALIDATED;
3409
                $label = $langs->transnoentitiesnoconv("PropalsToClose");
3410
                $labelShort = $langs->transnoentitiesnoconv("ToAcceptRefuse");
3411
            }
3412
            if ($mode == 'signed') {
3413
                $delay_warning = $conf->propal->facturation->warning_delay;
3414
                $status = self::STATUS_SIGNED;
3415
                $label = $langs->trans("PropalsToBill"); // We set here bill but may be billed or ordered
3416
                $labelShort = $langs->trans("ToBill");
3417
            }
3418
3419
            $response = new WorkboardResponse();
3420
            $response->warning_delay = $delay_warning / 60 / 60 / 24;
3421
            $response->label = $label;
3422
            $response->labelShort = $labelShort;
3423
            $response->url = constant('BASE_URL') . '/comm/propal/list.php?search_status=' . $status . '&mainmenu=commercial&leftmenu=propals';
3424
            $response->url_late = constant('BASE_URL') . '/comm/propal/list.php?search_status=' . $status . '&mainmenu=commercial&leftmenu=propals&sortfield=p.datep&sortorder=asc';
3425
            $response->img = img_object('', "propal");
3426
3427
            // This assignment in condition is not a bug. It allows walking the results.
3428
            while ($obj = $this->db->fetch_object($resql)) {
3429
                $response->nbtodo++;
3430
                $response->total += $obj->total_ht;
3431
3432
                if ($mode == 'opened') {
3433
                    $datelimit = $this->db->jdate($obj->datefin);
3434
                    if ($datelimit < ($now - $delay_warning)) {
3435
                        $response->nbtodolate++;
3436
                    }
3437
                }
3438
                // TODO Definir regle des propales a facturer en retard
3439
                // if ($mode == 'signed' && ! count($this->FactureListeArray($obj->rowid))) $this->nbtodolate++;
3440
            }
3441
3442
            return $response;
3443
        } else {
3444
            $this->error = $this->db->error();
3445
            return -1;
3446
        }
3447
    }
3448
3449
3450
    /**
3451
     *  Initialise an instance with random values.
3452
     *  Used to build previews or test instances.
3453
     *  id must be 0 if object instance is a specimen.
3454
     *
3455
     *  @return int
3456
     */
3457
    public function initAsSpecimen()
3458
    {
3459
        global $conf, $langs;
3460
3461
        // Load array of products prodids
3462
        $num_prods = 0;
3463
        $prodids = array();
3464
        $sql = "SELECT rowid";
3465
        $sql .= " FROM " . MAIN_DB_PREFIX . "product";
3466
        $sql .= " WHERE entity IN (" . getEntity('product') . ")";
3467
        $sql .= $this->db->plimit(100);
3468
3469
        $resql = $this->db->query($sql);
3470
        if ($resql) {
3471
            $num_prods = $this->db->num_rows($resql);
3472
            $i = 0;
3473
            while ($i < $num_prods) {
3474
                $i++;
3475
                $row = $this->db->fetch_row($resql);
3476
                $prodids[$i] = $row[0];
3477
            }
3478
        }
3479
3480
        // Initialise parameters
3481
        $this->id = 0;
3482
        $this->ref = 'SPECIMEN';
3483
        $this->ref_client = 'NEMICEPS';
3484
        $this->specimen = 1;
3485
        $this->socid = 1;
3486
        $this->date = time();
3487
        $this->fin_validite = $this->date + 3600 * 24 * 30;
3488
        $this->cond_reglement_id   = 1;
3489
        $this->cond_reglement_code = 'RECEP';
3490
        $this->mode_reglement_id   = 7;
3491
        $this->mode_reglement_code = 'CHQ';
3492
        $this->availability_id     = 1;
3493
        $this->availability_code   = 'AV_NOW';
3494
        $this->demand_reason_id    = 1;
3495
        $this->demand_reason_code  = 'SRC_00';
3496
        $this->note_public = 'This is a comment (public)';
3497
        $this->note_private = 'This is a comment (private)';
3498
3499
        $this->multicurrency_tx = 1;
3500
        $this->multicurrency_code = $conf->currency;
3501
3502
        // Lines
3503
        $nbp = 5;
3504
        $xnbp = 0;
3505
        while ($xnbp < $nbp) {
3506
            $line = new PropaleLigne($this->db);
3507
            $line->desc = $langs->trans("Description") . " " . $xnbp;
3508
            $line->qty = 1;
3509
            $line->subprice = 100;
3510
            $line->price = 100;
3511
            $line->tva_tx = 20;
3512
            $line->localtax1_tx = 0;
3513
            $line->localtax2_tx = 0;
3514
            if ($xnbp == 2) {
3515
                $line->total_ht = 50;
3516
                $line->total_ttc = 60;
3517
                $line->total_tva = 10;
3518
                $line->remise_percent = 50;
3519
            } else {
3520
                $line->total_ht = 100;
3521
                $line->total_ttc = 120;
3522
                $line->total_tva = 20;
3523
                $line->remise_percent = 0;
3524
            }
3525
3526
            if ($num_prods > 0) {
3527
                $prodid = mt_rand(1, $num_prods);
3528
                $line->fk_product = $prodids[$prodid];
3529
                $line->product_ref = 'SPECIMEN';
3530
            }
3531
3532
            $this->lines[$xnbp] = $line;
3533
3534
            $this->total_ht       += $line->total_ht;
3535
            $this->total_tva      += $line->total_tva;
3536
            $this->total_ttc      += $line->total_ttc;
3537
3538
            $xnbp++;
3539
        }
3540
3541
        return 1;
3542
    }
3543
3544
    /**
3545
     *      Load the indicators this->nb for the state board
3546
     *
3547
     *      @return     int         Return integer <0 if ko, >0 if ok
3548
     */
3549
    public function loadStateBoard()
3550
    {
3551
        global $user;
3552
3553
        $this->nb = array();
3554
        $clause = "WHERE";
3555
3556
        $sql = "SELECT count(p.rowid) as nb";
3557
        $sql .= " FROM " . MAIN_DB_PREFIX . "propal as p";
3558
        $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "societe as s ON p.fk_soc = s.rowid";
3559
        $sql .= " " . $clause . " p.entity IN (" . getEntity('propal') . ")";
3560
3561
        // If the internal user must only see his customers, force searching by him
3562
        $search_sale = 0;
3563
        if (!$user->hasRight('societe', 'client', 'voir')) {
3564
            $search_sale = $user->id;
3565
        }
3566
        // Search on sale representative
3567
        if ($search_sale && $search_sale != '-1') {
3568
            if ($search_sale == -2) {
3569
                $sql .= " AND NOT EXISTS (SELECT sc.fk_soc FROM " . MAIN_DB_PREFIX . "societe_commerciaux as sc WHERE sc.fk_soc = p.fk_soc)";
3570
            } elseif ($search_sale > 0) {
3571
                $sql .= " AND EXISTS (SELECT sc.fk_soc FROM " . MAIN_DB_PREFIX . "societe_commerciaux as sc WHERE sc.fk_soc = p.fk_soc AND sc.fk_user = " . ((int) $search_sale) . ")";
3572
            }
3573
        }
3574
3575
        $resql = $this->db->query($sql);
3576
        if ($resql) {
3577
            // This assignment in condition is not a bug. It allows walking the results.
3578
            while ($obj = $this->db->fetch_object($resql)) {
3579
                $this->nb["proposals"] = $obj->nb;
3580
            }
3581
            $this->db->free($resql);
3582
            return 1;
3583
        } else {
3584
            dol_print_error($this->db);
3585
            $this->error = $this->db->error();
3586
            return -1;
3587
        }
3588
    }
3589
3590
3591
    /**
3592
     *  Returns the reference to the following non used Proposal used depending on the active numbering module
3593
     *  defined into PROPALE_ADDON
3594
     *
3595
     *  @param  Societe     $soc    Object thirdparty
3596
     *  @return string              Reference libre pour la propale
3597
     */
3598
    public function getNextNumRef($soc)
3599
    {
3600
        global $conf, $langs;
3601
        $langs->load("propal");
3602
3603
        $classname = getDolGlobalString('PROPALE_ADDON');
3604
3605
        if (!empty($classname)) {
3606
            $mybool = false;
3607
3608
            $file = $classname . ".php";
3609
3610
            // Include file with class
3611
            $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
3612
            foreach ($dirmodels as $reldir) {
3613
                $dir = dol_buildpath($reldir . "core/modules/propale/");
3614
3615
                // Load file with numbering class (if found)
3616
                $mybool = ((bool) @include_once $dir . $file) || $mybool;
3617
            }
3618
3619
            if (!$mybool) {
3620
                dol_print_error(null, "Failed to include file " . $file);
3621
                return '';
3622
            }
3623
3624
            $obj = new $classname();
3625
            $numref = "";
3626
            $numref = $obj->getNextValue($soc, $this);
3627
3628
            if ($numref != "") {
3629
                return $numref;
3630
            } else {
3631
                $this->error = $obj->error;
3632
                //dol_print_error($db,"Propale::getNextNumRef ".$obj->error);
3633
                return "";
3634
            }
3635
        } else {
3636
            $langs->load("errors");
3637
            print $langs->trans("Error") . " " . $langs->trans("ErrorModuleSetupNotComplete", $langs->transnoentitiesnoconv("Proposal"));
3638
            return "";
3639
        }
3640
    }
3641
3642
    /**
3643
     * getTooltipContentArray
3644
     * @param array $params params to construct tooltip data
3645
     * @since v18
3646
     * @return array
3647
     */
3648
    public function getTooltipContentArray($params)
3649
    {
3650
        global $conf, $langs, $user;
3651
3652
        $langs->load('propal');
3653
        $datas = [];
3654
        $nofetch = !empty($params['nofetch']);
3655
3656
        if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
3657
            return ['optimize' => $langs->trans("Proposal")];
3658
        }
3659
        if ($user->hasRight('propal', 'lire')) {
3660
            $datas['picto'] = img_picto('', $this->picto) . ' <u class="paddingrightonly">' . $langs->trans("Proposal") . '</u>';
3661
            if (isset($this->status)) {
3662
                $datas['status'] = ' ' . $this->getLibStatut(5);
3663
            }
3664
            if (!empty($this->ref)) {
3665
                $datas['ref'] = '<br><b>' . $langs->trans('Ref') . ':</b> ' . $this->ref;
3666
            }
3667
            if (!$nofetch) {
3668
                $langs->load('companies');
3669
                if (empty($this->thirdparty)) {
3670
                    $this->fetch_thirdparty();
3671
                }
3672
                $datas['customer'] = '<br><b>' . $langs->trans('Customer') . ':</b> ' . $this->thirdparty->getNomUrl(1, '', 0, 1);
0 ignored issues
show
Bug introduced by
The method getNomUrl() does not exist on null. ( Ignorable by Annotation )

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

3672
                $datas['customer'] = '<br><b>' . $langs->trans('Customer') . ':</b> ' . $this->thirdparty->/** @scrutinizer ignore-call */ getNomUrl(1, '', 0, 1);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
3673
            }
3674
            if (!empty($this->ref_customer)) {
3675
                $datas['refcustomer'] = '<br><b>' . $langs->trans('RefCustomer') . ':</b> ' . $this->ref_customer;
3676
            }
3677
            if (!$nofetch) {
3678
                $langs->load('project');
3679
                if (is_null($this->project) || (is_object($this->project) && $this->project->isEmpty())) {
3680
                    $res = $this->fetch_project();
3681
                    if ($res > 0 && $this->project instanceof Project) {
3682
                        $datas['project'] = '<br><b>' . $langs->trans('Project') . ':</b> ' . $this->project->getNomUrl(1, '', 0, 1);
3683
                    }
3684
                }
3685
            }
3686
            if (!empty($this->total_ht)) {
3687
                $datas['amountht'] = '<br><b>' . $langs->trans('AmountHT') . ':</b> ' . price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency);
3688
            }
3689
            if (!empty($this->total_tva)) {
3690
                $datas['vat'] = '<br><b>' . $langs->trans('VAT') . ':</b> ' . price($this->total_tva, 0, $langs, 0, -1, -1, $conf->currency);
3691
            }
3692
            if (!empty($this->total_ttc)) {
3693
                $datas['amountttc'] = '<br><b>' . $langs->trans('AmountTTC') . ':</b> ' . price($this->total_ttc, 0, $langs, 0, -1, -1, $conf->currency);
3694
            }
3695
            if (!empty($this->date)) {
3696
                $datas['date'] = '<br><b>' . $langs->trans('Date') . ':</b> ' . dol_print_date($this->date, 'day');
3697
            }
3698
            if (!empty($this->delivery_date)) {
3699
                $datas['deliverydate'] = '<br><b>' . $langs->trans('DeliveryDate') . ':</b> ' . dol_print_date($this->delivery_date, 'dayhour');
3700
            }
3701
        }
3702
3703
        return $datas;
3704
    }
3705
3706
    /**
3707
     *  Return clicable link of object (with eventually picto)
3708
     *
3709
     *  @param      int     $withpicto                Add picto into link
3710
     *  @param      string  $option                   Where point the link ('expedition', 'document', ...)
3711
     *  @param      string  $get_params               Parameters added to url
3712
     *  @param      int     $notooltip                1=Disable tooltip
3713
     *  @param      int     $save_lastsearch_value    -1=Auto, 0=No save of lastsearch_values when clicking, 1=Save lastsearch_values whenclicking
3714
     *  @param      int     $addlinktonotes           -1=Disable, 0=Just add label show notes, 1=Add private note (only internal user), 2=Add public note (internal or external user), 3=Add private (internal user) and public note (internal and external user)
3715
     *  @return     string                            String with URL
3716
     */
3717
    public function getNomUrl($withpicto = 0, $option = '', $get_params = '', $notooltip = 0, $save_lastsearch_value = -1, $addlinktonotes = -1)
3718
    {
3719
        global $langs, $conf, $user, $hookmanager;
3720
3721
        if (!empty($conf->dol_no_mouse_hover)) {
3722
            $notooltip = 1; // Force disable tooltips
3723
        }
3724
3725
        $result = '';
3726
        $params = [
3727
            'id' => $this->id,
3728
            'objecttype' => $this->element,
3729
            'option' => $option,
3730
            'nofetch' => 1,
3731
        ];
3732
        $classfortooltip = 'classfortooltip';
3733
        $dataparams = '';
3734
        if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
3735
            $classfortooltip = 'classforajaxtooltip';
3736
            $dataparams = ' data-params="' . dol_escape_htmltag(json_encode($params)) . '"';
3737
            $label = '';
3738
        } else {
3739
            $label = implode($this->getTooltipContentArray($params));
3740
        }
3741
3742
        $url = '';
3743
        if ($user->hasRight('propal', 'lire')) {
3744
            if ($option == '') {
3745
                $url = constant('BASE_URL') . '/comm/propal/card.php?id=' . $this->id . $get_params;
3746
            } elseif ($option == 'compta') {  // deprecated
3747
                $url = constant('BASE_URL') . '/comm/propal/card.php?id=' . $this->id . $get_params;
3748
            } elseif ($option == 'expedition') {
3749
                $url = constant('BASE_URL') . '/expedition/propal.php?id=' . $this->id . $get_params;
3750
            } elseif ($option == 'document') {
3751
                $url = constant('BASE_URL') . '/comm/propal/document.php?id=' . $this->id . $get_params;
3752
            }
3753
3754
            if ($option != 'nolink') {
3755
                // Add param to save lastsearch_values or not
3756
                $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
3757
                if ($save_lastsearch_value == -1 && isset($_SERVER["PHP_SELF"]) && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
3758
                    $add_save_lastsearch_values = 1;
3759
                }
3760
                if ($add_save_lastsearch_values) {
3761
                    $url .= '&save_lastsearch_values=1';
3762
                }
3763
            }
3764
        }
3765
3766
        $linkclose = '';
3767
        if (empty($notooltip) && $user->hasRight('propal', 'lire')) {
3768
            if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
3769
                $label = $langs->trans("Proposal");
3770
                $linkclose .= ' alt="' . dol_escape_htmltag($label, 1) . '"';
3771
            }
3772
            $linkclose .= ($label ? ' title="' . dol_escape_htmltag($label, 1) . '"' : ' title="tocomplete"');
3773
            $linkclose .= $dataparams . ' class="' . $classfortooltip . '"';
3774
        }
3775
3776
        $linkstart = '<a href="' . $url . '"';
3777
        $linkstart .= $linkclose . '>';
3778
        $linkend = '</a>';
3779
3780
        $result .= $linkstart;
3781
        if ($withpicto) {
3782
            $result .= img_object(($notooltip ? '' : $label), ($this->picto ? $this->picto : 'generic'), (($withpicto != 2) ? 'class="paddingright"' : ''), 0, 0, $notooltip ? 0 : 1);
3783
        }
3784
        if ($withpicto != 2) {
3785
            $result .= $this->ref;
3786
        }
3787
        $result .= $linkend;
3788
3789
        if ($addlinktonotes >= 0) {
3790
            $txttoshow = '';
3791
3792
            if ($addlinktonotes == 0) {
3793
                if (!empty($this->note_private) || !empty($this->note_public)) {
3794
                    $txttoshow = $langs->trans('ViewPrivateNote');
3795
                }
3796
            } elseif ($addlinktonotes == 1) {
3797
                if (!empty($this->note_private)) {
3798
                    $txttoshow .= ($user->socid > 0 ? '' : dol_string_nohtmltag($this->note_private, 1));
3799
                }
3800
            } elseif ($addlinktonotes == 2) {
3801
                if (!empty($this->note_public)) {
3802
                    $txttoshow .= dol_string_nohtmltag($this->note_public, 1);
3803
                }
3804
            } elseif ($addlinktonotes == 3) {
3805
                if ($user->socid > 0) {
3806
                    if (!empty($this->note_public)) {
3807
                        $txttoshow .= dol_string_nohtmltag($this->note_public, 1);
3808
                    }
3809
                } else {
3810
                    if (!empty($this->note_public)) {
3811
                        $txttoshow .= dol_string_nohtmltag($this->note_public, 1);
3812
                    }
3813
                    if (!empty($this->note_private)) {
3814
                        if (!empty($txttoshow)) {
3815
                            $txttoshow .= '<br><br>';
3816
                        }
3817
                        $txttoshow .= dol_string_nohtmltag($this->note_private, 1);
3818
                    }
3819
                }
3820
            }
3821
3822
            if ($txttoshow) {
3823
                $result .= ' <span class="note inline-block">';
3824
                $result .= '<a href="' . constant('BASE_URL') . '/comm/propal/note.php?id=' . $this->id . '" class="classfortooltip" title="' . dol_escape_htmltag($txttoshow) . '">';
3825
                $result .= img_picto('', 'note');
3826
                $result .= '</a>';
3827
                $result .= '</span>';
3828
            }
3829
        }
3830
3831
        global $action;
3832
        $hookmanager->initHooks(array($this->element . 'dao'));
3833
        $parameters = array('id' => $this->id, 'getnomurl' => &$result);
3834
        $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
3835
        if ($reshook > 0) {
3836
            $result = $hookmanager->resPrint;
3837
        } else {
3838
            $result .= $hookmanager->resPrint;
3839
        }
3840
        return $result;
3841
    }
3842
3843
    /**
3844
     *  Retrieve an array of proposal lines
3845
     *
3846
     *  @param  string  $sqlforgedfilters       Filter on other fields
3847
     *  @return int                             >0 if OK, <0 if KO
3848
     */
3849
    public function getLinesArray($sqlforgedfilters = '')
3850
    {
3851
        return $this->fetch_lines(0, 0, $sqlforgedfilters);
3852
    }
3853
3854
    /**
3855
     *  Create a document onto disk according to template module.
3856
     *
3857
     *  @param      string      $modele         Force model to use ('' to not force)
3858
     *  @param      Translate   $outputlangs    Object langs to use for output
3859
     *  @param      int         $hidedetails    Hide details of lines
3860
     *  @param      int         $hidedesc       Hide description
3861
     *  @param      int         $hideref        Hide ref
3862
     *  @param      null|array  $moreparams     Array to provide more information
3863
     *  @return     int                         0 if KO, 1 if OK
3864
     */
3865
    public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
3866
    {
3867
        global $conf, $langs;
3868
3869
        $langs->load("propale");
3870
        $outputlangs->load("products");
3871
3872
        if (!dol_strlen($modele)) {
3873
            $modele = 'azur';
3874
3875
            if ($this->model_pdf) {
3876
                $modele = $this->model_pdf;
3877
            } elseif (getDolGlobalString('PROPALE_ADDON_PDF')) {
3878
                $modele = getDolGlobalString('PROPALE_ADDON_PDF');
3879
            }
3880
        }
3881
3882
        $modelpath = "core/modules/propale/doc/";
3883
3884
        return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
3885
    }
3886
3887
    /**
3888
     * Function used to replace a thirdparty id with another one.
3889
     *
3890
     * @param   DoliDB  $dbs        Database handler, because function is static we name it $dbs not $db to avoid breaking coding test
3891
     * @param   int     $origin_id  Old thirdparty id
3892
     * @param   int     $dest_id    New thirdparty id
3893
     * @return  bool
3894
     */
3895
    public static function replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
3896
    {
3897
        $tables = array(
3898
            'propal'
3899
        );
3900
3901
        return CommonObject::commonReplaceThirdparty($dbs, $origin_id, $dest_id, $tables);
3902
    }
3903
3904
    /**
3905
     * Function used to replace a product id with another one.
3906
     *
3907
     * @param DoliDB $db Database handler
3908
     * @param int $origin_id Old product id
3909
     * @param int $dest_id New product id
3910
     * @return bool
3911
     */
3912
    public static function replaceProduct(DoliDB $db, $origin_id, $dest_id)
3913
    {
3914
        $tables = array(
3915
            'propaldet'
3916
        );
3917
3918
        return CommonObject::commonReplaceProduct($db, $origin_id, $dest_id, $tables);
3919
    }
3920
3921
    /**
3922
     *  Return clicable link of object (with eventually picto)
3923
     *
3924
     *  @param      string      $option                 Where point the link (0=> main card, 1,2 => shipment, 'nolink'=>No link)
3925
     *  @param      array       $arraydata              Array of data
3926
     *  @return     string                              HTML Code for Kanban thumb.
3927
     */
3928
    public function getKanbanView($option = '', $arraydata = null)
3929
    {
3930
        global $langs;
3931
3932
        $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
3933
3934
        $return = '<div class="box-flex-item box-flex-grow-zero">';
3935
        $return .= '<div class="info-box info-box-sm">';
3936
        $return .= '<div class="info-box-icon bg-infobox-action">';
3937
        $return .= img_picto('', $this->picto);
3938
        $return .= '</div>';
3939
        $return .= '<div class="info-box-content">';
3940
        $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">' . (method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref) . '</span>';
3941
        if ($selected >= 0) {
3942
            $return .= '<input id="cb' . $this->id . '" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="' . $this->id . '"' . ($selected ? ' checked="checked"' : '') . '>';
3943
        }
3944
        if (!empty($arraydata['projectlink'])) {
3945
            $return .= '<span class="info-box-ref"> | ' . $arraydata['projectlink'] . '</span>';
3946
        }
3947
        $return .= '<br>';
3948
        if (property_exists($this, 'thirdparty') && is_object($this->thirdparty)) {
3949
            $return .= '<div class="info-box-ref tdoverflowmax150">' . $this->thirdparty->getNomUrl(1) . '</div>';
3950
        }
3951
        if (property_exists($this, 'total_ht')) {
3952
            $return .= '<span class="info-box-label amount" title="' . $langs->trans("AmountHT") . '">' . price($this->total_ht) . '</span>';
3953
        }
3954
        if (!empty($arraydata['authorlink'])) {
3955
            $return .= ' &nbsp; <span class="info-box-label">' . $arraydata['authorlink'] . '</span>';
3956
        }
3957
        if (method_exists($this, 'getLibStatut')) {
3958
            $return .= '<br><div class="info-box-status">' . $this->getLibStatut(3) . '</div>';
3959
        }
3960
        $return .= '</div>';
3961
        $return .= '</div>';
3962
        $return .= '</div>';
3963
        return $return;
3964
    }
3965
}
3966
3967
/**
3968
 *  Class to manage commercial proposal lines
3969
 */
3970
class PropaleLigne extends CommonObjectLine
3971
{
3972
    /**
3973
     * @var string ID to identify managed object
3974
     */
3975
    public $element = 'propaldet';
3976
3977
    /**
3978
     * @var string Name of table without prefix where object is stored
3979
     */
3980
    public $table_element = 'propaldet';
3981
3982
    /**
3983
     * @see CommonObjectLine
3984
     */
3985
    public $parent_element = 'propal';
3986
3987
    /**
3988
     * @see CommonObjectLine
3989
     */
3990
    public $fk_parent_attribute = 'fk_propal';
3991
3992
    public $oldline;
3993
3994
    // From llx_propaldet
3995
    public $fk_propal;
3996
    public $fk_parent_line;
3997
    public $desc; // Description ligne
3998
    public $fk_product; // Id produit predefini
3999
    /**
4000
     * @deprecated
4001
     * @see $product_type
4002
     */
4003
    public $fk_product_type;
4004
    /**
4005
     * Product type.
4006
     * @var int
4007
     * @see Product::TYPE_PRODUCT, Product::TYPE_SERVICE
4008
     */
4009
    public $product_type = Product::TYPE_PRODUCT;
4010
4011
    public $qty;
4012
4013
    public $tva_tx;
4014
    public $vat_src_code;
4015
4016
    /**
4017
     * Unit price before taxes
4018
     * @var float
4019
     */
4020
    public $subprice;
4021
    public $remise_percent;
4022
    public $fk_remise_except;
4023
4024
    public $rang = 0;
4025
4026
    public $fk_fournprice;
4027
    public $pa_ht;
4028
    public $marge_tx;
4029
    public $marque_tx;
4030
4031
    /**
4032
     * 1: frais de port
4033
     * 2: ecotaxe
4034
     * 3: option line (when qty = 0)
4035
     * @var int special code
4036
     */
4037
    public $special_code; // Tag for special lines (exclusive tags)
4038
4039
    public $info_bits = 0; // Some other info:
4040
    // Bit 0:   0 si TVA normal - 1 if TVA NPR
4041
    // Bit 1:   0 ligne normal - 1 if line with fixed discount
4042
4043
    public $total_ht; // Total HT  de la ligne toute quantite et incluant la remise ligne
4044
    public $total_tva; // Total TVA  de la ligne toute quantite et incluant la remise ligne
4045
    public $total_ttc; // Total TTC de la ligne toute quantite et incluant la remise ligne
4046
4047
    /**
4048
     * @deprecated
4049
     * @see $remise_percent, $fk_remise_except
4050
     */
4051
    public $remise;
4052
    /**
4053
     * @deprecated
4054
     * @see $subprice
4055
     */
4056
    public $price;
4057
4058
    // From llx_product
4059
    /**
4060
     * @deprecated
4061
     * @see $product_ref
4062
     */
4063
    public $ref;
4064
    /**
4065
     * Product reference
4066
     * @var string
4067
     */
4068
    public $product_ref;
4069
    /**
4070
     * @deprecated
4071
     * @see $product_label
4072
     */
4073
    public $libelle;
4074
    /**
4075
     * @deprecated
4076
     * @see $product_label
4077
     */
4078
    public $label;
4079
    /**
4080
     *  Product label
4081
     * @var string
4082
     */
4083
    public $product_label;
4084
    /**
4085
     * Product description
4086
     * @var string
4087
     */
4088
    public $product_desc;
4089
4090
    /**
4091
     * Product use lot
4092
     * @var string
4093
     */
4094
    public $product_tobatch;
4095
4096
    /**
4097
     * Product barcode
4098
     * @var string
4099
     */
4100
    public $product_barcode;
4101
4102
    public $localtax1_tx; // Local tax 1
4103
    public $localtax2_tx; // Local tax 2
4104
    public $localtax1_type; // Local tax 1 type
4105
    public $localtax2_type; // Local tax 2 type
4106
    public $total_localtax1; // Line total local tax 1
4107
    public $total_localtax2; // Line total local tax 2
4108
4109
    public $date_start;
4110
    public $date_end;
4111
4112
    public $skip_update_total; // Skip update price total for special lines
4113
4114
    // Multicurrency
4115
    public $fk_multicurrency;
4116
    public $multicurrency_code;
4117
    public $multicurrency_subprice;
4118
    public $multicurrency_total_ht;
4119
    public $multicurrency_total_tva;
4120
    public $multicurrency_total_ttc;
4121
4122
4123
    /**
4124
     *  Class line Constructor
4125
     *
4126
     *  @param  DoliDB  $db Database handler
4127
     */
4128
    public function __construct($db)
4129
    {
4130
        $this->db = $db;
4131
    }
4132
4133
    /**
4134
     *  Retrieve the propal line object
4135
     *
4136
     *  @param  int     $rowid      Propal line id
4137
     *  @return int                 Return integer <0 if KO, >0 if OK
4138
     */
4139
    public function fetch($rowid)
4140
    {
4141
        $sql = 'SELECT pd.rowid, pd.fk_propal, pd.fk_parent_line, pd.fk_product, pd.label as custom_label, pd.description, pd.price, pd.qty, pd.vat_src_code, pd.tva_tx,';
4142
        $sql .= ' pd.remise, pd.remise_percent, pd.fk_remise_except, pd.subprice,';
4143
        $sql .= ' pd.info_bits, pd.total_ht, pd.total_tva, pd.total_ttc, pd.fk_product_fournisseur_price as fk_fournprice, pd.buy_price_ht as pa_ht, pd.special_code, pd.rang,';
4144
        $sql .= ' pd.fk_unit,';
4145
        $sql .= ' pd.localtax1_tx, pd.localtax2_tx, pd.total_localtax1, pd.total_localtax2,';
4146
        $sql .= ' pd.fk_multicurrency, pd.multicurrency_code, pd.multicurrency_subprice, pd.multicurrency_total_ht, pd.multicurrency_total_tva, pd.multicurrency_total_ttc,';
4147
        $sql .= ' p.ref as product_ref, p.label as product_label, p.description as product_desc,';
4148
        $sql .= ' pd.date_start, pd.date_end, pd.product_type';
4149
        $sql .= ' FROM ' . MAIN_DB_PREFIX . 'propaldet as pd';
4150
        $sql .= ' LEFT JOIN ' . MAIN_DB_PREFIX . 'product as p ON pd.fk_product = p.rowid';
4151
        $sql .= ' WHERE pd.rowid = ' . ((int) $rowid);
4152
4153
        $result = $this->db->query($sql);
4154
        if ($result) {
4155
            $objp = $this->db->fetch_object($result);
4156
4157
            if ($objp) {
4158
                $this->id = $objp->rowid;
4159
                $this->rowid            = $objp->rowid; // deprecated
0 ignored issues
show
Deprecated Code introduced by
The property 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

4159
                /** @scrutinizer ignore-deprecated */ $this->rowid            = $objp->rowid; // 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...
4160
                $this->fk_propal = $objp->fk_propal;
4161
                $this->fk_parent_line = $objp->fk_parent_line;
4162
                $this->label            = $objp->custom_label;
4163
                $this->desc             = $objp->description;
4164
                $this->qty = $objp->qty;
4165
                $this->price            = $objp->price; // deprecated
4166
                $this->subprice = $objp->subprice;
4167
                $this->vat_src_code = $objp->vat_src_code;
4168
                $this->tva_tx           = $objp->tva_tx;
4169
                $this->remise           = $objp->remise; // deprecated
4170
                $this->remise_percent = $objp->remise_percent;
4171
                $this->fk_remise_except = $objp->fk_remise_except;
4172
                $this->fk_product = $objp->fk_product;
4173
                $this->info_bits        = $objp->info_bits;
4174
4175
                $this->total_ht         = $objp->total_ht;
4176
                $this->total_tva        = $objp->total_tva;
4177
                $this->total_ttc        = $objp->total_ttc;
4178
4179
                $this->fk_fournprice = $objp->fk_fournprice;
4180
4181
                $marginInfos            = getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $this->fk_fournprice, $objp->pa_ht);
4182
                $this->pa_ht            = $marginInfos[0];
4183
                $this->marge_tx         = $marginInfos[1];
4184
                $this->marque_tx        = $marginInfos[2];
4185
4186
                $this->special_code     = $objp->special_code;
4187
                $this->product_type     = $objp->product_type;
4188
                $this->rang = $objp->rang;
4189
4190
                $this->ref = $objp->product_ref; // deprecated
4191
                $this->product_ref = $objp->product_ref;
4192
                $this->libelle = $objp->product_label; // deprecated
4193
                $this->product_label    = $objp->product_label;
4194
                $this->product_desc     = $objp->product_desc;
4195
                $this->fk_unit          = $objp->fk_unit;
4196
4197
                $this->date_start       = $this->db->jdate($objp->date_start);
4198
                $this->date_end         = $this->db->jdate($objp->date_end);
4199
4200
                // Multicurrency
4201
                $this->fk_multicurrency = $objp->fk_multicurrency;
4202
                $this->multicurrency_code = $objp->multicurrency_code;
4203
                $this->multicurrency_subprice   = $objp->multicurrency_subprice;
4204
                $this->multicurrency_total_ht   = $objp->multicurrency_total_ht;
4205
                $this->multicurrency_total_tva  = $objp->multicurrency_total_tva;
4206
                $this->multicurrency_total_ttc  = $objp->multicurrency_total_ttc;
4207
4208
                $this->fetch_optionals();
4209
4210
                $this->db->free($result);
4211
4212
                return 1;
4213
            } else {
4214
                return 0;
4215
            }
4216
        } else {
4217
            return -1;
4218
        }
4219
    }
4220
4221
    /**
4222
     *  Insert object line propal in database
4223
     *
4224
     *  @param      int     $notrigger      1=Does not execute triggers, 0= execute triggers
4225
     *  @return     int                     Return integer <0 if KO, >0 if OK
4226
     */
4227
    public function insert($notrigger = 0)
4228
    {
4229
        global $conf, $user;
4230
4231
        $error = 0;
4232
4233
        dol_syslog(get_class($this) . "::insert rang=" . $this->rang);
4234
4235
        $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'.
4236
4237
        // Clean parameters
4238
        if (empty($this->tva_tx)) {
4239
            $this->tva_tx = 0;
4240
        }
4241
        if (empty($this->localtax1_tx)) {
4242
            $this->localtax1_tx = 0;
4243
        }
4244
        if (empty($this->localtax2_tx)) {
4245
            $this->localtax2_tx = 0;
4246
        }
4247
        if (empty($this->localtax1_type)) {
4248
            $this->localtax1_type = 0;
4249
        }
4250
        if (empty($this->localtax2_type)) {
4251
            $this->localtax2_type = 0;
4252
        }
4253
        if (empty($this->total_localtax1)) {
4254
            $this->total_localtax1 = 0;
4255
        }
4256
        if (empty($this->total_localtax2)) {
4257
            $this->total_localtax2 = 0;
4258
        }
4259
        if (empty($this->rang)) {
4260
            $this->rang = 0;
4261
        }
4262
        if (empty($this->remise_percent) || !is_numeric($this->remise_percent)) {
4263
            $this->remise_percent = 0;
4264
        }
4265
        if (empty($this->info_bits)) {
4266
            $this->info_bits = 0;
4267
        }
4268
        if (empty($this->special_code)) {
4269
            $this->special_code = 0;
4270
        }
4271
        if (empty($this->fk_parent_line)) {
4272
            $this->fk_parent_line = 0;
4273
        }
4274
        if (empty($this->fk_fournprice)) {
4275
            $this->fk_fournprice = 0;
4276
        }
4277
        if (!is_numeric($this->qty)) {
4278
            $this->qty = 0;
4279
        }
4280
        if (empty($this->pa_ht)) {
4281
            $this->pa_ht = 0;
4282
        }
4283
        if (empty($this->multicurrency_subprice)) {
4284
            $this->multicurrency_subprice = 0;
4285
        }
4286
        if (empty($this->multicurrency_total_ht)) {
4287
            $this->multicurrency_total_ht = 0;
4288
        }
4289
        if (empty($this->multicurrency_total_tva)) {
4290
            $this->multicurrency_total_tva = 0;
4291
        }
4292
        if (empty($this->multicurrency_total_ttc)) {
4293
            $this->multicurrency_total_ttc = 0;
4294
        }
4295
4296
        // if buy price not defined, define buyprice as configured in margin admin
4297
        if ($this->pa_ht == 0 && $pa_ht_isemptystring) {
4298
            if (($result = $this->defineBuyPrice($this->subprice, $this->remise_percent, $this->fk_product)) < 0) {
4299
                return $result;
4300
            } else {
4301
                $this->pa_ht = $result;
4302
            }
4303
        }
4304
4305
        // Check parameters
4306
        if ($this->product_type < 0) {
4307
            return -1;
4308
        }
4309
4310
        $this->db->begin();
4311
4312
        // Insert line into database
4313
        $sql = 'INSERT INTO ' . MAIN_DB_PREFIX . 'propaldet';
4314
        $sql .= ' (fk_propal, fk_parent_line, label, description, fk_product, product_type,';
4315
        $sql .= ' fk_remise_except, qty, vat_src_code, tva_tx, localtax1_tx, localtax2_tx, localtax1_type, localtax2_type,';
4316
        $sql .= ' subprice, remise_percent, ';
4317
        $sql .= ' info_bits, ';
4318
        $sql .= ' total_ht, total_tva, total_localtax1, total_localtax2, total_ttc, fk_product_fournisseur_price, buy_price_ht, special_code, rang,';
4319
        $sql .= ' fk_unit,';
4320
        $sql .= ' date_start, date_end';
4321
        $sql .= ', fk_multicurrency, multicurrency_code, multicurrency_subprice, multicurrency_total_ht, multicurrency_total_tva, multicurrency_total_ttc)';
4322
        $sql .= " VALUES (" . $this->fk_propal . ",";
4323
        $sql .= " " . ($this->fk_parent_line > 0 ? "'" . $this->db->escape($this->fk_parent_line) . "'" : "null") . ",";
4324
        $sql .= " " . (!empty($this->label) ? "'" . $this->db->escape($this->label) . "'" : "null") . ",";
4325
        $sql .= " '" . $this->db->escape($this->desc) . "',";
4326
        $sql .= " " . ($this->fk_product ? "'" . $this->db->escape($this->fk_product) . "'" : "null") . ",";
4327
        $sql .= " '" . $this->db->escape($this->product_type) . "',";
4328
        $sql .= " " . ($this->fk_remise_except ? "'" . $this->db->escape($this->fk_remise_except) . "'" : "null") . ",";
4329
        $sql .= " " . price2num($this->qty, 'MS') . ",";
4330
        $sql .= " " . (empty($this->vat_src_code) ? "''" : "'" . $this->db->escape($this->vat_src_code) . "'") . ",";
4331
        $sql .= " " . price2num($this->tva_tx) . ",";
4332
        $sql .= " " . price2num($this->localtax1_tx) . ",";
4333
        $sql .= " " . price2num($this->localtax2_tx) . ",";
4334
        $sql .= " '" . $this->db->escape($this->localtax1_type) . "',";
4335
        $sql .= " '" . $this->db->escape($this->localtax2_type) . "',";
4336
        $sql .= " " . (price2num($this->subprice) !== '' ? price2num($this->subprice, 'MU') : "null") . ",";
4337
        $sql .= " " . price2num($this->remise_percent) . ",";
4338
        $sql .= " " . (isset($this->info_bits) ? ((int) $this->info_bits) : "null") . ",";
4339
        $sql .= " " . price2num($this->total_ht, 'MT') . ",";
4340
        $sql .= " " . price2num($this->total_tva, 'MT') . ",";
4341
        $sql .= " " . price2num($this->total_localtax1, 'MT') . ",";
4342
        $sql .= " " . price2num($this->total_localtax2, 'MT') . ",";
4343
        $sql .= " " . price2num($this->total_ttc, 'MT') . ",";
4344
        $sql .= " " . (!empty($this->fk_fournprice) ? "'" . $this->db->escape($this->fk_fournprice) . "'" : "null") . ",";
4345
        $sql .= " " . (isset($this->pa_ht) ? "'" . price2num($this->pa_ht) . "'" : "null") . ",";
4346
        $sql .= ' ' . ((int) $this->special_code) . ',';
4347
        $sql .= ' ' . ((int) $this->rang) . ',';
4348
        $sql .= ' ' . (empty($this->fk_unit) ? 'NULL' : ((int) $this->fk_unit)) . ',';
4349
        $sql .= " " . (!empty($this->date_start) ? "'" . $this->db->idate($this->date_start) . "'" : "null") . ',';
4350
        $sql .= " " . (!empty($this->date_end) ? "'" . $this->db->idate($this->date_end) . "'" : "null");
4351
        $sql .= ", " . ($this->fk_multicurrency > 0 ? ((int) $this->fk_multicurrency) : 'null');
4352
        $sql .= ", '" . $this->db->escape($this->multicurrency_code) . "'";
4353
        $sql .= ", " . price2num($this->multicurrency_subprice, 'CU');
4354
        $sql .= ", " . price2num($this->multicurrency_total_ht, 'CT');
4355
        $sql .= ", " . price2num($this->multicurrency_total_tva, 'CT');
4356
        $sql .= ", " . price2num($this->multicurrency_total_ttc, 'CT');
4357
        $sql .= ')';
4358
4359
        dol_syslog(get_class($this) . '::insert', LOG_DEBUG);
4360
        $resql = $this->db->query($sql);
4361
        if ($resql) {
4362
            $this->rowid = $this->db->last_insert_id(MAIN_DB_PREFIX . 'propaldet');
0 ignored issues
show
Deprecated Code introduced by
The property 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

4362
            /** @scrutinizer ignore-deprecated */ $this->rowid = $this->db->last_insert_id(MAIN_DB_PREFIX . 'propaldet');

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...
4363
4364
            if (!$error) {
4365
                $this->id = $this->rowid;
0 ignored issues
show
Deprecated Code introduced by
The property 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

4365
                $this->id = /** @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...
4366
                $result = $this->insertExtraFields();
4367
                if ($result < 0) {
4368
                    $error++;
4369
                }
4370
            }
4371
4372
            if (!$error && !$notrigger) {
4373
                // Call trigger
4374
                $result = $this->call_trigger('LINEPROPAL_INSERT', $user);
4375
                if ($result < 0) {
4376
                    $this->db->rollback();
4377
                    return -1;
4378
                }
4379
                // End call triggers
4380
            }
4381
4382
            $this->db->commit();
4383
            return 1;
4384
        } else {
4385
            $this->error = $this->db->error() . " sql=" . $sql;
4386
            $this->db->rollback();
4387
            return -1;
4388
        }
4389
    }
4390
4391
    /**
4392
     *  Delete line in database
4393
     *
4394
     *  @param  User    $user       Object user
4395
     *  @param  int     $notrigger  1=Does not execute triggers, 0= execute triggers
4396
     *  @return  int                Return integer <0 if ko, >0 if ok
4397
     */
4398
    public function delete(User $user, $notrigger = 0)
4399
    {
4400
        global $conf;
4401
4402
        $error = 0;
4403
        $this->db->begin();
4404
4405
        if (!$notrigger) {
4406
            // Call trigger
4407
            $result = $this->call_trigger('LINEPROPAL_DELETE', $user);
4408
            if ($result < 0) {
4409
                $error++;
4410
            }
4411
        }
4412
        // End call triggers
4413
4414
        if (!$error) {
4415
            $sql = "DELETE FROM " . MAIN_DB_PREFIX . "propaldet WHERE rowid = " . ((int) $this->rowid);
0 ignored issues
show
Deprecated Code introduced by
The property 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

4415
            $sql = "DELETE FROM " . MAIN_DB_PREFIX . "propaldet 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...
4416
            dol_syslog("PropaleLigne::delete", LOG_DEBUG);
4417
            if ($this->db->query($sql)) {
4418
                // Remove extrafields
4419
                if (!$error) {
4420
                    $this->id = $this->rowid;
0 ignored issues
show
Deprecated Code introduced by
The property 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

4420
                    $this->id = /** @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...
4421
                    $result = $this->deleteExtraFields();
4422
                    if ($result < 0) {
4423
                        $error++;
4424
                        dol_syslog(get_class($this) . "::delete error -4 " . $this->error, LOG_ERR);
4425
                    }
4426
                }
4427
            } else {
4428
                $this->error = $this->db->error() . " sql=" . $sql;
4429
                $error++;
4430
            }
4431
        }
4432
4433
        if ($error) {
4434
            $this->db->rollback();
4435
            return -1;
4436
        } else {
4437
            $this->db->commit();
4438
            return 1;
4439
        }
4440
    }
4441
4442
    /**
4443
     *  Update propal line object into DB
4444
     *
4445
     *  @param  int     $notrigger  1=Does not execute triggers, 0= execute triggers
4446
     *  @return int                 Return integer <0 if ko, >0 if ok
4447
     */
4448
    public function update($notrigger = 0)
4449
    {
4450
        global $conf, $user;
4451
4452
        $error = 0;
4453
4454
        $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'.
4455
4456
        if (empty($this->id) && !empty($this->rowid)) {
0 ignored issues
show
Deprecated Code introduced by
The property 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

4456
        if (empty($this->id) && !empty(/** @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...
4457
            $this->id = $this->rowid;
0 ignored issues
show
Deprecated Code introduced by
The property 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

4457
            $this->id = /** @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...
4458
        }
4459
4460
        // Clean parameters
4461
        if (empty($this->tva_tx)) {
4462
            $this->tva_tx = 0;
4463
        }
4464
        if (empty($this->localtax1_tx)) {
4465
            $this->localtax1_tx = 0;
4466
        }
4467
        if (empty($this->localtax2_tx)) {
4468
            $this->localtax2_tx = 0;
4469
        }
4470
        if (empty($this->total_localtax1)) {
4471
            $this->total_localtax1 = 0;
4472
        }
4473
        if (empty($this->total_localtax2)) {
4474
            $this->total_localtax2 = 0;
4475
        }
4476
        if (empty($this->localtax1_type)) {
4477
            $this->localtax1_type = 0;
4478
        }
4479
        if (empty($this->localtax2_type)) {
4480
            $this->localtax2_type = 0;
4481
        }
4482
        if (empty($this->marque_tx)) {
4483
            $this->marque_tx = 0;
4484
        }
4485
        if (empty($this->marge_tx)) {
4486
            $this->marge_tx = 0;
4487
        }
4488
        if (empty($this->price)) {
4489
            $this->price = 0; // TODO A virer
4490
        }
4491
        if (empty($this->remise_percent)) {
4492
            $this->remise_percent = 0;
4493
        }
4494
        if (empty($this->info_bits)) {
4495
            $this->info_bits = 0;
4496
        }
4497
        if (empty($this->special_code)) {
4498
            $this->special_code = 0;
4499
        }
4500
        if (empty($this->fk_parent_line)) {
4501
            $this->fk_parent_line = 0;
4502
        }
4503
        if (empty($this->fk_fournprice)) {
4504
            $this->fk_fournprice = 0;
4505
        }
4506
        if (empty($this->subprice)) {
4507
            $this->subprice = 0;
4508
        }
4509
        if (empty($this->pa_ht)) {
4510
            $this->pa_ht = 0;
4511
        }
4512
4513
        // if buy price not defined, define buyprice as configured in margin admin
4514
        if ($this->pa_ht == 0 && $pa_ht_isemptystring) {
4515
            if (($result = $this->defineBuyPrice($this->subprice, $this->remise_percent, $this->fk_product)) < 0) {
4516
                return $result;
4517
            } else {
4518
                $this->pa_ht = $result;
4519
            }
4520
        }
4521
4522
        $this->db->begin();
4523
4524
        // Mise a jour ligne en base
4525
        $sql = "UPDATE " . MAIN_DB_PREFIX . "propaldet SET";
4526
        $sql .= " description='" . $this->db->escape($this->desc) . "'";
4527
        $sql .= ", label=" . (!empty($this->label) ? "'" . $this->db->escape($this->label) . "'" : "null");
4528
        $sql .= ", product_type=" . $this->product_type;
4529
        $sql .= ", vat_src_code = '" . (empty($this->vat_src_code) ? '' : $this->vat_src_code) . "'";
4530
        $sql .= ", tva_tx='" . price2num($this->tva_tx) . "'";
4531
        $sql .= ", localtax1_tx=" . price2num($this->localtax1_tx);
4532
        $sql .= ", localtax2_tx=" . price2num($this->localtax2_tx);
4533
        $sql .= ", localtax1_type='" . $this->db->escape($this->localtax1_type) . "'";
4534
        $sql .= ", localtax2_type='" . $this->db->escape($this->localtax2_type) . "'";
4535
        $sql .= ", qty='" . price2num($this->qty) . "'";
4536
        $sql .= ", subprice=" . price2num($this->subprice);
4537
        $sql .= ", remise_percent=" . price2num($this->remise_percent);
4538
        $sql .= ", price=" . (float) price2num($this->price); // TODO A virer
4539
        $sql .= ", remise=" . (float) price2num($this->remise); // TODO A virer
4540
        $sql .= ", info_bits='" . $this->db->escape($this->info_bits) . "'";
4541
        if (empty($this->skip_update_total)) {
4542
            $sql .= ", total_ht=" . price2num($this->total_ht);
4543
            $sql .= ", total_tva=" . price2num($this->total_tva);
4544
            $sql .= ", total_ttc=" . price2num($this->total_ttc);
4545
            $sql .= ", total_localtax1=" . price2num($this->total_localtax1);
4546
            $sql .= ", total_localtax2=" . price2num($this->total_localtax2);
4547
        }
4548
        $sql .= ", fk_product_fournisseur_price=" . (!empty($this->fk_fournprice) ? "'" . $this->db->escape($this->fk_fournprice) . "'" : "null");
4549
        $sql .= ", buy_price_ht=" . price2num($this->pa_ht);
4550
        $sql .= ", special_code=" . ((int) $this->special_code);
4551
        $sql .= ", fk_parent_line=" . ($this->fk_parent_line > 0 ? (int) $this->fk_parent_line : "null");
4552
        if (!empty($this->rang)) {
4553
            $sql .= ", rang=" . ((int) $this->rang);
4554
        }
4555
        $sql .= ", date_start=" . (!empty($this->date_start) ? "'" . $this->db->idate($this->date_start) . "'" : "null");
4556
        $sql .= ", date_end=" . (!empty($this->date_end) ? "'" . $this->db->idate($this->date_end) . "'" : "null");
4557
        $sql .= ", fk_unit=" . (!$this->fk_unit ? 'NULL' : $this->fk_unit);
4558
4559
        // Multicurrency
4560
        $sql .= ", multicurrency_subprice=" . price2num($this->multicurrency_subprice);
4561
        $sql .= ", multicurrency_total_ht=" . price2num($this->multicurrency_total_ht);
4562
        $sql .= ", multicurrency_total_tva=" . price2num($this->multicurrency_total_tva);
4563
        $sql .= ", multicurrency_total_ttc=" . price2num($this->multicurrency_total_ttc);
4564
4565
        $sql .= " WHERE rowid = " . ((int) $this->id);
4566
4567
        dol_syslog(get_class($this) . "::update", LOG_DEBUG);
4568
        $resql = $this->db->query($sql);
4569
        if ($resql) {
4570
            if (!$error) {
4571
                $result = $this->insertExtraFields();
4572
                if ($result < 0) {
4573
                    $error++;
4574
                }
4575
            }
4576
4577
            if (!$error && !$notrigger) {
4578
                // Call trigger
4579
                $result = $this->call_trigger('LINEPROPAL_MODIFY', $user);
4580
                if ($result < 0) {
4581
                    $this->db->rollback();
4582
                    return -1;
4583
                }
4584
                // End call triggers
4585
            }
4586
4587
            $this->db->commit();
4588
            return 1;
4589
        } else {
4590
            $this->error = $this->db->error();
4591
            $this->db->rollback();
4592
            return -2;
4593
        }
4594
    }
4595
4596
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4597
    /**
4598
     *  Update DB line fields total_xxx
4599
     *  Used by migration
4600
     *
4601
     *  @return     int     Return integer <0 if KO, >0 if OK
4602
     */
4603
    public function update_total()
4604
    {
4605
		// phpcs:enable
4606
        $this->db->begin();
4607
4608
        // Mise a jour ligne en base
4609
        $sql = "UPDATE " . MAIN_DB_PREFIX . "propaldet SET";
4610
        $sql .= " total_ht=" . price2num($this->total_ht, 'MT');
4611
        $sql .= ",total_tva=" . price2num($this->total_tva, 'MT');
4612
        $sql .= ",total_ttc=" . price2num($this->total_ttc, 'MT');
4613
        $sql .= " WHERE rowid = " . ((int) $this->rowid);
0 ignored issues
show
Deprecated Code introduced by
The property 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

4613
        $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...
4614
4615
        dol_syslog("PropaleLigne::update_total", LOG_DEBUG);
4616
4617
        $resql = $this->db->query($sql);
4618
        if ($resql) {
4619
            $this->db->commit();
4620
            return 1;
4621
        } else {
4622
            $this->error = $this->db->error();
4623
            $this->db->rollback();
4624
            return -2;
4625
        }
4626
    }
4627
}
4628